Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_xep_0167/__init__.py @ 4231:e11b13418ba6
plugin XEP-0353, XEP-0234, jingle: WebRTC data channel signaling implementation:
Implement XEP-0343: Signaling WebRTC Data Channels in Jingle. The current version of the
XEP (0.3.1) has no implementation and contains some flaws. After discussing this on xsf@,
Daniel (from Conversations) mentioned that they had a sprint with Larma (from Dino) to
work on another version and provided me with this link:
https://gist.github.com/iNPUTmice/6c56f3e948cca517c5fb129016d99e74 . I have used it for my
implementation.
This implementation reuses work done on Jingle A/V call (notably XEP-0176 and XEP-0167
plugins), with adaptations. When used, XEP-0234 will not handle the file itself as it
normally does. This is because WebRTC has several implementations (browser for web
interface, GStreamer for others), and file/data must be handled directly by the frontend.
This is particularly important for web frontends, as the file is not sent from the backend
but from the end-user's browser device.
Among the changes, there are:
- XEP-0343 implementation.
- `file_send` bridge method now use serialised dict as output.
- New `BaseTransportHandler.is_usable` method which get content data and returns a boolean
(default to `True`) to tell if this transport can actually be used in this context (when
we are initiator). Used in webRTC case to see if call data are available.
- Support of `application` media type, and everything necessary to handle data channels.
- Better confirmation message, with file name, size and description when available.
- When file is accepted in preflight, it is specified in following `action_new` signal for
actual file transfer. This way, frontend can avoid the display or 2 confirmation
messages.
- XEP-0166: when not specified, default `content` name is now its index number instead of
a UUID. This follows the behaviour of browsers.
- XEP-0353: better handling of events such as call taken by another device.
- various other updates.
rel 441
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 06 Apr 2024 12:57:23 +0200 |
parents | 832a7bdb3aea |
children | 79c8a70e1813 |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0167/__init__.py Sat Apr 06 12:21:04 2024 +0200 +++ b/libervia/backend/plugins/plugin_xep_0167/__init__.py Sat Apr 06 12:57:23 2024 +0200 @@ -81,6 +81,7 @@ mapping.host = host self._j = host.plugins["XEP-0166"] self._j.register_application(NS_JINGLE_RTP, self) + host.register_namespace("jingle-rtp", NS_JINGLE_RTP) host.bridge.add_method( "call_start", ".plugin", @@ -141,6 +142,50 @@ ) ) + def parse_call_data(self, call_data: dict) -> dict: + """Parse ``call_data`` and return corresponding contents end metadata""" + metadata = call_data.get("metadata") or {} + + if "sdp" in call_data: + sdp_data = mapping.parse_sdp(call_data["sdp"]) + to_delete = set() + for media, data in sdp_data.items(): + if media not in ("audio", "video", "application"): + continue + to_delete.add(media) + media_type, media_data = media, data + call_data[media_type] = media_data["application_data"] + transport_data = media_data["transport_data"] + try: + call_data[media_type]["fingerprint"] = transport_data["fingerprint"] + except KeyError: + log.warning("fingerprint is missing") + pass + try: + call_data[media_type]["id"] = media_data["id"] + except KeyError: + log.warning(f"no media ID found for {media_type}: {media_data}") + # FIXME: the 2 values below are linked to XEP-0343, they should be added + # there instead, maybe with some new trigger? + for key in ("sctp-port","max-message-size"): + value = transport_data.get(key) + if value is not None: + metadata[key] = value + try: + call_data[media_type]["ice-candidates"] = transport_data.get( + "candidates", [] + ) + metadata["ice-ufrag"] = transport_data["ufrag"] + metadata["ice-pwd"] = transport_data["pwd"] + except KeyError: + log.warning("ICE data are missing from SDP") + continue + for media in to_delete: + del sdp_data[media] + metadata.update(sdp_data.get("metadata", {})) + + return metadata + async def call_start( self, client: SatXMPPEntity, @@ -166,46 +211,9 @@ @raises exceptions.DataError: If media data is invalid or duplicate content name (mid) is found. """ + sid = str(uuid.uuid4()) + metadata = self.parse_call_data(call_data) contents = [] - metadata = call_data.get("metadata") or {} - - if "sdp" in call_data: - sdp_data = mapping.parse_sdp(call_data["sdp"]) - to_delete = set() - for media, data in sdp_data.items(): - if media not in ("audio", "video"): - continue - to_delete.add(media) - media_type, media_data = media, data - call_data[media_type] = media_data["application_data"] - transport_data = media_data["transport_data"] - try: - call_data[media_type]["fingerprint"] = transport_data["fingerprint"] - except KeyError: - log.warning("fingerprint is missing") - pass - try: - call_data[media_type]["id"] = media_data["id"] - except KeyError: - log.warning(f"no media ID found for {media_type}: {media_data}") - try: - call_data[media_type]["ice-candidates"] = transport_data.get( - "candidates", [] - ) - metadata["ice-ufrag"] = transport_data["ufrag"] - metadata["ice-pwd"] = transport_data["pwd"] - except KeyError: - log.warning("ICE data are missing from SDP") - continue - for media in to_delete: - del sdp_data[media] - metadata.update(sdp_data.get("metadata", {})) - - call_type = ( - C.META_SUBTYPE_CALL_VIDEO - if "video" in call_data - else C.META_SUBTYPE_CALL_AUDIO - ) seen_names = set() for media, media_data in call_data.items(): @@ -235,7 +243,12 @@ contents.append(content) if not contents: raise exceptions.DataError("no valid media data found: {call_data}") - sid = str(uuid.uuid4()) + + call_type = ( + C.META_SUBTYPE_CALL_VIDEO if "video" in call_data + else C.META_SUBTYPE_CALL_AUDIO + ) + defer.ensureDeferred( self._j.initiate( client, @@ -295,7 +308,7 @@ @param client: The client entity. @param session: The Jingle session. - @param media_type: Type of media (audio or video). + @param call_type: Type of media (audio or video). @return: True if the call has been accepted """ @@ -393,15 +406,13 @@ self, client: SatXMPPEntity, session: dict, cancel_error: exceptions.CancelError ) -> None: """The call has been rejected""" - # call_ended is use to send the signal only once even if there are audio and video - # contents + # call_ended is used to send the signal only once even if there are audio and + # video contents call_ended = session.get("call_ended", False) if call_ended: return - data = {"reason": getattr(cancel_error, "reason", "cancelled")} - text = getattr(cancel_error, "text", None) - if text: - data["text"] = text + data = {"reason": getattr(cancel_error, "reason", None) or "cancelled"} + data["text"] = str(cancel_error) self.host.bridge.call_ended( session["id"], data_format.serialise(data), client.profile )