annotate libervia/desktop_kivy/plugins/plugin_wid_calls.py @ 506:0480f883f0a6

plugin calls: update UI: - there is now a "search" UI to select a contact to call - "call" UI is displayed only when we actually are in a call - new control button to (un)mute audio and video - new control button to go to fullscreen/back to normal - add an extra "hang up" button directly in the call UI, so there is always one even in fullscreen mode - UI is similar to the one implemented in web frontend - notification + ringtone + desktop notification on incoming call - if an incoming call is cancelled from initiator, confirmation dialog is removed rel 425
author Goffi <goffi@goffi.org>
date Wed, 25 Oct 2023 15:28:44 +0200
parents f387992d8e37
children f0ce49b360c8
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
2 from pathlib import Path
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
3 import re
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 from typing import Optional, Callable
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
5 from urllib.parse import quote_plus
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 from functools import partial
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
7
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # from gi.repository import GLib
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 from gi.repository import GObject, Gst, GstWebRTC, GstSdp
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
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
19 from kivy.core.audio import Sound, SoundLoader
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
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
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
22 from kivy.properties import (
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
23 BooleanProperty,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
24 ColorProperty,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
25 NumericProperty,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
26 ObjectProperty,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
27 ReferenceListProperty,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
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
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
32 from kivy.uix.screenmanager import Screen
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
33 from kivy.uix.widget import Widget
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
34 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
35 from libervia.backend.core import log as logging
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
36 from libervia.backend.core.i18n import _
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 from libervia.backend.core import exceptions
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 from libervia.backend.tools.common import data_format
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 from libervia.frontends.quick_frontend import quick_widgets
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 from libervia.frontends.tools import jid, aio
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
41
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 from libervia.desktop_kivy import G
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 ..core import cagou_widget
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
45 from ..core import common
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
46 from ..core.behaviors import FilterBehavior
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
47
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 log = logging.getLogger(__name__)
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 install_gobject_iteration()
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 Gst.init(None)
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
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
55 PLUGIN_INFO = {
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 "name": _("calls"),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 "main": "Calls",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 "description": _("Audio/Video calls"),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 "icon_symbol": "phone",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 }
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
61
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 @dataclass
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 class TextureData:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 texture: Optional[Texture] = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 size: Optional[tuple[int, int]] = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
67
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
68
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
69 class SearchScreen(Screen):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
70 pass
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
71
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
72
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
73 class InCallScreen(Screen):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
74 pass
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
75
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
76
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 class CallButton(Button):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
78 parent_widget = ObjectProperty(None)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
79
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
80
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
81 class CallControlButton(common.SymbolButton):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
82 active = BooleanProperty(True)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
83 background_color = ColorProperty()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
84 margin_x = NumericProperty(0)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
85 margin_y = NumericProperty(0)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
86 margin = ReferenceListProperty(margin_x, margin_y)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
87
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
88
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 class VideoStreamWidget(Image):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 pass
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
91
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
92
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 class WebRTC:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 """WebRTC implementation for audio and video communication.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
95
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 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
97 handling audio and video calls.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
98
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 @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
100 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
101 microphone available will be used.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 """
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
103
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 test_mode: bool = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
105
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 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
107 self.parent_calls = parent_calls
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 self.profile = profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 self.pipeline = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
110 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
111
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 def sdp_set(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
114 return self._sdp_set
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
115
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 @sdp_set.setter
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 def sdp_set(self, is_set: bool):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
118 self._sdp_set = is_set
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 if is_set:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 self.on_ice_candidates_new(self.remote_candidates_buffer)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
121 for data in self.remote_candidates_buffer.values():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
122 data["candidates"].clear()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
123
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
125 def media_types(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
126 if self._media_types is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
127 raise Exception("self._media_types should not be None!")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 return self._media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
129
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
130 @media_types.setter
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 def media_types(self, new_media_types: dict) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 self._media_types = new_media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 self._media_types_inv = {v: k for k, v in new_media_types.items()}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
134
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
135 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 def media_types_inv(self) -> dict:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 if self._media_types_inv is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 raise Exception("self._media_types_inv should not be None!")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 return self._media_types_inv
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
140
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 def get_sdp_mline_index(self, media_type: str) -> int:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 """Gets the sdpMLineIndex for a given media type.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
143
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
144 @param media_type: The type of the media.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
146 for index, m_type in self.media_types.items():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
147 if m_type == media_type:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
148 return index
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
149 raise ValueError(f"Media type '{media_type}' not found")
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 _set_media_types(self, offer_sdp: str) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
152 """Sets media types from offer SDP
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
153
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
154 @param offer: RTC session description containing the offer
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
155 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
156 sdp_lines = offer_sdp.splitlines()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
157 media_types = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
158 mline_index = 0
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
159
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
160 for line in sdp_lines:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
161 if line.startswith("m="):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
162 media_types[mline_index] = line[2 : line.find(" ")]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
163 mline_index += 1
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
164
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
165 self.media_types = media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
166
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
167 def _a_call(self, method, *args, **kwargs):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
168 """Call an async method in main thread"""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
169
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
170 def wrapper(__):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
171 aio.run_async(method(*args, **kwargs))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
172 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
173
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
174 Clock.schedule_once(wrapper)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
175
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
176 def get_payload_types(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
177 self, sdpmsg, video_encoding: str, audio_encoding: str
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
178 ) -> dict[str, int | None]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
179 """Find the payload types for the specified video and audio encoding.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
180
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
181 Very simplistically finds the first payload type matching the encoding
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
182 name. More complex applications will want to match caps on
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
183 profile-level-id, packetization-mode, etc.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
184 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
185 # method coming from gstreamer example (Matthew Waters, Nirbheek Chauhan) at
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
186 # subprojects/gst-examples/webrtc/sendrecv/gst/webrtc_sendrecv.py
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
187 video_pt = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
188 audio_pt = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
189 for i in range(0, sdpmsg.medias_len()):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
190 media = sdpmsg.get_media(i)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
191 for j in range(0, media.formats_len()):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
192 fmt = media.get_format(j)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
193 if fmt == "webrtc-datachannel":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
194 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
195 pt = int(fmt)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
196 caps = media.get_caps_from_media(pt)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
197 s = caps.get_structure(0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
198 encoding_name = s["encoding-name"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
199 if video_pt is None and encoding_name == video_encoding:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
200 video_pt = pt
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
201 elif audio_pt is None and encoding_name == audio_encoding:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
202 audio_pt = pt
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
203 return {video_encoding: video_pt, audio_encoding: audio_pt}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
204
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
205 def parse_ice_candidate(self, candidate_string):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
206 """Parses the ice candidate string.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
207
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
208 @param candidate_string: The ice candidate string to be parsed.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
209 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
210 pattern = re.compile(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
211 r"candidate:(?P<foundation>\S+) (?P<component_id>\d+) (?P<transport>\S+) "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
212 r"(?P<priority>\d+) (?P<address>\S+) (?P<port>\d+) typ "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
213 r"(?P<type>\S+)(?: raddr (?P<rel_addr>\S+) rport "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
214 r"(?P<rel_port>\d+))?(?: generation (?P<generation>\d+))?"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
215 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
216 match = pattern.match(candidate_string)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
217 if match:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
218 candidate_dict = match.groupdict()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
219
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
220 # Apply the correct types to the dictionary values
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
221 candidate_dict["component_id"] = int(candidate_dict["component_id"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
222 candidate_dict["priority"] = int(candidate_dict["priority"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
223 candidate_dict["port"] = int(candidate_dict["port"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
224
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
225 if candidate_dict["rel_port"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
226 candidate_dict["rel_port"] = int(candidate_dict["rel_port"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
227
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
228 if candidate_dict["generation"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
229 candidate_dict["generation"] = candidate_dict["generation"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
230
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
231 # Remove None values
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
232 return {k: v for k, v in candidate_dict.items() if v is not None}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
233 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
234 log.warning(f"can't parse candidate: {candidate_string!r}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
235 return None
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 def build_ice_candidate(self, parsed_candidate):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
238 """Builds ICE candidate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
239
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
240 @param parsed_candidate: Dictionary containing parsed ICE candidate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
241 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
242 base_format = (
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
243 "candidate:{foundation} {component_id} {transport} {priority} "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
244 "{address} {port} typ {type}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
245 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
246
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
247 if parsed_candidate.get("rel_addr") and parsed_candidate.get("rel_port"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
248 base_format += " raddr {rel_addr} rport {rel_port}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
249
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
250 if parsed_candidate.get("generation"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
251 base_format += " generation {generation}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
252
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
253 return base_format.format(**parsed_candidate)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
254
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
255 def extract_ufrag_pwd(self, sdp: str) -> tuple[str, str]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
256 """Retrieves ICE password and user fragment for SDP offer.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
257
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
258 @param sdp: The Session Description Protocol offer string.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
259 @return: ufrag and pwd
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
260 @raise ValueError: Can't extract ufrag and password
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
261 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
262 ufrag_line = re.search(r"ice-ufrag:(\S+)", sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
263 pwd_line = re.search(r"ice-pwd:(\S+)", sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
264
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
265 if ufrag_line and pwd_line:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
266 ufrag = self.ufrag = ufrag_line.group(1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
267 pwd = self.pwd = pwd_line.group(1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
268 return ufrag, pwd
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
269 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
270 log.error(f"SDP with missing ice-ufrag or ice-pwd:\n{sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
271 raise ValueError("Can't extract ice-ufrag and ice-pwd from SDP")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
272
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
273 def reset_instance(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
274 """Inits or resets the instance variables to their default state."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
275 self.role: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
276 if self.pipeline is not None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
277 self.pipeline.set_state(Gst.State.NULL)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
278 self.pipeline = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
279 self.texture_map: dict[VideoStreamWidget, TextureData] = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
280 self._remote_video_pad = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
281 self.sid: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
282 self.offer: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
283 self.local_candidates_buffer = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
284 self.ufrag: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
285 self.pwd: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
286 self.callee: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
287 self._media_types = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
288 self._media_types_inv = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
289 self._sdp_set: bool = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
290 self.remote_candidates_buffer: dict[str, dict[str, list]] = {
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
291 "audio": {"candidates": []},
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
292 "video": {"candidates": []},
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
293 }
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
294 self._media_types = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
295 self._media_types_inv = None
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
296 self.audio_valve = None
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
297 self.video_valve = None
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 async def setup_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
300 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
301 role: str,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
302 audio_pt: int | None = 96,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
303 video_pt: int | None = 97,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
304 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
305 """Sets up the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
306
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
307 This method establishes the Gstreamer pipeline for audio and video communication.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
308 The method also manages STUN and TURN server configurations, signal watchers, and
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
309 various connection handlers for the webrtcbin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
310
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
311 @param role: The role for the call, either 'initiator' or 'responder'.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
312 @param audio_pt: The payload type for the audio stream.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
313 @param video_pt: The payload type for the video stream
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
314
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
315 @raises NotImplementedError: If audio_pt or video_pt is set to None.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
316 @raises AssertionError: If the role is not 'initiator' or 'responder'.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
317 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
318 assert role in ("initiator", "responder")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
319 self.role = role
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
320 if audio_pt is None or video_pt is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
321 raise NotImplementedError("None value is not handled yet")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
322
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
323 if self.test_mode:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
324 video_source_elt = "videotestsrc is-live=true pattern=ball"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
325 audio_source_elt = "audiotestsrc"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
326 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
327 video_source_elt = "v4l2src"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
328 audio_source_elt = "pulsesrc"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
329
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
330 self.gst_pipe_desc = f"""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
331 webrtcbin latency=100 name=sendrecv bundle-policy=max-compat
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
332
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
333 input-selector name=video_selector
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
334 ! videorate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
335 ! video/x-raw,framerate=30/1
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
336 ! tee name=t
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
337
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
338 {video_source_elt} name=video_src ! queue ! video_selector.
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
339 videotestsrc is-live=true pattern=black ! queue ! video_selector.
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
340
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
341 t.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
342 ! queue max-size-buffers=5 max-size-time=0 max-size-bytes=0 leaky=downstream
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
343 ! videoconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
344 ! vp8enc deadline=1 keyframe-max-dist=60
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
345 ! rtpvp8pay picture-id-mode=15-bit
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
346 ! application/x-rtp,media=video,encoding-name=VP8,payload={video_pt}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
347 ! sendrecv.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
348
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
349 t.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
350 ! queue max-size-buffers=5 max-size-time=0 max-size-bytes=0
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
351 ! videoconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
352 ! appsink name=local_video_sink emit-signals=true drop=true max-buffers=1 sync=True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
353
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
354 {audio_source_elt} name=audio_src
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
355 ! valve name=audio_valve
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
356 ! queue max-size-buffers=10 max-size-time=0 max-size-bytes=0
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
357 ! audioconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
358 ! audioresample
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
359 ! opusenc audio-type=voice
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
360 ! rtpopuspay
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
361 ! application/x-rtp,media=audio,encoding-name=OPUS,payload={audio_pt}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
362 ! sendrecv.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
363 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
364
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
365 log.debug(f"Gstreamer pipeline: {self.gst_pipe_desc}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
366
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
367 # Create the pipeline
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
368 self.pipeline = Gst.parse_launch(self.gst_pipe_desc)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
369 if not self.pipeline:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
370 raise exceptions.InternalError("Failed to create Gstreamer pipeline.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
371
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
372 self.webrtc = self.pipeline.get_by_name("sendrecv")
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
373 self.video_src = self.pipeline.get_by_name("video_src")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
374 self.video_selector = self.pipeline.get_by_name("video_selector")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
375 self.audio_valve = self.pipeline.get_by_name("audio_valve")
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
376
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
377 if self.parent_calls.video_muted:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
378 self.on_video_mute(True)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
379 if self.parent_calls.audio_muted:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
380 self.on_audio_mute(True)
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
381
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
382 # set STUN and TURN servers
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
383 external_disco = data_format.deserialise(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
384 await G.host.a_bridge.external_disco_get("", self.profile), type_check=list
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
385 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
386
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
387 for server in external_disco:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
388 if server["type"] == "stun":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
389 if server["transport"] == "tcp":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
390 log.info(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
391 "ignoring TCP STUN server, GStreamer only support one STUN server"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
392 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
393 url = f"stun://{server['host']}:{server['port']}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
394 log.debug(f"adding stun server: {url}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
395 self.webrtc.set_property("stun-server", url)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
396 elif server["type"] == "turn":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
397 url = "{scheme}://{username}:{password}@{host}:{port}".format(
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
398 scheme="turns" if server["transport"] == "tcp" else "turn",
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
399 username=quote_plus(server["username"]),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
400 password=quote_plus(server["password"]),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
401 host=server["host"],
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
402 port=server["port"],
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 log.debug(f"adding turn server: {url}")
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 if not self.webrtc.emit("add-turn-server", url):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
407 log.warning(f"Erreur while adding TURN server {url}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
408
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
409 # local video feedback
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
410 local_video_sink = self.pipeline.get_by_name("local_video_sink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
411 local_video_sink.set_property("emit-signals", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
412 local_video_sink.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
413 "new-sample",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
414 self.on_new_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
415 self.update_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
416 self.parent_calls.local_video,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
417 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
418 local_video_sink_caps = Gst.Caps.from_string(f"video/x-raw,format=RGB")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
419 local_video_sink.set_property("caps", local_video_sink_caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
420
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
421 # Create bus and associate signal watchers
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
422 self.bus = self.pipeline.get_bus()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
423 if not self.bus:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
424 log.error("Failed to get bus from pipeline.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
425 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
426
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
427 self.bus.add_signal_watch()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
428 self.webrtc.connect("pad-added", self.on_pad_added)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
429 self.bus.connect("message::error", self.on_bus_error)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
430 self.bus.connect("message::eos", self.on_bus_eos)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
431 self.webrtc.connect("on-negotiation-needed", self.on_negotiation_needed)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
432 self.webrtc.connect("on-ice-candidate", self.on_ice_candidate)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
433 self.webrtc.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
434 "notify::ice-gathering-state", self.on_ice_gathering_state_change
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
435 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
436 self.webrtc.connect("notify::ice-connection-state", self.on_ice_connection_state)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
437
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
438 def start_pipeline(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
439 """Starts the GStreamer pipeline."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
440 log.debug("starting the pipeline")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
441 self.pipeline.set_state(Gst.State.PLAYING)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
442
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
443 def on_negotiation_needed(self, webrtc):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
444 """Initiate SDP offer when negotiation is needed."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
445 log.debug("Negotiation needed.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
446 if self.role == "initiator":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
447 log.debug("Creating offer…")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
448 promise = Gst.Promise.new_with_change_func(self.on_offer_created)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
449 self.webrtc.emit("create-offer", None, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
450
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
451 def on_offer_created(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
452 """Callback for when SDP offer is created."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
453 log.info("on_offer_created called")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
454 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
455 reply = promise.get_reply()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
456 if reply is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
457 log.error("Promise reply is None. Offer creation might have failed.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
458 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
459 offer = reply["offer"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
460 self.offer = offer.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
461 log.info(f"SDP offer created: \n{self.offer}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
462 self._set_media_types(self.offer)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
463 promise = Gst.Promise.new()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
464 self.webrtc.emit("set-local-description", offer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
465 promise.interrupt()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
466 self._a_call(self._start_call)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
467
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
468 def on_answer_set(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
469 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
470
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
471 def on_answer_created(self, promise, _, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
472 """Callback for when SDP answer is created."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
473 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
474 reply = promise.get_reply()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
475 answer = reply["answer"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
476 promise = Gst.Promise.new()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
477 self.webrtc.emit("set-local-description", answer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
478 promise.interrupt()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
479 answer_sdp = answer.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
480 log.info(f"SDP answer set: \n{answer_sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
481 self.sdp_set = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
482 self._a_call(G.host.a_bridge.call_answer_sdp, self.sid, answer_sdp, self.profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
483
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
484 def on_offer_set(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
485 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
486 promise = Gst.Promise.new_with_change_func(self.on_answer_created, None, None)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
487 self.webrtc.emit("create-answer", None, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
488
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
489 def on_new_sample(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
490 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
491 video_sink: Gst.Element,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
492 update_sample_method: Callable,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
493 video_widget: VideoStreamWidget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
494 ) -> bool:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
495 """Handles a new sample from the video sink.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
496
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
497 @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
498 @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
499 @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
500 @return: Always False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
501 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
502 sample = video_sink.emit("pull-sample")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
503 if sample is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
504 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
505
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
506 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
507 texture_data = self.texture_map[video_widget]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
508 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
509 texture_data = self.texture_map[video_widget] = TextureData()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
510
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
511 # Get the current video size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
512 video_pad = video_sink.get_static_pad("sink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
513 assert video_pad is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
514 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
515 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
516
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
517 # Compare with the texture size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
518 texture_size = texture_data.size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
519 if texture_size != stream_size:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
520 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
521 texture_data.size = stream_size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
522 # 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
523 texture_data.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
524
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
525 Clock.schedule_once(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
526 partial(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
527 update_sample_method,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
528 sample=sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
529 video_widget=video_widget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
530 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
531 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
532 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
533
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
534 def update_sample(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
535 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
536 dt: float,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
537 sample: Optional[Gst.Sample],
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
538 video_widget: VideoStreamWidget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
539 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
540 """Updates the video sample.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
541
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
542 This method runs in the main thread.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
543
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
544 @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
545 @param sample: The video sample to update.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
546 @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
547 @param texture: The texture object.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
548 @param texture_size: The texture size.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
549 @param video_widget: The video widget.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
550 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
551 if sample is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
552 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
553 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
554 texture_data = self.texture_map[video_widget]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
555 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
556 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
557 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
558
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
559 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
560 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
561 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
562 assert texture is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
563 texture_data.texture = texture
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
564 texture.flip_vertical()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
565 video_widget.texture = texture
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
566
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
567 mapinfo = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
568 buf = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
569 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
570 buf = sample.get_buffer()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
571 _, mapinfo = buf.map(Gst.MapFlags.READ)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
572
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
573 buffer = mapinfo.data.tobytes()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
574
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
575 if texture_data.texture is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
576 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
577 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
578 texture_data.texture.blit_buffer(buffer, colorfmt="rgb")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
579 assert video_widget.canvas is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
580 video_widget.canvas.ask_update()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
581 buf.unmap(mapinfo)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
582 finally:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
583 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
584 buf.unmap(mapinfo)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
585
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
586 def on_remote_decodebin_stream(self, _, pad: Gst.Pad) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
587 """Handle the stream from the remote decodebin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
588
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
589 This method processes the incoming stream from the remote decodebin, determining
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
590 whether it's video or audio. It then sets up the appropriate GStreamer elements
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
591 for video/audio processing and adds them to the pipeline.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
592
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
593 @param pad: The Gst.Pad from the remote decodebin producing the stream.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
594 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
595 assert self.pipeline is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
596 if not pad.has_current_caps():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
597 log.error(f"{pad} has no caps, ignoring")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
598 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
599
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
600 self.pipeline.set_state(Gst.State.PAUSED)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
601 caps = pad.get_current_caps()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
602 assert len(caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
603 s = caps[0]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
604 name = s.get_name()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
605 log.debug(f"====> NAME START: {name}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
606
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
607 q = Gst.ElementFactory.make("queue")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
608 q.set_property("max-size-time", 0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
609 q.set_property("max-size-bytes", 0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
610 q.set_property("max-size-buffers", 5)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
611
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
612 if name.startswith("video"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
613 log.debug("===> VIDEO OK")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
614
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
615 self._remote_video_pad = pad
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
616
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
617 # Check and log the original size of the video
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
618 width = s.get_int("width").value
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
619 height = s.get_int("height").value
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
620 log.info(f"Original video size: {width}x{height}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
621
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
622 # This is a fix for an issue found with Movim on desktop: a non standard
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
623 # resolution is used (990x557) resulting in bad alignement and no color in
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
624 # rendered image
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
625 adjust_resolution = width % 4 != 0 or height % 4 != 0
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
626 if adjust_resolution:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
627 log.info("non standard resolution, we need to adjust size")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
628 width = (width + 3) // 4 * 4
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
629 height = (height + 3) // 4 * 4
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
630 log.info(f"Adjusted video size: {width}x{height}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
631
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
632 conv = Gst.ElementFactory.make("videoconvert")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
633 remote_video_sink = Gst.ElementFactory.make("appsink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
634
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
635 appsink_caps = Gst.Caps.from_string("video/x-raw,format=RGB")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
636 remote_video_sink.set_property("caps", appsink_caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
637
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
638 remote_video_sink.set_property("emit-signals", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
639 remote_video_sink.set_property("drop", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
640 remote_video_sink.set_property("max-buffers", 1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
641 remote_video_sink.set_property("sync", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
642 remote_video_sink.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
643 "new-sample",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
644 self.on_new_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
645 self.update_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
646 self.parent_calls.remote_video,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
647 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
648
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
649 if adjust_resolution:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
650 videoscale = Gst.ElementFactory.make("videoscale")
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
651 adjusted_caps = Gst.Caps.from_string(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
652 f"video/x-raw,width={width},height={height}"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
653 )
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
654 capsfilter = Gst.ElementFactory.make("capsfilter")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
655 capsfilter.set_property("caps", adjusted_caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
656
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
657 self.pipeline.add(q, conv, videoscale, capsfilter, remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
658 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
659 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
660 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
661 conv.link(videoscale)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
662 videoscale.link(capsfilter)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
663 capsfilter.link(remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
664 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
665 self.pipeline.add(q, conv, remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
666 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
667 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
668 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
669 conv.link(remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
670
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
671 elif name.startswith("audio"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
672 log.debug("===> Audio OK")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
673 conv = Gst.ElementFactory.make("audioconvert")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
674 resample = Gst.ElementFactory.make("audioresample")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
675 remote_audio_sink = Gst.ElementFactory.make("autoaudiosink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
676 self.pipeline.add(q, conv, resample, remote_audio_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
677 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
678 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
679 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
680 conv.link(resample)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
681 resample.link(remote_audio_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
682 self.pipeline.set_state(Gst.State.PLAYING)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
683
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
684 def on_pad_added(self, __, pad: Gst.Pad) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
685 """Handle the addition of a new pad to the element.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
686
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
687 When a new source pad is added to the element, this method creates a decodebin,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
688 connects it to handle the stream, and links the pad to the decodebin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
689
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
690 @param __: Placeholder for the signal source. Not used in this method.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
691 @param pad: The newly added pad.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
692 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
693
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
694 log.debug("on_pad_added")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
695 if pad.direction != Gst.PadDirection.SRC:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
696 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
697
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
698 decodebin = Gst.ElementFactory.make("decodebin")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
699 decodebin.connect("pad-added", self.on_remote_decodebin_stream)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
700 self.pipeline.add(decodebin)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
701 decodebin.sync_state_with_parent()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
702 pad.link(decodebin.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
703
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
704 async def _start_call(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
705 """Initiate the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
706
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
707 Initiates a call with the callee using the stored offer. If there are any buffered
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
708 local ICE candidates, they are sent as part of the initiation.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
709 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
710 assert self.callee is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
711 self.sid = await G.host.a_bridge.call_start(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
712 str(self.callee), data_format.serialise({"sdp": self.offer}), self.profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
713 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
714 if self.local_candidates_buffer:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
715 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
716 f"sending buffered local ICE candidates: {self.local_candidates_buffer}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
717 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
718 if self.pwd is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
719 sdp = self.webrtc.props.local_description.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
720 self.extract_ufrag_pwd(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
721 ice_data = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
722 for media_type, candidates in self.local_candidates_buffer.items():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
723 ice_data[media_type] = {
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
724 "ufrag": self.ufrag,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
725 "pwd": self.pwd,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
726 "candidates": candidates,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
727 }
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
728 await G.host.a_bridge.ice_candidates_add(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
729 self.sid, data_format.serialise(ice_data), self.profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
730 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
731 self.local_candidates_buffer.clear()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
732
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
733 def _remote_sdp_set(self, promise) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
734 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
735 self.sdp_set = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
736
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
737 def on_accepted_call(self, sdp: str, profile: str) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
738 """Outgoing call has been accepted.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
739
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
740 @param sdp: The SDP answer string received from the other party.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
741 @param profile: Profile used for the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
742 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
743 log.debug(f"SDP answer received: \n{sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
744
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
745 __, sdpmsg = GstSdp.SDPMessage.new_from_text(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
746 answer = GstWebRTC.WebRTCSessionDescription.new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
747 GstWebRTC.WebRTCSDPType.ANSWER, sdpmsg
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
748 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
749 promise = Gst.Promise.new_with_change_func(self._remote_sdp_set)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
750 self.webrtc.emit("set-remote-description", answer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
751
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
752 async def answer_call(self, sdp: str, profile: str) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
753 """Answer an incoming call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
754
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
755 @param sdp: The SDP offer string received from the initiator.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
756 @param profile: Profile used for the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
757
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
758 @raise AssertionError: Raised when either "VP8" or "OPUS" is not present in
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
759 payload types.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
760 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
761 log.debug(f"SDP offer received: \n{sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
762 self._set_media_types(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
763 __, offer_sdp_msg = GstSdp.SDPMessage.new_from_text(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
764 payload_types = self.get_payload_types(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
765 offer_sdp_msg, video_encoding="VP8", audio_encoding="OPUS"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
766 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
767 assert "VP8" in payload_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
768 assert "OPUS" in payload_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
769 await self.setup_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
770 "responder", audio_pt=payload_types["OPUS"], video_pt=payload_types["VP8"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
771 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
772 self.start_pipeline()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
773 offer = GstWebRTC.WebRTCSessionDescription.new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
774 GstWebRTC.WebRTCSDPType.OFFER, offer_sdp_msg
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
775 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
776 promise = Gst.Promise.new_with_change_func(self.on_offer_set)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
777 self.webrtc.emit("set-remote-description", offer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
778
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
779 def on_ice_candidate(self, webrtc, mline_index, candidate_sdp):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
780 """Handles the on-ice-candidate signal of webrtcbin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
781
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
782 @param webrtc: The webrtcbin element.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
783 @param mlineindex: The mline index.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
784 @param candidate: The ICE candidate.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
785 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
786 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
787 f"Local ICE candidate. MLine Index: {mline_index}, Candidate: {candidate_sdp}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
788 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
789 parsed_candidate = self.parse_ice_candidate(candidate_sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
790 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
791 media_type = self.media_types[mline_index]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
792 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
793 raise exceptions.InternalError("can't find media type")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
794
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
795 if self.sid is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
796 log.debug("buffering local ICE candidate")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
797 self.local_candidates_buffer.setdefault(media_type, []).append(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
798 parsed_candidate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
799 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
800 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
801 sdp = self.webrtc.props.local_description.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
802 assert sdp is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
803 ufrag, pwd = self.extract_ufrag_pwd(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
804 ice_data = {"ufrag": ufrag, "pwd": pwd, "candidates": [parsed_candidate]}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
805 self._a_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
806 G.host.a_bridge.ice_candidates_add,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
807 self.sid,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
808 data_format.serialise({media_type: ice_data}),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
809 self.profile,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
810 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
811
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
812 def on_ice_candidates_new(self, candidates: dict) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
813 """Handle new ICE candidates.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
814
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
815 @param candidates: A dictionary containing media types ("audio" or "video") as
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
816 keys and corresponding ICE data as values.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
817
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
818 @raise exceptions.InternalError: Raised when sdp mline index is not found.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
819 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
820 if not self.sdp_set:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
821 log.debug("buffering remote ICE candidate")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
822 for media_type in ("audio", "video"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
823 media_candidates = candidates.get(media_type)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
824 if media_candidates:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
825 buffer = self.remote_candidates_buffer[media_type]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
826 buffer["candidates"].extend(media_candidates["candidates"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
827 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
828 for media_type, ice_data in candidates.items():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
829 for candidate in ice_data["candidates"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
830 candidate_sdp = self.build_ice_candidate(candidate)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
831 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
832 mline_index = self.get_sdp_mline_index(media_type)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
833 except Exception as e:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
834 raise exceptions.InternalError(f"Can't find sdp mline index: {e}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
835 self.webrtc.emit("add-ice-candidate", mline_index, candidate_sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
836 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
837 f"Remote ICE candidate added. MLine Index: {mline_index}, "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
838 f"{candidate_sdp}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
839 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
840
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
841 def on_ice_gathering_state_change(self, pspec, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
842 state = self.webrtc.get_property("ice-gathering-state")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
843 log.debug(f"ICE gathering state changed to {state}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
844
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
845 def on_ice_connection_state(self, pspec, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
846 state = self.webrtc.props.ice_connection_state
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
847 if state == GstWebRTC.WebRTCICEConnectionState.FAILED:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
848 log.error("ICE connection failed")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
849 log.info(f"ICE connection state changed to {state}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
850
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
851 def on_bus_error(self, bus: Gst.Bus, message: Gst.Message) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
852 """Handles the GStreamer bus error messages.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
853
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
854 @param bus: The GStreamer bus.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
855 @param message: The error message.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
856 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
857 err, debug = message.parse_error()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
858 log.error(f"Error from {message.src.get_name()}: {err.message}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
859 log.error(f"Debugging info: {debug}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
860
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
861 def on_bus_eos(self, bus: Gst.Bus, message: Gst.Message) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
862 """Handles the GStreamer bus eos messages.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
863
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
864 @param bus: The GStreamer bus.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
865 @param message: The eos message.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
866 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
867 log.info("End of stream")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
868
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
869 def on_audio_mute(self, muted: bool) -> None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
870 if self.audio_valve is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
871 self.audio_valve.set_property("drop", muted)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
872 state = "muted" if muted else "unmuted"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
873 log.info(f"audio is now {state}")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
874
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
875 def on_video_mute(self, muted: bool) -> None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
876 if self.video_selector is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
877 # when muted, we switch to a black image and deactivate the camera
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
878 if not muted:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
879 self.video_src.set_state(Gst.State.PLAYING)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
880 pad = self.video_selector.get_static_pad("sink_1" if muted else "sink_0")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
881 self.video_selector.props.active_pad = pad
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
882 if muted:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
883 self.video_src.set_state(Gst.State.NULL)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
884 state = "muted" if muted else "unmuted"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
885 log.info(f"video is now {state}")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
886
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
887 async def end_call(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
888 """Stop streaming and clean instance"""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
889 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
890
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
891
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
892 class Calls(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
893 quick_widgets.QuickWidget,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
894 cagou_widget.LiberviaDesktopKivyWidget,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
895 FilterBehavior
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
896 ):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
897 audio_muted = BooleanProperty(False)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
898 call_layout = ObjectProperty()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
899 call_screen = ObjectProperty()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
900 fullscreen = BooleanProperty(False)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
901 in_call = BooleanProperty(False)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
902 incoming_call_dialog: dict[str, Widget] = {}
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
903 jid_selector = ObjectProperty()
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
904 local_video = ObjectProperty()
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
905 remote_video = ObjectProperty()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
906 ringtone: Sound | None = None
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
907 screen_manager = ObjectProperty()
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
908 signals_registered = False
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
909 use_header_input = True
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
910 video_muted = BooleanProperty(False)
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
911
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
912 def __init__(self, host, target, profiles):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
913 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
914 cagou_widget.LiberviaDesktopKivyWidget.__init__(self)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
915 call_btn = CallButton(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
916 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
917 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
918 self.header_input_add_extra(call_btn)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
919 self.webrtc = WebRTC(self, self.profile)
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
920 self.previous_fullscreen = None
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
921 self.bind(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
922 audio_muted=lambda __, value: self.webrtc.on_audio_mute(value),
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
923 video_muted=lambda __, value: self.webrtc.on_video_mute(value),
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
924 )
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
925 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
926
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
927 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
928 def sid(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
929 return self.webrtc.sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
930
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
931 def hang_up(self):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
932 if self.sid is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
933 aio.run_async(self.toggle_call())
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
934
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
935 async def toggle_call(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
936 """Toggle between making a call and hanging up.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
937
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
938 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
939 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
940 if self.sid is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
941 # Initiate the call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
942 log.info("initiating call")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
943 callee = jid.JID(self.header_input.text.strip())
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
944 if not callee:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
945 return
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
946 if not callee.is_valid():
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
947 G.host.add_note(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
948 _("Calls"),
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
949 _("Can't make a call: invalid destinee {}").format(repr(callee)),
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
950 level=C.XMLUI_DATA_LVL_ERROR
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
951 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
952 return
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
953
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
954 self.webrtc.callee = callee
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
955 await self.webrtc.setup_call("initiator")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
956 self.webrtc.start_pipeline()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
957 self.in_call = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
958 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
959 # Terminate the call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
960 sid = self.sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
961 await self.end_call({"reason": "terminated"}, self.profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
962 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
963 self.in_call = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
964
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
965 async def on_incoming_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
966 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
967 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
968 """Handle incoming call notifications.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
969
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
970 @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
971 @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
972 @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
973 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
974 if self.sid is not None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
975 # FIXME: show a double remote call notification
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
976 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
977 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
978 sid = action_data["session_id"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
979 self.in_call = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
980 self.webrtc.sid = sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
981 # 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
982 # will be implemented
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
983 log.warning("auto-accepting remote call")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
984 await G.host.a_bridge.action_launch(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
985 action_id, data_format.serialise({"cancelled": False}), profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
986 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
987
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
988 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
989 """Call has been accepted, connection can be established
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
990
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
991 @param session_id: Session identifier
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
992 @param setup_data: Data with following keys:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
993 role: initiator or responser
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
994 sdp: Session Description Protocol data
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
995 @param profile: Profile associated
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
996 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
997 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
998 role = setup_data["role"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
999 sdp = setup_data["sdp"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1000 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1001 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
1002 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1003 if role == "initiator":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1004 self.webrtc.on_accepted_call(sdp, profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1005 elif role == "responder":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1006 await self.webrtc.answer_call(sdp, profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1007 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1008 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
1009
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1010 def reset_instance(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1011 self.in_call = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1012 self.local_video.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1013 self.remote_video.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1014
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1015 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
1016 """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
1017
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1018 @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
1019 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1020 await self.webrtc.end_call()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1021 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1022
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1023 def on_in_call(self, instance, in_call: bool) -> None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1024 if in_call:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1025 self.screen_manager.transition.direction = "up"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1026 self.screen_manager.current = "call"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1027 else:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1028 self.fullscreen = False
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1029 self.screen_manager.transition.direction = "down"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1030 self.screen_manager.current = "search"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1031
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1032 def on_fullscreen(self, instance, fullscreen: bool) -> None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1033 if fullscreen:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1034 G.host.app.show_head_widget(False, animation=False)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1035 self.call_layout.parent.remove_widget(self.call_layout)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1036 G.host.show_extra_ui(self.call_layout)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1037 self.previous_fullscreen = Window.fullscreen
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1038 Window.fullscreen = "auto"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1039 else:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1040 G.host.app.show_head_widget(True, animation=False)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1041 G.host.close_ui()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1042 self.call_screen.add_widget(self.call_layout)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1043 Window.fullscreen = self.previous_fullscreen or False
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1044
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1045 def on_header_wid_input(self):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1046 aio.run_async(self.toggle_call())
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1047
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1048 def on_header_wid_input_complete(self, wid, text, **kwargs):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1049 """we filter items when text is entered in input box"""
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1050 for layout in self.jid_selector.items_layouts:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1051 self.do_filter(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1052 layout,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1053 text,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1054 # we append nick to jid to filter on both
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1055 lambda c: c.jid + c.data.get('nick', ''),
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1056 width_cb=lambda c: c.base_width,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1057 height_cb=lambda c: c.minimum_height,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1058 continue_tests=[lambda c: not isinstance(c, common.ContactButton)])
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1059
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1060 def on_jid_select(self, contact_button):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1061 self.header_input.text = contact_button.jid
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1062 aio.run_async(self.toggle_call())
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1063
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1064 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1065 def ice_candidates_new_handler(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1066 cls, sid: str, candidates_s: str, profile: str
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1067 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1068 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
1069 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
1070 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1071 wid.webrtc.on_ice_candidates_new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1072 data_format.deserialise(candidates_s),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1073 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1074
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1075 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1076 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
1077 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
1078 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
1079 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1080 aio.run_async(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1081 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
1082 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1083
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1084 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1085 def call_ended_handler(cls, sid: str, data_s: str, profile: str) -> None:
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1086 if sid in cls.incoming_call_dialog:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1087 dialog_wid = cls.incoming_call_dialog.pop(sid)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1088 G.host.del_notif_widget(dialog_wid)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1089 G.host.add_note(_("Call cancelled"), _("The call has been cancelled."))
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1090
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1091
499
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1092 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
1093 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
1094 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1095 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
1096
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1097 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1098 def action_new_handler(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
1099 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
1100 ) -> None:
506
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1101 if profile in G.host.profiles:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1102 if cls.ringtone is None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1103 cls.ringtone = SoundLoader.load(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1104 str(Path(G.host.media_dir) / "sounds/notifications/ring_1.mp3")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1105 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1106 if cls.ringtone is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1107 cls.ringtone.play()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1108 peer_jid = jid.JID(action_data["from_jid"]).bare
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1109 sid = action_data["session_id"]
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1110 notif_body = f"{peer_jid} is calling you."
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1111 notif_title = "Incoming call"
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1112 G.host.desktop_notif(notif_body, title=notif_title, duration=10)
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1113
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1114 def on_call_answer(accepted, __):
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1115 del cls.incoming_call_dialog[sid]
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1116 if cls.ringtone is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1117 cls.ringtone.stop()
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1118 if accepted:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1119 wid = G.host.do_action("calls", str(peer_jid), [profile])
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1120 aio.run_async(wid.on_incoming_call(action_data, action_id, profile))
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1121 else:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1122 aio.run_async(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1123 G.host.a_bridge.action_launch(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1124 action_id, data_format.serialise({"cancelled": True}), profile
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1125 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1126 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1127
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1128 dialog_wid = G.host.show_dialog(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1129 notif_body, notif_title, type="yes/no", answer_cb=on_call_answer
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1130 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1131 cls.incoming_call_dialog[sid] = dialog_wid
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1132
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1133
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1134 if G.host is not None:
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1135 log.debug("registering signals")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1136 G.host.register_signal(
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1137 "ice_candidates_new",
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1138 handler=Calls.ice_candidates_new_handler,
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1139 iface="plugin",
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1140 )
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1141 G.host.register_signal("call_setup", handler=Calls.call_setup_handler, iface="plugin")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1142 G.host.register_signal("call_ended", handler=Calls.call_ended_handler, iface="plugin")
0480f883f0a6 plugin calls: update UI:
Goffi <goffi@goffi.org>
parents: 499
diff changeset
1143 G.host.register_action_handler(C.META_TYPE_CALL, Calls.action_new_handler)