Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0167/__init__.py @ 4116:23fa52acf72c
plugin XEP-0167, XEP-0176: transport-info and ICE candidate sending are delayed if session is not active yet
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 21 Aug 2023 15:19:45 +0200 |
parents | 0da563780ffc |
children | 07370d2a9bde |
comparison
equal
deleted
inserted
replaced
4115:0da563780ffc | 4116:23fa52acf72c |
---|---|
113 async_=True, | 113 async_=True, |
114 ) | 114 ) |
115 | 115 |
116 # args: session_id, serialised setup data (dict with keys "role" and "sdp"), | 116 # args: session_id, serialised setup data (dict with keys "role" and "sdp"), |
117 # profile | 117 # profile |
118 host.bridge.add_signal( | 118 host.bridge.add_signal("call_setup", ".plugin", signature="sss") |
119 "call_setup", ".plugin", signature="sss" | |
120 ) | |
121 | 119 |
122 # args: session_id, data, profile | 120 # args: session_id, data, profile |
123 host.bridge.add_signal( | 121 host.bridge.add_signal("call_ended", ".plugin", signature="sss") |
124 "call_ended", ".plugin", signature="sss" | |
125 ) | |
126 | 122 |
127 # args: session_id, info_type, extra, profile | 123 # args: session_id, info_type, extra, profile |
128 host.bridge.add_signal( | 124 host.bridge.add_signal("call_info", ".plugin", signature="ssss") |
129 "call_info", ".plugin", signature="ssss" | |
130 ) | |
131 | 125 |
132 def get_handler(self, client): | 126 def get_handler(self, client): |
133 return XEP_0167_handler() | 127 return XEP_0167_handler() |
134 | 128 |
135 # bridge methods | 129 # bridge methods |
192 try: | 186 try: |
193 call_data[media_type]["id"] = media_data["id"] | 187 call_data[media_type]["id"] = media_data["id"] |
194 except KeyError: | 188 except KeyError: |
195 log.warning(f"no media ID found for {media_type}: {media_data}") | 189 log.warning(f"no media ID found for {media_type}: {media_data}") |
196 try: | 190 try: |
197 call_data[media_type]["ice-candidates"] = transport_data["candidates"] | 191 call_data[media_type]["ice-candidates"] = transport_data.get( |
192 "candidates", [] | |
193 ) | |
198 metadata["ice-ufrag"] = transport_data["ufrag"] | 194 metadata["ice-ufrag"] = transport_data["ufrag"] |
199 metadata["ice-pwd"] = transport_data["pwd"] | 195 metadata["ice-pwd"] = transport_data["pwd"] |
200 except KeyError: | 196 except KeyError: |
201 log.warning("ICE data are missing from SDP") | 197 log.warning("ICE data are missing from SDP") |
202 continue | 198 continue |
248 peer_metadata={}, | 244 peer_metadata={}, |
249 ) | 245 ) |
250 ) | 246 ) |
251 return sid | 247 return sid |
252 | 248 |
253 def _call_answer_sdp( | 249 def _call_answer_sdp(self, session_id: str, answer_sdp: str, profile: str) -> None: |
254 self, | |
255 session_id: str, | |
256 answer_sdp: str, | |
257 profile: str | |
258 ) -> None: | |
259 client = self.host.get_client(profile) | 250 client = self.host.get_client(profile) |
260 session = self._j.get_session(client, session_id) | 251 session = self._j.get_session(client, session_id) |
261 try: | 252 try: |
262 answer_sdp_d = session.pop("answer_sdp_d") | 253 answer_sdp_d = session.pop("answer_sdp_d") |
263 except KeyError: | 254 except KeyError: |
272 data_s: str, | 263 data_s: str, |
273 profile_key: str, | 264 profile_key: str, |
274 ): | 265 ): |
275 client = self.host.get_client(profile_key) | 266 client = self.host.get_client(profile_key) |
276 return defer.ensureDeferred( | 267 return defer.ensureDeferred( |
277 self.call_end( | 268 self.call_end(client, session_id, data_format.deserialise(data_s)) |
278 client, session_id, data_format.deserialise(data_s) | |
279 ) | |
280 ) | 269 ) |
281 | 270 |
282 async def call_end( | 271 async def call_end( |
283 self, | 272 self, |
284 client: SatXMPPEntity, | 273 client: SatXMPPEntity, |
295 await self._j.terminate(client, self._j.REASON_SUCCESS, session) | 284 await self._j.terminate(client, self._j.REASON_SUCCESS, session) |
296 | 285 |
297 # jingle callbacks | 286 # jingle callbacks |
298 | 287 |
299 async def confirm_incoming_call( | 288 async def confirm_incoming_call( |
300 self, | 289 self, client: SatXMPPEntity, session: dict, call_type: str |
301 client: SatXMPPEntity, | |
302 session: dict, | |
303 call_type: str | |
304 ) -> bool: | 290 ) -> bool: |
305 """Prompt the user for a call confirmation. | 291 """Prompt the user for a call confirmation. |
306 | 292 |
307 @param client: The client entity. | 293 @param client: The client entity. |
308 @param session: The Jingle session. | 294 @param session: The Jingle session. |
339 session["call_accepted"] = True | 325 session["call_accepted"] = True |
340 | 326 |
341 return accepted | 327 return accepted |
342 | 328 |
343 async def jingle_preflight( | 329 async def jingle_preflight( |
344 self, | 330 self, client: SatXMPPEntity, session: dict, description_elt: domish.Element |
345 client: SatXMPPEntity, | |
346 session: dict, | |
347 description_elt: domish.Element | |
348 ) -> None: | 331 ) -> None: |
349 """Perform preflight checks for an incoming call session. | 332 """Perform preflight checks for an incoming call session. |
350 | 333 |
351 Check if the calls is audio only or audio/video, then, prompts the user for | 334 Check if the calls is audio only or audio/video, then, prompts the user for |
352 confirmation. | 335 confirmation. |
378 except defer.CancelledError as e: | 361 except defer.CancelledError as e: |
379 # raised when call is retracted before user has answered or rejected | 362 # raised when call is retracted before user has answered or rejected |
380 self.host.bridge.call_ended( | 363 self.host.bridge.call_ended( |
381 session["id"], | 364 session["id"], |
382 data_format.serialise({"reason": "retracted"}), | 365 data_format.serialise({"reason": "retracted"}), |
383 client.profile | 366 client.profile, |
384 ) | 367 ) |
385 raise e | 368 raise e |
386 | 369 |
387 if not accepted: | 370 if not accepted: |
388 raise exceptions.CancelError("User declined the incoming call.") | 371 raise exceptions.CancelError("User declined the incoming call.") |
390 async def jingle_preflight_info( | 373 async def jingle_preflight_info( |
391 self, | 374 self, |
392 client: SatXMPPEntity, | 375 client: SatXMPPEntity, |
393 session: dict, | 376 session: dict, |
394 info_type: str, | 377 info_type: str, |
395 info_data: dict|None = None | 378 info_data: dict | None = None, |
396 ) -> None: | 379 ) -> None: |
397 if info_type == "ringing": | 380 if info_type == "ringing": |
398 if not session.get("ringing", False): | 381 if not session.get("ringing", False): |
399 self.host.bridge.call_info( | 382 self.host.bridge.call_info(session["id"], "ringing", "", client.profile) |
400 session["id"], "ringing", "", client.profile | |
401 ) | |
402 # we indicate that the ringing has started, to avoid sending several times | 383 # we indicate that the ringing has started, to avoid sending several times |
403 # the signal | 384 # the signal |
404 session["ringing"] = True | 385 session["ringing"] = True |
405 else: | 386 else: |
406 log.warning(f"Unknown preflight info type: {info_type!r}") | 387 log.warning(f"Unknown preflight info type: {info_type!r}") |
407 | 388 |
408 async def jingle_preflight_cancel( | 389 async def jingle_preflight_cancel( |
409 self, | 390 self, client: SatXMPPEntity, session: dict, cancel_error: exceptions.CancelError |
410 client: SatXMPPEntity, | |
411 session: dict, | |
412 cancel_error: exceptions.CancelError | |
413 ) -> None: | 391 ) -> None: |
414 """The call has been rejected""" | 392 """The call has been rejected""" |
415 # call_ended is use to send the signal only once even if there are audio and video | 393 # call_ended is use to send the signal only once even if there are audio and video |
416 # contents | 394 # contents |
417 call_ended = session.get("call_ended", False) | 395 call_ended = session.get("call_ended", False) |
418 if call_ended: | 396 if call_ended: |
419 return | 397 return |
420 data = { | 398 data = {"reason": getattr(cancel_error, "reason", "cancelled")} |
421 "reason": getattr(cancel_error, "reason", "cancelled") | |
422 } | |
423 text = getattr(cancel_error, "text", None) | 399 text = getattr(cancel_error, "text", None) |
424 if text: | 400 if text: |
425 data["text"] = text | 401 data["text"] = text |
426 self.host.bridge.call_ended( | 402 self.host.bridge.call_ended( |
427 session["id"], | 403 session["id"], data_format.serialise(data), client.profile |
428 data_format.serialise(data), | |
429 client.profile | |
430 ) | 404 ) |
431 session["call_ended"] = True | 405 session["call_ended"] = True |
432 | |
433 | 406 |
434 def jingle_session_init( | 407 def jingle_session_init( |
435 self, | 408 self, |
436 client: SatXMPPEntity, | 409 client: SatXMPPEntity, |
437 session: dict, | 410 session: dict, |
505 # we should have the answer long before 2 min | 478 # we should have the answer long before 2 min |
506 answer_sdp_d.addTimeout(2 * 60, reactor) | 479 answer_sdp_d.addTimeout(2 * 60, reactor) |
507 | 480 |
508 self.host.bridge.call_setup( | 481 self.host.bridge.call_setup( |
509 session["id"], | 482 session["id"], |
510 data_format.serialise({ | 483 data_format.serialise( |
511 "role": session["role"], | 484 { |
512 "sdp": sdp, | 485 "role": session["role"], |
513 }), | 486 "sdp": sdp, |
514 client.profile | 487 } |
488 ), | |
489 client.profile, | |
515 ) | 490 ) |
516 | 491 |
517 answer_sdp = await answer_sdp_d | 492 answer_sdp = await answer_sdp_d |
518 | 493 |
519 parsed_answer = mapping.parse_sdp(answer_sdp) | 494 parsed_answer = mapping.parse_sdp(answer_sdp) |
557 # we only send the signal for first content, as it means that the whole | 532 # we only send the signal for first content, as it means that the whole |
558 # session is accepted | 533 # session is accepted |
559 answer_sdp = mapping.generate_sdp_from_session(session) | 534 answer_sdp = mapping.generate_sdp_from_session(session) |
560 self.host.bridge.call_setup( | 535 self.host.bridge.call_setup( |
561 session["id"], | 536 session["id"], |
562 data_format.serialise({ | 537 data_format.serialise( |
563 "role": session["role"], | 538 { |
564 "sdp": answer_sdp, | 539 "role": session["role"], |
565 }), | 540 "sdp": answer_sdp, |
566 client.profile | 541 } |
542 ), | |
543 client.profile, | |
567 ) | 544 ) |
568 else: | 545 else: |
569 log.warning(f"FIXME: unmanaged action {action}") | 546 log.warning(f"FIXME: unmanaged action {action}") |
570 | 547 |
571 self.host.trigger.point( | 548 self.host.trigger.point( |
633 session: dict, | 610 session: dict, |
634 content_name: str, | 611 content_name: str, |
635 reason_elt: domish.Element, | 612 reason_elt: domish.Element, |
636 ) -> None: | 613 ) -> None: |
637 reason, text = self._j.parse_reason_elt(reason_elt) | 614 reason, text = self._j.parse_reason_elt(reason_elt) |
638 data = { | 615 data = {"reason": reason} |
639 "reason": reason | |
640 } | |
641 if text: | 616 if text: |
642 data["text"] = text | 617 data["text"] = text |
643 self.host.bridge.call_ended( | 618 self.host.bridge.call_ended( |
644 session["id"], data_format.serialise(data), client.profile | 619 session["id"], data_format.serialise(data), client.profile |
645 ) | 620 ) |