Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / protocols / jabber / jingle / rtp.c
blob1bb596c6e917d18af92e076088539f2f3edac477
1 /**
2 * @file rtp.c
4 * purple
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "internal.h"
26 #include "glibcompat.h"
28 #ifdef USE_VV
30 #include "jabber.h"
31 #include "jingle.h"
32 #include "google/google_p2p.h"
33 #include "media.h"
34 #include "mediamanager.h"
35 #include "iceudp.h"
36 #include "rawudp.h"
37 #include "rtp.h"
38 #include "session.h"
39 #include "debug.h"
41 #include <string.h>
43 struct _JingleRtp
45 JingleContent parent;
48 typedef struct
50 gchar *media_type;
51 gchar *ssrc;
52 } JingleRtpPrivate;
54 static JingleContent *jingle_rtp_parse_internal(PurpleXmlNode *rtp);
55 static PurpleXmlNode *jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action);
56 static void jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action);
58 static PurpleMedia *jingle_rtp_get_media(JingleSession *session);
60 enum {
61 PROP_0,
62 PROP_MEDIA_TYPE,
63 PROP_SSRC,
64 PROP_LAST
67 static GParamSpec *properties[PROP_LAST];
69 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
70 JingleRtp,
71 jingle_rtp,
72 JINGLE_TYPE_CONTENT,
74 G_ADD_PRIVATE_DYNAMIC(JingleRtp)
77 /******************************************************************************
78 * Helpers
79 *****************************************************************************/
80 static JingleTransport *
81 jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates)
83 JingleTransport *transport;
85 transport = jingle_transport_create(type);
86 if (!transport)
87 return NULL;
89 for (; candidates; candidates = g_list_next(candidates)) {
90 PurpleMediaCandidate *candidate = candidates->data;
91 gchar *id = jabber_get_next_id(jingle_session_get_js(session));
92 jingle_transport_add_local_candidate(transport, id, generation, candidate);
93 g_free(id);
96 return transport;
99 static void jingle_rtp_ready(JingleSession *session);
101 static void
102 jingle_rtp_candidates_prepared_cb(PurpleMedia *media,
103 gchar *sid, gchar *name, JingleSession *session)
105 JingleContent *content = jingle_session_find_content(
106 session, sid, NULL);
107 JingleTransport *oldtransport, *transport;
108 GList *candidates;
110 purple_debug_info("jingle-rtp", "jingle_rtp_candidates_prepared_cb\n");
112 if (content == NULL) {
113 purple_debug_error("jingle-rtp",
114 "jingle_rtp_candidates_prepared_cb: "
115 "Can't find session %s\n", sid);
116 return;
119 oldtransport = jingle_content_get_transport(content);
120 candidates = purple_media_get_local_candidates(media, sid, name);
121 transport = jingle_rtp_candidates_to_transport(
122 session, jingle_transport_get_transport_type(oldtransport),
123 0, candidates);
125 g_list_free(candidates);
126 g_object_unref(oldtransport);
128 jingle_content_set_pending_transport(content, transport);
129 jingle_content_accept_transport(content);
131 jingle_rtp_ready(session);
134 static void
135 jingle_rtp_codecs_changed_cb(PurpleMedia *media, gchar *sid,
136 JingleSession *session)
138 purple_debug_info("jingle-rtp", "jingle_rtp_codecs_changed_cb: "
139 "session_id: %s jingle_session: %p\n", sid, session);
140 jingle_rtp_ready(session);
143 static void
144 jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session)
146 JingleContent *content = jingle_session_find_content(session, sid, NULL);
147 JingleTransport *transport;
148 gchar *id;
150 purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
152 if (content == NULL) {
153 purple_debug_error("jingle-rtp",
154 "jingle_rtp_new_candidate_cb: "
155 "Can't find session %s\n", sid);
156 return;
159 transport = jingle_content_get_transport(content);
161 id = jabber_get_next_id(jingle_session_get_js(session));
162 jingle_transport_add_local_candidate(transport, id, 1, candidate);
163 g_free(id);
165 g_object_unref(transport);
167 jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO));
170 static void
171 jingle_rtp_initiate_ack_cb(JabberStream *js, const char *from,
172 JabberIqType type, const char *id,
173 PurpleXmlNode *packet, gpointer data)
175 JingleSession *session = data;
177 if (type == JABBER_IQ_ERROR || purple_xmlnode_get_child(packet, "error")) {
178 purple_media_end(jingle_rtp_get_media(session), NULL, NULL);
179 g_object_unref(session);
180 return;
184 static void
185 jingle_rtp_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
186 gchar *sid, gchar *name, JingleSession *session)
188 purple_debug_info("jingle-rtp", "state-changed: state %d "
189 "id: %s name: %s\n", state, sid ? sid : "(null)",
190 name ? name : "(null)");
193 static void
194 jingle_rtp_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
195 gchar *sid, gchar *name, gboolean local,
196 JingleSession *session)
198 purple_debug_info("jingle-rtp", "stream-info: type %d "
199 "id: %s name: %s\n", type, sid ? sid : "(null)",
200 name ? name : "(null)");
202 g_return_if_fail(JINGLE_IS_SESSION(session));
204 if (type == PURPLE_MEDIA_INFO_HANGUP ||
205 type == PURPLE_MEDIA_INFO_REJECT) {
206 jabber_iq_send(jingle_session_terminate_packet(
207 session, type == PURPLE_MEDIA_INFO_HANGUP ?
208 "success" : "decline"));
210 g_signal_handlers_disconnect_by_func(G_OBJECT(media),
211 G_CALLBACK(jingle_rtp_state_changed_cb),
212 session);
213 g_signal_handlers_disconnect_by_func(G_OBJECT(media),
214 G_CALLBACK(jingle_rtp_stream_info_cb),
215 session);
216 g_signal_handlers_disconnect_by_func(G_OBJECT(media),
217 G_CALLBACK(jingle_rtp_new_candidate_cb),
218 session);
220 g_object_unref(session);
221 } else if (type == PURPLE_MEDIA_INFO_ACCEPT &&
222 jingle_session_is_initiator(session) == FALSE) {
223 jingle_rtp_ready(session);
227 static void
228 jingle_rtp_ready(JingleSession *session)
230 PurpleMedia *media = jingle_rtp_get_media(session);
232 if (purple_media_candidates_prepared(media, NULL, NULL) &&
233 purple_media_codecs_ready(media, NULL) &&
234 (jingle_session_is_initiator(session) == TRUE ||
235 purple_media_accepted(media, NULL, NULL))) {
236 if (jingle_session_is_initiator(session)) {
237 JabberIq *iq = jingle_session_to_packet(
238 session, JINGLE_SESSION_INITIATE);
239 jabber_iq_set_callback(iq,
240 jingle_rtp_initiate_ack_cb, session);
241 jabber_iq_send(iq);
242 } else {
243 jabber_iq_send(jingle_session_to_packet(session,
244 JINGLE_SESSION_ACCEPT));
247 g_signal_handlers_disconnect_by_func(G_OBJECT(media),
248 G_CALLBACK(jingle_rtp_candidates_prepared_cb),
249 session);
250 g_signal_handlers_disconnect_by_func(G_OBJECT(media),
251 G_CALLBACK(jingle_rtp_codecs_changed_cb),
252 session);
253 g_signal_connect(G_OBJECT(media), "new-candidate",
254 G_CALLBACK(jingle_rtp_new_candidate_cb),
255 session);
259 static PurpleMedia *
260 jingle_rtp_create_media(JingleContent *content)
262 JingleSession *session = jingle_content_get_session(content);
263 JabberStream *js = jingle_session_get_js(session);
264 gchar *remote_jid = jingle_session_get_remote_jid(session);
266 PurpleMedia *media = purple_media_manager_create_media(
267 purple_media_manager_get(),
268 purple_connection_get_account(js->gc),
269 "fsrtpconference", remote_jid,
270 jingle_session_is_initiator(session));
271 g_free(remote_jid);
273 if (!media) {
274 purple_debug_error("jingle-rtp", "Couldn't create media session\n");
275 return NULL;
278 purple_media_set_protocol_data(media, session);
280 /* connect callbacks */
281 g_signal_connect(G_OBJECT(media), "candidates-prepared",
282 G_CALLBACK(jingle_rtp_candidates_prepared_cb), session);
283 g_signal_connect(G_OBJECT(media), "codecs-changed",
284 G_CALLBACK(jingle_rtp_codecs_changed_cb), session);
285 g_signal_connect(G_OBJECT(media), "state-changed",
286 G_CALLBACK(jingle_rtp_state_changed_cb), session);
287 g_signal_connect(G_OBJECT(media), "stream-info",
288 G_CALLBACK(jingle_rtp_stream_info_cb), session);
290 g_object_unref(session);
291 return media;
294 static gboolean
295 jingle_rtp_init_media(JingleContent *content)
297 JingleSession *session = jingle_content_get_session(content);
298 PurpleMedia *media = jingle_rtp_get_media(session);
299 gchar *creator;
300 gchar *media_type;
301 gchar *remote_jid;
302 gchar *senders;
303 gchar *name;
304 const gchar *transmitter;
305 gboolean is_audio;
306 gboolean is_creator;
307 PurpleMediaSessionType type;
308 JingleTransport *transport;
309 GParameter *params = NULL;
310 guint num_params;
312 /* maybe this create ought to just be in initiate and handle initiate */
313 if (media == NULL) {
314 media = jingle_rtp_create_media(content);
316 if (media == NULL)
317 return FALSE;
320 media_type = jingle_rtp_get_media_type(content);
321 if (media_type == NULL) {
322 g_object_unref(session);
323 return FALSE;
326 name = jingle_content_get_name(content);
327 remote_jid = jingle_session_get_remote_jid(session);
328 senders = jingle_content_get_senders(content);
329 transport = jingle_content_get_transport(content);
331 if (JINGLE_IS_RAWUDP(transport))
332 transmitter = "rawudp";
333 else if (JINGLE_IS_ICEUDP(transport))
334 transmitter = "nice";
335 else if (JINGLE_IS_GOOGLE_P2P(transport))
336 transmitter = "nice";
337 else
338 transmitter = "notransmitter";
339 g_object_unref(transport);
341 is_audio = purple_strequal(media_type, "audio");
343 if (purple_strequal(senders, "both"))
344 type = is_audio ? PURPLE_MEDIA_AUDIO
345 : PURPLE_MEDIA_VIDEO;
346 else if (purple_strequal(senders, "initiator") ==
347 jingle_session_is_initiator(session))
348 type = is_audio ? PURPLE_MEDIA_SEND_AUDIO
349 : PURPLE_MEDIA_SEND_VIDEO;
350 else
351 type = is_audio ? PURPLE_MEDIA_RECV_AUDIO
352 : PURPLE_MEDIA_RECV_VIDEO;
354 params =
355 jingle_get_params(jingle_session_get_js(session), NULL, 0, 0, 0,
356 NULL, NULL, &num_params);
358 creator = jingle_content_get_creator(content);
359 if (creator == NULL) {
360 g_free(name);
361 g_free(media_type);
362 g_free(remote_jid);
363 g_free(senders);
364 g_free(params);
365 g_object_unref(session);
366 return FALSE;
369 if (purple_strequal(creator, "initiator"))
370 is_creator = jingle_session_is_initiator(session);
371 else
372 is_creator = !jingle_session_is_initiator(session);
373 g_free(creator);
375 if(!purple_media_add_stream(media, name, remote_jid,
376 type, is_creator, transmitter, num_params, params)) {
377 purple_media_end(media, NULL, NULL);
378 /* TODO: How much clean-up is necessary here? (does calling
379 purple_media_end lead to cleaning up Jingle structs?) */
380 return FALSE;
383 g_free(name);
384 g_free(media_type);
385 g_free(remote_jid);
386 g_free(senders);
387 g_free(params);
388 g_object_unref(session);
390 return TRUE;
393 static GList *
394 jingle_rtp_parse_codecs(PurpleXmlNode *description)
396 GList *codecs = NULL;
397 PurpleXmlNode *codec_element = NULL;
398 const char *encoding_name,*id, *clock_rate;
399 PurpleMediaCodec *codec;
400 const gchar *media = purple_xmlnode_get_attrib(description, "media");
401 PurpleMediaSessionType type;
403 if (media == NULL) {
404 purple_debug_warning("jingle-rtp", "missing media type\n");
405 return NULL;
408 if (purple_strequal(media, "video")) {
409 type = PURPLE_MEDIA_VIDEO;
410 } else if (purple_strequal(media, "audio")) {
411 type = PURPLE_MEDIA_AUDIO;
412 } else {
413 purple_debug_warning("jingle-rtp", "unknown media type: %s\n",
414 media);
415 return NULL;
418 for (codec_element = purple_xmlnode_get_child(description, "payload-type") ;
419 codec_element ;
420 codec_element = purple_xmlnode_get_next_twin(codec_element)) {
421 PurpleXmlNode *param;
422 gchar *codec_str;
423 encoding_name = purple_xmlnode_get_attrib(codec_element, "name");
425 id = purple_xmlnode_get_attrib(codec_element, "id");
426 clock_rate = purple_xmlnode_get_attrib(codec_element, "clockrate");
428 codec = purple_media_codec_new(atoi(id), encoding_name,
429 type,
430 clock_rate ? atoi(clock_rate) : 0);
432 for (param = purple_xmlnode_get_child(codec_element, "parameter");
433 param; param = purple_xmlnode_get_next_twin(param)) {
434 purple_media_codec_add_optional_parameter(codec,
435 purple_xmlnode_get_attrib(param, "name"),
436 purple_xmlnode_get_attrib(param, "value"));
439 codec_str = purple_media_codec_to_string(codec);
440 purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str);
441 g_free(codec_str);
443 codecs = g_list_append(codecs, codec);
445 return codecs;
448 static void
449 jingle_rtp_add_payloads(PurpleXmlNode *description, GList *codecs)
451 for (; codecs ; codecs = codecs->next) {
452 PurpleMediaCodec *codec = (PurpleMediaCodec*)codecs->data;
453 GList *iter = purple_media_codec_get_optional_parameters(codec);
454 gchar *id, *name, *clockrate, *channels;
455 gchar *codec_str;
456 PurpleXmlNode *payload = purple_xmlnode_new_child(description, "payload-type");
458 id = g_strdup_printf("%d",
459 purple_media_codec_get_id(codec));
460 name = purple_media_codec_get_encoding_name(codec);
461 clockrate = g_strdup_printf("%d",
462 purple_media_codec_get_clock_rate(codec));
463 channels = g_strdup_printf("%d",
464 purple_media_codec_get_channels(codec));
466 purple_xmlnode_set_attrib(payload, "name", name);
467 purple_xmlnode_set_attrib(payload, "id", id);
468 purple_xmlnode_set_attrib(payload, "clockrate", clockrate);
469 purple_xmlnode_set_attrib(payload, "channels", channels);
471 g_free(channels);
472 g_free(clockrate);
473 g_free(name);
474 g_free(id);
476 for (; iter; iter = g_list_next(iter)) {
477 PurpleKeyValuePair *mparam = iter->data;
478 PurpleXmlNode *param = purple_xmlnode_new_child(payload, "parameter");
479 purple_xmlnode_set_attrib(param, "name", mparam->key);
480 purple_xmlnode_set_attrib(param, "value", mparam->value);
483 codec_str = purple_media_codec_to_string(codec);
484 purple_debug_info("jingle", "adding codec: %s\n", codec_str);
485 g_free(codec_str);
489 /******************************************************************************
490 * JingleContent Implementation
491 *****************************************************************************/
492 static PurpleXmlNode *
493 jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action)
495 PurpleXmlNode *node = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->to_xml(rtp, content, action);
496 PurpleXmlNode *description = purple_xmlnode_get_child(node, "description");
497 if (description != NULL) {
498 JingleSession *session = jingle_content_get_session(rtp);
499 PurpleMedia *media = jingle_rtp_get_media(session);
500 gchar *media_type = jingle_rtp_get_media_type(rtp);
501 gchar *ssrc = jingle_rtp_get_ssrc(rtp);
502 gchar *name = jingle_content_get_name(rtp);
503 GList *codecs = purple_media_get_codecs(media, name);
505 purple_xmlnode_set_attrib(description, "media", media_type);
507 if (ssrc != NULL)
508 purple_xmlnode_set_attrib(description, "ssrc", ssrc);
510 g_free(media_type);
511 g_free(name);
512 g_object_unref(session);
514 jingle_rtp_add_payloads(description, codecs);
515 purple_media_codec_list_free(codecs);
517 return node;
520 static JingleContent *
521 jingle_rtp_parse_internal(PurpleXmlNode *rtp)
523 JingleContent *content = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->parse(rtp);
524 PurpleXmlNode *description = purple_xmlnode_get_child(rtp, "description");
525 const gchar *media_type = purple_xmlnode_get_attrib(description, "media");
526 const gchar *ssrc = purple_xmlnode_get_attrib(description, "ssrc");
527 purple_debug_info("jingle-rtp", "rtp parse\n");
528 g_object_set(content, "media-type", media_type, NULL);
529 if (ssrc != NULL)
530 g_object_set(content, "ssrc", ssrc, NULL);
531 return content;
534 static void
535 jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action)
537 switch (action) {
538 case JINGLE_SESSION_ACCEPT:
539 case JINGLE_SESSION_INITIATE: {
540 JingleSession *session;
541 JingleTransport *transport;
542 PurpleXmlNode *description;
543 GList *candidates;
544 GList *codecs;
545 gchar *name;
546 gchar *remote_jid;
547 PurpleMedia *media;
549 session = jingle_content_get_session(content);
551 if (action == JINGLE_SESSION_INITIATE &&
552 !jingle_rtp_init_media(content)) {
553 /* XXX: send error */
554 jabber_iq_send(jingle_session_terminate_packet(
555 session, "general-error"));
556 g_object_unref(session);
557 break;
560 transport = jingle_transport_parse(
561 purple_xmlnode_get_child(xmlcontent, "transport"));
562 description = purple_xmlnode_get_child(xmlcontent, "description");
563 candidates = jingle_transport_get_remote_candidates(transport);
564 codecs = jingle_rtp_parse_codecs(description);
565 name = jingle_content_get_name(content);
566 remote_jid = jingle_session_get_remote_jid(session);
568 media = jingle_rtp_get_media(session);
569 purple_media_set_remote_codecs(media,
570 name, remote_jid, codecs);
571 purple_media_add_remote_candidates(media,
572 name, remote_jid, candidates);
574 if (action == JINGLE_SESSION_ACCEPT)
575 purple_media_stream_info(media,
576 PURPLE_MEDIA_INFO_ACCEPT,
577 name, remote_jid, FALSE);
579 g_free(remote_jid);
580 g_free(name);
581 g_object_unref(session);
582 break;
584 case JINGLE_SESSION_TERMINATE: {
585 JingleSession *session = jingle_content_get_session(content);
586 PurpleMedia *media = jingle_rtp_get_media(session);
588 if (media != NULL) {
589 purple_media_end(media, NULL, NULL);
592 g_object_unref(session);
593 break;
595 case JINGLE_TRANSPORT_INFO: {
596 JingleSession *session = jingle_content_get_session(content);
597 JingleTransport *transport = jingle_transport_parse(
598 purple_xmlnode_get_child(xmlcontent, "transport"));
599 GList *candidates = jingle_transport_get_remote_candidates(transport);
600 gchar *name = jingle_content_get_name(content);
601 gchar *remote_jid =
602 jingle_session_get_remote_jid(session);
604 purple_media_add_remote_candidates(
605 jingle_rtp_get_media(session),
606 name, remote_jid, candidates);
608 g_free(remote_jid);
609 g_free(name);
610 g_object_unref(session);
611 break;
613 case JINGLE_DESCRIPTION_INFO: {
614 JingleSession *session =
615 jingle_content_get_session(content);
616 PurpleXmlNode *description = purple_xmlnode_get_child(
617 xmlcontent, "description");
618 GList *codecs, *iter, *iter2, *remote_codecs =
619 jingle_rtp_parse_codecs(description);
620 gchar *name = jingle_content_get_name(content);
621 gchar *remote_jid =
622 jingle_session_get_remote_jid(session);
623 PurpleMedia *media;
625 media = jingle_rtp_get_media(session);
628 * This may have problems if description-info is
629 * received without the optional parameters for a
630 * codec with configuration info (such as THEORA
631 * or H264). The local configuration info may be
632 * set for the remote codec.
634 * As of 2.6.3 there's no API to support getting
635 * the remote codecs specifically, just the
636 * intersection. Another option may be to cache
637 * the remote codecs received in initiate/accept.
639 codecs = purple_media_get_codecs(media, name);
641 for (iter = codecs; iter; iter = g_list_next(iter)) {
642 guint id;
644 id = purple_media_codec_get_id(iter->data);
645 iter2 = remote_codecs;
647 for (; iter2; iter2 = g_list_next(iter2)) {
648 if (purple_media_codec_get_id(
649 iter2->data) != id)
650 continue;
652 g_object_unref(iter->data);
653 iter->data = iter2->data;
654 remote_codecs = g_list_delete_link(
655 remote_codecs, iter2);
656 break;
660 codecs = g_list_concat(codecs, remote_codecs);
662 purple_media_set_remote_codecs(media,
663 name, remote_jid, codecs);
665 purple_media_codec_list_free (codecs);
666 g_free(remote_jid);
667 g_free(name);
668 g_object_unref(session);
669 break;
671 default:
672 break;
677 /******************************************************************************
678 * GObject Stuff
679 *****************************************************************************/
680 static void
681 jingle_rtp_init (JingleRtp *rtp)
685 static void
686 jingle_rtp_finalize (GObject *rtp)
688 JingleRtpPrivate *priv = jingle_rtp_get_instance_private(JINGLE_RTP(rtp));
689 purple_debug_info("jingle-rtp","jingle_rtp_finalize\n");
691 g_free(priv->media_type);
692 g_free(priv->ssrc);
694 G_OBJECT_CLASS(jingle_rtp_parent_class)->finalize(rtp);
697 static void
698 jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
700 JingleRtp *rtp = JINGLE_RTP(object);
701 JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp);
703 switch (prop_id) {
704 case PROP_MEDIA_TYPE:
705 g_free(priv->media_type);
706 priv->media_type = g_value_dup_string(value);
707 break;
708 case PROP_SSRC:
709 g_free(priv->ssrc);
710 priv->ssrc = g_value_dup_string(value);
711 break;
712 default:
713 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
714 break;
718 static void
719 jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
721 JingleRtp *rtp = JINGLE_RTP(object);
722 JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp);
724 switch (prop_id) {
725 case PROP_MEDIA_TYPE:
726 g_value_set_string(value, priv->media_type);
727 break;
728 case PROP_SSRC:
729 g_value_set_string(value, priv->ssrc);
730 break;
731 default:
732 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
733 break;
738 static void
739 jingle_rtp_class_finalize(JingleRtpClass *klass) {
742 static void
743 jingle_rtp_class_init (JingleRtpClass *klass)
745 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
746 JingleContentClass *content_class = JINGLE_CONTENT_CLASS(klass);
748 obj_class->finalize = jingle_rtp_finalize;
749 obj_class->set_property = jingle_rtp_set_property;
750 obj_class->get_property = jingle_rtp_get_property;
752 content_class->to_xml = jingle_rtp_to_xml_internal;
753 content_class->parse = jingle_rtp_parse_internal;
754 content_class->description_type = JINGLE_APP_RTP;
755 content_class->handle_action = jingle_rtp_handle_action_internal;
757 properties[PROP_MEDIA_TYPE] = g_param_spec_string("media-type",
758 "Media Type",
759 "The media type (\"audio\" or \"video\") for this rtp session.",
760 NULL,
761 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
763 properties[PROP_SSRC] = g_param_spec_string("ssrc",
764 "ssrc",
765 "The ssrc for this rtp session.",
766 NULL,
767 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
769 g_object_class_install_properties(obj_class, PROP_LAST, properties);
772 /******************************************************************************
773 * Public API
774 *****************************************************************************/
775 void
776 jingle_rtp_register(PurplePlugin *plugin) {
777 jingle_rtp_register_type(G_TYPE_MODULE(plugin));
780 gchar *
781 jingle_rtp_get_media_type(JingleContent *content)
783 gchar *media_type;
784 g_object_get(content, "media-type", &media_type, NULL);
785 return media_type;
788 gchar *
789 jingle_rtp_get_ssrc(JingleContent *content)
791 gchar *ssrc;
792 g_object_get(content, "ssrc", &ssrc, NULL);
793 return ssrc;
796 static PurpleMedia *
797 jingle_rtp_get_media(JingleSession *session)
799 JabberStream *js = jingle_session_get_js(session);
800 PurpleMedia *media = NULL;
801 GList *iter = purple_media_manager_get_media_by_account(
802 purple_media_manager_get(),
803 purple_connection_get_account(js->gc));
805 for (; iter; iter = g_list_delete_link(iter, iter)) {
806 JingleSession *media_session =
807 purple_media_get_protocol_data(iter->data);
808 if (media_session == session) {
809 media = iter->data;
810 break;
813 if (iter != NULL)
814 g_list_free(iter);
816 return media;
819 gboolean
820 jingle_rtp_initiate_media(JabberStream *js, const gchar *who,
821 PurpleMediaSessionType type)
823 /* create content negotiation */
824 JingleSession *session;
825 JingleContent *content;
826 JingleTransport *transport;
827 JabberBuddy *jb;
828 JabberBuddyResource *jbr;
829 gboolean ret = FALSE;
830 const gchar *transport_type;
832 gchar *resource = NULL, *me = NULL, *sid = NULL;
834 /* construct JID to send to */
835 jb = jabber_buddy_find(js, who, FALSE);
836 if (!jb) {
837 purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n");
838 goto out;
841 resource = jabber_get_resource(who);
842 jbr = jabber_buddy_find_resource(jb, resource);
844 if (!jbr) {
845 purple_debug_error("jingle-rtp", "Could not find buddy's resource - %s\n", resource);
846 goto out;
849 if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_ICEUDP)) {
850 transport_type = JINGLE_TRANSPORT_ICEUDP;
851 } else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) {
852 transport_type = JINGLE_TRANSPORT_RAWUDP;
853 } else if (jabber_resource_has_capability(jbr, NS_GOOGLE_TRANSPORT_P2P)) {
854 transport_type = NS_GOOGLE_TRANSPORT_P2P;
855 } else {
856 purple_debug_error("jingle-rtp", "Resource doesn't support "
857 "the same transport types\n");
858 goto out;
861 /* set ourselves as initiator */
862 me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
864 sid = jabber_get_next_id(js);
865 session = jingle_session_create(js, sid, me, who, TRUE);
868 if (type & PURPLE_MEDIA_AUDIO) {
869 JingleRtpPrivate *priv = NULL;
871 transport = jingle_transport_create(transport_type);
872 content = jingle_content_create(JINGLE_APP_RTP, "initiator",
873 "session", "audio-session", "both", transport);
875 priv = jingle_rtp_get_instance_private(JINGLE_RTP(content));
877 jingle_session_add_content(session, content);
878 priv->media_type = g_strdup("audio");
879 jingle_rtp_init_media(content);
880 g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]);
882 if (type & PURPLE_MEDIA_VIDEO) {
883 JingleRtpPrivate *priv = NULL;
885 transport = jingle_transport_create(transport_type);
886 content = jingle_content_create(JINGLE_APP_RTP, "initiator",
887 "session", "video-session", "both", transport);
889 priv = jingle_rtp_get_instance_private(JINGLE_RTP(content));
891 jingle_session_add_content(session, content);
892 priv->media_type = g_strdup("video");
893 jingle_rtp_init_media(content);
894 g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]);
897 if (jingle_rtp_get_media(session) == NULL) {
898 goto out;
901 ret = TRUE;
903 out:
904 g_free(me);
905 g_free(resource);
906 g_free(sid);
907 return ret;
910 void
911 jingle_rtp_terminate_session(JabberStream *js, const gchar *who)
913 JingleSession *session;
914 /* XXX: This may cause file transfers and xml sessions to stop as well */
915 session = jingle_session_find_by_jid(js, who);
917 if (session) {
918 PurpleMedia *media = jingle_rtp_get_media(session);
919 if (media) {
920 purple_debug_info("jingle-rtp", "hanging up media\n");
921 purple_media_stream_info(media,
922 PURPLE_MEDIA_INFO_HANGUP,
923 NULL, NULL, TRUE);
928 #endif /* USE_VV */