Tue, 04 Feb 2020 03:05:18 +0000
Merged in fbellet/pidgin/port-changes-from-branch-2.x.y-to-default (pull request #632)
Port changes from branch 2.x.y to default
Approved-by: Eion Robb <eionrobb@gmail.com>
Approved-by: John Bailey <rekkanoryo@rekkanoryo.org>
Approved-by: Gary Kramlich <grim@reaperworld.com>
/** * @file rtp.c * * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "internal.h" #ifdef USE_VV #include "jabber.h" #include "jingle.h" #include "google/google_p2p.h" #include "media.h" #include "mediamanager.h" #include "iceudp.h" #include "rawudp.h" #include "rtp.h" #include "session.h" #include "debug.h" #include <string.h> struct _JingleRtp { JingleContent parent; }; typedef struct { gchar *media_type; gchar *ssrc; } JingleRtpPrivate; static JingleContent *jingle_rtp_parse_internal(PurpleXmlNode *rtp); static PurpleXmlNode *jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action); static void jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action); static PurpleMedia *jingle_rtp_get_media(JingleSession *session); enum { PROP_0, PROP_MEDIA_TYPE, PROP_SSRC, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; G_DEFINE_DYNAMIC_TYPE_EXTENDED( JingleRtp, jingle_rtp, JINGLE_TYPE_CONTENT, 0, G_ADD_PRIVATE_DYNAMIC(JingleRtp) ); /****************************************************************************** * Helpers *****************************************************************************/ static JingleTransport * jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates) { JingleTransport *transport; transport = jingle_transport_create(type); if (!transport) return NULL; for (; candidates; candidates = g_list_next(candidates)) { PurpleMediaCandidate *candidate = candidates->data; if (purple_media_candidate_get_protocol(candidate) == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) { gchar *id = jabber_get_next_id(jingle_session_get_js(session)); jingle_transport_add_local_candidate(transport, id, generation, candidate); g_free(id); } } return transport; } static void jingle_rtp_ready(JingleSession *session); static void jingle_rtp_candidates_prepared_cb(PurpleMedia *media, gchar *sid, gchar *name, JingleSession *session) { JingleContent *content = jingle_session_find_content( session, sid, NULL); JingleTransport *oldtransport, *transport; GList *candidates; purple_debug_info("jingle-rtp", "jingle_rtp_candidates_prepared_cb\n"); if (content == NULL) { purple_debug_error("jingle-rtp", "jingle_rtp_candidates_prepared_cb: " "Can't find session %s\n", sid); return; } oldtransport = jingle_content_get_transport(content); candidates = purple_media_get_local_candidates(media, sid, name); transport = jingle_rtp_candidates_to_transport( session, jingle_transport_get_transport_type(oldtransport), 0, candidates); purple_media_candidate_list_free(candidates); g_object_unref(oldtransport); jingle_content_set_pending_transport(content, transport); jingle_content_accept_transport(content); jingle_rtp_ready(session); } static void jingle_rtp_codecs_changed_cb(PurpleMedia *media, gchar *sid, JingleSession *session) { purple_debug_info("jingle-rtp", "jingle_rtp_codecs_changed_cb: " "session_id: %s jingle_session: %p\n", sid, session); jingle_rtp_ready(session); } static void jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session) { JingleContent *content = jingle_session_find_content(session, sid, NULL); JingleTransport *transport; gchar *id; purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n"); if (content == NULL) { purple_debug_error("jingle-rtp", "jingle_rtp_new_candidate_cb: " "Can't find session %s\n", sid); return; } transport = jingle_content_get_transport(content); id = jabber_get_next_id(jingle_session_get_js(session)); jingle_transport_add_local_candidate(transport, id, 1, candidate); g_free(id); g_object_unref(transport); jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO)); } static void jingle_rtp_initiate_ack_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data) { JingleSession *session = data; if (type == JABBER_IQ_ERROR || purple_xmlnode_get_child(packet, "error")) { purple_media_end(jingle_rtp_get_media(session), NULL, NULL); g_object_unref(session); return; } } static void jingle_rtp_state_changed_cb(PurpleMedia *media, PurpleMediaState state, gchar *sid, gchar *name, JingleSession *session) { purple_debug_info("jingle-rtp", "state-changed: state %d " "id: %s name: %s\n", state, sid ? sid : "(null)", name ? name : "(null)"); } static void jingle_rtp_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sid, gchar *name, gboolean local, JingleSession *session) { purple_debug_info("jingle-rtp", "stream-info: type %d " "id: %s name: %s\n", type, sid ? sid : "(null)", name ? name : "(null)"); g_return_if_fail(JINGLE_IS_SESSION(session)); if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) { jabber_iq_send(jingle_session_terminate_packet( session, type == PURPLE_MEDIA_INFO_HANGUP ? "success" : "decline")); g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(jingle_rtp_state_changed_cb), session); g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(jingle_rtp_stream_info_cb), session); g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(jingle_rtp_new_candidate_cb), session); g_object_unref(session); /* The same signal is emited *four* times in case of acceptance * by purple_media_stream_info() (stream acceptance, session * acceptance, participant acceptance, and conference acceptance). * We only react to the first one, where sid and name are given * non-null values. */ } else if (type == PURPLE_MEDIA_INFO_ACCEPT && sid && name && jingle_session_is_initiator(session) == FALSE) { jingle_rtp_ready(session); } } static void jingle_rtp_ready(JingleSession *session) { PurpleMedia *media = jingle_rtp_get_media(session); if (purple_media_candidates_prepared(media, NULL, NULL) && purple_media_codecs_ready(media, NULL) && (jingle_session_is_initiator(session) == TRUE || purple_media_accepted(media, NULL, NULL))) { if (jingle_session_is_initiator(session)) { JabberIq *iq = jingle_session_to_packet( session, JINGLE_SESSION_INITIATE); jabber_iq_set_callback(iq, jingle_rtp_initiate_ack_cb, session); jabber_iq_send(iq); } else { jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_ACCEPT)); } g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(jingle_rtp_candidates_prepared_cb), session); g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(jingle_rtp_codecs_changed_cb), session); g_signal_connect(G_OBJECT(media), "new-candidate", G_CALLBACK(jingle_rtp_new_candidate_cb), session); } } static PurpleMedia * jingle_rtp_create_media(JingleContent *content) { JingleSession *session = jingle_content_get_session(content); JabberStream *js = jingle_session_get_js(session); gchar *remote_jid = jingle_session_get_remote_jid(session); PurpleMedia *media = purple_media_manager_create_media( purple_media_manager_get(), purple_connection_get_account(js->gc), "fsrtpconference", remote_jid, jingle_session_is_initiator(session)); g_free(remote_jid); if (!media) { purple_debug_error("jingle-rtp", "Couldn't create media session\n"); return NULL; } purple_media_set_protocol_data(media, session); /* connect callbacks */ g_signal_connect(G_OBJECT(media), "candidates-prepared", G_CALLBACK(jingle_rtp_candidates_prepared_cb), session); g_signal_connect(G_OBJECT(media), "codecs-changed", G_CALLBACK(jingle_rtp_codecs_changed_cb), session); g_signal_connect(G_OBJECT(media), "state-changed", G_CALLBACK(jingle_rtp_state_changed_cb), session); g_signal_connect(G_OBJECT(media), "stream-info", G_CALLBACK(jingle_rtp_stream_info_cb), session); g_object_unref(session); return media; } static gboolean jingle_rtp_init_media(JingleContent *content) { JingleSession *session = jingle_content_get_session(content); PurpleMedia *media = jingle_rtp_get_media(session); gchar *creator; gchar *media_type; gchar *remote_jid; gchar *senders; gchar *name; const gchar *transmitter; gboolean is_audio; gboolean is_creator; PurpleMediaSessionType type; JingleTransport *transport; GParameter *params = NULL; guint num_params; /* maybe this create ought to just be in initiate and handle initiate */ if (media == NULL) { media = jingle_rtp_create_media(content); if (media == NULL) return FALSE; } media_type = jingle_rtp_get_media_type(content); if (media_type == NULL) { g_object_unref(session); return FALSE; } name = jingle_content_get_name(content); remote_jid = jingle_session_get_remote_jid(session); senders = jingle_content_get_senders(content); transport = jingle_content_get_transport(content); if (JINGLE_IS_RAWUDP(transport)) transmitter = "rawudp"; else if (JINGLE_IS_ICEUDP(transport)) transmitter = "nice"; else if (JINGLE_IS_GOOGLE_P2P(transport)) transmitter = "nice"; else transmitter = "notransmitter"; g_object_unref(transport); is_audio = purple_strequal(media_type, "audio"); if (purple_strequal(senders, "both")) type = is_audio ? PURPLE_MEDIA_AUDIO : PURPLE_MEDIA_VIDEO; else if (purple_strequal(senders, "initiator") == jingle_session_is_initiator(session)) type = is_audio ? PURPLE_MEDIA_SEND_AUDIO : PURPLE_MEDIA_SEND_VIDEO; else type = is_audio ? PURPLE_MEDIA_RECV_AUDIO : PURPLE_MEDIA_RECV_VIDEO; params = jingle_get_params(jingle_session_get_js(session), NULL, 0, 0, 0, NULL, NULL, &num_params); creator = jingle_content_get_creator(content); if (creator == NULL) { g_free(name); g_free(media_type); g_free(remote_jid); g_free(senders); g_free(params); g_object_unref(session); return FALSE; } if (purple_strequal(creator, "initiator")) is_creator = jingle_session_is_initiator(session); else is_creator = !jingle_session_is_initiator(session); g_free(creator); if(!purple_media_add_stream(media, name, remote_jid, type, is_creator, transmitter, num_params, params)) { purple_media_end(media, NULL, NULL); /* TODO: How much clean-up is necessary here? (does calling purple_media_end lead to cleaning up Jingle structs?) */ return FALSE; } g_free(name); g_free(media_type); g_free(remote_jid); g_free(senders); g_free(params); g_object_unref(session); return TRUE; } static GList * jingle_rtp_parse_codecs(PurpleXmlNode *description) { GList *codecs = NULL; PurpleXmlNode *codec_element = NULL; const char *encoding_name,*id, *clock_rate; PurpleMediaCodec *codec; const gchar *media = purple_xmlnode_get_attrib(description, "media"); PurpleMediaSessionType type; if (media == NULL) { purple_debug_warning("jingle-rtp", "missing media type\n"); return NULL; } if (purple_strequal(media, "video")) { type = PURPLE_MEDIA_VIDEO; } else if (purple_strequal(media, "audio")) { type = PURPLE_MEDIA_AUDIO; } else { purple_debug_warning("jingle-rtp", "unknown media type: %s\n", media); return NULL; } for (codec_element = purple_xmlnode_get_child(description, "payload-type") ; codec_element ; codec_element = purple_xmlnode_get_next_twin(codec_element)) { PurpleXmlNode *param; gchar *codec_str; encoding_name = purple_xmlnode_get_attrib(codec_element, "name"); id = purple_xmlnode_get_attrib(codec_element, "id"); clock_rate = purple_xmlnode_get_attrib(codec_element, "clockrate"); codec = purple_media_codec_new(atoi(id), encoding_name, type, clock_rate ? atoi(clock_rate) : 0); for (param = purple_xmlnode_get_child(codec_element, "parameter"); param; param = purple_xmlnode_get_next_twin(param)) { purple_media_codec_add_optional_parameter(codec, purple_xmlnode_get_attrib(param, "name"), purple_xmlnode_get_attrib(param, "value")); } codec_str = purple_media_codec_to_string(codec); purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str); g_free(codec_str); codecs = g_list_append(codecs, codec); } return codecs; } static void jingle_rtp_add_payloads(PurpleXmlNode *description, GList *codecs) { for (; codecs ; codecs = codecs->next) { PurpleMediaCodec *codec = (PurpleMediaCodec*)codecs->data; GList *iter = purple_media_codec_get_optional_parameters(codec); gchar *id, *name, *clockrate, *channels; gchar *codec_str; PurpleXmlNode *payload = purple_xmlnode_new_child(description, "payload-type"); id = g_strdup_printf("%d", purple_media_codec_get_id(codec)); name = purple_media_codec_get_encoding_name(codec); clockrate = g_strdup_printf("%d", purple_media_codec_get_clock_rate(codec)); channels = g_strdup_printf("%d", purple_media_codec_get_channels(codec)); purple_xmlnode_set_attrib(payload, "name", name); purple_xmlnode_set_attrib(payload, "id", id); purple_xmlnode_set_attrib(payload, "clockrate", clockrate); purple_xmlnode_set_attrib(payload, "channels", channels); g_free(channels); g_free(clockrate); g_free(name); g_free(id); for (; iter; iter = g_list_next(iter)) { PurpleKeyValuePair *mparam = iter->data; PurpleXmlNode *param = purple_xmlnode_new_child(payload, "parameter"); purple_xmlnode_set_attrib(param, "name", mparam->key); purple_xmlnode_set_attrib(param, "value", mparam->value); } codec_str = purple_media_codec_to_string(codec); purple_debug_info("jingle", "adding codec: %s\n", codec_str); g_free(codec_str); } } /****************************************************************************** * JingleContent Implementation *****************************************************************************/ static PurpleXmlNode * jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action) { PurpleXmlNode *node = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->to_xml(rtp, content, action); PurpleXmlNode *description = purple_xmlnode_get_child(node, "description"); if (description != NULL) { JingleSession *session = jingle_content_get_session(rtp); PurpleMedia *media = jingle_rtp_get_media(session); gchar *media_type = jingle_rtp_get_media_type(rtp); gchar *ssrc = jingle_rtp_get_ssrc(rtp); gchar *name = jingle_content_get_name(rtp); GList *codecs = purple_media_get_codecs(media, name); purple_xmlnode_set_attrib(description, "media", media_type); if (ssrc != NULL) purple_xmlnode_set_attrib(description, "ssrc", ssrc); g_free(media_type); g_free(name); g_object_unref(session); jingle_rtp_add_payloads(description, codecs); purple_media_codec_list_free(codecs); } return node; } static JingleContent * jingle_rtp_parse_internal(PurpleXmlNode *rtp) { JingleContent *content = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->parse(rtp); PurpleXmlNode *description = purple_xmlnode_get_child(rtp, "description"); const gchar *media_type = purple_xmlnode_get_attrib(description, "media"); const gchar *ssrc = purple_xmlnode_get_attrib(description, "ssrc"); purple_debug_info("jingle-rtp", "rtp parse\n"); g_object_set(content, "media-type", media_type, NULL); if (ssrc != NULL) g_object_set(content, "ssrc", ssrc, NULL); return content; } static void jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action) { switch (action) { case JINGLE_SESSION_ACCEPT: case JINGLE_SESSION_INITIATE: { JingleSession *session; JingleTransport *transport; PurpleXmlNode *description; GList *candidates; GList *codecs; gchar *name; gchar *remote_jid; PurpleMedia *media; session = jingle_content_get_session(content); if (action == JINGLE_SESSION_INITIATE && !jingle_rtp_init_media(content)) { /* XXX: send error */ jabber_iq_send(jingle_session_terminate_packet( session, "general-error")); g_object_unref(session); break; } transport = jingle_transport_parse( purple_xmlnode_get_child(xmlcontent, "transport")); description = purple_xmlnode_get_child(xmlcontent, "description"); candidates = jingle_transport_get_remote_candidates(transport); codecs = jingle_rtp_parse_codecs(description); name = jingle_content_get_name(content); remote_jid = jingle_session_get_remote_jid(session); media = jingle_rtp_get_media(session); purple_media_set_remote_codecs(media, name, remote_jid, codecs); purple_media_add_remote_candidates(media, name, remote_jid, candidates); if (action == JINGLE_SESSION_ACCEPT) purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT, name, remote_jid, FALSE); g_free(remote_jid); g_free(name); g_object_unref(session); g_object_unref(transport); purple_media_candidate_list_free(candidates); purple_media_codec_list_free(codecs); break; } case JINGLE_SESSION_TERMINATE: { JingleSession *session = jingle_content_get_session(content); PurpleMedia *media = jingle_rtp_get_media(session); if (media != NULL) { purple_media_end(media, NULL, NULL); } g_object_unref(session); break; } case JINGLE_TRANSPORT_INFO: { JingleSession *session = jingle_content_get_session(content); JingleTransport *transport = jingle_transport_parse( purple_xmlnode_get_child(xmlcontent, "transport")); GList *candidates = jingle_transport_get_remote_candidates(transport); gchar *name = jingle_content_get_name(content); gchar *remote_jid = jingle_session_get_remote_jid(session); purple_media_add_remote_candidates( jingle_rtp_get_media(session), name, remote_jid, candidates); g_free(remote_jid); g_free(name); g_object_unref(session); g_object_unref(transport); purple_media_candidate_list_free(candidates); break; } case JINGLE_DESCRIPTION_INFO: { JingleSession *session = jingle_content_get_session(content); PurpleXmlNode *description = purple_xmlnode_get_child( xmlcontent, "description"); GList *codecs, *iter, *iter2, *remote_codecs = jingle_rtp_parse_codecs(description); gchar *name = jingle_content_get_name(content); gchar *remote_jid = jingle_session_get_remote_jid(session); PurpleMedia *media; media = jingle_rtp_get_media(session); /* * This may have problems if description-info is * received without the optional parameters for a * codec with configuration info (such as THEORA * or H264). The local configuration info may be * set for the remote codec. * * As of 2.6.3 there's no API to support getting * the remote codecs specifically, just the * intersection. Another option may be to cache * the remote codecs received in initiate/accept. */ codecs = purple_media_get_codecs(media, name); for (iter = codecs; iter; iter = g_list_next(iter)) { guint id; id = purple_media_codec_get_id(iter->data); iter2 = remote_codecs; for (; iter2; iter2 = g_list_next(iter2)) { if (purple_media_codec_get_id( iter2->data) != id) continue; g_object_unref(iter->data); iter->data = iter2->data; remote_codecs = g_list_delete_link( remote_codecs, iter2); break; } } codecs = g_list_concat(codecs, remote_codecs); purple_media_set_remote_codecs(media, name, remote_jid, codecs); purple_media_codec_list_free (codecs); g_free(remote_jid); g_free(name); g_object_unref(session); break; } default: break; } } /****************************************************************************** * GObject Stuff *****************************************************************************/ static void jingle_rtp_init (JingleRtp *rtp) { } static void jingle_rtp_finalize (GObject *rtp) { JingleRtpPrivate *priv = jingle_rtp_get_instance_private(JINGLE_RTP(rtp)); purple_debug_info("jingle-rtp","jingle_rtp_finalize\n"); g_free(priv->media_type); g_free(priv->ssrc); G_OBJECT_CLASS(jingle_rtp_parent_class)->finalize(rtp); } static void jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { JingleRtp *rtp = JINGLE_RTP(object); JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp); switch (prop_id) { case PROP_MEDIA_TYPE: g_free(priv->media_type); priv->media_type = g_value_dup_string(value); break; case PROP_SSRC: g_free(priv->ssrc); priv->ssrc = g_value_dup_string(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { JingleRtp *rtp = JINGLE_RTP(object); JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp); switch (prop_id) { case PROP_MEDIA_TYPE: g_value_set_string(value, priv->media_type); break; case PROP_SSRC: g_value_set_string(value, priv->ssrc); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void jingle_rtp_class_finalize(JingleRtpClass *klass) { } static void jingle_rtp_class_init (JingleRtpClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); JingleContentClass *content_class = JINGLE_CONTENT_CLASS(klass); obj_class->finalize = jingle_rtp_finalize; obj_class->set_property = jingle_rtp_set_property; obj_class->get_property = jingle_rtp_get_property; content_class->to_xml = jingle_rtp_to_xml_internal; content_class->parse = jingle_rtp_parse_internal; content_class->description_type = JINGLE_APP_RTP; content_class->handle_action = jingle_rtp_handle_action_internal; properties[PROP_MEDIA_TYPE] = g_param_spec_string("media-type", "Media Type", "The media type (\"audio\" or \"video\") for this rtp session.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_SSRC] = g_param_spec_string("ssrc", "ssrc", "The ssrc for this rtp session.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, PROP_LAST, properties); } /****************************************************************************** * Public API *****************************************************************************/ void jingle_rtp_register(PurplePlugin *plugin) { jingle_rtp_register_type(G_TYPE_MODULE(plugin)); } gchar * jingle_rtp_get_media_type(JingleContent *content) { gchar *media_type; g_object_get(content, "media-type", &media_type, NULL); return media_type; } gchar * jingle_rtp_get_ssrc(JingleContent *content) { gchar *ssrc; g_object_get(content, "ssrc", &ssrc, NULL); return ssrc; } static PurpleMedia * jingle_rtp_get_media(JingleSession *session) { JabberStream *js = jingle_session_get_js(session); PurpleMedia *media = NULL; GList *iter = purple_media_manager_get_media_by_account( purple_media_manager_get(), purple_connection_get_account(js->gc)); for (; iter; iter = g_list_delete_link(iter, iter)) { JingleSession *media_session = purple_media_get_protocol_data(iter->data); if (media_session == session) { media = iter->data; break; } } if (iter != NULL) g_list_free(iter); return media; } gboolean jingle_rtp_initiate_media(JabberStream *js, const gchar *who, PurpleMediaSessionType type) { /* create content negotiation */ JingleSession *session; JingleContent *content; JingleTransport *transport; JabberBuddy *jb; JabberBuddyResource *jbr; gboolean ret = FALSE; const gchar *transport_type; gchar *resource = NULL, *me = NULL, *sid = NULL; /* construct JID to send to */ jb = jabber_buddy_find(js, who, FALSE); if (!jb) { purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n"); goto out; } resource = jabber_get_resource(who); jbr = jabber_buddy_find_resource(jb, resource); if (!jbr) { purple_debug_error("jingle-rtp", "Could not find buddy's resource - %s\n", resource); goto out; } if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_ICEUDP)) { transport_type = JINGLE_TRANSPORT_ICEUDP; } else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) { transport_type = JINGLE_TRANSPORT_RAWUDP; } else if (jabber_resource_has_capability(jbr, NS_GOOGLE_TRANSPORT_P2P)) { transport_type = NS_GOOGLE_TRANSPORT_P2P; } else { purple_debug_error("jingle-rtp", "Resource doesn't support " "the same transport types\n"); goto out; } /* set ourselves as initiator */ me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource); sid = jabber_get_next_id(js); session = jingle_session_create(js, sid, me, who, TRUE); if (type & PURPLE_MEDIA_AUDIO) { JingleRtpPrivate *priv = NULL; transport = jingle_transport_create(transport_type); content = jingle_content_create(JINGLE_APP_RTP, "initiator", "session", "audio-session", "both", transport); priv = jingle_rtp_get_instance_private(JINGLE_RTP(content)); jingle_session_add_content(session, content); priv->media_type = g_strdup("audio"); jingle_rtp_init_media(content); g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]); } if (type & PURPLE_MEDIA_VIDEO) { JingleRtpPrivate *priv = NULL; transport = jingle_transport_create(transport_type); content = jingle_content_create(JINGLE_APP_RTP, "initiator", "session", "video-session", "both", transport); priv = jingle_rtp_get_instance_private(JINGLE_RTP(content)); jingle_session_add_content(session, content); priv->media_type = g_strdup("video"); jingle_rtp_init_media(content); g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]); } if (jingle_rtp_get_media(session) == NULL) { goto out; } ret = TRUE; out: g_free(me); g_free(resource); g_free(sid); return ret; } void jingle_rtp_terminate_session(JabberStream *js, const gchar *who) { JingleSession *session; /* XXX: This may cause file transfers and xml sessions to stop as well */ session = jingle_session_find_by_jid(js, who); if (session) { PurpleMedia *media = jingle_rtp_get_media(session); if (media) { purple_debug_info("jingle-rtp", "hanging up media\n"); purple_media_stream_info(media, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE); } } } #endif /* USE_VV */