comparison libervia/cli/call_webrtc.py @ 4210:9218d4331bb2

cli (call): `tui` output implementation: - Moved original UI to a separated class, and use if with the `simple` output - By default, best output is automatically selected. For now `gui` is selected if possible, and `simple` is used as fallback. - The new `tui` output can be used to have the videos directly embedded in the terminal, either with real videos for compatible terminal emulators, or with Unicode blocks. - Text contrôls are used for both `simple` and `tui` outputs - several options can be used with `--oo` (will be documented in next commit). rel 428
author Goffi <goffi@goffi.org>
date Fri, 16 Feb 2024 18:46:06 +0100
parents
children
comparison
equal deleted inserted replaced
4209:fe29fbdabce6 4210:9218d4331bb2
1 #!/usr/bin/env python3
2
3 # Libervia CLI
4 # Copyright (C) 2009-2024 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 from dataclasses import dataclass
20
21 from libervia.backend.tools.common import data_format
22 from libervia.frontends.tools import aio, jid
23
24
25 @dataclass
26 class CallData:
27 callee: jid.JID
28 sid: str | None = None
29 action_id: str | None = None
30
31
32 class WebRTCCall:
33 def __init__(self, host, profile: str, callee: jid.JID, **kwargs):
34 """Create and setup a webRTC instance
35
36 @param profile: profile making or receiving the call
37 @param callee: peer jid
38 @param kwargs: extra kw args to use when instantiating WebRTC
39 """
40 from libervia.frontends.tools import webrtc
41
42 aio.install_glib_asyncio_iteration()
43 self.host = host
44 self.profile = profile
45 self.webrtc = webrtc.WebRTC(host.bridge, profile, **kwargs)
46 self.webrtc.callee = callee
47 host.bridge.register_signal(
48 "ice_candidates_new", self.on_ice_candidates_new, "plugin"
49 )
50 host.bridge.register_signal("call_setup", self.on_call_setup, "plugin")
51 host.bridge.register_signal("call_ended", self.on_call_ended, "plugin")
52
53 @classmethod
54 async def make_webrtc_call(
55 cls,
56 host,
57 profile: str,
58 call_data: CallData,
59 **kwargs
60 ) -> "WebRTCCall":
61 """Create the webrtc_call instance
62
63 @param call_data: Call data of the command
64 @param kwargs: extra args used to instanciate WebRTCCall
65
66 """
67 webrtc_call = cls(host, profile, call_data.callee, **kwargs)
68 if call_data.sid is None:
69 # we are making the call
70 await webrtc_call.start()
71 else:
72 # we are receiving the call
73 webrtc_call.sid = call_data.sid
74 if call_data.action_id is not None:
75 await host.bridge.action_launch(
76 call_data.action_id,
77 data_format.serialise({"cancelled": False}),
78 profile
79 )
80 return webrtc_call
81
82 @property
83 def sid(self) -> str | None:
84 return self.webrtc.sid
85
86 @sid.setter
87 def sid(self, new_sid: str | None) -> None:
88 self.webrtc.sid = new_sid
89
90 async def on_ice_candidates_new(
91 self, sid: str, candidates_s: str, profile: str
92 ) -> None:
93 if sid != self.webrtc.sid or profile != self.profile:
94 return
95 self.webrtc.on_ice_candidates_new(
96 data_format.deserialise(candidates_s),
97 )
98
99 async def on_call_setup(self, sid: str, setup_data_s: str, profile: str) -> None:
100 if sid != self.webrtc.sid or profile != self.profile:
101 return
102 setup_data = data_format.deserialise(setup_data_s)
103 try:
104 role = setup_data["role"]
105 sdp = setup_data["sdp"]
106 except KeyError:
107 self.host.disp(f"Invalid setup data received: {setup_data}", error=True)
108 return
109 if role == "initiator":
110 self.webrtc.on_accepted_call(sdp, profile)
111 elif role == "responder":
112 await self.webrtc.answer_call(sdp, profile)
113 else:
114 self.host.disp(
115 f"Invalid role received during setup: {setup_data}", error=True
116 )
117 # we want to be sure that call is ended if user presses `Ctrl + c` or anything
118 # else stops the session.
119 self.host.add_on_quit_callback(
120 lambda: self.host.bridge.call_end(sid, "", profile)
121 )
122
123 async def on_call_ended(self, sid: str, data_s: str, profile: str) -> None:
124 if sid != self.webrtc.sid or profile != self.profile:
125 return
126 await self.webrtc.end_call()
127 await self.host.a_quit()
128
129 async def start(self):
130 """Start a call.
131
132 To be used only if we are initiator
133 """
134 await self.webrtc.setup_call("initiator")
135 self.webrtc.start_pipeline()