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
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
32 #include "mediamanager.h"
41 struct _JingleRtpPrivate
47 #define JINGLE_RTP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_RTP, JingleRtpPrivate))
49 static void jingle_rtp_class_init (JingleRtpClass
*klass
);
50 static void jingle_rtp_init (JingleRtp
*rtp
);
51 static void jingle_rtp_finalize (GObject
*object
);
52 static void jingle_rtp_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
53 static void jingle_rtp_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
54 static JingleContent
*jingle_rtp_parse_internal(xmlnode
*rtp
);
55 static xmlnode
*jingle_rtp_to_xml_internal(JingleContent
*rtp
, xmlnode
*content
, JingleActionType action
);
56 static void jingle_rtp_handle_action_internal(JingleContent
*content
, xmlnode
*jingle
, JingleActionType action
);
58 static PurpleMedia
*jingle_rtp_get_media(JingleSession
*session
);
60 static JingleContentClass
*parent_class
= NULL
;
65 static guint jingle_rtp_signals
[LAST_SIGNAL
] = {0};
77 static GType type
= 0;
80 static const GTypeInfo info
= {
81 sizeof(JingleRtpClass
),
84 (GClassInitFunc
) jingle_rtp_class_init
,
89 (GInstanceInitFunc
) jingle_rtp_init
,
92 type
= g_type_register_static(JINGLE_TYPE_CONTENT
, "JingleRtp", &info
, 0);
98 jingle_rtp_class_init (JingleRtpClass
*klass
)
100 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
101 parent_class
= g_type_class_peek_parent(klass
);
103 gobject_class
->finalize
= jingle_rtp_finalize
;
104 gobject_class
->set_property
= jingle_rtp_set_property
;
105 gobject_class
->get_property
= jingle_rtp_get_property
;
106 klass
->parent_class
.to_xml
= jingle_rtp_to_xml_internal
;
107 klass
->parent_class
.parse
= jingle_rtp_parse_internal
;
108 klass
->parent_class
.description_type
= JINGLE_APP_RTP
;
109 klass
->parent_class
.handle_action
= jingle_rtp_handle_action_internal
;
111 g_object_class_install_property(gobject_class
, PROP_MEDIA_TYPE
,
112 g_param_spec_string("media-type",
114 "The media type (\"audio\" or \"video\") for this rtp session.",
117 g_object_class_install_property(gobject_class
, PROP_SSRC
,
118 g_param_spec_string("ssrc",
120 "The ssrc for this rtp session.",
124 g_type_class_add_private(klass
, sizeof(JingleRtpPrivate
));
128 jingle_rtp_init (JingleRtp
*rtp
)
130 rtp
->priv
= JINGLE_RTP_GET_PRIVATE(rtp
);
131 memset(rtp
->priv
, 0, sizeof(*rtp
->priv
));
135 jingle_rtp_finalize (GObject
*rtp
)
137 JingleRtpPrivate
*priv
= JINGLE_RTP_GET_PRIVATE(rtp
);
138 purple_debug_info("jingle-rtp","jingle_rtp_finalize\n");
140 g_free(priv
->media_type
);
143 G_OBJECT_CLASS(parent_class
)->finalize(rtp
);
147 jingle_rtp_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
150 g_return_if_fail(JINGLE_IS_RTP(object
));
152 rtp
= JINGLE_RTP(object
);
155 case PROP_MEDIA_TYPE
:
156 g_free(rtp
->priv
->media_type
);
157 rtp
->priv
->media_type
= g_value_dup_string(value
);
160 g_free(rtp
->priv
->ssrc
);
161 rtp
->priv
->ssrc
= g_value_dup_string(value
);
164 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
170 jingle_rtp_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
173 g_return_if_fail(JINGLE_IS_RTP(object
));
175 rtp
= JINGLE_RTP(object
);
178 case PROP_MEDIA_TYPE
:
179 g_value_set_string(value
, rtp
->priv
->media_type
);
182 g_value_set_string(value
, rtp
->priv
->ssrc
);
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
191 jingle_rtp_get_media_type(JingleContent
*content
)
194 g_object_get(content
, "media-type", &media_type
, NULL
);
199 jingle_rtp_get_ssrc(JingleContent
*content
)
202 g_object_get(content
, "ssrc", &ssrc
, NULL
);
207 jingle_rtp_get_media(JingleSession
*session
)
209 JabberStream
*js
= jingle_session_get_js(session
);
210 PurpleMedia
*media
= NULL
;
211 GList
*iter
= purple_media_manager_get_media_by_account(
212 purple_media_manager_get(),
213 purple_connection_get_account(js
->gc
));
215 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
216 JingleSession
*media_session
=
217 purple_media_get_prpl_data(iter
->data
);
218 if (media_session
== session
) {
229 static JingleRawUdpCandidate
*
230 jingle_rtp_candidate_to_rawudp(JingleSession
*session
, guint generation
,
231 PurpleMediaCandidate
*candidate
)
233 gchar
*id
= jabber_get_next_id(jingle_session_get_js(session
));
234 gchar
*ip
= purple_media_candidate_get_ip(candidate
);
235 JingleRawUdpCandidate
*rawudp_candidate
=
236 jingle_rawudp_candidate_new(id
, generation
,
237 purple_media_candidate_get_component_id(candidate
),
238 ip
, purple_media_candidate_get_port(candidate
));
241 return rawudp_candidate
;
244 static JingleIceUdpCandidate
*
245 jingle_rtp_candidate_to_iceudp(JingleSession
*session
, guint generation
,
246 PurpleMediaCandidate
*candidate
)
248 gchar
*id
= jabber_get_next_id(jingle_session_get_js(session
));
249 gchar
*ip
= purple_media_candidate_get_ip(candidate
);
250 gchar
*username
= purple_media_candidate_get_username(candidate
);
251 gchar
*password
= purple_media_candidate_get_password(candidate
);
252 PurpleMediaCandidateType type
=
253 purple_media_candidate_get_candidate_type(candidate
);
255 JingleIceUdpCandidate
*iceudp_candidate
= jingle_iceudp_candidate_new(
256 purple_media_candidate_get_component_id(candidate
),
257 purple_media_candidate_get_foundation(candidate
),
258 generation
, id
, ip
, 0,
259 purple_media_candidate_get_port(candidate
),
260 purple_media_candidate_get_priority(candidate
), "udp",
261 type
== PURPLE_MEDIA_CANDIDATE_TYPE_HOST
? "host" :
262 type
== PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
? "srflx" :
263 type
== PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
? "prflx" :
264 type
== PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
? "relay" :
265 "", username
, password
);
266 iceudp_candidate
->reladdr
=
267 purple_media_candidate_get_base_ip(candidate
);
268 iceudp_candidate
->relport
=
269 purple_media_candidate_get_base_port(candidate
);
274 return iceudp_candidate
;
277 static JingleTransport
*
278 jingle_rtp_candidates_to_transport(JingleSession
*session
, GType type
, guint generation
, GList
*candidates
)
280 if (type
== JINGLE_TYPE_RAWUDP
) {
281 JingleTransport
*transport
= jingle_transport_create(JINGLE_TRANSPORT_RAWUDP
);
282 JingleRawUdpCandidate
*rawudp_candidate
;
283 for (; candidates
; candidates
= g_list_next(candidates
)) {
284 PurpleMediaCandidate
*candidate
= candidates
->data
;
285 rawudp_candidate
= jingle_rtp_candidate_to_rawudp(
286 session
, generation
, candidate
);
287 jingle_rawudp_add_local_candidate(
288 JINGLE_RAWUDP(transport
),
292 } else if (type
== JINGLE_TYPE_ICEUDP
) {
293 JingleTransport
*transport
= jingle_transport_create(JINGLE_TRANSPORT_ICEUDP
);
294 JingleIceUdpCandidate
*iceudp_candidate
;
295 for (; candidates
; candidates
= g_list_next(candidates
)) {
296 PurpleMediaCandidate
*candidate
= candidates
->data
;
297 iceudp_candidate
= jingle_rtp_candidate_to_iceudp(
298 session
, generation
, candidate
);
299 jingle_iceudp_add_local_candidate(
300 JINGLE_ICEUDP(transport
),
310 jingle_rtp_transport_to_candidates(JingleTransport
*transport
)
312 const gchar
*type
= jingle_transport_get_transport_type(transport
);
314 if (!strcmp(type
, JINGLE_TRANSPORT_RAWUDP
)) {
315 GList
*candidates
= jingle_rawudp_get_remote_candidates(JINGLE_RAWUDP(transport
));
317 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
)) {
318 JingleRawUdpCandidate
*candidate
= candidates
->data
;
319 ret
= g_list_append(ret
, purple_media_candidate_new(
320 "", candidate
->component
,
321 PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
,
322 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
,
323 candidate
->ip
, candidate
->port
));
327 } else if (!strcmp(type
, JINGLE_TRANSPORT_ICEUDP
)) {
328 GList
*candidates
= jingle_iceudp_get_remote_candidates(JINGLE_ICEUDP(transport
));
330 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
)) {
331 JingleIceUdpCandidate
*candidate
= candidates
->data
;
332 PurpleMediaCandidate
*new_candidate
= purple_media_candidate_new(
333 candidate
->foundation
, candidate
->component
,
334 !strcmp(candidate
->type
, "host") ?
335 PURPLE_MEDIA_CANDIDATE_TYPE_HOST
:
336 !strcmp(candidate
->type
, "srflx") ?
337 PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
:
338 !strcmp(candidate
->type
, "prflx") ?
339 PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
:
340 !strcmp(candidate
->type
, "relay") ?
341 PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: 0,
342 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
,
343 candidate
->ip
, candidate
->port
);
344 g_object_set(new_candidate
,
345 "base-ip", candidate
->reladdr
,
346 "base-port", candidate
->relport
,
347 "username", candidate
->username
,
348 "password", candidate
->password
,
349 "priority", candidate
->priority
, NULL
);
350 ret
= g_list_append(ret
, new_candidate
);
359 static void jingle_rtp_ready(JingleSession
*session
);
362 jingle_rtp_candidates_prepared_cb(PurpleMedia
*media
,
363 gchar
*sid
, gchar
*name
, JingleSession
*session
)
365 JingleContent
*content
= jingle_session_find_content(
367 JingleTransport
*oldtransport
, *transport
;
370 purple_debug_info("jingle-rtp", "jingle_rtp_candidates_prepared_cb\n");
372 if (content
== NULL
) {
373 purple_debug_error("jingle-rtp",
374 "jingle_rtp_candidates_prepared_cb: "
375 "Can't find session %s\n", sid
);
379 oldtransport
= jingle_content_get_transport(content
);
380 candidates
= purple_media_get_local_candidates(media
, sid
, name
);
381 transport
= JINGLE_TRANSPORT(jingle_rtp_candidates_to_transport(
382 session
, JINGLE_IS_RAWUDP(oldtransport
) ?
383 JINGLE_TYPE_RAWUDP
: JINGLE_TYPE_ICEUDP
,
386 g_list_free(candidates
);
387 g_object_unref(oldtransport
);
389 jingle_content_set_pending_transport(content
, transport
);
390 jingle_content_accept_transport(content
);
392 jingle_rtp_ready(session
);
396 jingle_rtp_codecs_changed_cb(PurpleMedia
*media
, gchar
*sid
,
397 JingleSession
*session
)
399 purple_debug_info("jingle-rtp", "jingle_rtp_codecs_changed_cb: "
400 "session_id: %s jingle_session: %p\n", sid
, session
);
401 jingle_rtp_ready(session
);
405 jingle_rtp_new_candidate_cb(PurpleMedia
*media
, gchar
*sid
, gchar
*name
, PurpleMediaCandidate
*candidate
, JingleSession
*session
)
407 JingleContent
*content
= jingle_session_find_content(
409 JingleTransport
*transport
;
411 purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
413 if (content
== NULL
) {
414 purple_debug_error("jingle-rtp",
415 "jingle_rtp_new_candidate_cb: "
416 "Can't find session %s\n", sid
);
420 transport
= jingle_content_get_transport(content
);
422 if (JINGLE_IS_ICEUDP(transport
))
423 jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport
),
424 jingle_rtp_candidate_to_iceudp(
425 session
, 1, candidate
));
426 else if (JINGLE_IS_RAWUDP(transport
))
427 jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport
),
428 jingle_rtp_candidate_to_rawudp(
429 session
, 1, candidate
));
431 g_object_unref(transport
);
433 jabber_iq_send(jingle_session_to_packet(session
,
434 JINGLE_TRANSPORT_INFO
));
438 jingle_rtp_initiate_ack_cb(JabberStream
*js
, const char *from
,
439 JabberIqType type
, const char *id
,
440 xmlnode
*packet
, gpointer data
)
442 JingleSession
*session
= data
;
444 if (type
== JABBER_IQ_ERROR
|| xmlnode_get_child(packet
, "error")) {
445 purple_media_end(jingle_rtp_get_media(session
), NULL
, NULL
);
446 g_object_unref(session
);
452 jingle_rtp_state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
453 gchar
*sid
, gchar
*name
, JingleSession
*session
)
455 purple_debug_info("jingle-rtp", "state-changed: state %d "
456 "id: %s name: %s\n", state
, sid
? sid
: "(null)",
457 name
? name
: "(null)");
461 jingle_rtp_stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
462 gchar
*sid
, gchar
*name
, gboolean local
,
463 JingleSession
*session
)
465 purple_debug_info("jingle-rtp", "stream-info: type %d "
466 "id: %s name: %s\n", type
, sid
? sid
: "(null)",
467 name
? name
: "(null)");
469 g_return_if_fail(JINGLE_IS_SESSION(session
));
471 if (type
== PURPLE_MEDIA_INFO_HANGUP
||
472 type
== PURPLE_MEDIA_INFO_REJECT
) {
473 jabber_iq_send(jingle_session_terminate_packet(
474 session
, type
== PURPLE_MEDIA_INFO_HANGUP
?
475 "success" : "decline"));
477 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
478 G_CALLBACK(jingle_rtp_state_changed_cb
),
480 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
481 G_CALLBACK(jingle_rtp_stream_info_cb
),
483 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
484 G_CALLBACK(jingle_rtp_new_candidate_cb
),
487 g_object_unref(session
);
488 } else if (type
== PURPLE_MEDIA_INFO_ACCEPT
&&
489 jingle_session_is_initiator(session
) == FALSE
) {
490 jingle_rtp_ready(session
);
495 jingle_rtp_ready(JingleSession
*session
)
497 PurpleMedia
*media
= jingle_rtp_get_media(session
);
499 if (purple_media_candidates_prepared(media
, NULL
, NULL
) &&
500 purple_media_codecs_ready(media
, NULL
) &&
501 (jingle_session_is_initiator(session
) == TRUE
||
502 purple_media_accepted(media
, NULL
, NULL
))) {
503 if (jingle_session_is_initiator(session
)) {
504 JabberIq
*iq
= jingle_session_to_packet(
505 session
, JINGLE_SESSION_INITIATE
);
506 jabber_iq_set_callback(iq
,
507 jingle_rtp_initiate_ack_cb
, session
);
510 jabber_iq_send(jingle_session_to_packet(session
,
511 JINGLE_SESSION_ACCEPT
));
514 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
515 G_CALLBACK(jingle_rtp_candidates_prepared_cb
),
517 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
518 G_CALLBACK(jingle_rtp_codecs_changed_cb
),
520 g_signal_connect(G_OBJECT(media
), "new-candidate",
521 G_CALLBACK(jingle_rtp_new_candidate_cb
),
527 jingle_rtp_create_media(JingleContent
*content
)
529 JingleSession
*session
= jingle_content_get_session(content
);
530 JabberStream
*js
= jingle_session_get_js(session
);
531 gchar
*remote_jid
= jingle_session_get_remote_jid(session
);
533 PurpleMedia
*media
= purple_media_manager_create_media(
534 purple_media_manager_get(),
535 purple_connection_get_account(js
->gc
),
536 "fsrtpconference", remote_jid
,
537 jingle_session_is_initiator(session
));
541 purple_debug_error("jingle-rtp", "Couldn't create media session\n");
545 purple_media_set_prpl_data(media
, session
);
547 /* connect callbacks */
548 g_signal_connect(G_OBJECT(media
), "candidates-prepared",
549 G_CALLBACK(jingle_rtp_candidates_prepared_cb
), session
);
550 g_signal_connect(G_OBJECT(media
), "codecs-changed",
551 G_CALLBACK(jingle_rtp_codecs_changed_cb
), session
);
552 g_signal_connect(G_OBJECT(media
), "state-changed",
553 G_CALLBACK(jingle_rtp_state_changed_cb
), session
);
554 g_signal_connect(G_OBJECT(media
), "stream-info",
555 G_CALLBACK(jingle_rtp_stream_info_cb
), session
);
557 g_object_unref(session
);
562 jingle_rtp_init_media(JingleContent
*content
)
564 JingleSession
*session
= jingle_content_get_session(content
);
565 PurpleMedia
*media
= jingle_rtp_get_media(session
);
571 const gchar
*transmitter
;
574 PurpleMediaSessionType type
;
575 JingleTransport
*transport
;
576 GParameter
*params
= NULL
;
579 /* maybe this create ought to just be in initiate and handle initiate */
581 media
= jingle_rtp_create_media(content
);
587 name
= jingle_content_get_name(content
);
588 media_type
= jingle_rtp_get_media_type(content
);
589 remote_jid
= jingle_session_get_remote_jid(session
);
590 senders
= jingle_content_get_senders(content
);
591 transport
= jingle_content_get_transport(content
);
593 if (media_type
== NULL
) {
598 g_object_unref(transport
);
599 g_object_unref(session
);
603 if (JINGLE_IS_RAWUDP(transport
))
604 transmitter
= "rawudp";
605 else if (JINGLE_IS_ICEUDP(transport
))
606 transmitter
= "nice";
608 transmitter
= "notransmitter";
609 g_object_unref(transport
);
611 is_audio
= purple_strequal(media_type
, "audio");
613 if (purple_strequal(senders
, "both"))
614 type
= is_audio
? PURPLE_MEDIA_AUDIO
615 : PURPLE_MEDIA_VIDEO
;
616 else if (purple_strequal(senders
, "initiator") ==
617 jingle_session_is_initiator(session
))
618 type
= is_audio
? PURPLE_MEDIA_SEND_AUDIO
619 : PURPLE_MEDIA_SEND_VIDEO
;
621 type
= is_audio
? PURPLE_MEDIA_RECV_AUDIO
622 : PURPLE_MEDIA_RECV_VIDEO
;
625 jingle_get_params(jingle_session_get_js(session
), NULL
, 0, 0, 0,
626 NULL
, NULL
, &num_params
);
628 creator
= jingle_content_get_creator(content
);
629 if (creator
== NULL
) {
635 g_object_unref(session
);
639 if (purple_strequal(creator
, "initiator"))
640 is_creator
= jingle_session_is_initiator(session
);
642 is_creator
= !jingle_session_is_initiator(session
);
645 if(!purple_media_add_stream(media
, name
, remote_jid
,
646 type
, is_creator
, transmitter
, num_params
, params
)) {
647 purple_media_end(media
, NULL
, NULL
);
648 /* TODO: How much clean-up is necessary here? (does calling
649 purple_media_end lead to cleaning up Jingle structs?) */
658 g_object_unref(session
);
664 jingle_rtp_parse_codecs(xmlnode
*description
)
666 GList
*codecs
= NULL
;
667 xmlnode
*codec_element
= NULL
;
668 const char *encoding_name
,*id
, *clock_rate
;
669 PurpleMediaCodec
*codec
;
670 const gchar
*media
= xmlnode_get_attrib(description
, "media");
671 PurpleMediaSessionType type
;
674 purple_debug_warning("jingle-rtp", "missing media type\n");
678 if (purple_strequal(media
, "video")) {
679 type
= PURPLE_MEDIA_VIDEO
;
680 } else if (purple_strequal(media
, "audio")) {
681 type
= PURPLE_MEDIA_AUDIO
;
683 purple_debug_warning("jingle-rtp", "unknown media type: %s\n",
688 for (codec_element
= xmlnode_get_child(description
, "payload-type") ;
690 codec_element
= xmlnode_get_next_twin(codec_element
)) {
693 encoding_name
= xmlnode_get_attrib(codec_element
, "name");
695 id
= xmlnode_get_attrib(codec_element
, "id");
696 clock_rate
= xmlnode_get_attrib(codec_element
, "clockrate");
698 codec
= purple_media_codec_new(atoi(id
), encoding_name
,
700 clock_rate
? atoi(clock_rate
) : 0);
702 for (param
= xmlnode_get_child(codec_element
, "parameter");
703 param
; param
= xmlnode_get_next_twin(param
)) {
704 purple_media_codec_add_optional_parameter(codec
,
705 xmlnode_get_attrib(param
, "name"),
706 xmlnode_get_attrib(param
, "value"));
709 codec_str
= purple_media_codec_to_string(codec
);
710 purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str
);
713 codecs
= g_list_append(codecs
, codec
);
718 static JingleContent
*
719 jingle_rtp_parse_internal(xmlnode
*rtp
)
721 JingleContent
*content
= parent_class
->parse(rtp
);
722 xmlnode
*description
= xmlnode_get_child(rtp
, "description");
723 const gchar
*media_type
= xmlnode_get_attrib(description
, "media");
724 const gchar
*ssrc
= xmlnode_get_attrib(description
, "ssrc");
725 purple_debug_info("jingle-rtp", "rtp parse\n");
726 g_object_set(content
, "media-type", media_type
, NULL
);
728 g_object_set(content
, "ssrc", ssrc
, NULL
);
733 jingle_rtp_add_payloads(xmlnode
*description
, GList
*codecs
)
735 for (; codecs
; codecs
= codecs
->next
) {
736 PurpleMediaCodec
*codec
= (PurpleMediaCodec
*)codecs
->data
;
737 GList
*iter
= purple_media_codec_get_optional_parameters(codec
);
738 gchar
*id
, *name
, *clockrate
, *channels
;
740 xmlnode
*payload
= xmlnode_new_child(description
, "payload-type");
742 id
= g_strdup_printf("%d",
743 purple_media_codec_get_id(codec
));
744 name
= purple_media_codec_get_encoding_name(codec
);
745 clockrate
= g_strdup_printf("%d",
746 purple_media_codec_get_clock_rate(codec
));
747 channels
= g_strdup_printf("%d",
748 purple_media_codec_get_channels(codec
));
750 xmlnode_set_attrib(payload
, "name", name
);
751 xmlnode_set_attrib(payload
, "id", id
);
752 xmlnode_set_attrib(payload
, "clockrate", clockrate
);
753 xmlnode_set_attrib(payload
, "channels", channels
);
760 for (; iter
; iter
= g_list_next(iter
)) {
761 PurpleKeyValuePair
*mparam
= iter
->data
;
762 xmlnode
*param
= xmlnode_new_child(payload
, "parameter");
763 xmlnode_set_attrib(param
, "name", mparam
->key
);
764 xmlnode_set_attrib(param
, "value", mparam
->value
);
767 codec_str
= purple_media_codec_to_string(codec
);
768 purple_debug_info("jingle", "adding codec: %s\n", codec_str
);
774 jingle_rtp_to_xml_internal(JingleContent
*rtp
, xmlnode
*content
, JingleActionType action
)
776 xmlnode
*node
= parent_class
->to_xml(rtp
, content
, action
);
777 xmlnode
*description
= xmlnode_get_child(node
, "description");
778 if (description
!= NULL
) {
779 JingleSession
*session
= jingle_content_get_session(rtp
);
780 PurpleMedia
*media
= jingle_rtp_get_media(session
);
781 gchar
*media_type
= jingle_rtp_get_media_type(rtp
);
782 gchar
*ssrc
= jingle_rtp_get_ssrc(rtp
);
783 gchar
*name
= jingle_content_get_name(rtp
);
784 GList
*codecs
= purple_media_get_codecs(media
, name
);
786 xmlnode_set_attrib(description
, "media", media_type
);
789 xmlnode_set_attrib(description
, "ssrc", ssrc
);
793 g_object_unref(session
);
795 jingle_rtp_add_payloads(description
, codecs
);
796 purple_media_codec_list_free(codecs
);
802 jingle_rtp_handle_action_internal(JingleContent
*content
, xmlnode
*xmlcontent
, JingleActionType action
)
805 case JINGLE_SESSION_ACCEPT
:
806 case JINGLE_SESSION_INITIATE
: {
807 JingleSession
*session
;
808 JingleTransport
*transport
;
809 xmlnode
*description
;
816 session
= jingle_content_get_session(content
);
818 if (action
== JINGLE_SESSION_INITIATE
&&
819 !jingle_rtp_init_media(content
)) {
820 /* XXX: send error */
821 jabber_iq_send(jingle_session_terminate_packet(
822 session
, "general-error"));
823 g_object_unref(session
);
827 transport
= jingle_transport_parse(
828 xmlnode_get_child(xmlcontent
, "transport"));
829 description
= xmlnode_get_child(xmlcontent
, "description");
830 candidates
= jingle_rtp_transport_to_candidates(transport
);
831 codecs
= jingle_rtp_parse_codecs(description
);
832 name
= jingle_content_get_name(content
);
833 remote_jid
= jingle_session_get_remote_jid(session
);
835 media
= jingle_rtp_get_media(session
);
836 purple_media_set_remote_codecs(media
,
837 name
, remote_jid
, codecs
);
838 purple_media_add_remote_candidates(media
,
839 name
, remote_jid
, candidates
);
841 if (action
== JINGLE_SESSION_ACCEPT
)
842 purple_media_stream_info(media
,
843 PURPLE_MEDIA_INFO_ACCEPT
,
844 name
, remote_jid
, FALSE
);
848 g_object_unref(session
);
851 case JINGLE_SESSION_TERMINATE
: {
852 JingleSession
*session
= jingle_content_get_session(content
);
853 PurpleMedia
*media
= jingle_rtp_get_media(session
);
856 purple_media_end(media
, NULL
, NULL
);
859 g_object_unref(session
);
862 case JINGLE_TRANSPORT_INFO
: {
863 JingleSession
*session
= jingle_content_get_session(content
);
864 JingleTransport
*transport
= jingle_transport_parse(
865 xmlnode_get_child(xmlcontent
, "transport"));
866 GList
*candidates
= jingle_rtp_transport_to_candidates(transport
);
867 gchar
*name
= jingle_content_get_name(content
);
869 jingle_session_get_remote_jid(session
);
871 purple_media_add_remote_candidates(
872 jingle_rtp_get_media(session
),
873 name
, remote_jid
, candidates
);
877 g_object_unref(session
);
880 case JINGLE_DESCRIPTION_INFO
: {
881 JingleSession
*session
=
882 jingle_content_get_session(content
);
883 xmlnode
*description
= xmlnode_get_child(
884 xmlcontent
, "description");
885 GList
*codecs
, *iter
, *iter2
, *remote_codecs
=
886 jingle_rtp_parse_codecs(description
);
887 gchar
*name
= jingle_content_get_name(content
);
889 jingle_session_get_remote_jid(session
);
892 media
= jingle_rtp_get_media(session
);
895 * This may have problems if description-info is
896 * received without the optional parameters for a
897 * codec with configuration info (such as THEORA
898 * or H264). The local configuration info may be
899 * set for the remote codec.
901 * As of 2.6.3 there's no API to support getting
902 * the remote codecs specifically, just the
903 * intersection. Another option may be to cache
904 * the remote codecs received in initiate/accept.
906 codecs
= purple_media_get_codecs(media
, name
);
908 for (iter
= codecs
; iter
; iter
= g_list_next(iter
)) {
911 id
= purple_media_codec_get_id(iter
->data
);
912 iter2
= remote_codecs
;
914 for (; iter2
; iter2
= g_list_next(iter2
)) {
915 if (purple_media_codec_get_id(
919 g_object_unref(iter
->data
);
920 iter
->data
= iter2
->data
;
921 remote_codecs
= g_list_delete_link(
922 remote_codecs
, iter2
);
927 codecs
= g_list_concat(codecs
, remote_codecs
);
929 purple_media_set_remote_codecs(media
,
930 name
, remote_jid
, codecs
);
932 purple_media_codec_list_free (codecs
);
935 g_object_unref(session
);
944 jingle_rtp_initiate_media(JabberStream
*js
, const gchar
*who
,
945 PurpleMediaSessionType type
)
947 /* create content negotiation */
948 JingleSession
*session
;
949 JingleContent
*content
;
950 JingleTransport
*transport
;
952 JabberBuddyResource
*jbr
;
953 const gchar
*transport_type
;
955 gchar
*resource
= NULL
, *me
= NULL
, *sid
= NULL
;
957 /* construct JID to send to */
958 jb
= jabber_buddy_find(js
, who
, FALSE
);
960 purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n");
964 resource
= jabber_get_resource(who
);
965 jbr
= jabber_buddy_find_resource(jb
, resource
);
969 purple_debug_error("jingle-rtp", "Could not find buddy's resource - %s\n", resource
);
973 if (jabber_resource_has_capability(jbr
, JINGLE_TRANSPORT_ICEUDP
)) {
974 transport_type
= JINGLE_TRANSPORT_ICEUDP
;
975 } else if (jabber_resource_has_capability(jbr
, JINGLE_TRANSPORT_RAWUDP
)) {
976 transport_type
= JINGLE_TRANSPORT_RAWUDP
;
978 purple_debug_error("jingle-rtp", "Resource doesn't support "
979 "the same transport types\n");
983 /* set ourselves as initiator */
984 me
= g_strdup_printf("%s@%s/%s", js
->user
->node
, js
->user
->domain
, js
->user
->resource
);
986 sid
= jabber_get_next_id(js
);
987 session
= jingle_session_create(js
, sid
, me
, who
, TRUE
);
991 if (type
& PURPLE_MEDIA_AUDIO
) {
992 transport
= jingle_transport_create(transport_type
);
993 content
= jingle_content_create(JINGLE_APP_RTP
, "initiator",
994 "session", "audio-session", "both", transport
);
995 jingle_session_add_content(session
, content
);
996 JINGLE_RTP(content
)->priv
->media_type
= g_strdup("audio");
997 jingle_rtp_init_media(content
);
999 if (type
& PURPLE_MEDIA_VIDEO
) {
1000 transport
= jingle_transport_create(transport_type
);
1001 content
= jingle_content_create(JINGLE_APP_RTP
, "initiator",
1002 "session", "video-session", "both", transport
);
1003 jingle_session_add_content(session
, content
);
1004 JINGLE_RTP(content
)->priv
->media_type
= g_strdup("video");
1005 jingle_rtp_init_media(content
);
1010 if (jingle_rtp_get_media(session
) == NULL
) {
1018 jingle_rtp_terminate_session(JabberStream
*js
, const gchar
*who
)
1020 JingleSession
*session
;
1021 /* XXX: This may cause file transfers and xml sessions to stop as well */
1022 session
= jingle_session_find_by_jid(js
, who
);
1025 PurpleMedia
*media
= jingle_rtp_get_media(session
);
1027 purple_debug_info("jingle-rtp", "hanging up media\n");
1028 purple_media_stream_info(media
,
1029 PURPLE_MEDIA_INFO_HANGUP
,