annotate libervia/desktop_kivy/plugins/plugin_wid_calls.py @ 499:f387992d8e37

plugins: new "call" plugin for A/V calls: this is the base implementation for calls plugin, handling one2one calls. For now, the interface is very basic, call is done by specifying the bare jid of the destinee, then press the "call" button. Incoming calls are automatically accepted. rel 424
author Goffi <goffi@goffi.org>
date Wed, 04 Oct 2023 22:54:36 +0200
parents
children 0480f883f0a6
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
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
2 import re
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
3 from typing import Optional, Callable
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 from urllib.parse import quote_plus
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
5 from functools import partial
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
6
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # from gi.repository import GLib
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 from gi.repository import GObject, Gst, GstWebRTC, GstSdp
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
9
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
10 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 from gi.overrides import Gst as _
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 except ImportError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 print(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 "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
15 "your system."
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 from kivy.clock import Clock
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
18 from kivy.graphics.texture import Texture
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
19 from kivy.properties import BooleanProperty, ObjectProperty
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 from kivy.support import install_gobject_iteration
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 from kivy.uix.button import Button
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 from kivy.uix.image import Image
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 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
24 from libervia.backend.core import log as logging
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from libervia.backend.core.i18n import _
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from libervia.backend.core import exceptions
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 from libervia.backend.tools.common import data_format
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
28 from libervia.frontends.quick_frontend import quick_widgets
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
29 from libervia.frontends.tools import jid, aio
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
30
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 from libervia.desktop_kivy import G
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
32
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 from ..core import cagou_widget
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
34
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
35 log = logging.getLogger(__name__)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
36
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 install_gobject_iteration()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
38
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 Gst.init(None)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
40
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 PLUGIN_INFO = {
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 "name": _("calls"),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 "main": "Calls",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 "description": _("Audio/Video calls"),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 "icon_symbol": "phone",
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
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 @dataclass
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 class TextureData:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 texture: Optional[Texture] = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
53 size: Optional[tuple[int, int]] = None
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
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 class CallButton(Button):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 parent_widget = ObjectProperty(None)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
58
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
59
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 class VideoStreamWidget(Image):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 pass
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
62
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
63
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 class WebRTC:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 """WebRTC implementation for audio and video communication.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
66
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 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
68 handling audio and video calls.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
69
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 @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
71 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
72 microphone available will be used.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
74 test_mode: bool = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
75
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
76
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 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
78 self.parent_calls = parent_calls
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 self.profile = profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
80 self.pipeline = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
82
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 def sdp_set(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 return self._sdp_set
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
86
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 @sdp_set.setter
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 def sdp_set(self, is_set: bool):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 self._sdp_set = is_set
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 if is_set:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
91 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
92 for data in self.remote_candidates_buffer.values():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 data["candidates"].clear()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
94
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 def media_types(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 if self._media_types is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
98 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
99 return self._media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
100
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 @media_types.setter
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 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
103 self._media_types = new_media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 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
105
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 def media_types_inv(self) -> dict:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 if self._media_types_inv is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 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
110 return self._media_types_inv
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 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
113 """Gets the sdpMLineIndex for a given media type.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
114
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
115 @param media_type: The type of the media.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 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
118 if m_type == media_type:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 return index
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 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
121
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
122 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
123 """Sets media types from offer SDP
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
124
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
125 @param offer: RTC session description containing the offer
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
126 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
127 sdp_lines = offer_sdp.splitlines()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 media_types = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 mline_index = 0
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
130
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 for line in sdp_lines:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 if line.startswith("m="):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 media_types[mline_index] = line[2 : line.find(" ")]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
134 mline_index += 1
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
135
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 self.media_types = media_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
137
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 def _a_call(self, method, *args, **kwargs):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 """Call an async method in main thread"""
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 wrapper(__):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 aio.run_async(method(*args, **kwargs))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
143 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
144
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 Clock.schedule_once(wrapper)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
146
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
147 def get_payload_types(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
148 self, sdpmsg, video_encoding: str, audio_encoding: str
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
149 ) -> dict[str, int | None]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
150 """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
151
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
152 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
153 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
154 profile-level-id, packetization-mode, etc.
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 # 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
157 # 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
158 video_pt = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
159 audio_pt = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
160 for i in range(0, sdpmsg.medias_len()):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
161 media = sdpmsg.get_media(i)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
162 for j in range(0, media.formats_len()):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
163 fmt = media.get_format(j)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
164 if fmt == "webrtc-datachannel":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
165 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
166 pt = int(fmt)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
167 caps = media.get_caps_from_media(pt)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
168 s = caps.get_structure(0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
169 encoding_name = s["encoding-name"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
170 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
171 video_pt = pt
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
172 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
173 audio_pt = pt
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
174 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
175
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
176 def parse_ice_candidate(self, candidate_string):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
177 """Parses the ice candidate string.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
178
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
179 @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
180 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
181 pattern = re.compile(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
182 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
183 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
184 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
185 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
186 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
187 match = pattern.match(candidate_string)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
188 if match:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
189 candidate_dict = match.groupdict()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
190
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
191 # Apply the correct types to the dictionary values
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
192 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
193 candidate_dict["priority"] = int(candidate_dict["priority"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
194 candidate_dict["port"] = int(candidate_dict["port"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
195
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
196 if candidate_dict["rel_port"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
197 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
198
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
199 if candidate_dict["generation"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
200 candidate_dict["generation"] = candidate_dict["generation"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
201
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
202 # Remove None values
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
203 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
204 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
205 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
206 return None
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 def build_ice_candidate(self, parsed_candidate):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
209 """Builds ICE candidate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
210
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
211 @param parsed_candidate: Dictionary containing parsed ICE candidate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
212 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
213 base_format = (
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
214 "candidate:{foundation} {component_id} {transport} {priority} "
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
215 "{address} {port} typ {type}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
216 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
217
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
218 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
219 base_format += " raddr {rel_addr} rport {rel_port}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
220
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
221 if parsed_candidate.get("generation"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
222 base_format += " generation {generation}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
223
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
224 return base_format.format(**parsed_candidate)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
225
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
226 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
227 """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
228
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
229 @param sdp: The Session Description Protocol offer string.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
230 @return: ufrag and pwd
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
231 @raise ValueError: Can't extract ufrag and password
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
232 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
233 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
234 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
235
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
236 if ufrag_line and pwd_line:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
237 ufrag = self.ufrag = ufrag_line.group(1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
238 pwd = self.pwd = pwd_line.group(1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
239 return ufrag, pwd
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
240 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
241 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
242 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
243
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
244 def reset_instance(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
245 """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
246 self.role: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
247 if self.pipeline is not None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
248 self.pipeline.set_state(Gst.State.NULL)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
249 self.pipeline = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
250 self.texture_map: dict[VideoStreamWidget, TextureData] = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
251 self._remote_video_pad = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
252 self.sid: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
253 self.offer: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
254 self.local_candidates_buffer = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
255 self.ufrag: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
256 self.pwd: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
257 self.callee: str | None = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
258 self._media_types = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
259 self._media_types_inv = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
260 self._sdp_set: bool = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
261 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
262 "audio": {"candidates": []},
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
263 "video": {"candidates": []},
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 self._media_types = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
266 self._media_types_inv = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
267
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
268 async def setup_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
269 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
270 role: str,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
271 audio_pt: int | None = 96,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
272 video_pt: int | None = 97,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
273 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
274 """Sets up the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
275
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
276 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
277 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
278 various connection handlers for the webrtcbin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
279
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
280 @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
281 @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
282 @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
283
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
284 @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
285 @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
286 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
287 assert role in ("initiator", "responder")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
288 self.role = role
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
289 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
290 raise NotImplementedError("None value is not handled yet")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
291
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
292 if self.test_mode:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
293 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
294 audio_source_elt = "audiotestsrc"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
295 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
296 video_source_elt = "v4l2src"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
297 audio_source_elt = "pulsesrc"
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 self.gst_pipe_desc = f"""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
300 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
301
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
302 {video_source_elt} name=video_src
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
303 ! videorate
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
304 ! video/x-raw,framerate=30/1
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
305 ! tee name=t
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 t.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
308 ! 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
309 ! videoconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
310 ! vp8enc deadline=1 keyframe-max-dist=60
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
311 ! rtpvp8pay picture-id-mode=15-bit
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
312 ! 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
313 ! sendrecv.
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 t.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
316 ! 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
317 ! videoconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
318 ! 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
319
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
320 {audio_source_elt} name=audio_src
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
321 ! 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
322 ! audioconvert
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
323 ! audioresample
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
324 ! opusenc audio-type=voice
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
325 ! rtpopuspay
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
326 ! 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
327 ! sendrecv.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
328 """
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 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
331
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
332 # Create the pipeline
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
333 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
334 if not self.pipeline:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
335 raise exceptions.InternalError("Failed to create Gstreamer pipeline.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
336
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
337 self.webrtc = self.pipeline.get_by_name("sendrecv")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
338
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
339 self.video_src = self.pipeline.get_by_name("video_src")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
340 self.audio_src = self.pipeline.get_by_name("audio_src")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
341
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
342 # set STUN and TURN servers
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
343 external_disco = data_format.deserialise(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
344 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
345 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
346
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
347 for server in external_disco:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
348 if server["type"] == "stun":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
349 if server["transport"] == "tcp":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
350 log.info(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
351 "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
352 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
353 url = f"stun://{server['host']}:{server['port']}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
354 log.debug(f"adding stun server: {url}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
355 self.webrtc.set_property("stun-server", url)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
356 elif server["type"] == "turn":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
357 url = "{scheme}://{username}:{password}@{host}:{port}".format(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
358 scheme = "turns" if server["transport"] == "tcp" else "turn",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
359 username=quote_plus(server["username"]),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
360 password=quote_plus(server["password"]),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
361 host=server["host"],
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
362 port=server["port"],
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 log.debug(f"adding turn server: {url}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
365
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
366 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
367 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
368
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
369 # local video feedback
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
370 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
371 local_video_sink.set_property("emit-signals", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
372 local_video_sink.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
373 "new-sample",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
374 self.on_new_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
375 self.update_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
376 self.parent_calls.local_video,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
377 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
378 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
379 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
380
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
381 # Create bus and associate signal watchers
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
382 self.bus = self.pipeline.get_bus()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
383 if not self.bus:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
384 log.error("Failed to get bus from pipeline.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
385 return
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 self.bus.add_signal_watch()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
388 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
389 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
390 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
391 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
392 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
393 self.webrtc.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
394 "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
395 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
396 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
397
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
398 def start_pipeline(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
399 """Starts the GStreamer pipeline."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
400 log.debug("starting the pipeline")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
401 self.pipeline.set_state(Gst.State.PLAYING)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
402
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
403 def on_negotiation_needed(self, webrtc):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
404 """Initiate SDP offer when negotiation is needed."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
405 log.debug("Negotiation needed.")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
406 if self.role == "initiator":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
407 log.debug("Creating offer…")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
408 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
409 self.webrtc.emit("create-offer", None, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
410
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
411 def on_offer_created(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
412 """Callback for when SDP offer is created."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
413 log.info("on_offer_created called")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
414 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
415 reply = promise.get_reply()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
416 if reply is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
417 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
418 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
419 offer = reply["offer"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
420 self.offer = offer.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
421 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
422 self._set_media_types(self.offer)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
423 promise = Gst.Promise.new()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
424 self.webrtc.emit("set-local-description", offer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
425 promise.interrupt()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
426 self._a_call(self._start_call)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
427
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
428 def on_answer_set(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
429 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
430
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
431 def on_answer_created(self, promise, _, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
432 """Callback for when SDP answer is created."""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
433 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
434 reply = promise.get_reply()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
435 answer = reply["answer"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
436 promise = Gst.Promise.new()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
437 self.webrtc.emit("set-local-description", answer, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
438 promise.interrupt()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
439 answer_sdp = answer.sdp.as_text()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
440 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
441 self.sdp_set = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
442 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
443
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
444 def on_offer_set(self, promise):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
445 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
446 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
447 self.webrtc.emit("create-answer", None, promise)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
448
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
449 def on_new_sample(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
450 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
451 video_sink: Gst.Element,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
452 update_sample_method: Callable,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
453 video_widget: VideoStreamWidget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
454 ) -> bool:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
455 """Handles a new sample from the video sink.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
456
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
457 @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
458 @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
459 @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
460 @return: Always False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
461 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
462 sample = video_sink.emit("pull-sample")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
463 if sample is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
464 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
465
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
466 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
467 texture_data = self.texture_map[video_widget]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
468 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
469 texture_data = self.texture_map[video_widget] = TextureData()
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 # Get the current video size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
472 video_pad = video_sink.get_static_pad("sink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
473 assert video_pad is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
474 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
475 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
476
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
477 # Compare with the texture size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
478 texture_size = texture_data.size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
479 if texture_size != stream_size:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
480 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
481 texture_data.size = stream_size
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
482 # 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
483 texture_data.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
484
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
485 Clock.schedule_once(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
486 partial(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
487 update_sample_method,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
488 sample=sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
489 video_widget=video_widget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
490 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
491 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
492 return False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
493
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
494 def update_sample(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
495 self,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
496 dt: float,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
497 sample: Optional[Gst.Sample],
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
498 video_widget: VideoStreamWidget,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
499 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
500 """Updates the video sample.
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 This method runs in the main thread.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
503
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
504 @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
505 @param sample: The video sample to update.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
506 @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
507 @param texture: The texture object.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
508 @param texture_size: The texture size.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
509 @param video_widget: The video widget.
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 if sample is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
512 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
513 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
514 texture_data = self.texture_map[video_widget]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
515 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
516 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
517 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
518
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
519 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
520 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
521 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
522 assert texture is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
523 texture_data.texture = texture
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
524 texture.flip_vertical()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
525 video_widget.texture = texture
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
526
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
527 mapinfo = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
528 buf = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
529 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
530 buf = sample.get_buffer()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
531 _, mapinfo = buf.map(Gst.MapFlags.READ)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
532
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
533 buffer = mapinfo.data.tobytes()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
534
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
535 if texture_data.texture is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
536 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
537 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
538 texture_data.texture.blit_buffer(buffer, colorfmt="rgb")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
539 assert video_widget.canvas is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
540 video_widget.canvas.ask_update()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
541 buf.unmap(mapinfo)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
542 finally:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
543 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
544 buf.unmap(mapinfo)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
545
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
546 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
547 """Handle the stream from the remote decodebin.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
548
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
549 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
550 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
551 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
552
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
553 @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
554 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
555 assert self.pipeline is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
556 if not pad.has_current_caps():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
557 log.error(f"{pad} has no caps, ignoring")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
558 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
559
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
560 self.pipeline.set_state(Gst.State.PAUSED)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
561 caps = pad.get_current_caps()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
562 assert len(caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
563 s = caps[0]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
564 name = s.get_name()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
565 log.debug(f"====> NAME START: {name}")
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 q = Gst.ElementFactory.make("queue")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
568 q.set_property("max-size-time", 0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
569 q.set_property("max-size-bytes", 0)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
570 q.set_property("max-size-buffers", 5)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
571
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
572 if name.startswith("video"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
573 log.debug("===> VIDEO OK")
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 self._remote_video_pad = pad
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
576
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
577 # 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
578 width = s.get_int("width").value
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
579 height = s.get_int("height").value
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
580 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
581
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
582 # 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
583 # 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
584 # rendered image
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
585 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
586 if adjust_resolution:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
587 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
588 width = (width + 3) // 4 * 4
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
589 height = (height + 3) // 4 * 4
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
590 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
591
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
592 conv = Gst.ElementFactory.make("videoconvert")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
593 remote_video_sink = Gst.ElementFactory.make("appsink")
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 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
596 remote_video_sink.set_property("caps", appsink_caps)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
597
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
598 remote_video_sink.set_property("emit-signals", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
599 remote_video_sink.set_property("drop", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
600 remote_video_sink.set_property("max-buffers", 1)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
601 remote_video_sink.set_property("sync", True)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
602 remote_video_sink.connect(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
603 "new-sample",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
604 self.on_new_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
605 self.update_sample,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
606 self.parent_calls.remote_video,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
607 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
608
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
609 if adjust_resolution:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
610 videoscale = Gst.ElementFactory.make("videoscale")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
611 adjusted_caps = Gst.Caps.from_string(f"video/x-raw,width={width},height={height}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
612 capsfilter = Gst.ElementFactory.make("capsfilter")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
613 capsfilter.set_property("caps", adjusted_caps)
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.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
616 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
617 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
618 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
619 conv.link(videoscale)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
620 videoscale.link(capsfilter)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
621 capsfilter.link(remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
622 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
623 self.pipeline.add(q, conv, remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
624 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
625 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
626 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
627 conv.link(remote_video_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
628
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
629 elif name.startswith("audio"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
630 log.debug("===> Audio OK")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
631 conv = Gst.ElementFactory.make("audioconvert")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
632 resample = Gst.ElementFactory.make("audioresample")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
633 remote_audio_sink = Gst.ElementFactory.make("autoaudiosink")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
634 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
635 self.pipeline.sync_children_states()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
636 pad.link(q.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
637 q.link(conv)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
638 conv.link(resample)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
639 resample.link(remote_audio_sink)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
640 self.pipeline.set_state(Gst.State.PLAYING)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
641
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
642 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
643 """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
644
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
645 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
646 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
647
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
648 @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
649 @param pad: The newly added pad.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
650 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
651
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
652 log.debug("on_pad_added")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
653 if pad.direction != Gst.PadDirection.SRC:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
654 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
655
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
656 decodebin = Gst.ElementFactory.make("decodebin")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
657 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
658 self.pipeline.add(decodebin)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
659 decodebin.sync_state_with_parent()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
660 pad.link(decodebin.get_static_pad("sink"))
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
661
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
662 async def _start_call(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
663 """Initiate the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
664
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
665 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
666 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
667 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
668 assert self.callee is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
669 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
670 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
671 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
672 if self.local_candidates_buffer:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
673 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
674 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
675 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
676 if self.pwd is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
677 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
678 self.extract_ufrag_pwd(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
679 ice_data = {}
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
680 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
681 ice_data[media_type] = {
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
682 "ufrag": self.ufrag,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
683 "pwd": self.pwd,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
684 "candidates": candidates,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
685 }
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
686 await G.host.a_bridge.ice_candidates_add(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
687 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
688 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
689 self.local_candidates_buffer.clear()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
690
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
691 def _remote_sdp_set(self, promise) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
692 assert promise.wait() == Gst.PromiseResult.REPLIED
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
693 self.sdp_set = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
694
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
695 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
696 """Outgoing call has been accepted.
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 @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
699 @param profile: Profile used for the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
700 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
701 log.debug(f"SDP answer received: \n{sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
702
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
703 __, sdpmsg = GstSdp.SDPMessage.new_from_text(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
704 answer = GstWebRTC.WebRTCSessionDescription.new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
705 GstWebRTC.WebRTCSDPType.ANSWER, sdpmsg
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 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
708 self.webrtc.emit("set-remote-description", answer, promise)
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 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
711 """Answer an incoming call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
712
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
713 @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
714 @param profile: Profile used for the call.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
715
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
716 @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
717 payload types.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
718 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
719 log.debug(f"SDP offer received: \n{sdp}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
720 self._set_media_types(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
721 __, 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
722 payload_types = self.get_payload_types(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
723 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
724 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
725 assert "VP8" in payload_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
726 assert "OPUS" in payload_types
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
727 await self.setup_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
728 "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
729 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
730 self.start_pipeline()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
731 offer = GstWebRTC.WebRTCSessionDescription.new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
732 GstWebRTC.WebRTCSDPType.OFFER, offer_sdp_msg
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
733 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
734 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
735 self.webrtc.emit("set-remote-description", offer, promise)
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_ice_candidate(self, webrtc, mline_index, candidate_sdp):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
738 """Handles the on-ice-candidate signal of webrtcbin.
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 webrtc: The webrtcbin element.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
741 @param mlineindex: The mline index.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
742 @param candidate: The ICE candidate.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
743 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
744 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
745 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
746 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
747 parsed_candidate = self.parse_ice_candidate(candidate_sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
748 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
749 media_type = self.media_types[mline_index]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
750 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
751 raise exceptions.InternalError("can't find media type")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
752
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
753 if self.sid is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
754 log.debug("buffering local ICE candidate")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
755 self.local_candidates_buffer.setdefault(media_type, []).append(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
756 parsed_candidate
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 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
759 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
760 assert sdp is not None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
761 ufrag, pwd = self.extract_ufrag_pwd(sdp)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
762 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
763 self._a_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
764 G.host.a_bridge.ice_candidates_add,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
765 self.sid,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
766 data_format.serialise({media_type: ice_data}),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
767 self.profile,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
768 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
769
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
770 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
771 """Handle new ICE candidates.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
772
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
773 @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
774 keys and corresponding ICE data as values.
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 @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
777 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
778 if not self.sdp_set:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
779 log.debug("buffering remote ICE candidate")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
780 for media_type in ("audio", "video"):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
781 media_candidates = candidates.get(media_type)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
782 if media_candidates:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
783 buffer = self.remote_candidates_buffer[media_type]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
784 buffer["candidates"].extend(media_candidates["candidates"])
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
785 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
786 for media_type, ice_data in candidates.items():
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
787 for candidate in ice_data["candidates"]:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
788 candidate_sdp = self.build_ice_candidate(candidate)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
789 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
790 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
791 except Exception as e:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
792 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
793 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
794 log.debug(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
795 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
796 f"{candidate_sdp}"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
797 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
798
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
799 def on_ice_gathering_state_change(self, pspec, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
800 state = self.webrtc.get_property("ice-gathering-state")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
801 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
802
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
803 def on_ice_connection_state(self, pspec, __):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
804 state = self.webrtc.props.ice_connection_state
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
805 if state == GstWebRTC.WebRTCICEConnectionState.FAILED:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
806 log.error("ICE connection failed")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
807 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
808
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
809 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
810 """Handles the GStreamer bus error messages.
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 @param bus: The GStreamer bus.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
813 @param message: The error message.
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 err, debug = message.parse_error()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
816 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
817 log.error(f"Debugging info: {debug}")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
818
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
819 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
820 """Handles the GStreamer bus eos messages.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
821
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
822 @param bus: The GStreamer bus.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
823 @param message: The eos message.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
824 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
825 log.info("End of stream")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
826
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
827 async def end_call(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
828 """Stop streaming and clean instance"""
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
829 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
830
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
831
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
832 class Calls(quick_widgets.QuickWidget, cagou_widget.LiberviaDesktopKivyWidget):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
833 remote_video = ObjectProperty()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
834 local_video = ObjectProperty()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
835 use_header_input = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
836 signals_registered = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
837 in_call = BooleanProperty(False)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
838
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
839 def __init__(self, host, target, profiles):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
840 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
841 cagou_widget.LiberviaDesktopKivyWidget.__init__(self)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
842 call_btn = CallButton(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
843 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
844 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
845 self.header_input_add_extra(call_btn)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
846 self.webrtc = WebRTC(self, self.profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
847
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
848 if not self.__class__.signals_registered:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
849 log.debug("registering signals")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
850 G.host.register_signal(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
851 "ice_candidates_new",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
852 handler=self.__class__.ice_candidates_new_handler,
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
853 iface="plugin",
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
854 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
855 G.host.register_signal(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
856 "call_setup", handler=self.__class__.call_setup_handler, iface="plugin"
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
857 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
858 G.host.register_signal(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
859 "call_ended", handler=self.__class__.call_ended_handler, iface="plugin"
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 G.host.register_action_handler(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
862 C.META_TYPE_CALL, self.__class__.action_new_handler
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 self.__class__.signals_registered = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
865
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
866 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
867
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
868 @property
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
869 def sid(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
870 return self.webrtc.sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
871
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
872 async def toggle_call(self):
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
873 """Toggle between making a call and hanging up.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
874
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
875 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
876 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
877 if self.sid is None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
878 # Initiate the call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
879 log.info("initiating call")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
880 callee = jid.JID(self.header_input.text.strip())
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
881 self.webrtc.callee = callee
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
882 await self.webrtc.setup_call("initiator")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
883 self.webrtc.start_pipeline()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
884 self.in_call = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
885 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
886 # Terminate the call
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
887 sid = self.sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
888 await self.end_call({"reason": "terminated"}, self.profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
889 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
890 self.in_call = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
891
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
892 async def on_incoming_call(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
893 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
894 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
895 """Handle incoming call notifications.
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
896
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
897 @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
898 @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
899 @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
900 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
901 if self.sid is not None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
902 # FIXME: show a double remote call notification
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
903 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
904 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
905 sid = action_data["session_id"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
906 self.in_call = True
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
907 self.webrtc.sid = sid
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
908 # 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
909 # will be implemented
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
910 log.warning("auto-accepting remote call")
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
911 await G.host.a_bridge.action_launch(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
912 action_id, data_format.serialise({"cancelled": False}), profile
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
913 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
914
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
915 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
916 """Call has been accepted, connection can be established
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 @param session_id: Session identifier
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
919 @param setup_data: Data with following keys:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
920 role: initiator or responser
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
921 sdp: Session Description Protocol data
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
922 @param profile: Profile associated
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
923 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
924 try:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
925 role = setup_data["role"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
926 sdp = setup_data["sdp"]
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
927 except KeyError:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
928 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
929 return
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
930 if role == "initiator":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
931 self.webrtc.on_accepted_call(sdp, profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
932 elif role == "responder":
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
933 await self.webrtc.answer_call(sdp, profile)
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
934 else:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
935 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
936
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
937 def reset_instance(self) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
938 self.in_call = False
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
939 self.local_video.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
940 self.remote_video.texture = None
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
941
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
942 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
943 """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
944
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
945 @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
946 """
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
947 await self.webrtc.end_call()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
948 self.reset_instance()
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
949
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
950 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
951 def ice_candidates_new_handler(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
952 cls, sid: str, candidates_s: str, profile: str
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
953 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
954 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
955 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
956 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
957 wid.webrtc.on_ice_candidates_new(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
958 data_format.deserialise(candidates_s),
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
959 )
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
960
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
961 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
962 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
963 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
964 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
965 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
966 aio.run_async(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
967 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
968 )
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 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
971 def call_ended_handler(cls, sid: str, data_s: str, profile: str) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
972 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
973 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
974 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
975 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
976
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
977 @classmethod
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
978 def action_new_handler(
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
979 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
980 ) -> None:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
981 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
982 if profile not in wid.profiles:
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
983 continue
f387992d8e37 plugins: new "call" plugin for A/V calls:
Goffi <goffi@goffi.org>
parents:
diff changeset
984 aio.run_async(wid.on_remote_call(action_data, action_id, profile))