annotate libervia/web/pages/calls/_browser/webrtc.py @ 1613:c4407befc52a default tip @

browser (chat): fix attachments URL for messages.
author Goffi <goffi@goffi.org>
date Mon, 17 Jun 2024 17:37:15 +0200
parents 4a9679369856
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 import json
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
2 import re
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
3
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 from bridge import AsyncBridge as Bridge
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
5 from browser import aio, console as log, document, window, DOMNode
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
6 import dialog
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
7 from javascript import JSObject, NULL
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
8 import jid
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
9 from js_modules.cbor_x import CBOR
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 log.warning = log.warn
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 profile = window.profile or ""
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 bridge = Bridge()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
14
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
16 class FileSender:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
17 CHUNK_SIZE = 64 * 1024
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
18
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
19 def __init__(self, session_id, file, data_channel):
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
20 self.session_id = session_id
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
21 self.file = file
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
22 self.data_channel = data_channel
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
23 data_channel.bind("open", self._on_open)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
24 self.offset = 0
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
25
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
26 def _on_open(self, __):
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
27 log.info(f"Data channel open, starting to send {self.file.name}.")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
28 self.send_file()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
29
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
30 def _on_reader_load(self, event):
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
31 self.data_channel.send(event.target.result)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
32 self.offset += self.CHUNK_SIZE
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
33 self.send_file()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
34
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
35 def send_file(self):
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
36 if self.offset < self.file.size:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
37 chunk = self.file.slice(self.offset, self.offset + self.CHUNK_SIZE)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
38 reader = window.FileReader.new()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
39 reader.onload = self._on_reader_load
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
40 reader.readAsArrayBuffer(chunk)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
41 else:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
42 log.info(f"file {self.file.name} sent.")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
43 self.data_channel.close()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
44 if self.session_id is not None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
45 aio.run(bridge.call_end(self.session_id, ""))
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
46
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
47
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
48 class FileReceiver:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
49
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
50 def __init__(self, session_id: str, data_channel, extra_data: dict) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
51 """Initializes the file receiver with a data channel.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
52
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
53 @param data_channel: The RTCDataChannel through which file data is received.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
54 """
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
55 self.session_id = session_id
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
56 self.data_channel = data_channel
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
57 self.received_chunks = []
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
58 self.file_data = extra_data.get("file_data", {})
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
59 data_channel.bind("message", self._on_message)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
60 data_channel.bind("close", self._on_close)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
61 log.debug("File receiver created.")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
62
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
63 def _on_message(self, event) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
64 """Handles incoming message events from the data channel.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
65
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
66 @param event: The event containing the data chunk.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
67 """
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
68 self.received_chunks.append(event.data)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
69
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
70 def _on_close(self, __) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
71 """Handles the data channel's close event.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
72
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
73 Assembles the received chunks into a Blob and triggers a file download.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
74 """
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
75 # The file is complete, we assemble the chunks in a blob
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
76 blob = window.Blob.new(self.received_chunks)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
77 url = window.URL.createObjectURL(blob)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
78
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
79 # and create the <a> element to download the file.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
80 a = document.createElement("a")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
81 a.href = url
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
82 a.download = self.file_data.get("name", "received_file")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
83 document.body.appendChild(a)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
84 a.click()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
85
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
86 # We now clean up.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
87 document.body.removeChild(a)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
88 window.URL.revokeObjectURL(url)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
89 log.info("File received.")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
90 aio.run(bridge.call_end(self.session_id, ""))
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
91
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
92
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
93 class RemoteControler:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
94 """Send input events to controlled device"""
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
95
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
96 def __init__(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
97 self,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
98 session_id: str,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
99 capture_elt: DOMNode,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
100 data_channel: JSObject,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
101 simulate_mouse: bool = True
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
102 ) -> None:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
103 """Initialize a RemoteControler instance.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
104
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
105 @param session_id: ID of the Jingle Session
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
106 @param capture_elt: element where the input events are captured.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
107 @param data_channel: WebRTCDataChannel instance linking to controlled device.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
108 @simulate_mouse: if True, touch event will be converted to mouse events.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
109 """
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
110 self.session_id = session_id
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
111 self.capture_elt = capture_elt
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
112 self.capture_elt.bind("click", self._on_capture_elt_click)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
113 self.data_channel = data_channel
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
114 data_channel.bind("open", self._on_open)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
115 self.simulate_mouse = simulate_mouse
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
116 self.last_mouse_position = (0, 0)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
117
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
118 def _on_capture_elt_click(self, __):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
119 self.capture_elt.focus()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
120
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
121 def _on_open(self, __):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
122 log.info(f"Data channel open, starting to send inputs.")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
123 self.start_capture()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
124
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
125 def start_capture(self) -> None:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
126 """Start capturing input events to send them to the controlled device."""
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
127 for event_name in [
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
128 "mousedown",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
129 "mouseup",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
130 "mousemove",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
131 "keydown",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
132 "keyup",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
133 "touchstart",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
134 "touchend",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
135 "touchmove",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
136 "wheel",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
137 ]:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
138 self.capture_elt.bind(event_name, self._send_event)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
139 self.capture_elt.focus()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
140
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
141 def get_stream_coordinates(self, client_x: float, client_y: float) -> tuple[float, float]:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
142 """Calculate coordinates relative to the actual video stream.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
143
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
144 This method calculates the coordinates relative to the video content inside the <video>
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
145 element, considering any scaling or letterboxing due to aspect ratio differences.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
146
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
147 @param client_x: The clientX value from the event, relative to the viewport.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
148 @param client_y: The clientY value from the event, relative to the viewport.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
149 @return: The x and y coordinates relative to the actual video stream.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
150 """
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
151 video_element = self.capture_elt
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
152 video_rect = video_element.getBoundingClientRect()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
153
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
154 # Calculate offsets relative to the capture element
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
155 element_x = client_x - video_rect.left
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
156 element_y = client_y - video_rect.top
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
157
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
158 element_width, element_height = video_rect.width, video_rect.height
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
159 stream_width, stream_height = video_element.videoWidth, video_element.videoHeight
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
160
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
161 if not all((element_width, element_height, stream_width, stream_height)):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
162 log.warning("Invalid dimensions for video or element, using offsets.")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
163 return element_x, element_y
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
164
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
165 element_aspect_ratio = element_width / element_height
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
166 stream_aspect_ratio = stream_width / stream_height
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
167
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
168 # Calculate scale and offset based on aspect ratio comparison
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
169 if stream_aspect_ratio > element_aspect_ratio:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
170 # Video is more "wide" than the element: letterboxes will be top and bottom
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
171 scale = element_width / stream_width
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
172 scaled_height = stream_height * scale
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
173 offset_x, offset_y = 0, (element_height - scaled_height) / 2
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
174 else:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
175 # Video is more "tall" than the element: letterboxes will be on the sides
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
176 scale = element_height / stream_height
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
177 scaled_width = stream_width * scale
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
178 offset_x, offset_y = (element_width - scaled_width) / 2, 0
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
179
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
180 # Mouse coordinates relative to the video stream
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
181 x = (element_x - offset_x) / scale
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
182 y = (element_y - offset_y) / scale
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
183
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
184 # Ensure the coordinates are within the bounds of the video stream
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
185 x = max(0.0, min(x, stream_width))
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
186 y = max(0.0, min(y, stream_height))
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
187
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
188 return x, y
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
189
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
190 def _send_event(self, event: JSObject) -> None:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
191 """Serialize and send the event to the controlled device through the data channel."""
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
192 event.preventDefault()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
193 serialized_event = self._serialize_event(event)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
194 # TODO: we should join events instead
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
195 self.data_channel.send(CBOR.encode(serialized_event))
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
196
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
197 def _serialize_event(self, event: JSObject) -> dict[str, object]:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
198 """Serialize event data for transmission.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
199
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
200 @param event: an input event.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
201 @return: event data to send to peer.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
202 """
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
203 if event.type.startswith("key"):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
204 ret = {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
205 "type": event.type,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
206 "key": event.key,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
207 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
208 if event.location:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
209 ret["location"] = event.location
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
210 return ret
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
211 elif event.type.startswith("mouse"):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
212 x, y = self.get_stream_coordinates(event.clientX, event.clientY)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
213 return {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
214 "type": event.type,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
215 "buttons": event.buttons,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
216 "x": x,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
217 "y": y,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
218 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
219 elif event.type.startswith("touch"):
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
220 touches = [
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
221 {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
222 "identifier": touch.identifier,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
223 **dict(zip(["x", "y"], self.get_stream_coordinates(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
224 touch.clientX,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
225 touch.clientY
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
226 ))),
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
227 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
228 for touch in event.touches
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
229 ]
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
230 if self.simulate_mouse:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
231 # In simulate mouse mode, we send mouse events.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
232 if touches:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
233 touch_data = touches[0]
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
234 x, y = touch_data["x"], touch_data["y"]
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
235 self.last_mouse_position = (x, y)
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
236 else:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
237 x, y = self.last_mouse_position
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
238
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
239 mouse_event: dict[str, object] = {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
240 "x": x,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
241 "y": y,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
242 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
243 if event.type == "touchstart":
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
244 mouse_event.update({
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
245 "type": "mousedown",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
246 "buttons": 1,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
247 })
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
248 elif event.type == "touchend":
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
249 mouse_event.update({
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
250 "type": "mouseup",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
251 "buttons": 1,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
252 })
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
253 elif event.type == "touchmove":
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
254 mouse_event.update({
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
255 "type": "mousemove",
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
256 })
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
257 return mouse_event
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
258 else:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
259 # Normal mode, with send touch events.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
260 return {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
261 "type": event.type,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
262 "touches": touches
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
263 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
264 elif event.type == "wheel":
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
265 return {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
266 "type": event.type,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
267 "deltaX": event.deltaX,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
268 "deltaY": event.deltaY,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
269 "deltaZ": event.deltaZ,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
270 "deltaMode": event.deltaMode,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
271 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
272 else:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
273 raise Exception(f"Internal Error: unexpected event {event.type=}")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
274
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
275
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
276 class WebRTC:
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
277
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
278 def __init__(
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
279 self,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
280 screen_sharing_cb=None,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
281 on_connection_established_cb=None,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
282 on_reconnect_cb=None,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
283 on_connection_lost_cb=None,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
284 on_video_devices=None,
1565
d282dbdd5ffd browser (calls): restore UI state on reset:
Goffi <goffi@goffi.org>
parents: 1564
diff changeset
285 on_reset_cb=None,
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
286 file_only: bool = False,
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
287 extra_data: dict | None = None,
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
288 local_video_elt = None,
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
289 remote_video_elt = None,
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
290 local_stream = None
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
291 ):
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
292 """Initialise WebRTC instance.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
293
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
294 @param screen_sharing_cb: callable function for screen sharing event
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
295 @param on_connection_established_cb: callable function for connection established
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
296 event
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
297 @param on_reconnect_cb: called when a reconnection is triggered.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
298 @param on_connection_lost_cb: called when the connection is lost.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
299 @param on_video_devices: called when new video devices are set.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
300 @param on_reset_cb: called on instance reset.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
301 @param file_only: indicates a file transfer only session.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
302 @param extra_data: optional dictionary containing additional data.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
303 Notably used for file transfer, where ``file_data`` key is used.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
304 """
1565
d282dbdd5ffd browser (calls): restore UI state on reset:
Goffi <goffi@goffi.org>
parents: 1564
diff changeset
305 # reset
d282dbdd5ffd browser (calls): restore UI state on reset:
Goffi <goffi@goffi.org>
parents: 1564
diff changeset
306 self.on_reset_cb = on_reset_cb
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
307 self.reset_instance()
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
308
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
309 # ICE events
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
310 bridge.register_signal("ice_candidates_new", self._on_ice_candidates_new)
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
311 bridge.register_signal("ice_restart", self._on_ice_restart)
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
312
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
313 # connection events callbacks
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
314 self.on_connection_established_cb = on_connection_established_cb
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
315 self.on_reconnect_cb = on_reconnect_cb
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
316 self.on_connection_lost_cb = on_connection_lost_cb
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
317
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
318 # video devices
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
319 self.on_video_devices = on_video_devices
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
320 self.video_devices = []
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
321 self.has_multiple_cameras = False
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
322 self.current_camera = None
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
323
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
324 self.file_only = file_only
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
325 if not file_only:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
326 # Initially populate the video devices list
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
327 aio.run(self._populate_video_devices())
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
328
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
329 # video elements
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
330 self.local_video_elt = local_video_elt
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
331 assert remote_video_elt is not None
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
332 self.remote_video_elt = remote_video_elt
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
333 else:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
334 self.file_sender = None
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
335
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
336 self.local_stream = local_stream
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
337
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
338 # muting
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
339 self.is_audio_muted = None
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
340 self.is_video_muted = None
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
341
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
342 # screen sharing
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
343 self._is_sharing_screen = False
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
344 self.screen_sharing_cb = screen_sharing_cb
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
345
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
346 # extra
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
347 if extra_data is None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
348 extra_data = {}
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
349 self.extra_data = extra_data
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
350
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
351 @property
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
352 def is_sharing_screen(self) -> bool:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
353 return self._is_sharing_screen
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
354
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
355 @is_sharing_screen.setter
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
356 def is_sharing_screen(self, sharing: bool) -> None:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
357 if sharing != self._is_sharing_screen:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
358 self._is_sharing_screen = sharing
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
359 if self.screen_sharing_cb is not None:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
360 self.screen_sharing_cb(sharing)
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
361
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
362 def reset_instance(self):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
363 """Inits or resets the instance variables to their default state."""
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
364 self._peer_connection = None
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
365 self._media_types = None
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
366 self._media_types_inv = None
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
367 self.ufrag = None
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
368 self.pwd = None
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
369 self.sid = None
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
370 self.local_candidates = None
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
371 self.remote_stream = None
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
372 self.remote_candidates_buffer = {
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
373 "audio": {"candidates": []},
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
374 "video": {"candidates": []},
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
375 "application": {"candidates": []},
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
376 }
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
377 self.local_candidates_buffer = {}
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
378 self.media_candidates = {}
1565
d282dbdd5ffd browser (calls): restore UI state on reset:
Goffi <goffi@goffi.org>
parents: 1564
diff changeset
379 if self.on_reset_cb is not None:
d282dbdd5ffd browser (calls): restore UI state on reset:
Goffi <goffi@goffi.org>
parents: 1564
diff changeset
380 self.on_reset_cb()
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
381
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
382 async def _populate_video_devices(self):
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
383 devices = await window.navigator.mediaDevices.enumerateDevices()
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
384 devices_ids = set()
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
385 self.video_devices.clear()
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
386 for device in devices:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
387 if device.kind != "videoinput":
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
388 continue
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
389 # we can have multiple devices with same IDs in some corner cases (e.g.
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
390 # infrared camera)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
391 device_id = device.deviceId
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
392 if device_id in devices_ids:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
393 continue
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
394 devices_ids.add(device_id)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
395 self.video_devices.append(device)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
396 self.has_multiple_cameras = len(self.video_devices) > 1
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
397 if self.on_video_devices is not None:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
398 self.on_video_devices(self.has_multiple_cameras)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
399 # Set the initial camera to the default (usually front on mobile)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
400 if self.video_devices:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
401 self.current_camera = self.video_devices[0].deviceId
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
402 log.debug(
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
403 f"devices populated: {self.video_devices=} {self.has_multiple_cameras=}"
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
404 )
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
405
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
406 @property
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
407 def media_types(self):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
408 if self._media_types is None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
409 raise Exception("self._media_types should not be None!")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
410 return self._media_types
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
411
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
412 @media_types.setter
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
413 def media_types(self, new_media_types: dict) -> None:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
414 self._media_types = new_media_types
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
415 self._media_types_inv = {v: k for k, v in new_media_types.items()}
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
416
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
417 @property
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
418 def media_types_inv(self) -> dict:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
419 if self._media_types_inv is None:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
420 raise Exception("self._media_types_inv should not be None!")
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
421 return self._media_types_inv
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
422
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
423 def get_sdp_mline_index(self, media_type):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
424 """Gets the sdpMLineIndex for a given media type.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
425
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
426 @param media_type: The type of the media.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
427 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
428 for index, m_type in self.media_types.items():
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
429 if m_type == media_type:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
430 return index
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
431 raise ValueError(f"Media type '{media_type}' not found")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
432
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
433 def extract_ufrag_pwd(self, sdp: str) -> tuple[str, str]:
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
434 """Retrieves ICE password and user fragment for SDP offer.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
435
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
436 @param sdp: The Session Description Protocol offer string.
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
437 @return: ufrag and pwd
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
438 @raise ValueError: Can't extract ufrag and password
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
439 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
440 ufrag_line = re.search(r"ice-ufrag:(\S+)", sdp)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
441 pwd_line = re.search(r"ice-pwd:(\S+)", sdp)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
442
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
443 if ufrag_line and pwd_line:
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
444 ufrag = self.ufrag = ufrag_line.group(1)
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
445 pwd = self.pwd = pwd_line.group(1)
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
446 return ufrag, pwd
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
447 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
448 log.error(f"SDP with missing ice-ufrag or ice-pwd:\n{sdp}")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
449 raise ValueError("Can't extract ice-ufrag and ice-pwd from SDP")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
450
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
451 def extract_fingerprint_data(self, sdp):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
452 """Retrieves fingerprint data from an SDP offer.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
453
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
454 @param sdp: The Session Description Protocol offer string.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
455 @return: A dictionary containing the fingerprint data.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
456 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
457 fingerprint_line = re.search(r"a=fingerprint:(\S+)\s+(\S+)", sdp)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
458 if fingerprint_line:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
459 algorithm, fingerprint = fingerprint_line.groups()
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
460 fingerprint_data = {"hash": algorithm, "fingerprint": fingerprint}
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
461
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
462 setup_line = re.search(r"a=setup:(\S+)", sdp)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
463 if setup_line:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
464 setup = setup_line.group(1)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
465 fingerprint_data["setup"] = setup
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
466
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
467 return fingerprint_data
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
468 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
469 raise ValueError("fingerprint should not be missing")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
470
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
471 def parse_ice_candidate(self, candidate_string):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
472 """Parses the ice candidate string.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
473
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
474 @param candidate_string: The ice candidate string to be parsed.
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
475 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
476 pattern = re.compile(
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
477 r"candidate:(?P<foundation>\S+) (?P<component_id>\d+) (?P<transport>\S+) "
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
478 r"(?P<priority>\d+) (?P<address>\S+) (?P<port>\d+) typ "
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
479 r"(?P<type>\S+)(?: raddr (?P<rel_addr>\S+) rport "
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
480 r"(?P<rel_port>\d+))?(?: generation (?P<generation>\d+))?"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
481 )
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
482 match = pattern.match(candidate_string)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
483 if match:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
484 candidate_dict = match.groupdict()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
485
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
486 # Apply the correct types to the dictionary values
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
487 candidate_dict["component_id"] = int(candidate_dict["component_id"])
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
488 candidate_dict["priority"] = int(candidate_dict["priority"])
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
489 candidate_dict["port"] = int(candidate_dict["port"])
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
490
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
491 if candidate_dict["rel_port"]:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
492 candidate_dict["rel_port"] = int(candidate_dict["rel_port"])
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
493
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
494 if candidate_dict["generation"]:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
495 candidate_dict["generation"] = candidate_dict["generation"]
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
496
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
497 # Remove None values
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
498 return {k: v for k, v in candidate_dict.items() if v is not None}
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
499 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
500 log.warning(f"can't parse candidate: {candidate_string!r}")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
501 return None
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
502
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
503 def build_ice_candidate(self, parsed_candidate):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
504 """Builds ICE candidate
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
505
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
506 @param parsed_candidate: Dictionary containing parsed ICE candidate
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
507 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
508 base_format = (
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
509 "candidate:{foundation} {component_id} {transport} {priority} "
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
510 "{address} {port} typ {type}"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
511 )
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
512
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
513 if parsed_candidate.get("rel_addr") and parsed_candidate.get("rel_port"):
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
514 base_format += " raddr {rel_addr} rport {rel_port}"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
515
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
516 if parsed_candidate.get("generation"):
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
517 base_format += " generation {generation}"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
518
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
519 return base_format.format(**parsed_candidate)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
520
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
521 def on_ice_candidate(self, event):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
522 """Handles ICE candidate event
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
523
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
524 @param event: Event containing the ICE candidate
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
525 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
526 log.debug(f"on ice candidate {event.candidate=}")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
527 if event.candidate and event.candidate.candidate:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
528 parsed_candidate = self.parse_ice_candidate(event.candidate.candidate)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
529 if parsed_candidate is None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
530 return
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
531 try:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
532 media_type = self.media_types[event.candidate.sdpMLineIndex]
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
533 except (TypeError, IndexError):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
534 log.error(
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
535 f"Can't find media type.\n{event.candidate=}\n{self._media_types=}"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
536 )
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
537 return
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
538 self.media_candidates.setdefault(media_type, []).append(parsed_candidate)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
539 log.debug(f"ICE candidate [{media_type}]: {event.candidate.candidate}")
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
540 if self.sid is None:
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
541 log.debug("buffering candidate")
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
542 self.local_candidates_buffer.setdefault(media_type, []).append(
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
543 parsed_candidate
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
544 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
545 else:
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
546 ufrag, pwd = self.extract_ufrag_pwd(
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
547 self._peer_connection.localDescription.sdp
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
548 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
549
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
550 ice_data = {"ufrag": ufrag, "pwd": pwd, "candidates": [parsed_candidate]}
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
551 aio.run(
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
552 bridge.ice_candidates_add(
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
553 self.sid, json.dumps({media_type: ice_data})
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
554 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
555 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
556
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
557 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
558 log.debug("All ICE candidates gathered")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
559
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
560 def on_ice_connection_state_change(self, event):
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
561 """Log ICE connection change, mainly used for debugging"""
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
562 state = self._peer_connection.iceConnectionState
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
563 log.info(f"ICE Connection State changed to: {state}")
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
564
1561
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
565 if state == "connected":
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
566 if self.on_connection_established_cb is not None:
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
567 self.on_connection_established_cb()
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
568 elif state == "failed":
1563
e3449beac8d8 browser (calls): Add clear search + formatting
Goffi <goffi@goffi.org>
parents: 1561
diff changeset
569 log.error(
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
570 "ICE connection failed. Check network connectivity and ICE"
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
571 " configurations."
1563
e3449beac8d8 browser (calls): Add clear search + formatting
Goffi <goffi@goffi.org>
parents: 1561
diff changeset
572 )
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
573 elif state == "disconnected":
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
574 log.warning("ICE connection was disconnected.")
1561
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
575 if self.on_connection_lost_cb is not None:
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
576 self.on_connection_lost_cb()
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
577
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
578 def on_ice_candidate_error(self, event):
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
579 """Log ICE error, useful for debugging"""
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
580 log.error(f"ICE Candidate Error: {event.errorText} (Code: {event.errorCode})")
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
581 log.debug(
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
582 f"URL: {event.url}, Host candidate: {event.hostCandidate}, Port: {event.port}"
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
583 )
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
584
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
585 def _set_media_types(self, offer):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
586 """Sets media types from offer SDP
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
587
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
588 @param offer: RTC session description containing the offer
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
589 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
590 sdp_lines = offer.sdp.splitlines()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
591 media_types = {}
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
592 mline_index = 0
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
593
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
594 for line in sdp_lines:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
595 if line.startswith("m="):
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
596 media_types[mline_index] = line[2 : line.find(" ")]
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
597 mline_index += 1
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
598
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
599 self.media_types = media_types
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
600
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
601 def on_ice_gathering_state_change(self, event):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
602 """Handles ICE gathering state change
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
603
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
604 @param event: Event containing the ICE gathering state change
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
605 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
606 connection = event.target
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
607 log.debug(f"on_ice_gathering_state_change {connection.iceGatheringState=}")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
608 if connection.iceGatheringState == "complete":
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
609 log.info("ICE candidates gathering done")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
610
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
611 async def _create_peer_connection(
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
612 self,
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
613 ) -> JSObject:
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
614 """Creates peer connection"""
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
615 if self._peer_connection is not None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
616 raise Exception("create_peer_connection can't be called twice!")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
617
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
618 external_disco = json.loads(await bridge.external_disco_get(""))
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
619 ice_servers = []
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
620
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
621 for server in external_disco:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
622 ice_server = {}
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
623 if server["type"] == "stun":
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
624 ice_server["urls"] = f"stun:{server['host']}:{server['port']}"
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
625 elif server["type"] == "turn":
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
626 ice_server["urls"] = (
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
627 f"turn:{server['host']}:{server['port']}?transport={server['transport']}"
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
628 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
629 ice_server["username"] = server["username"]
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
630 ice_server["credential"] = server["password"]
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
631 ice_servers.append(ice_server)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
632
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
633 rtc_configuration = {"iceServers": ice_servers}
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
634
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
635 peer_connection = window.RTCPeerConnection.new(rtc_configuration)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
636 peer_connection.addEventListener("track", self.on_track)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
637 peer_connection.addEventListener("negotiationneeded", self.on_negotiation_needed)
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
638 peer_connection.addEventListener(
1563
e3449beac8d8 browser (calls): Add clear search + formatting
Goffi <goffi@goffi.org>
parents: 1561
diff changeset
639 "iceconnectionstatechange", self.on_ice_connection_state_change
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
640 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
641 peer_connection.addEventListener("icecandidate", self.on_ice_candidate)
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
642 peer_connection.addEventListener("icecandidateerror", self.on_ice_candidate_error)
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
643 peer_connection.addEventListener(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
644 "icegatheringstatechange", self.on_ice_gathering_state_change
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
645 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
646
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
647 self._peer_connection = peer_connection
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
648 window.pc = self._peer_connection
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
649 return peer_connection
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
650
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
651 async def _get_user_media(self, audio: bool = True, video: bool = True) -> None:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
652 """
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
653 Gets user media (camera and microphone).
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
654
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
655 @param audio: True if an audio flux is required.
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
656 @param video: True if a video flux is required.
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
657 """
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
658 media_constraints = {"audio": audio, "video": video}
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
659 if self.local_stream is None:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
660 self.local_stream = await window.navigator.mediaDevices.getUserMedia(
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
661 media_constraints
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
662 )
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
663
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
664 if not self.local_stream:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
665 log.error("Failed to get the media stream.")
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
666 return
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
667
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
668 if self.local_video_elt is not None:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
669 self.local_video_elt.srcObject = self.local_stream
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
670
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
671 for track in self.local_stream.getTracks():
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
672 self._peer_connection.addTrack(track)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
673
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
674 async def _replace_user_video(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
675 self,
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
676 screen: bool = False,
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
677 ) -> JSObject | None:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
678 """Replaces the user video track with either a camera or desktop sharing track.
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
679
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
680 @param screen: True if desktop sharing is required. False will use the camera.
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
681 @return: The local media stream or None if failed.
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
682 """
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
683 if screen:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
684 media_constraints = {"video": {"cursor": "always"}}
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
685 new_stream = await window.navigator.mediaDevices.getDisplayMedia(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
686 media_constraints
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
687 )
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
688 else:
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
689 if self.local_video_elt is not None and self.local_video_elt.srcObject:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
690 for track in self.local_video_elt.srcObject.getTracks():
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
691 if track.kind == "video":
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
692 track.stop()
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
693
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
694 media_constraints = {
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
695 "video": (
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
696 {"deviceId": self.current_camera} if self.current_camera else True
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
697 )
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
698 }
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
699
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
700 new_stream = await window.navigator.mediaDevices.getUserMedia(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
701 media_constraints
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
702 )
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
703
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
704 if not new_stream:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
705 log.error("Failed to get the media stream.")
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
706 return None
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
707
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
708 new_video_tracks = [
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
709 track for track in new_stream.getTracks() if track.kind == "video"
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
710 ]
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
711
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
712 if not new_video_tracks:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
713 log.error("Failed to retrieve the video track from the new stream.")
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
714 return None
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
715
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
716 # Retrieve the current local stream's video track.
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
717 if self.local_video_elt is None:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
718 local_stream = None
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
719 else:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
720 local_stream = self.local_video_elt.srcObject
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
721 if local_stream:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
722 local_video_tracks = [
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
723 track for track in local_stream.getTracks() if track.kind == "video"
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
724 ]
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
725 if local_video_tracks:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
726 # Remove the old video track and add the new one to the local stream.
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
727 local_stream.removeTrack(local_video_tracks[0])
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
728 local_stream.addTrack(new_video_tracks[0])
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
729
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
730 video_sender = next(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
731 (
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
732 sender
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
733 for sender in self._peer_connection.getSenders()
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
734 if sender.track and sender.track.kind == "video"
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
735 ),
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
736 None,
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
737 )
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
738 if video_sender:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
739 await video_sender.replaceTrack(new_video_tracks[0])
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
740
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
741 if screen:
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
742 # For screen sharing, we track the end event to properly stop the sharing
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
743 # when the user clicks on the browser's stop sharing dialog.
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
744 def on_track_ended(event):
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
745 aio.run(self.toggle_screen_sharing())
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
746
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
747 new_video_tracks[0].bind("ended", on_track_ended)
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
748
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
749 self.is_sharing_screen = screen
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
750
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
751 return local_stream
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
752
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
753 async def switch_camera(self) -> None:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
754 """Switches to the next available camera.
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
755
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
756 This method cycles through the list of available video devices, replaces the
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
757 current video track with the next one in the user's local video element, and then
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
758 updates the sender's track in the peer connection. If there's only one camera or
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
759 if an error occurs while switching, the method logs the error and does nothing.
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
760 """
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
761 log.info("switching camera")
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
762 if not self.has_multiple_cameras:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
763 log.error("No multiple cameras to switch.")
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
764 return
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
765
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
766 current_camera_index = -1
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
767 for i, device_info in enumerate(self.video_devices):
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
768 if device_info.deviceId == self.current_camera:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
769 current_camera_index = i
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
770 break
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
771
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
772 if current_camera_index == -1:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
773 log.error("Current camera not found in available devices.")
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
774 return
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
775
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
776 # Switch to the next camera in the list
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
777 next_camera_index = (current_camera_index + 1) % len(self.video_devices)
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
778 self.current_camera = self.video_devices[next_camera_index].deviceId
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
779 log.debug(f"{next_camera_index=} {self.current_camera=}")
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
780
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
781 new_stream = await window.navigator.mediaDevices.getUserMedia(
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
782 {"video": {"deviceId": self.current_camera}}
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
783 )
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
784
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
785 new_video_tracks = [
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
786 track for track in new_stream.getTracks() if track.kind == "video"
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
787 ]
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
788
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
789 if not new_video_tracks:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
790 log.error("Failed to retrieve the video track from the new stream.")
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
791 return
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
792
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
793 # Update local video element's stream
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
794 if self.local_video_elt is None:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
795 local_stream = None
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
796 else:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
797 local_stream = self.local_video_elt.srcObject
1564
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
798 if local_stream:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
799 local_video_tracks = [
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
800 track for track in local_stream.getTracks() if track.kind == "video"
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
801 ]
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
802 if local_video_tracks:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
803 local_video_tracks[0].stop()
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
804 local_stream.removeTrack(local_video_tracks[0])
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
805 local_stream.addTrack(new_video_tracks[0])
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
806 self.local_video_elt.srcObject = local_stream
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
807
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
808 # update remove video stream
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
809 video_sender = next(
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
810 (
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
811 sender
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
812 for sender in self._peer_connection.getSenders()
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
813 if sender.track and sender.track.kind == "video"
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
814 ),
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
815 None,
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
816 )
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
817
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
818 if video_sender:
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
819 await video_sender.replaceTrack(new_video_tracks[0])
bd3c880f4a47 browser (calls): add camera switching:
Goffi <goffi@goffi.org>
parents: 1563
diff changeset
820
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
821 async def _gather_ice_candidates(self, is_initiator: bool, remote_candidates=None):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
822 """Get ICE candidates and wait to have them all before returning them
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
823
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
824 @param is_initiator: Boolean indicating if the user is the initiator of the connection
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
825 @param remote_candidates: Remote ICE candidates, if any
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
826 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
827 if self._peer_connection is None:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
828 raise Exception(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
829 "The peer connection must be created before gathering ICE candidates!"
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
830 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
831
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
832 self.media_candidates.clear()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
833
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
834 if is_initiator:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
835 offer = await self._peer_connection.createOffer()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
836 self._set_media_types(offer)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
837 await self._peer_connection.setLocalDescription(offer)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
838 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
839 answer = await self._peer_connection.createAnswer()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
840 self._set_media_types(answer)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
841 await self._peer_connection.setLocalDescription(answer)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
842
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
843 if not is_initiator:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
844 log.debug(self._peer_connection.localDescription.sdp)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
845 log.debug(self._peer_connection.localDescription.sdp)
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
846 ufrag, pwd = self.extract_ufrag_pwd(self._peer_connection.localDescription.sdp)
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
847 return {
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
848 "ufrag": ufrag,
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
849 "pwd": pwd,
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
850 "candidates": self.media_candidates,
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
851 }
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
852
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
853 async def accept_call(self, session_id: str, sdp: str, profile: str) -> None:
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
854 """Call has been accepted, connection can be established
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
855
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
856 @param session_id: Session identifier
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
857 @param sdp: Session Description Protocol data
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
858 @param profile: Profile associated
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
859 """
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
860 await self._peer_connection.setRemoteDescription({"type": "answer", "sdp": sdp})
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
861 await self.on_ice_candidates_new(self.remote_candidates_buffer)
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
862 self.remote_candidates_buffer.clear()
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
863
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
864 def _on_ice_candidates_new(self, sid: str, candidates_s: str, profile: str) -> None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
865 """Called when new ICE candidates are received
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
866
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
867 @param sid: Session identifier
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
868 @param candidates_s: ICE candidates serialized
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
869 @param profile: Profile associated with the action
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
870 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
871 if sid != self.sid:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
872 log.debug(f"ignoring peer ice candidates for {sid=} ({self.sid=}).")
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
873 return
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
874 candidates = json.loads(candidates_s)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
875 aio.run(self.on_ice_candidates_new(candidates))
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
876
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
877 def _on_ice_restart(self, sid: str, side: str, profile: str):
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
878 if sid != self.sid:
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
879 log.debug(f"ignoring peer ice candidates for {sid=} ({self.sid=}).")
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
880 return
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
881 log.debug("ICE has been restarted")
1561
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
882 if self.on_reconnect_cb is not None:
7dbb131bbb9e browser (calls): update status on various events (connection established, connection lost, etc.)
Goffi <goffi@goffi.org>
parents: 1559
diff changeset
883 self.on_reconnect_cb()
1559
410064b31dca browser (calls): add some logs useful for debugging
Goffi <goffi@goffi.org>
parents: 1553
diff changeset
884
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
885 async def on_ice_candidates_new(self, candidates: dict) -> None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
886 """Called when new ICE canidates are received from peer
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
887
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
888 @param candidates: Dictionary containing new ICE candidates
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
889 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
890 log.debug(f"new peer candidates received: {candidates}")
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
891
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
892 # try:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
893 # # FIXME: javascript.NULL must be used here, once we move to Brython 3.12.3+
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
894 # remoteDescription_is_none = self._peer_connection.remoteDescription is None
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
895 # except Exception as e:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
896 # # FIXME: should be fine in Brython 3.12.3+
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
897 # log.debug("Workaround for Brython bug activated.")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
898 # remoteDescription_is_none = True
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
899
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
900 if (
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
901 self._peer_connection is None
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
902 or self._peer_connection.remoteDescription is NULL
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
903 ):
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
904 for media_type in ("audio", "video", "application"):
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
905 media_candidates = candidates.get(media_type)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
906 if media_candidates:
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
907 buffer = self.remote_candidates_buffer[media_type]
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
908 buffer["candidates"].extend(media_candidates["candidates"])
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
909 return
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
910 for media_type, ice_data in candidates.items():
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
911 for candidate in ice_data["candidates"]:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
912 candidate_sdp = self.build_ice_candidate(candidate)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
913 try:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
914 sdp_mline_index = self.get_sdp_mline_index(media_type)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
915 except Exception as e:
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
916 log.warning(f"Can't get sdp_mline_index: {e}")
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
917 continue
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
918 else:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
919 ice_candidate = window.RTCIceCandidate.new(
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
920 {"candidate": candidate_sdp, "sdpMLineIndex": sdp_mline_index}
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
921 )
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
922 await self._peer_connection.addIceCandidate(ice_candidate)
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
923
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
924 def on_track(self, event):
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
925 """New track has been received from peer
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
926
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
927 @param event: Event associated with the new track
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
928 """
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
929 if event.streams and event.streams[0]:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
930 remote_stream = event.streams[0]
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
931 self.remote_video_elt.srcObject = remote_stream
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
932 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
933 if self.remote_stream is None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
934 self.remote_stream = window.MediaStream.new()
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
935 self.remote_video_elt.srcObject = self.remote_stream
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
936 self.remote_stream.addTrack(event.track)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
937
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
938 def on_negotiation_needed(self, event) -> None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
939 log.debug(f"on_negotiation_needed {event=}")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
940 # TODO
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
941
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
942 def _on_data_channel(self, event) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
943 """Handles the data channel event from the peer connection.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
944
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
945 @param event: The event associated with the opening of a data channel.
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
946 """
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
947 data_channel = event.channel
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
948 self.file_receiver = FileReceiver(self.sid, data_channel, self.extra_data)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
949
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
950 async def answer_call(self, sid: str, offer_sdp: str, profile: str):
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
951 """We respond to the call"""
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
952 log.debug("answering call")
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
953 if sid != self.sid:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
954 raise Exception(f"Internal Error: unexpected sid: {sid=} {self.sid=}")
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
955 await self._create_peer_connection()
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
956
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
957 await self._peer_connection.setRemoteDescription(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
958 {"type": "offer", "sdp": offer_sdp}
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
959 )
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
960 await self.on_ice_candidates_new(self.remote_candidates_buffer)
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
961 self.remote_candidates_buffer.clear()
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
962 if self.file_only:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
963 self._peer_connection.bind("datachannel", self._on_data_channel)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
964 else:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
965 await self._get_user_media()
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
966
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
967 # Gather local ICE candidates
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
968 local_ice_data = await self._gather_ice_candidates(False)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
969 self.local_candidates = local_ice_data["candidates"]
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
970
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
971 await bridge.call_answer_sdp(sid, self._peer_connection.localDescription.sdp)
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
972
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
973 async def _get_call_data(self) -> dict:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
974 """Start a WebRTC call"""
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
975 await self._gather_ice_candidates(True)
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
976
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
977 return {"sdp": self._peer_connection.localDescription.sdp}
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
978
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
979 async def _send_buffered_local_candidates(self) -> None:
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
980 if self.local_candidates_buffer:
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
981 log.debug(
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
982 f"sending buffered local ICE candidates: {self.local_candidates_buffer}"
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
983 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
984 assert self.pwd is not None
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
985 ice_data = {}
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
986 for media_type, candidates in self.local_candidates_buffer.items():
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
987 ice_data[media_type] = {
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
988 "ufrag": self.ufrag,
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
989 "pwd": self.pwd,
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
990 "candidates": candidates,
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
991 }
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
992 await bridge.ice_candidates_add(
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
993 self.sid,
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
994 json.dumps(ice_data),
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
995 )
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
996 self.local_candidates_buffer.clear()
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
997
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
998 async def prepare_call(
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
999 self,
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1000 audio: bool = True,
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1001 video: bool = True
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1002 ) -> dict:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1003 """Prepare a call.
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1004
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1005 Create RTCPeerConnection instance, and get use media.
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1006
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1007 @param audio: True if an audio flux is required
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1008 @param video: True if a video flux is required
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1009 @return: Call Data
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1010 """
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1011 await self._create_peer_connection()
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1012 await self._get_user_media(audio, video)
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1013 return await self._get_call_data()
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1014
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1015 async def make_call(
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1016 self, callee_jid: jid.JID, audio: bool = True, video: bool = True
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1017 ) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1018 """
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1019 @param audio: True if an audio flux is required
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1020 @param video: True if a video flux is required
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1021 """
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1022 call_data = await self.prepare_call(audio, video)
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1023 log.info(f"calling {callee_jid!r}")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1024 self.sid = await bridge.call_start(str(callee_jid), json.dumps(call_data))
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1025 log.debug(f"Call SID: {self.sid}")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1026 await self._send_buffered_local_candidates()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1027
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1028 async def start_remote_control(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1029 self, callee_jid: jid.JID, audio: bool = True, video: bool = True
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1030 ) -> None:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1031 """Starts a Remote Control session.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1032
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1033 If both audio and video are False, no screenshare will be done, the input will be
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1034 sent without feedback.
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1035 @param audio: True if an audio flux is required
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1036 @param video: True if a video flux is required
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1037 """
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1038 if audio or not video:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1039 raise NotImplementedError("Only video screenshare is supported for now.")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1040 peer_connection = await self._create_peer_connection()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1041 if video:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1042 peer_connection.addTransceiver("video", {"direction": "recvonly"})
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1043 data_channel = peer_connection.createDataChannel("remote-control")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1044
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1045 call_data = await self._get_call_data()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1046
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1047 try:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1048 remote_control_data = json.loads(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1049 await bridge.remote_control_start(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1050 str(callee_jid),
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1051 json.dumps(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1052 {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1053 "devices": {
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1054 "keyboard": {},
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1055 "mouse": {},
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1056 "wheel": {}
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1057 },
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1058 "call_data": call_data,
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1059 }
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1060 ),
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1061 )
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1062 )
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1063 except Exception as e:
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1064 dialog.notification.show(f"Can't start remote control: {e}", level="error")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1065 return
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1066
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1067 self.sid = remote_control_data["session_id"]
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1068
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1069 log.debug(f"Remote Control SID: {self.sid}")
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1070 await self._send_buffered_local_candidates()
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1071 self.remote_controller = RemoteControler(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1072 self.sid, self.remote_video_elt, data_channel
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1073 )
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1074
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1075 def _on_opened_data_channel(self, event):
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1076 log.info("Datachannel has been opened.")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1077
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1078 async def send_file(self, callee_jid: jid.JID, file: JSObject) -> None:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1079 assert self.file_only
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1080 peer_connection = await self._create_peer_connection()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1081 data_channel = peer_connection.createDataChannel("file")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1082 call_data = await self._get_call_data()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1083 log.info(f"sending file to {callee_jid!r}")
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1084 file_meta = {"size": file.size}
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1085 if file.type:
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1086 file_meta["media_type"] = file.type
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1087
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1088 try:
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1089 file_data = json.loads(
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1090 await bridge.file_jingle_send(
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1091 str(callee_jid),
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1092 "",
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1093 file.name,
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1094 "",
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1095 json.dumps({"webrtc": True, "call_data": call_data, **file_meta}),
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1096 )
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1097 )
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1098 except Exception as e:
1602
6feac4a25e60 browser: Remote Control implementation:
Goffi <goffi@goffi.org>
parents: 1600
diff changeset
1099 dialog.notification.show(f"Can't send file: {e}", level="error")
1600
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1100 return
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1101
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1102 self.sid = file_data["session_id"]
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1103
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1104 log.debug(f"File Transfer SID: {self.sid}")
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1105 await self._send_buffered_local_candidates()
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1106 self.file_sender = FileSender(self.sid, file, data_channel)
0a4433a343a3 browser (calls): implement WebRTC file sharing:
Goffi <goffi@goffi.org>
parents: 1577
diff changeset
1107
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1108 async def end_call(self) -> None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1109 """Stop streaming and clean instance"""
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1110 if self._peer_connection is None:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1111 log.debug("There is currently no call to end.")
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1112 else:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1113 self._peer_connection.removeEventListener("track", self.on_track)
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1114 self._peer_connection.removeEventListener(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1115 "negotiationneeded", self.on_negotiation_needed
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1116 )
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1117 self._peer_connection.removeEventListener(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1118 "icecandidate", self.on_ice_candidate
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1119 )
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1120 self._peer_connection.removeEventListener(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1121 "icegatheringstatechange", self.on_ice_gathering_state_change
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1122 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1123
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1124 # Base64 encoded 1x1 black pixel image
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1125 # this is a trick to reset the image displayed, so we don't see last image of
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1126 # last stream
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1127 black_image_data = (
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1128 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0"
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1129 "lEQVR42mP8/wcAAwAB/uzNq7sAAAAASUVORK5CYII="
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1130 )
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1131
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1132 remote_video = self.remote_video_elt
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1133 local_video = self.local_video_elt
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1134 if local_video is not None:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1135 if local_video.srcObject:
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1136 for track in local_video.srcObject.getTracks():
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1137 track.stop()
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1138 local_video.src = black_image_data
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1139
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1140 if remote_video.srcObject:
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1141 for track in remote_video.srcObject.getTracks():
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1142 track.stop()
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1143 remote_video.src = black_image_data
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1144
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1145 self._peer_connection.close()
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1146 self.reset_instance()
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1147
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1148 def toggle_media_mute(self, media_type: str) -> bool:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1149 """Toggle mute/unmute for media tracks.
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1150
1566
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
1151 @param media_type: "audio" or "video". Determines which media tracks
e65d2ef1ded4 browser (calls/webrtc): send ICE candidates when received:
Goffi <goffi@goffi.org>
parents: 1565
diff changeset
1152 to process.
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1153 """
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1154 assert media_type in ("audio", "video"), "Invalid media type"
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1155
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1156 local_video = self.local_video_elt
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1157 is_muted_attr = f"is_{media_type}_muted"
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1158
1604
4a9679369856 browser (call): implements group calls:
Goffi <goffi@goffi.org>
parents: 1602
diff changeset
1159 if local_video is not None and local_video.srcObject:
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1160 track_getter = getattr(
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1161 local_video.srcObject, f"get{media_type.capitalize()}Tracks"
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1162 )
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1163 for track in track_getter():
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1164 track.enabled = not track.enabled
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1165 setattr(self, is_muted_attr, not track.enabled)
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1166
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1167 media_name = self.media_types_inv.get(media_type)
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1168 if media_name is not None:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1169 extra = {"name": str(media_name)}
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1170 aio.run(
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1171 bridge.call_info(
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1172 self.sid,
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1173 "mute" if getattr(self, is_muted_attr) else "unmute",
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1174 json.dumps(extra),
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1175 )
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1176 )
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1177
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1178 return getattr(self, is_muted_attr)
1517
b8ed9726525b browser: "calls" implementation, first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1179
1549
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1180 def toggle_audio_mute(self) -> bool:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1181 """Toggle mute/unmute for audio tracks."""
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1182 return self.toggle_media_mute("audio")
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1183
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1184 def toggle_video_mute(self) -> bool:
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1185 """Toggle mute/unmute for video tracks."""
e47c24204449 browser (calls): update call to handle search, control buttons, and better UI/UX:
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
1186 return self.toggle_media_mute("video")
1553
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1187
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1188 async def toggle_screen_sharing(self):
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1189 log.debug(f"toggle_screen_sharing {self._is_sharing_screen=}")
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1190
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1191 if self._is_sharing_screen:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1192 await self._replace_user_video(screen=False)
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1193 else:
83c2a6faa2ae browser (calls): screen sharing implementation:
Goffi <goffi@goffi.org>
parents: 1549
diff changeset
1194 await self._replace_user_video(screen=True)