comparison libpurple/protocols/jabber/jingle/rtp.c @ 26023:7252e3d0c627

Add files I missed committing before and remove a few unnecessary functions.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Mon, 20 Oct 2008 00:11:33 +0000
parents
children 1a2d446cb8a0
comparison
equal deleted inserted replaced
26022:46387cbfaf85 26023:7252e3d0c627
1 /**
2 * @file rtp.c
3 *
4 * purple
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19 */
20
21 #include "config.h"
22
23 #include "jabber.h"
24 #include "jingle.h"
25 #include "media.h"
26 #include "mediamanager.h"
27 #include "rawudp.h"
28 #include "rtp.h"
29 #include "session.h"
30 #include "debug.h"
31
32 #include <string.h>
33
34 struct _JingleRtpPrivate
35 {
36 gchar *media_type;
37 gboolean candidates_ready;
38 gboolean codecs_ready;
39 };
40
41 #define JINGLE_RTP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_RTP, JingleRtpPrivate))
42
43 static void jingle_rtp_class_init (JingleRtpClass *klass);
44 static void jingle_rtp_init (JingleRtp *rtp);
45 static void jingle_rtp_finalize (GObject *object);
46 static void jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
47 static void jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
48 static JingleContent *jingle_rtp_parse_internal(xmlnode *rtp);
49 static xmlnode *jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionType action);
50 static void jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *jingle, JingleActionType action);
51
52 static PurpleMedia *jingle_rtp_get_media(JingleSession *session);
53
54 static JingleContentClass *parent_class = NULL;
55 #if 0
56 enum {
57 LAST_SIGNAL
58 };
59 static guint jingle_rtp_signals[LAST_SIGNAL] = {0};
60 #endif
61
62 enum {
63 PROP_0,
64 PROP_MEDIA_TYPE,
65 };
66
67 GType
68 jingle_rtp_get_type()
69 {
70 static GType type = 0;
71
72 if (type == 0) {
73 static const GTypeInfo info = {
74 sizeof(JingleRtpClass),
75 NULL,
76 NULL,
77 (GClassInitFunc) jingle_rtp_class_init,
78 NULL,
79 NULL,
80 sizeof(JingleRtp),
81 0,
82 (GInstanceInitFunc) jingle_rtp_init,
83 NULL
84 };
85 type = g_type_register_static(JINGLE_TYPE_CONTENT, "JingleRtp", &info, 0);
86 }
87 return type;
88 }
89
90 static void
91 jingle_rtp_class_init (JingleRtpClass *klass)
92 {
93 GObjectClass *gobject_class = (GObjectClass*)klass;
94 parent_class = g_type_class_peek_parent(klass);
95
96 gobject_class->finalize = jingle_rtp_finalize;
97 gobject_class->set_property = jingle_rtp_set_property;
98 gobject_class->get_property = jingle_rtp_get_property;
99 klass->parent_class.to_xml = jingle_rtp_to_xml_internal;
100 klass->parent_class.parse = jingle_rtp_parse_internal;
101 klass->parent_class.description_type = JINGLE_APP_RTP;
102 klass->parent_class.handle_action = jingle_rtp_handle_action_internal;
103
104 g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
105 g_param_spec_string("media-type",
106 "Media Type",
107 "The media type (\"audio\" or \"video\") for this rtp session.",
108 NULL,
109 G_PARAM_READWRITE));
110 g_type_class_add_private(klass, sizeof(JingleRtpPrivate));
111 }
112
113 static void
114 jingle_rtp_init (JingleRtp *rtp)
115 {
116 rtp->priv = JINGLE_RTP_GET_PRIVATE(rtp);
117 memset(rtp->priv, 0, sizeof(rtp->priv));
118 }
119
120 static void
121 jingle_rtp_finalize (GObject *rtp)
122 {
123 JingleRtpPrivate *priv = JINGLE_RTP_GET_PRIVATE(rtp);
124 purple_debug_info("jingle-rtp","jingle_rtp_finalize\n");
125
126 g_free(priv->media_type);
127 }
128
129 static void
130 jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
131 {
132 JingleRtp *rtp;
133 g_return_if_fail(JINGLE_IS_RTP(object));
134
135 rtp = JINGLE_RTP(object);
136
137 switch (prop_id) {
138 case PROP_MEDIA_TYPE:
139 g_free(rtp->priv->media_type);
140 rtp->priv->media_type = g_value_dup_string(value);
141 break;
142 default:
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144 break;
145 }
146 }
147
148 static void
149 jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
150 {
151 JingleRtp *rtp;
152 g_return_if_fail(JINGLE_IS_RTP(object));
153
154 rtp = JINGLE_RTP(object);
155
156 switch (prop_id) {
157 case PROP_MEDIA_TYPE:
158 g_value_set_string(value, rtp->priv->media_type);
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162 break;
163 }
164 }
165
166 static gboolean
167 jingle_rtp_ready_to_initiate(JingleSession *session, PurpleMedia *media)
168 {
169 if (jingle_session_is_initiator(session)) {
170 GList *iter = jingle_session_get_contents(session);
171 for (; iter; iter = g_list_next(iter)) {
172 JingleContent *content = iter->data;
173 gchar *name = jingle_content_get_name(content);
174 if (!JINGLE_IS_RTP(content)
175 || JINGLE_RTP_GET_PRIVATE(content)->codecs_ready == FALSE
176 || JINGLE_RTP_GET_PRIVATE(content)->candidates_ready == FALSE) {
177 g_free(name);
178 return FALSE;
179 }
180 g_free(name);
181 }
182 return TRUE;
183 }
184 return FALSE;
185 }
186
187 gchar *
188 jingle_rtp_get_media_type(JingleContent *content)
189 {
190 gchar *media_type;
191 g_object_get(content, "media-type", &media_type, NULL);
192 return media_type;
193 }
194
195 static PurpleMedia *
196 jingle_rtp_get_media(JingleSession *session)
197 {
198 JabberStream *js = jingle_session_get_js(session);
199 gchar *sid = jingle_session_get_sid(session);
200
201 PurpleMedia *media = (PurpleMedia *) (js->medias) ?
202 g_hash_table_lookup(js->medias, sid) : NULL;
203 g_free(sid);
204
205 return media;
206 }
207
208 static JingleTransport *
209 jingle_rtp_candidate_to_transport(GType type, guint generation, FsCandidate *candidate)
210 {
211 if (type == JINGLE_TYPE_RAWUDP) {
212 return JINGLE_TRANSPORT(jingle_rawudp_create(generation,
213 candidate->foundation, candidate->ip, candidate->port));
214 #if 0
215 } else if (type == JINGLE_TYPE_ICEUDP) {
216 return NULL;
217 #endif
218 } else {
219 return NULL;
220 }
221 }
222
223 static FsCandidate *
224 jingle_rtp_transport_to_candidate(xmlnode *transport)
225 {
226 xmlnode *candidate = xmlnode_get_child(transport, "candidate");
227 const gchar *type = xmlnode_get_namespace(transport);
228 if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) {
229 return fs_candidate_new("", FS_COMPONENT_RTP,
230 FS_CANDIDATE_TYPE_SRFLX, FS_NETWORK_PROTOCOL_UDP,
231 xmlnode_get_attrib(candidate, "ip"),
232 atoi(xmlnode_get_attrib(candidate, "port")));
233 #if 0
234 } else if (type == JINGLE_TRANSPORT_ICEUDP) {
235 return NULL;
236 #endif
237 } else {
238 return NULL;
239 }
240 }
241
242 static void
243 jingle_rtp_accept_cb(PurpleMedia *media, JingleSession *session)
244 {
245 jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO));
246 jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_ACCEPT));
247 }
248
249 static void
250 jingle_rtp_reject_cb(PurpleMedia *media, JingleSession *session)
251 {
252 jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE));
253 g_object_unref(session);
254 }
255
256 static void
257 jingle_rtp_hangup_cb(PurpleMedia *media, JingleSession *session)
258 {
259 jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE));
260 g_object_unref(session);
261 }
262
263 static void
264 jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, FsCandidate *candidate, JingleSession *session)
265 {
266 purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
267
268 if (candidate->component_id == 1) {
269 JingleContent *content = jingle_session_find_content(session, sid, "initiator");
270 JingleTransport *transport =
271 JINGLE_TRANSPORT(jingle_rtp_candidate_to_transport(
272 JINGLE_TYPE_RAWUDP, 0, candidate));
273 jingle_content_set_pending_transport(content, transport);
274 jingle_content_accept_transport(content);
275 }
276 }
277
278 static void
279 jingle_rtp_candidates_prepared_cb(PurpleMedia *media, gchar *sid, gchar *name, JingleSession *session)
280 {
281 JingleContent *content = jingle_session_find_content(session, sid, "initiator");
282 JINGLE_RTP_GET_PRIVATE(content)->candidates_ready = TRUE;
283
284 if (jingle_rtp_ready_to_initiate(session, media))
285 jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_INITIATE));
286 }
287
288 static void
289 jingle_rtp_candidate_pair_established_cb(PurpleMedia *media, FsCandidate *local_candidate, FsCandidate *remote_candidate, JingleSession *session)
290 {
291
292 }
293
294 static void
295 jingle_rtp_codecs_ready_cb(PurpleMedia *media, gchar *sid, JingleSession *session)
296 {
297 JingleContent *content = jingle_session_find_content(session, sid, "initiator");
298
299 if (content == NULL)
300 content = jingle_session_find_content(session, sid, "responder");
301
302 if (JINGLE_RTP_GET_PRIVATE(content)->codecs_ready == FALSE) {
303 JINGLE_RTP_GET_PRIVATE(content)->codecs_ready =
304 purple_media_codecs_ready(media, sid);
305
306 if (jingle_rtp_ready_to_initiate(session, media))
307 jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_INITIATE));
308 }
309 }
310
311 static PurpleMedia *
312 jingle_rtp_create_media(JingleContent *content)
313 {
314 JingleSession *session = jingle_content_get_session(content);
315 JabberStream *js = jingle_session_get_js(session);
316 gchar *remote_jid = jingle_session_get_remote_jid(session);
317 gchar *sid = jingle_session_get_sid(session);
318
319 PurpleMedia *media = purple_media_manager_create_media(purple_media_manager_get(),
320 js->gc, "fsrtpconference", remote_jid);
321 g_free(remote_jid);
322
323 if (!media) {
324 purple_debug_error("jingle-rtp", "Couldn't create media session\n");
325 return NULL;
326 }
327
328 /* insert it into the hash table */
329 if (!js->medias) {
330 purple_debug_info("jingle-rtp", "Creating hash table for media\n");
331 js->medias = g_hash_table_new(g_str_hash, g_str_equal);
332 }
333 purple_debug_info("jingle-rtp", "inserting media with sid: %s into table\n", sid);
334 g_hash_table_insert(js->medias, sid, media);
335
336 /* connect callbacks */
337 g_signal_connect(G_OBJECT(media), "accepted",
338 G_CALLBACK(jingle_rtp_accept_cb), session);
339 g_signal_connect(G_OBJECT(media), "reject",
340 G_CALLBACK(jingle_rtp_reject_cb), session);
341 g_signal_connect(G_OBJECT(media), "hangup",
342 G_CALLBACK(jingle_rtp_hangup_cb), session);
343 g_signal_connect(G_OBJECT(media), "new-candidate",
344 G_CALLBACK(jingle_rtp_new_candidate_cb), session);
345 g_signal_connect(G_OBJECT(media), "candidates-prepared",
346 G_CALLBACK(jingle_rtp_candidates_prepared_cb), session);
347 g_signal_connect(G_OBJECT(media), "candidate-pair",
348 G_CALLBACK(jingle_rtp_candidate_pair_established_cb), session);
349 g_signal_connect(G_OBJECT(media), "codecs-ready",
350 G_CALLBACK(jingle_rtp_codecs_ready_cb), session);
351
352 g_object_unref(session);
353 return media;
354 }
355
356 static gboolean
357 jingle_rtp_init_media(JingleContent *content)
358 {
359 JingleSession *session = jingle_content_get_session(content);
360 PurpleMedia *media = jingle_rtp_get_media(session);
361 gchar *media_type;
362 gchar *remote_jid;
363 gchar *senders;
364 gchar *name;
365 const gchar *transmitter;
366 FsMediaType type;
367 FsStreamDirection direction;
368 JingleTransport *transport;
369
370 /* maybe this create ought to just be in initiate and handle initiate */
371 if (media == NULL)
372 media = jingle_rtp_create_media(content);
373
374 if (media == NULL)
375 return FALSE;
376
377 name = jingle_content_get_name(content);
378 media_type = jingle_rtp_get_media_type(content);
379 remote_jid = jingle_session_get_remote_jid(session);
380 senders = jingle_content_get_senders(content);
381 transport = jingle_content_get_transport(content);
382
383 if (JINGLE_IS_RAWUDP(transport))
384 transmitter = "rawudp";
385 else
386 transmitter = "notransmitter";
387
388 if (!strcmp(media_type, "audio"))
389 type = FS_MEDIA_TYPE_AUDIO;
390 else
391 type = FS_MEDIA_TYPE_VIDEO;
392
393 if (!strcmp(senders, "both"))
394 direction = FS_DIRECTION_BOTH;
395 else if (!strcmp(senders, "initiator")
396 && jingle_session_is_initiator(session))
397 direction = FS_DIRECTION_SEND;
398 else
399 direction = FS_DIRECTION_RECV;
400
401 purple_media_add_stream(media, name, remote_jid,
402 purple_media_from_fs(type, direction),
403 transmitter, 0, NULL);
404
405
406 g_free(name);
407 g_free(media_type);
408 g_free(remote_jid);
409 g_free(senders);
410 g_object_unref(session);
411
412 /* needs to be after all the streams have been added */
413 purple_media_ready(media);
414
415 return TRUE;
416 }
417
418 static GList *
419 jingle_rtp_parse_codecs(xmlnode *description)
420 {
421 GList *codecs = NULL;
422 xmlnode *codec_element = NULL;
423 const char *encoding_name,*id, *clock_rate;
424 FsCodec *codec;
425 const gchar *media = xmlnode_get_attrib(description, "media");
426 FsMediaType type = !strcmp(media, "video") ? FS_MEDIA_TYPE_VIDEO :
427 !strcmp(media, "audio") ? FS_MEDIA_TYPE_AUDIO : 0;
428
429 for (codec_element = xmlnode_get_child(description, "payload-type") ;
430 codec_element ;
431 codec_element = xmlnode_get_next_twin(codec_element)) {
432 xmlnode *param;
433 gchar *codec_str;
434 encoding_name = xmlnode_get_attrib(codec_element, "name");
435
436 id = xmlnode_get_attrib(codec_element, "id");
437 clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
438
439 codec = fs_codec_new(atoi(id), encoding_name,
440 type,
441 clock_rate ? atoi(clock_rate) : 0);
442
443 for (param = xmlnode_get_child(codec_element, "parameter");
444 param; param = xmlnode_get_next_twin(param)) {
445 fs_codec_add_optional_parameter(codec,
446 xmlnode_get_attrib(param, "name"),
447 xmlnode_get_attrib(param, "value"));
448 }
449
450 codec_str = fs_codec_to_string(codec);
451 purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str);
452 g_free(codec_str);
453
454 codecs = g_list_append(codecs, codec);
455 }
456 return codecs;
457 }
458
459 static JingleContent *
460 jingle_rtp_parse_internal(xmlnode *rtp)
461 {
462 JingleContent *content = parent_class->parse(rtp);
463 xmlnode *description = xmlnode_get_child(rtp, "description");
464 const gchar *media_type = xmlnode_get_attrib(description, "media");
465 purple_debug_info("jingle-rtp", "rtp parse\n");
466 g_object_set(content, "media-type", media_type, NULL);
467 return content;
468 }
469
470 static void
471 jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
472 {
473 for (; codecs ; codecs = codecs->next) {
474 FsCodec *codec = (FsCodec*)codecs->data;
475 GList *iter = codec->optional_params;
476 char id[8], clockrate[10], channels[10];
477 gchar *codec_str;
478 xmlnode *payload = xmlnode_new_child(description, "payload-type");
479
480 g_snprintf(id, sizeof(id), "%d", codec->id);
481 g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate);
482 g_snprintf(channels, sizeof(channels), "%d", codec->channels);
483
484 xmlnode_set_attrib(payload, "name", codec->encoding_name);
485 xmlnode_set_attrib(payload, "id", id);
486 xmlnode_set_attrib(payload, "clockrate", clockrate);
487 xmlnode_set_attrib(payload, "channels", channels);
488
489 for (; iter; iter = g_list_next(iter)) {
490 FsCodecParameter *fsparam = iter->data;
491 xmlnode *param = xmlnode_new_child(payload, "parameter");
492 xmlnode_set_attrib(param, "name", fsparam->name);
493 xmlnode_set_attrib(param, "value", fsparam->value);
494 }
495
496 codec_str = fs_codec_to_string(codec);
497 purple_debug_info("jingle", "adding codec: %s\n", codec_str);
498 g_free(codec_str);
499 }
500 }
501
502 static xmlnode *
503 jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionType action)
504 {
505 xmlnode *node = parent_class->to_xml(rtp, content, action);
506 xmlnode *description = xmlnode_get_child(node, "description");
507 if (description != NULL) {
508 JingleSession *session = jingle_content_get_session(rtp);
509 PurpleMedia *media = jingle_rtp_get_media(session);
510 gchar *media_type = jingle_rtp_get_media_type(rtp);
511 gchar *name = jingle_content_get_name(rtp);
512 GList *codecs = purple_media_get_local_codecs(media, name);
513
514 xmlnode_set_attrib(description, "media", media_type);
515
516 g_free(media_type);
517 g_free(name);
518 g_object_unref(session);
519
520 jingle_rtp_add_payloads(description, codecs);
521 }
522 return node;
523 }
524
525 static void
526 jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *jingle, JingleActionType action)
527 {
528 switch (action) {
529 case JINGLE_SESSION_ACCEPT: {
530 JingleSession *session = jingle_content_get_session(content);
531 xmlnode *description = xmlnode_get_child(jingle, "content/description");
532 GList *codecs = jingle_rtp_parse_codecs(description);
533
534 purple_media_set_remote_codecs(jingle_rtp_get_media(session),
535 jingle_content_get_name(content),
536 jingle_session_get_remote_jid(session), codecs);
537
538 g_object_unref(session);
539 break;
540 }
541 case JINGLE_SESSION_INITIATE: {
542 JingleSession *session = jingle_content_get_session(content);
543 xmlnode *description = xmlnode_get_child(jingle, "content/description");
544 xmlnode *transport = xmlnode_get_child(jingle, "content/transport");
545 FsCandidate *candidate = jingle_rtp_transport_to_candidate(transport);
546 GList *candidates = g_list_append(NULL, candidate);
547 GList *codecs = jingle_rtp_parse_codecs(description);
548
549 jingle_rtp_init_media(content);
550
551 purple_media_set_remote_codecs(jingle_rtp_get_media(session),
552 jingle_content_get_name(content),
553 jingle_session_get_remote_jid(session), codecs);
554
555 /* manufacture rtcp candidate */
556 candidate = fs_candidate_copy(candidate);
557 candidate->component_id = 2;
558 candidate->port = candidate->port + 1;
559 candidates = g_list_append(candidates, candidate);
560
561 purple_media_add_remote_candidates(jingle_rtp_get_media(session),
562 jingle_content_get_name(content),
563 jingle_session_get_remote_jid(session),
564 candidates);
565
566 g_object_unref(session);
567 break;
568 }
569 case JINGLE_SESSION_TERMINATE: {
570 JingleSession *session = jingle_content_get_session(content);
571 purple_media_got_hangup(jingle_rtp_get_media(session));
572 g_object_unref(session);
573 break;
574 }
575 case JINGLE_TRANSPORT_INFO: {
576 JingleSession *session = jingle_content_get_session(content);
577 xmlnode *transport = xmlnode_get_child(jingle, "content/transport");
578 FsCandidate *candidate = jingle_rtp_transport_to_candidate(transport);
579 GList *candidates = g_list_append(NULL, candidate);
580
581 /* manufacture rtcp candidate */
582 candidate = fs_candidate_copy(candidate);
583 candidate->component_id = 2;
584 candidate->port = candidate->port + 1;
585 candidates = g_list_append(candidates, candidate);
586
587 purple_media_add_remote_candidates(jingle_rtp_get_media(session),
588 jingle_content_get_name(content),
589 jingle_session_get_remote_jid(session),
590 candidates);
591 purple_media_got_accept(jingle_rtp_get_media(session));
592 g_object_unref(session);
593 break;
594 }
595 default:
596 break;
597 }
598 }
599
600 PurpleMedia *
601 jingle_rtp_initiate_media(JabberStream *js, const gchar *who,
602 PurpleMediaSessionType type)
603 {
604 /* create content negotiation */
605 JingleSession *session;
606 JingleContent *content;
607 JingleTransport *transport;
608 JabberBuddy *jb;
609 JabberBuddyResource *jbr;
610
611 gchar *jid = NULL, *me = NULL, *sid = NULL;
612
613 /* construct JID to send to */
614 jb = jabber_buddy_find(js, who, FALSE);
615 if (!jb) {
616 purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n");
617 return NULL;
618 }
619 jbr = jabber_buddy_find_resource(jb, NULL);
620 if (!jbr) {
621 purple_debug_error("jingle-rtp", "Could not find buddy's resource\n");
622 }
623
624 if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) {
625 jid = g_strdup_printf("%s/%s", who, jbr->name);
626 } else {
627 jid = g_strdup(who);
628 }
629
630 /* set ourselves as initiator */
631 me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
632
633 sid = jabber_get_next_id(js);
634 session = jingle_session_create(js, sid, me, jid, TRUE);
635 g_free(sid);
636
637 transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
638 content = jingle_content_create(JINGLE_APP_RTP, "initiator", "session", "test-session", "both", transport);
639 jingle_session_add_content(session, content);
640 if (purple_media_to_fs_media_type(type) == FS_MEDIA_TYPE_AUDIO)
641 JINGLE_RTP(content)->priv->media_type = g_strdup("audio");
642 else
643 JINGLE_RTP(content)->priv->media_type = g_strdup("video");
644
645 jingle_rtp_init_media(content);
646
647 purple_media_wait(jingle_rtp_get_media(session));
648
649 g_free(jid);
650 g_free(me);
651
652 return NULL;
653 }
654
655 void
656 jingle_rtp_terminate_session(JabberStream *js, const gchar *who)
657 {
658 JingleSession *session;
659 /* XXX: This may cause file transfers and xml sessions to stop as well */
660 session = jingle_session_find_by_jid(js, who);
661
662 if (session) {
663 PurpleMedia *media = jingle_rtp_get_media(session);
664 if (media) {
665 purple_debug_info("jingle-rtp", "hanging up media\n");
666 purple_media_hangup(media);
667 }
668 }
669 }
670