Mercurial > libervia-desktop-kivy
annotate libervia/desktop_kivy/plugins/plugin_wid_calls.py @ 514:d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
- Add a new "file" icon in call UI to send a file via WebRTC.
- Handle new preflight mechanism, and WebRTC file transfer.
- Native file chooser handling has been moved to new `core.file_chooser` module, and now
supports "save" and "dir" modes (based on `plyer`).
rel 442
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 06 Apr 2024 13:37:27 +0200 |
parents | 644a8d165e5a |
children | 11851162dd4c |
rev | line source |
---|---|
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
1 from dataclasses import dataclass |
506 | 2 from pathlib import Path |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
3 from typing import Optional, Callable |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
4 from functools import partial |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
5 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
6 # from gi.repository import GLib |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
7 from gi.repository import GObject, Gst, GstWebRTC, GstSdp |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
8 from kivy.metrics import dp |
514
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
9 from libervia.desktop_kivy.core.file_chooser import FileChooser |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
10 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
11 try: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
12 from gi.overrides import Gst as _ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
13 except ImportError: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
14 print( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
15 "no GStreamer python overrides available, please install relevant pacakges on " |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
16 "your system." |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
17 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
18 from kivy.clock import Clock |
506 | 19 from kivy.core.audio import Sound, SoundLoader |
20 from kivy.core.window import Window | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
21 from kivy.graphics.texture import Texture |
506 | 22 from kivy.properties import ( |
23 BooleanProperty, | |
24 ColorProperty, | |
25 NumericProperty, | |
26 ObjectProperty, | |
27 ReferenceListProperty, | |
28 ) | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
29 from kivy.support import install_gobject_iteration |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
30 from kivy.uix.button import Button |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
31 from kivy.uix.image import Image |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
32 from kivy.uix.popup import Popup |
506 | 33 from kivy.uix.screenmanager import Screen |
34 from kivy.uix.widget import Widget | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
35 from libervia.backend.core.constants import Const as C |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
36 from libervia.backend.core import log as logging |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
37 from libervia.backend.core.i18n import _ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
38 from libervia.backend.core import exceptions |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
39 from libervia.backend.tools.common import data_format |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
40 from libervia.frontends.quick_frontend import quick_widgets |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
41 from libervia.frontends.tools import aio, display_servers, jid, webrtc |
514
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
42 from libervia.frontends.tools.webrtc_file import WebRTCFileSender |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
43 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
44 from libervia.desktop_kivy import G |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
45 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
46 from ..core import cagou_widget |
506 | 47 from ..core import common |
48 from ..core.behaviors import FilterBehavior | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
49 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
50 log = logging.getLogger(__name__) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
51 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
52 install_gobject_iteration() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
53 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
54 Gst.init(None) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
55 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
56 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
57 PLUGIN_INFO = { |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
58 "name": _("calls"), |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
59 "main": "Calls", |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
60 "description": _("Audio/Video calls"), |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
61 "icon_symbol": "phone", |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
62 } |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
63 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
64 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
65 @dataclass |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
66 class TextureData: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
67 texture: Optional[Texture] = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
68 size: Optional[tuple[int, int]] = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
69 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
70 |
506 | 71 class SearchScreen(Screen): |
72 pass | |
73 | |
74 | |
75 class InCallScreen(Screen): | |
76 pass | |
77 | |
78 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
79 class CallButton(Button): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
80 parent_widget = ObjectProperty(None) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
81 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
82 |
506 | 83 class CallControlButton(common.SymbolButton): |
84 active = BooleanProperty(True) | |
85 background_color = ColorProperty() | |
86 margin_x = NumericProperty(0) | |
87 margin_y = NumericProperty(0) | |
88 margin = ReferenceListProperty(margin_x, margin_y) | |
89 | |
90 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
91 class VideoStreamWidget(Image): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
92 pass |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
93 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
94 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
95 class WebRTC: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
96 """WebRTC implementation for audio and video communication. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
97 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
98 This class encapsulates the WebRTC functionalities required for initiating and |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
99 handling audio and video calls. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
100 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
101 @attribute test_mode: A flag to indicate whether the WebRTC instance is in test mode. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
102 If true, test video and audio sources will be used. Otherwise first webcam and |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
103 microphone available will be used. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
104 """ |
506 | 105 |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
106 test_mode: bool = False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
107 |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
108 PROXIED_PROPERTIES = { |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
109 'audio_muted', 'callee', 'desktop_sharing', 'sid', 'video_muted', 'desktop_sharing_data' |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
110 } |
512
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
111 PROXIED_METHODS = {'answer_call', 'bind', 'end_call', 'on_accepted_call', 'on_ice_candidates_new', 'setup_call', 'start_pipeline'} |
509 | 112 |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
113 def __init__(self, parent_calls: "Calls", profile: str) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
114 self.parent_calls = parent_calls |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
115 self.profile = profile |
509 | 116 self.webrtc = webrtc.WebRTC( |
117 G.host.a_bridge, | |
118 profile, | |
119 sinks=webrtc.SINKS_TEST if self.test_mode else webrtc.SINKS_APP, | |
120 appsink_data=webrtc.AppSinkData( | |
121 local_video_cb=partial( | |
122 self.on_new_sample, | |
123 update_sample_method=self.update_sample, | |
124 video_widget=self.parent_calls.local_video | |
125 ), | |
126 remote_video_cb=partial( | |
127 self.on_new_sample, | |
128 update_sample_method=self.update_sample, | |
129 video_widget=self.parent_calls.remote_video | |
130 ) | |
131 ), | |
132 reset_cb=self.on_reset | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
133 |
509 | 134 ) |
135 self.pipeline = None | |
506 | 136 |
509 | 137 def __getattr__(self, name): |
138 if name in self.PROXIED_PROPERTIES or name in self.PROXIED_METHODS: | |
139 return getattr(self.webrtc, name) | |
140 raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
141 |
509 | 142 def __setattr__(self, name, value): |
143 if name in self.PROXIED_PROPERTIES: | |
144 setattr(self.webrtc, name, value) | |
145 else: | |
146 super().__setattr__(name, value) | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
147 |
509 | 148 def on_reset(self): |
149 self.texture_map: dict[VideoStreamWidget, TextureData] = {} | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
150 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
151 def on_new_sample( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
152 self, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
153 video_sink: Gst.Element, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
154 update_sample_method: Callable, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
155 video_widget: VideoStreamWidget, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
156 ) -> bool: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
157 """Handles a new sample from the video sink. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
158 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
159 @param video_sink: The video sink to pull samples from. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
160 @param update_sample_method: The method to call for updating the sample. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
161 @param video_widget: The video widget (either local_video or remote_video). |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
162 @return: Always False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
163 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
164 sample = video_sink.emit("pull-sample") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
165 if sample is None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
166 return False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
167 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
168 try: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
169 texture_data = self.texture_map[video_widget] |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
170 except KeyError: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
171 texture_data = self.texture_map[video_widget] = TextureData() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
172 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
173 # Get the current video size |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
174 video_pad = video_sink.get_static_pad("sink") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
175 assert video_pad is not None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
176 s = video_pad.get_current_caps().get_structure(0) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
177 stream_size = (s.get_value("width"), s.get_value("height")) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
178 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
179 # Compare with the texture size |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
180 texture_size = texture_data.size |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
181 if texture_size != stream_size: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
182 log.debug(f"sample size update: {texture_size} => {stream_size}") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
183 texture_data.size = stream_size |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
184 # texture needs to be recreated, so we reset the one in texture_data |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
185 texture_data.texture = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
186 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
187 Clock.schedule_once( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
188 partial( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
189 update_sample_method, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
190 sample=sample, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
191 video_widget=video_widget, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
192 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
193 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
194 return False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
195 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
196 def update_sample( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
197 self, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
198 dt: float, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
199 sample: Optional[Gst.Sample], |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
200 video_widget: VideoStreamWidget, |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
201 ) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
202 """Updates the video sample. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
203 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
204 This method runs in the main thread. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
205 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
206 @param dt: Time delta since the last call. This is passed by Clock.schedule_once. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
207 @param sample: The video sample to update. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
208 @param texture_id: identifier of the texture data (e.g. "local" or "remote") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
209 @param texture: The texture object. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
210 @param texture_size: The texture size. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
211 @param video_widget: The video widget. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
212 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
213 if sample is None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
214 return |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
215 try: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
216 texture_data = self.texture_map[video_widget] |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
217 except KeyError: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
218 log.warning(f"no texture data found for {video_widget}") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
219 return |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
220 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
221 if texture_data.texture is None and texture_data.size is not None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
222 log.debug(f"===> creating texture for {video_widget}: {texture_data.size=}") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
223 texture = Texture.create(size=texture_data.size, colorfmt="rgb") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
224 assert texture is not None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
225 texture_data.texture = texture |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
226 texture.flip_vertical() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
227 video_widget.texture = texture |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
228 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
229 mapinfo = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
230 buf = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
231 try: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
232 buf = sample.get_buffer() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
233 _, mapinfo = buf.map(Gst.MapFlags.READ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
234 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
235 buffer = mapinfo.data.tobytes() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
236 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
237 if texture_data.texture is None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
238 log.debug("can't copy the buffer, texture is None") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
239 return |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
240 texture_data.texture.blit_buffer(buffer, colorfmt="rgb") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
241 assert video_widget.canvas is not None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
242 video_widget.canvas.ask_update() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
243 buf.unmap(mapinfo) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
244 finally: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
245 if buf is not None and mapinfo is not None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
246 buf.unmap(mapinfo) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
247 |
512
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
248 |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
249 class WindowSelectButton(Button): |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
250 pass |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
251 |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
252 |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
253 class DesktopScreenDialog(Popup): |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
254 windows_buttons = ObjectProperty() |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
255 |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
256 def __init__(self, calls: "Calls", **kwargs): |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
257 super().__init__(**kwargs) |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
258 self.calls = calls |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
259 if display_servers.detect() == display_servers.X11: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
260 windows_data = display_servers.x11_list_windows() |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
261 for window_data in windows_data: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
262 log.debug(f"- X11 windows found: {window_data}") |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
263 button = WindowSelectButton( |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
264 text = window_data["title"], |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
265 on_press=partial(self.on_window_selected, xid=window_data["id"]) |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
266 ) |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
267 self.windows_buttons.add_widget(button) |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
268 |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
269 def on_window_selected(self, instance, xid: str) -> None: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
270 if xid is not None: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
271 self.calls.webrtc.desktop_sharing_data = {"xid": xid} |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
272 else: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
273 self.calls.webrtc.desktop_sharing_data = None |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
274 self.calls.desktop_sharing = True |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
275 self.dismiss() |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
276 |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
277 |
506 | 278 class Calls( |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
279 quick_widgets.QuickWidget, |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
280 cagou_widget.LiberviaDesktopKivyWidget, |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
281 FilterBehavior |
506 | 282 ): |
283 audio_muted = BooleanProperty(False) | |
284 call_layout = ObjectProperty() | |
285 call_screen = ObjectProperty() | |
286 fullscreen = BooleanProperty(False) | |
287 in_call = BooleanProperty(False) | |
288 incoming_call_dialog: dict[str, Widget] = {} | |
289 jid_selector = ObjectProperty() | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
290 local_video = ObjectProperty() |
506 | 291 remote_video = ObjectProperty() |
292 ringtone: Sound | None = None | |
293 screen_manager = ObjectProperty() | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
294 signals_registered = False |
506 | 295 use_header_input = True |
296 video_muted = BooleanProperty(False) | |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
297 desktop_sharing = BooleanProperty(False) |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
298 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
299 def __init__(self, host, target, profiles): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
300 quick_widgets.QuickWidget.__init__(self, G.host, target, profiles) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
301 cagou_widget.LiberviaDesktopKivyWidget.__init__(self) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
302 call_btn = CallButton( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
303 parent_widget=self, on_press=lambda *__: aio.run_async(self.toggle_call()) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
304 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
305 self.header_input_add_extra(call_btn) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
306 self.webrtc = WebRTC(self, self.profile) |
512
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
307 self.webrtc.bind( |
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
308 desktop_sharing=partial(setattr, self, "desktop_sharing") |
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
309 ) |
506 | 310 self.previous_fullscreen = None |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
311 self.reset_instance() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
312 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
313 @property |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
314 def sid(self): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
315 return self.webrtc.sid |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
316 |
506 | 317 def hang_up(self): |
318 if self.sid is not None: | |
319 aio.run_async(self.toggle_call()) | |
320 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
321 async def toggle_call(self): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
322 """Toggle between making a call and hanging up. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
323 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
324 This function will initiate or terminate a call based on the call state. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
325 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
326 if self.sid is None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
327 # Initiate the call |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
328 log.info("initiating call") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
329 callee = jid.JID(self.header_input.text.strip()) |
506 | 330 if not callee: |
331 return | |
332 if not callee.is_valid(): | |
333 G.host.add_note( | |
334 _("Calls"), | |
335 _("Can't make a call: invalid destinee {}").format(repr(callee)), | |
336 level=C.XMLUI_DATA_LVL_ERROR | |
337 ) | |
338 return | |
339 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
340 self.webrtc.callee = callee |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
341 await self.webrtc.setup_call("initiator") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
342 self.webrtc.start_pipeline() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
343 self.in_call = True |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
344 else: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
345 # Terminate the call |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
346 sid = self.sid |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
347 await self.end_call({"reason": "terminated"}, self.profile) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
348 await G.host.a_bridge.call_end(sid, "", self.profile) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
349 self.in_call = False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
350 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
351 async def on_incoming_call( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
352 self, action_data: dict, action_id: str, profile: str |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
353 ) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
354 """Handle incoming call notifications. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
355 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
356 @param action_data: A dictionary containing data related to the incoming call |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
357 @param action_id: The ID corresponding to the incoming call action. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
358 @param profile: The profile associated with the incoming call. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
359 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
360 if self.sid is not None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
361 # FIXME: show a double remote call notification |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
362 log.warning("Ignoring new remote call as we are already in a call.") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
363 return |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
364 sid = action_data["session_id"] |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
365 self.in_call = True |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
366 self.webrtc.sid = sid |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
367 # FIXME: we accept all remote calls for now, will be changed when proper UI/UX |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
368 # will be implemented |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
369 log.warning("auto-accepting remote call") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
370 await G.host.a_bridge.action_launch( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
371 action_id, data_format.serialise({"cancelled": False}), profile |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
372 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
373 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
374 async def on_call_setup(self, setup_data: dict, profile: str) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
375 """Call has been accepted, connection can be established |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
376 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
377 @param session_id: Session identifier |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
378 @param setup_data: Data with following keys: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
379 role: initiator or responser |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
380 sdp: Session Description Protocol data |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
381 @param profile: Profile associated |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
382 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
383 try: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
384 role = setup_data["role"] |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
385 sdp = setup_data["sdp"] |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
386 except KeyError: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
387 log.error(f"Invalid setup data received: {setup_data}") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
388 return |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
389 if role == "initiator": |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
390 self.webrtc.on_accepted_call(sdp, profile) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
391 elif role == "responder": |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
392 await self.webrtc.answer_call(sdp, profile) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
393 else: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
394 log.error(f"Invalid role received during setup: {setup_data}") |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
395 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
396 def reset_instance(self) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
397 self.in_call = False |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
398 self.local_video.texture = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
399 self.remote_video.texture = None |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
400 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
401 async def end_call(self, data: dict, profile: str) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
402 """End current call if any and reset the instance |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
403 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
404 @param data: end call data, often includes the reason of the call ending. |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
405 """ |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
406 await self.webrtc.end_call() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
407 self.reset_instance() |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
408 |
506 | 409 def on_in_call(self, instance, in_call: bool) -> None: |
410 if in_call: | |
411 self.screen_manager.transition.direction = "up" | |
412 self.screen_manager.current = "call" | |
413 else: | |
414 self.fullscreen = False | |
415 self.screen_manager.transition.direction = "down" | |
416 self.screen_manager.current = "search" | |
417 | |
509 | 418 def on_audio_muted(self, instance, muted: bool) -> None: |
419 self.webrtc.audio_muted = muted | |
420 | |
421 def on_video_muted(self, instance, muted: bool) -> None: | |
422 self.webrtc.video_muted = muted | |
423 | |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
424 def on_desktop_btn_press(self) -> None: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
425 if self.desktop_sharing: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
426 self.desktop_sharing = False |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
427 else: |
512
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
428 if display_servers.detect() == display_servers.X11: |
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
429 DesktopScreenDialog(self).open() |
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
430 else: |
644a8d165e5a
plugin calls: use the new binding feature of webrtc module:
Goffi <goffi@goffi.org>
parents:
510
diff
changeset
|
431 self.desktop_sharing = True |
510
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
432 |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
433 def on_desktop_sharing(self, instance, active: bool) -> None: |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
434 self.webrtc.desktop_sharing = active |
97ab236e8f20
plugin calls: implement desktop sharing:
Goffi <goffi@goffi.org>
parents:
509
diff
changeset
|
435 |
514
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
436 def on_file_btn_press(self) -> None: |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
437 aio.run_async(self.on_file_press()) |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
438 |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
439 async def on_file_press(self): |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
440 callee = self.webrtc.callee |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
441 if callee is None: |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
442 return |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
443 file_to_send = await FileChooser.a_open() |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
444 if file_to_send is None: |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
445 return |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
446 file_sender = WebRTCFileSender( |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
447 G.host.a_bridge, |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
448 self.profile |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
449 ) |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
450 await file_sender.send_file_webrtc(file_to_send, self.webrtc.callee) |
d78728d7fd6a
plugin wid calls, core: implements WebRTC DataChannel file transfer:
Goffi <goffi@goffi.org>
parents:
512
diff
changeset
|
451 |
506 | 452 def on_fullscreen(self, instance, fullscreen: bool) -> None: |
453 if fullscreen: | |
454 G.host.app.show_head_widget(False, animation=False) | |
455 self.call_layout.parent.remove_widget(self.call_layout) | |
456 G.host.show_extra_ui(self.call_layout) | |
457 self.previous_fullscreen = Window.fullscreen | |
458 Window.fullscreen = "auto" | |
459 else: | |
460 G.host.app.show_head_widget(True, animation=False) | |
461 G.host.close_ui() | |
462 self.call_screen.add_widget(self.call_layout) | |
463 Window.fullscreen = self.previous_fullscreen or False | |
464 | |
465 def on_header_wid_input(self): | |
466 aio.run_async(self.toggle_call()) | |
467 | |
468 def on_header_wid_input_complete(self, wid, text, **kwargs): | |
469 """we filter items when text is entered in input box""" | |
470 for layout in self.jid_selector.items_layouts: | |
471 self.do_filter( | |
472 layout, | |
473 text, | |
474 # we append nick to jid to filter on both | |
475 lambda c: c.jid + c.data.get('nick', ''), | |
476 width_cb=lambda c: c.base_width, | |
477 height_cb=lambda c: c.minimum_height, | |
478 continue_tests=[lambda c: not isinstance(c, common.ContactButton)]) | |
479 | |
480 def on_jid_select(self, contact_button): | |
481 self.header_input.text = contact_button.jid | |
482 aio.run_async(self.toggle_call()) | |
483 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
484 @classmethod |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
485 def ice_candidates_new_handler( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
486 cls, sid: str, candidates_s: str, profile: str |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
487 ) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
488 for wid in G.host.get_visible_list(cls): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
489 if profile not in wid.profiles or sid != wid.sid: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
490 continue |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
491 wid.webrtc.on_ice_candidates_new( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
492 data_format.deserialise(candidates_s), |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
493 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
494 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
495 @classmethod |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
496 def call_setup_handler(cls, sid: str, setup_data_s: str, profile: str) -> None: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
497 for wid in G.host.get_visible_list(cls): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
498 if profile not in wid.profiles or sid != wid.sid: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
499 continue |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
500 aio.run_async( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
501 wid.on_call_setup(data_format.deserialise(setup_data_s), profile) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
502 ) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
503 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
504 @classmethod |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
505 def call_ended_handler(cls, sid: str, data_s: str, profile: str) -> None: |
506 | 506 if sid in cls.incoming_call_dialog: |
507 dialog_wid = cls.incoming_call_dialog.pop(sid) | |
508 G.host.del_notif_widget(dialog_wid) | |
509 G.host.add_note(_("Call cancelled"), _("The call has been cancelled.")) | |
510 | |
511 | |
499
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
512 for wid in G.host.get_visible_list(cls): |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
513 if profile not in wid.profiles or sid != wid.sid: |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
514 continue |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
515 aio.run_async(wid.end_call(data_format.deserialise(data_s), profile)) |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
516 |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
517 @classmethod |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
518 def action_new_handler( |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
519 cls, action_data: dict, action_id: str, security_limit: int, profile: str |
f387992d8e37
plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff
changeset
|
520 ) -> None: |
506 | 521 if profile in G.host.profiles: |
522 if cls.ringtone is None: | |
523 cls.ringtone = SoundLoader.load( | |
524 str(Path(G.host.media_dir) / "sounds/notifications/ring_1.mp3") | |
525 ) | |
526 if cls.ringtone is not None: | |
527 cls.ringtone.play() | |
528 peer_jid = jid.JID(action_data["from_jid"]).bare | |
529 sid = action_data["session_id"] | |
530 notif_body = f"{peer_jid} is calling you." | |
531 notif_title = "Incoming call" | |
532 G.host.desktop_notif(notif_body, title=notif_title, duration=10) | |
533 | |
534 def on_call_answer(accepted, __): | |
535 del cls.incoming_call_dialog[sid] | |
536 if cls.ringtone is not None: | |
537 cls.ringtone.stop() | |
538 if accepted: | |
539 wid = G.host.do_action("calls", str(peer_jid), [profile]) | |
540 aio.run_async(wid.on_incoming_call(action_data, action_id, profile)) | |
541 else: | |
542 aio.run_async( | |
543 G.host.a_bridge.action_launch( | |
544 action_id, data_format.serialise({"cancelled": True}), profile | |
545 ) | |
546 ) | |
547 | |
548 dialog_wid = G.host.show_dialog( | |
549 notif_body, notif_title, type="yes/no", answer_cb=on_call_answer | |
550 ) | |
551 cls.incoming_call_dialog[sid] = dialog_wid | |
552 | |
553 | |
554 if G.host is not None: | |
555 log.debug("registering signals") | |
556 G.host.register_signal( | |
557 "ice_candidates_new", | |
558 handler=Calls.ice_candidates_new_handler, | |
559 iface="plugin", | |
560 ) | |
561 G.host.register_signal("call_setup", handler=Calls.call_setup_handler, iface="plugin") | |
562 G.host.register_signal("call_ended", handler=Calls.call_ended_handler, iface="plugin") | |
563 G.host.register_action_handler(C.META_TYPE_CALL, Calls.action_new_handler) |