From dfd79401044834b164c50f5948986719eabf8127 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 21 Mar 2021 12:41:32 +0100 Subject: Add support for Jingle RTP sessions (XEP-0167) to xmpp-vala Co-authored-by: fiaxh --- .../xep/0167_jingle_rtp/jingle_rtp_module.vala | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala (limited to 'xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala') diff --git a/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala b/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala new file mode 100644 index 00000000..35e03168 --- /dev/null +++ b/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala @@ -0,0 +1,175 @@ +using Gee; +using Xmpp; +using Xmpp.Xep; + +namespace Xmpp.Xep.JingleRtp { + +public const string NS_URI = "urn:xmpp:jingle:apps:rtp:1"; +public const string NS_URI_AUDIO = "urn:xmpp:jingle:apps:rtp:audio"; +public const string NS_URI_VIDEO = "urn:xmpp:jingle:apps:rtp:video"; + +public abstract class Module : XmppStreamModule { + public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "0167_jingle_rtp"); + + private ContentType content_type; + public SessionInfoType session_info_type = new SessionInfoType(); + + protected Module() { + content_type = new ContentType(this); + } + + public abstract async Gee.List get_supported_payloads(string media); + public abstract async PayloadType? pick_payload_type(string media, Gee.List payloads); + public abstract Stream create_stream(Jingle.Content content); + public abstract void close_stream(Stream stream); + + public async Jingle.Session start_call(XmppStream stream, Jid receiver_full_jid, bool video) throws Jingle.Error { + + Jingle.Module jingle_module = stream.get_module(Jingle.Module.IDENTITY); + + Jid? my_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid; + if (my_jid == null) { + throw new Jingle.Error.GENERAL("Couldn't determine own JID"); + } + + ArrayList contents = new ArrayList(); + + // Create audio content + Parameters audio_content_parameters = new Parameters(this, "audio", yield get_supported_payloads("audio")); + Jingle.Transport? audio_transport = yield jingle_module.select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty()); + if (audio_transport == null) { + throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable audio transports"); + } + Jingle.TransportParameters audio_transport_params = audio_transport.create_transport_parameters(stream, content_type.required_components, my_jid, receiver_full_jid); + Jingle.Content audio_content = new Jingle.Content.initiate_sent("voice", Jingle.Senders.BOTH, + content_type, audio_content_parameters, + audio_transport, audio_transport_params, + null, null, + my_jid, receiver_full_jid); + contents.add(audio_content); + + Jingle.Content? video_content = null; + if (video) { + // Create video content + Parameters video_content_parameters = new Parameters(this, "video", yield get_supported_payloads("video")); + Jingle.Transport? video_transport = yield stream.get_module(Jingle.Module.IDENTITY).select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty()); + if (video_transport == null) { + throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable video transports"); + } + Jingle.TransportParameters video_transport_params = video_transport.create_transport_parameters(stream, content_type.required_components, my_jid, receiver_full_jid); + video_content = new Jingle.Content.initiate_sent("webcam", Jingle.Senders.BOTH, + content_type, video_content_parameters, + video_transport, video_transport_params, + null, null, + my_jid, receiver_full_jid); + contents.add(video_content); + } + + // Create session + try { + Jingle.Session session = yield jingle_module.create_session(stream, contents, receiver_full_jid); + return session; + } catch (Jingle.Error e) { + throw new Jingle.Error.GENERAL(@"Couldn't create Jingle session: $(e.message)"); + } + } + + public async Jingle.Content add_outgoing_video_content(XmppStream stream, Jingle.Session session) { + Jid my_jid = session.local_full_jid; + Jid receiver_full_jid = session.peer_full_jid; + + Jingle.Content? content = null; + foreach (Jingle.Content c in session.contents.values) { + Parameters? parameters = c.content_params as Parameters; + if (parameters == null) continue; + + if (parameters.media == "video") { + content = c; + break; + } + } + + if (content == null) { + // Content for video does not yet exist -> create it + Parameters video_content_parameters = new Parameters(this, "video", yield get_supported_payloads("video")); + Jingle.Transport? video_transport = yield stream.get_module(Jingle.Module.IDENTITY).select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty()); + if (video_transport == null) { + throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable video transports"); + } + Jingle.TransportParameters video_transport_params = video_transport.create_transport_parameters(stream, content_type.required_components, my_jid, receiver_full_jid); + content = new Jingle.Content.initiate_sent("webcam", + session.we_initiated ? Jingle.Senders.INITIATOR : Jingle.Senders.RESPONDER, + content_type, video_content_parameters, + video_transport, video_transport_params, + null, null, + my_jid, receiver_full_jid); + + session.add_content.begin(content); + } else { + // Content for video already exists -> modify senders + bool we_initiated = session.we_initiated; + Jingle.Senders want_sender = we_initiated ? Jingle.Senders.INITIATOR : Jingle.Senders.RESPONDER; + if (content.senders == Jingle.Senders.BOTH || content.senders == want_sender) { + warning("want to add video but senders is already both/target"); + } else if (content.senders == Jingle.Senders.NONE) { + content.modify(want_sender); + } else { + content.modify(Jingle.Senders.BOTH); + } + } + + return content; + } + + public override void attach(XmppStream stream) { + stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); + stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI_AUDIO); + stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI_VIDEO); + stream.get_module(Jingle.Module.IDENTITY).register_content_type(content_type); + stream.get_module(Jingle.Module.IDENTITY).register_session_info_type(session_info_type); + } + + public override void detach(XmppStream stream) { + stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); + stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI_AUDIO); + stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI_VIDEO); + } + + public async bool is_available(XmppStream stream, Jid full_jid) { + bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); + if (has_feature == null || !(!)has_feature) { + return false; + } + return yield stream.get_module(Jingle.Module.IDENTITY).is_available(stream, content_type.required_transport_type, content_type.required_components, full_jid); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return IDENTITY.id; } +} + +public class Crypto { + public string cryptoSuite { get; private set; } + public string keyParams { get; private set; } + public string? sessionParams { get; private set; } + public string? tag { get; private set; } + + public static Crypto parse(StanzaNode node) { + Crypto crypto = new Crypto(); + crypto.cryptoSuite = node.get_attribute("crypto-suite"); + crypto.keyParams = node.get_attribute("key-params"); + crypto.sessionParams = node.get_attribute("session-params"); + crypto.tag = node.get_attribute("tag"); + return crypto; + } + + public StanzaNode to_xml() { + StanzaNode node = new StanzaNode.build("crypto", NS_URI) + .put_attribute("crypto-suite", cryptoSuite) + .put_attribute("key-params", keyParams); + if (sessionParams != null) node.put_attribute("session-params", sessionParams); + if (tag != null) node.put_attribute("tag", tag); + return node; + } +} + +} -- cgit v1.2.3-70-g09d2