comparison libpurple/media.c @ 23821:12a16471f94e

Refactored PurpleMedia to make creating audio or video sessions virtually identical. Audio, video, and audio/video sessions now work. Also added videotestsrc to the video plugin preference.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Fri, 06 Jun 2008 07:43:03 +0000
parents d048100a43ab
children bfaad8393463
comparison
equal deleted inserted replaced
23820:42e17cc5b6d2 23821:12a16471f94e
36 #ifdef USE_VV 36 #ifdef USE_VV
37 37
38 #include <gst/interfaces/propertyprobe.h> 38 #include <gst/interfaces/propertyprobe.h>
39 #include <gst/farsight/fs-conference-iface.h> 39 #include <gst/farsight/fs-conference-iface.h>
40 40
41 struct _PurpleMediaSession
42 {
43 gchar *id;
44 PurpleMedia *media;
45 GstElement *src;
46 GstElement *sink;
47 FsSession *session;
48 GHashTable *streams; /* FsStream list map to participant's name */
49 FsMediaType type;
50 GHashTable *local_candidates; /* map to participant's name? */
51 FsCandidate *local_candidate;
52 FsCandidate *remote_candidate;
53 };
54
41 struct _PurpleMediaPrivate 55 struct _PurpleMediaPrivate
42 { 56 {
43 FsConference *conference; 57 FsConference *conference;
44 58
45 char *name; 59 char *name;
46 PurpleConnection *connection; 60 PurpleConnection *connection;
47 GstElement *audio_src; 61
48 GstElement *audio_sink; 62 GHashTable *sessions; /* PurpleMediaSession table */
49 GstElement *video_src; 63 GHashTable *participants; /* FsParticipant table */
50 GstElement *video_sink; 64
51 65 GstElement *pipeline;
52 FsSession *audio_session;
53 FsSession *video_session;
54
55 GList *participants; /* FsParticipant list */
56 GList *audio_streams; /* FsStream list */
57 GList *video_streams; /* FsStream list */
58
59 /* might be able to just combine these two */
60 GstElement *audio_pipeline;
61 GstElement *video_pipeline;
62
63 /* this will need to be stored/handled per stream
64 * once having multiple streams is supported */
65 GList *local_candidates;
66
67 FsCandidate *local_candidate;
68 FsCandidate *remote_candidate;
69 }; 66 };
70 67
71 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate)) 68 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
72 69
73 static void purple_media_class_init (PurpleMediaClass *klass); 70 static void purple_media_class_init (PurpleMediaClass *klass);
86 ACCEPTED, 83 ACCEPTED,
87 HANGUP, 84 HANGUP,
88 REJECT, 85 REJECT,
89 GOT_HANGUP, 86 GOT_HANGUP,
90 GOT_ACCEPT, 87 GOT_ACCEPT,
88 NEW_CANDIDATE,
91 CANDIDATES_PREPARED, 89 CANDIDATES_PREPARED,
92 CANDIDATE_PAIR, 90 CANDIDATE_PAIR,
93 LAST_SIGNAL 91 LAST_SIGNAL
94 }; 92 };
95 static guint purple_media_signals[LAST_SIGNAL] = {0}; 93 static guint purple_media_signals[LAST_SIGNAL] = {0};
97 enum { 95 enum {
98 PROP_0, 96 PROP_0,
99 PROP_FS_CONFERENCE, 97 PROP_FS_CONFERENCE,
100 PROP_NAME, 98 PROP_NAME,
101 PROP_CONNECTION, 99 PROP_CONNECTION,
102 PROP_AUDIO_SRC,
103 PROP_AUDIO_SINK,
104 PROP_VIDEO_SRC,
105 PROP_VIDEO_SINK,
106 PROP_VIDEO_SESSION,
107 PROP_AUDIO_SESSION
108 }; 100 };
109 101
110 GType 102 GType
111 purple_media_get_type() 103 purple_media_get_type()
112 { 104 {
157 g_object_class_install_property(gobject_class, PROP_CONNECTION, 149 g_object_class_install_property(gobject_class, PROP_CONNECTION,
158 g_param_spec_pointer("connection", 150 g_param_spec_pointer("connection",
159 "Connection", 151 "Connection",
160 "The PurpleConnection associated with this session", 152 "The PurpleConnection associated with this session",
161 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); 153 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
162
163 g_object_class_install_property(gobject_class, PROP_AUDIO_SRC,
164 g_param_spec_object("audio-src",
165 "Audio source",
166 "The GstElement used to source audio",
167 GST_TYPE_ELEMENT,
168 G_PARAM_READWRITE));
169
170 g_object_class_install_property(gobject_class, PROP_AUDIO_SINK,
171 g_param_spec_object("audio-sink",
172 "Audio sink",
173 "The GstElement used to sink audio",
174 GST_TYPE_ELEMENT,
175 G_PARAM_READWRITE));
176
177 g_object_class_install_property(gobject_class, PROP_VIDEO_SRC,
178 g_param_spec_object("video-src",
179 "Video source",
180 "The GstElement used to source video",
181 GST_TYPE_ELEMENT,
182 G_PARAM_READWRITE));
183
184 g_object_class_install_property(gobject_class, PROP_VIDEO_SINK,
185 g_param_spec_object("video-sink",
186 "Audio source",
187 "The GstElement used to sink video",
188 GST_TYPE_ELEMENT,
189 G_PARAM_READWRITE));
190
191 g_object_class_install_property(gobject_class, PROP_VIDEO_SESSION,
192 g_param_spec_object("video-session",
193 "Video stream",
194 "The FarsightStream used for video",
195 FS_TYPE_SESSION,
196 G_PARAM_READWRITE));
197
198 g_object_class_install_property(gobject_class, PROP_AUDIO_SESSION,
199 g_param_spec_object("audio-session",
200 "Audio stream",
201 "The FarsightStream used for audio",
202 FS_TYPE_SESSION,
203 G_PARAM_READWRITE));
204 154
205 purple_media_signals[READY] = g_signal_new("ready", G_TYPE_FROM_CLASS(klass), 155 purple_media_signals[READY] = g_signal_new("ready", G_TYPE_FROM_CLASS(klass),
206 G_SIGNAL_RUN_LAST, 0, NULL, NULL, 156 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
207 g_cclosure_marshal_VOID__VOID, 157 g_cclosure_marshal_VOID__VOID,
208 G_TYPE_NONE, 0); 158 G_TYPE_NONE, 0);
228 G_TYPE_NONE, 0); 178 G_TYPE_NONE, 0);
229 purple_media_signals[GOT_ACCEPT] = g_signal_new("got-accept", G_TYPE_FROM_CLASS(klass), 179 purple_media_signals[GOT_ACCEPT] = g_signal_new("got-accept", G_TYPE_FROM_CLASS(klass),
230 G_SIGNAL_RUN_LAST, 0, NULL, NULL, 180 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
231 g_cclosure_marshal_VOID__VOID, 181 g_cclosure_marshal_VOID__VOID,
232 G_TYPE_NONE, 0); 182 G_TYPE_NONE, 0);
183 purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
184 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
185 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
186 G_TYPE_NONE, 3, G_TYPE_POINTER,
187 G_TYPE_POINTER, FS_TYPE_CANDIDATE);
233 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass), 188 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
234 G_SIGNAL_RUN_LAST, 0, NULL, NULL, 189 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
235 g_cclosure_marshal_VOID__VOID, 190 g_cclosure_marshal_VOID__VOID,
236 G_TYPE_NONE, 0); 191 G_TYPE_NONE, 0);
237 purple_media_signals[CANDIDATE_PAIR] = g_signal_new("candidate-pair", G_TYPE_FROM_CLASS(klass), 192 purple_media_signals[CANDIDATE_PAIR] = g_signal_new("candidate-pair", G_TYPE_FROM_CLASS(klass),
252 207
253 static void 208 static void
254 purple_media_finalize (GObject *media) 209 purple_media_finalize (GObject *media)
255 { 210 {
256 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media); 211 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
257 GList *iter;
258 purple_debug_info("media","purple_media_finalize\n"); 212 purple_debug_info("media","purple_media_finalize\n");
259 213
260 g_free(priv->name); 214 g_free(priv->name);
261 215
262 if (priv->audio_pipeline) { 216 if (priv->pipeline) {
263 gst_element_set_state(priv->audio_pipeline, GST_STATE_NULL); 217 gst_element_set_state(priv->pipeline, GST_STATE_NULL);
264 gst_object_unref(priv->audio_pipeline); 218 gst_object_unref(priv->pipeline);
265 } 219 }
266 if (priv->video_pipeline) {
267 gst_element_set_state(priv->video_pipeline, GST_STATE_NULL);
268 gst_object_unref(priv->video_pipeline);
269 }
270
271 if (priv->audio_src)
272 gst_object_unref(priv->audio_src);
273 if (priv->audio_sink)
274 gst_object_unref(priv->audio_sink);
275 if (priv->video_src)
276 gst_object_unref(priv->video_src);
277 if (priv->video_sink)
278 gst_object_unref(priv->video_sink);
279
280 for (iter = priv->audio_streams; iter; iter = g_list_next(iter)) {
281 g_object_unref(iter->data);
282 }
283 g_list_free(priv->audio_streams);
284
285 for (iter = priv->video_streams; iter; iter = g_list_next(iter)) {
286 g_object_unref(iter->data);
287 }
288 g_list_free(priv->video_streams);
289
290 if (priv->audio_session)
291 g_object_unref(priv->audio_session);
292 if (priv->video_session)
293 g_object_unref(priv->video_session);
294
295 for (iter = priv->participants; iter; iter = g_list_next(iter)) {
296 g_object_unref(iter->data);
297 }
298 g_list_free(priv->participants);
299
300 for (iter = priv->local_candidates; iter; iter = g_list_next(iter)) {
301 g_free(iter->data);
302 }
303 g_list_free(priv->local_candidates);
304
305 if (priv->local_candidate)
306 g_free(priv->local_candidate);
307 if (priv->remote_candidate)
308 g_free(priv->remote_candidate);
309 220
310 gst_object_unref(priv->conference); 221 gst_object_unref(priv->conference);
311 222
312 parent_class->finalize(media); 223 parent_class->finalize(media);
313 } 224 }
332 media->priv->name = g_value_dup_string(value); 243 media->priv->name = g_value_dup_string(value);
333 break; 244 break;
334 case PROP_CONNECTION: 245 case PROP_CONNECTION:
335 media->priv->connection = g_value_get_pointer(value); 246 media->priv->connection = g_value_get_pointer(value);
336 break; 247 break;
337 case PROP_AUDIO_SRC:
338 if (media->priv->audio_src)
339 gst_object_unref(media->priv->audio_src);
340 media->priv->audio_src = g_value_get_object(value);
341 gst_object_ref(media->priv->audio_src);
342 gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)),
343 media->priv->audio_src);
344 break;
345 case PROP_AUDIO_SINK:
346 if (media->priv->audio_sink)
347 gst_object_unref(media->priv->audio_sink);
348 media->priv->audio_sink = g_value_get_object(value);
349 gst_object_ref(media->priv->audio_sink);
350 gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)),
351 media->priv->audio_sink);
352 break;
353 case PROP_VIDEO_SRC:
354 if (media->priv->video_src)
355 gst_object_unref(media->priv->video_src);
356 media->priv->video_src = g_value_get_object(value);
357 gst_object_ref(media->priv->video_src);
358 break;
359 case PROP_VIDEO_SINK:
360 if (media->priv->video_sink)
361 gst_object_unref(media->priv->video_sink);
362 media->priv->video_sink = g_value_get_object(value);
363 gst_object_ref(media->priv->video_sink);
364 break;
365 case PROP_VIDEO_SESSION:
366 if (media->priv->video_session)
367 g_object_unref(media->priv->video_session);
368 media->priv->video_session = g_value_get_object(value);
369 gst_object_ref(media->priv->video_session);
370 break;
371 case PROP_AUDIO_SESSION:
372 if (media->priv->audio_session)
373 g_object_unref(media->priv->audio_session);
374 media->priv->audio_session = g_value_get_object(value);
375 gst_object_ref(media->priv->audio_session);
376 break;
377
378 default: 248 default:
379 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380 break; 250 break;
381 } 251 }
382 } 252 }
397 g_value_set_string(value, media->priv->name); 267 g_value_set_string(value, media->priv->name);
398 break; 268 break;
399 case PROP_CONNECTION: 269 case PROP_CONNECTION:
400 g_value_set_pointer(value, media->priv->connection); 270 g_value_set_pointer(value, media->priv->connection);
401 break; 271 break;
402 case PROP_AUDIO_SRC:
403 g_value_set_object(value, media->priv->audio_src);
404 break;
405 case PROP_AUDIO_SINK:
406 g_value_set_object(value, media->priv->audio_sink);
407 break;
408 case PROP_VIDEO_SRC:
409 g_value_set_object(value, media->priv->video_src);
410 break;
411 case PROP_VIDEO_SINK:
412 g_value_set_object(value, media->priv->video_sink);
413 break;
414 case PROP_VIDEO_SESSION:
415 g_value_set_object(value, media->priv->video_session);
416 break;
417 case PROP_AUDIO_SESSION:
418 g_value_set_object(value, media->priv->audio_session);
419 break;
420
421 default: 272 default:
422 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 273 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
423 break; 274 break;
424 } 275 }
425 276
277 }
278
279 static PurpleMediaSession*
280 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
281 {
282 return (PurpleMediaSession*) (media->priv->sessions) ?
283 g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
284 }
285
286 static FsParticipant*
287 purple_media_get_participant(PurpleMedia *media, const gchar *name)
288 {
289 return (FsParticipant*) (media->priv->participants) ?
290 g_hash_table_lookup(media->priv->participants, name) : NULL;
291 }
292
293 static FsStream*
294 purple_media_session_get_stream(PurpleMediaSession *session, const gchar *name)
295 {
296 return (FsStream*) (session->streams) ?
297 g_hash_table_lookup(session->streams, name) : NULL;
298 }
299
300 static GList*
301 purple_media_session_get_local_candidates(PurpleMediaSession *session, const gchar *name)
302 {
303 return (GList*) (session->local_candidates) ?
304 g_hash_table_lookup(session->local_candidates, name) : NULL;
305 }
306
307 static void
308 purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
309 {
310 if (!media->priv->sessions) {
311 purple_debug_info("media", "Creating hash table for sessions\n");
312 media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
313 }
314 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
315 }
316
317 static FsParticipant *
318 purple_media_add_participant(PurpleMedia *media, const gchar *name)
319 {
320 FsParticipant *participant = purple_media_get_participant(media, name);
321
322 if (participant)
323 return participant;
324
325 participant = fs_conference_new_participant(media->priv->conference, g_strdup(name), NULL);
326
327 if (!media->priv->participants) {
328 purple_debug_info("media", "Creating hash table for participants\n");
329 media->priv->participants = g_hash_table_new(g_str_hash, g_str_equal);
330 }
331
332 g_hash_table_insert(media->priv->participants, g_strdup(name), participant);
333
334 return participant;
335 }
336
337 static void
338 purple_media_insert_stream(PurpleMediaSession *session, const gchar *name, FsStream *stream)
339 {
340 if (!session->streams) {
341 purple_debug_info("media", "Creating hash table for streams\n");
342 session->streams = g_hash_table_new(g_str_hash, g_str_equal);
343 }
344
345 g_hash_table_insert(session->streams, g_strdup(name), stream);
346 }
347
348 static void
349 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
350 FsCandidate *candidate)
351 {
352 GList *candidates = purple_media_session_get_local_candidates(session, name);
353
354 candidates = g_list_append(candidates, candidate);
355
356 if (!session->local_candidates) {
357 purple_debug_info("media", "Creating hash table for local candidates\n");
358 session->local_candidates = g_hash_table_new(g_str_hash, g_str_equal);
359 }
360
361 g_hash_table_insert(session->local_candidates, g_strdup(name), candidates);
362 }
363
364 GList *
365 purple_media_get_session_names(PurpleMedia *media)
366 {
367 return g_hash_table_get_keys(media->priv->sessions);
426 } 368 }
427 369
428 void 370 void
429 purple_media_get_elements(PurpleMedia *media, GstElement **audio_src, GstElement **audio_sink, 371 purple_media_get_elements(PurpleMedia *media, GstElement **audio_src, GstElement **audio_sink,
430 GstElement **video_src, GstElement **video_sink) 372 GstElement **video_src, GstElement **video_sink)
439 g_object_get(G_OBJECT(media), "video-sink", *video_sink, NULL); 381 g_object_get(G_OBJECT(media), "video-sink", *video_sink, NULL);
440 382
441 } 383 }
442 384
443 void 385 void
444 purple_media_set_audio_src(PurpleMedia *media, GstElement *audio_src) 386 purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src)
445 { 387 {
446 g_object_set(G_OBJECT(media), "audio-src", audio_src, NULL); 388 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
389 GstPad *sinkpad;
390 GstPad *srcpad;
391
392 if (session->src)
393 gst_object_unref(session->src);
394 session->src = src;
395 gst_bin_add(GST_BIN(purple_media_get_pipeline(media)),
396 session->src);
397
398 g_object_get(session->session, "sink-pad", &sinkpad, NULL);
399 srcpad = gst_element_get_static_pad(src, "ghostsrc");
400 purple_debug_info("media", "connecting pad: %s\n",
401 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
402 ? "success" : "failure");
447 } 403 }
448 404
449 void 405 void
450 purple_media_set_audio_sink(PurpleMedia *media, GstElement *audio_sink) 406 purple_media_set_sink(PurpleMedia *media, const gchar *sess_id, GstElement *sink)
451 { 407 {
452 g_object_set(G_OBJECT(media), "audio-sink", audio_sink, NULL); 408 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
453 } 409 if (session->sink)
454 410 gst_object_unref(session->sink);
455 void 411 session->sink = sink;
456 purple_media_set_video_src(PurpleMedia *media, GstElement *video_src) 412 gst_bin_add(GST_BIN(purple_media_get_pipeline(media)),
457 { 413 session->sink);
458 g_object_set(G_OBJECT(media), "video-src", video_src, NULL);
459 }
460
461 void
462 purple_media_set_video_sink(PurpleMedia *media, GstElement *video_sink)
463 {
464 g_object_set(G_OBJECT(media), "video-sink", video_sink, NULL);
465 } 414 }
466 415
467 GstElement * 416 GstElement *
468 purple_media_get_audio_src(PurpleMedia *media) 417 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
469 { 418 {
470 GstElement *ret; 419 return purple_media_get_session(media, sess_id)->src;
471 g_object_get(G_OBJECT(media), "audio-src", &ret, NULL);
472 return ret;
473 } 420 }
474 421
475 GstElement * 422 GstElement *
476 purple_media_get_audio_sink(PurpleMedia *media) 423 purple_media_get_sink(PurpleMedia *media, const gchar *sess_id)
477 { 424 {
478 GstElement *ret; 425 return purple_media_get_session(media, sess_id)->src;
479 g_object_get(G_OBJECT(media), "audio-sink", &ret, NULL);
480 return ret;
481 } 426 }
482 427
483 GstElement * 428 GstElement *
484 purple_media_get_video_src(PurpleMedia *media) 429 purple_media_get_pipeline(PurpleMedia *media)
485 { 430 {
486 GstElement *ret; 431 if (!media->priv->pipeline) {
487 g_object_get(G_OBJECT(media), "video-src", &ret, NULL); 432 media->priv->pipeline = gst_pipeline_new(media->priv->name);
488 return ret; 433 gst_bin_add(GST_BIN(media->priv->pipeline), GST_ELEMENT(media->priv->conference));
489 } 434 }
490 435
491 GstElement * 436 return media->priv->pipeline;
492 purple_media_get_video_sink(PurpleMedia *media)
493 {
494 GstElement *ret;
495 g_object_get(G_OBJECT(media), "video-sink", &ret, NULL);
496 return ret;
497 }
498
499 GstElement *
500 purple_media_get_audio_pipeline(PurpleMedia *media)
501 {
502 if (!media->priv->audio_pipeline) {
503 media->priv->audio_pipeline = gst_pipeline_new(media->priv->name);
504 gst_bin_add(GST_BIN(media->priv->audio_pipeline), GST_ELEMENT(media->priv->conference));
505 }
506
507 return media->priv->audio_pipeline;
508 } 437 }
509 438
510 PurpleConnection * 439 PurpleConnection *
511 purple_media_get_connection(PurpleMedia *media) 440 purple_media_get_connection(PurpleMedia *media)
512 { 441 {
670 GstPad *ghost; 599 GstPad *ghost;
671 const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device"); 600 const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device");
672 601
673 purple_debug_info("media", "purple_media_audio_init_src\n"); 602 purple_debug_info("media", "purple_media_audio_init_src\n");
674 603
675 *sendbin = gst_bin_new("sendbin"); 604 *sendbin = gst_bin_new("purplesendaudiobin");
676 src = gst_element_factory_make("alsasrc", "asrc"); 605 src = gst_element_factory_make("alsasrc", "asrc");
677 *sendlevel = gst_element_factory_make("level", "sendlevel"); 606 *sendlevel = gst_element_factory_make("level", "sendlevel");
678 gst_bin_add_many(GST_BIN(*sendbin), src, *sendlevel, NULL); 607 gst_bin_add_many(GST_BIN(*sendbin), src, *sendlevel, NULL);
679 gst_element_link(src, *sendlevel); 608 gst_element_link(src, *sendlevel);
680 pad = gst_element_get_pad(*sendlevel, "src"); 609 pad = gst_element_get_pad(*sendlevel, "src");
698 } 627 }
699 } 628 }
700 } 629 }
701 630
702 void 631 void
632 purple_media_video_init_src(GstElement **sendbin)
633 {
634 GstElement *src;
635 GstPad *pad;
636 GstPad *ghost;
637 const gchar *video_plugin = purple_prefs_get_string("/purple/media/video/plugin");
638 const gchar *video_device = purple_prefs_get_string("/purple/media/video/device");
639
640 purple_debug_info("media", "purple_media_video_init_src\n");
641
642 *sendbin = gst_bin_new("purplesendvideobin");
643 src = gst_element_factory_make(video_plugin, "videosrc");
644 gst_bin_add(GST_BIN(*sendbin), src);
645
646 if (!strcmp(video_plugin, "videotestsrc")) {
647 /* unless is-live is set to true it doesn't throttle videotestsrc */
648 g_object_set (G_OBJECT(src), "is-live", TRUE, NULL);
649 }
650 pad = gst_element_get_pad(src, "src");
651 ghost = gst_ghost_pad_new("ghostsrc", pad);
652 gst_element_add_pad(*sendbin, ghost);
653
654 /* set current video device on "src"... */
655 if (video_device) {
656 GList *devices = purple_media_get_devices(src);
657 GList *dev = devices;
658 purple_debug_info("media", "Setting device of GstElement src to %s\n",
659 video_device);
660 for (; dev ; dev = dev->next) {
661 GValue *device = (GValue *) dev->data;
662 char *name = purple_media_get_device_name(src, device);
663 if (strcmp(name, video_device) == 0) {
664 purple_media_element_set_device(src, device);
665 }
666 g_free(name);
667 }
668 }
669 }
670
671 void
703 purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel) 672 purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel)
704 { 673 {
705 GstElement *sink; 674 GstElement *sink;
706 GstPad *pad, *ghost; 675 GstPad *pad, *ghost;
707 676
708 purple_debug_info("media", "purple_media_audio_init_recv\n"); 677 purple_debug_info("media", "purple_media_audio_init_recv\n");
709 678
710 *recvbin = gst_bin_new("pidginrecvbin"); 679 *recvbin = gst_bin_new("pidginrecvaudiobin");
711 sink = gst_element_factory_make("alsasink", "asink"); 680 sink = gst_element_factory_make("alsasink", "asink");
712 g_object_set(G_OBJECT(sink), "sync", FALSE, NULL); 681 g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
713 *recvlevel = gst_element_factory_make("level", "recvlevel"); 682 *recvlevel = gst_element_factory_make("level", "recvlevel");
714 gst_bin_add_many(GST_BIN(*recvbin), sink, *recvlevel, NULL); 683 gst_bin_add_many(GST_BIN(*recvbin), sink, *recvlevel, NULL);
715 gst_element_link(*recvlevel, sink); 684 gst_element_link(*recvlevel, sink);
719 g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL); 688 g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL);
720 689
721 purple_debug_info("media", "purple_media_audio_init_recv end\n"); 690 purple_debug_info("media", "purple_media_audio_init_recv end\n");
722 } 691 }
723 692
693 void
694 purple_media_video_init_recv(GstElement **recvbin)
695 {
696 GstElement *sink;
697 GstPad *pad, *ghost;
698
699 purple_debug_info("media", "purple_media_video_init_recv\n");
700
701 *recvbin = gst_bin_new("pidginrecvvideobin");
702 sink = gst_element_factory_make("autovideosink", "purplevideosink");
703 gst_bin_add(GST_BIN(*recvbin), sink);
704 pad = gst_element_get_pad(sink, "sink");
705 ghost = gst_ghost_pad_new("ghostsink", pad);
706 gst_element_add_pad(*recvbin, ghost);
707
708 purple_debug_info("media", "purple_media_video_init_recv end\n");
709 }
710
724 static void 711 static void
725 purple_media_new_local_candidate(FsStream *stream, 712 purple_media_new_local_candidate(FsStream *stream,
726 FsCandidate *local_candidate, 713 FsCandidate *local_candidate,
727 PurpleMedia *media) 714 PurpleMediaSession *session)
728 { 715 {
716 gchar *name;
717 FsParticipant *participant;
729 purple_debug_info("media", "got new local candidate: %s\n", local_candidate->candidate_id); 718 purple_debug_info("media", "got new local candidate: %s\n", local_candidate->candidate_id);
730 media->priv->local_candidates = g_list_append(media->priv->local_candidates, 719 g_object_get(stream, "participant", &participant, NULL);
731 fs_candidate_copy(local_candidate)); 720 g_object_get(participant, "cname", &name, NULL);
732 } 721 g_object_unref(participant);
733 722
734 static void 723 purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate));
735 purple_media_candidates_prepared(FsStream *stream, PurpleMedia *media) 724
736 { 725 g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
737 g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED], 0); 726 0, session->id, name, fs_candidate_copy(local_candidate));
727
728 g_free(name);
729 }
730
731 static void
732 purple_media_candidates_prepared(FsStream *stream, PurpleMediaSession *session)
733 {
734 gchar *name;
735 FsParticipant *participant;
736 g_object_get(stream, "participant", &participant, NULL);
737 g_object_get(participant, "cname", &name, NULL);
738 g_object_unref(participant);
739 g_signal_emit(session->media, purple_media_signals[CANDIDATES_PREPARED], 0);
740 g_free(name);
738 } 741 }
739 742
740 /* callback called when a pair of transport candidates (local and remote) 743 /* callback called when a pair of transport candidates (local and remote)
741 * has been established */ 744 * has been established */
742 static void 745 static void
743 purple_media_candidate_pair_established(FsStream *stream, 746 purple_media_candidate_pair_established(FsStream *stream,
744 FsCandidate *native_candidate, 747 FsCandidate *native_candidate,
745 FsCandidate *remote_candidate, 748 FsCandidate *remote_candidate,
746 PurpleMedia *media) 749 PurpleMediaSession *session)
747 { 750 {
748 media->priv->local_candidate = fs_candidate_copy(native_candidate); 751 session->local_candidate = fs_candidate_copy(native_candidate);
749 media->priv->remote_candidate = fs_candidate_copy(remote_candidate); 752 session->remote_candidate = fs_candidate_copy(remote_candidate);
750 753
751 purple_debug_info("media", "candidate pair established\n"); 754 purple_debug_info("media", "candidate pair established\n");
752 g_signal_emit(media, purple_media_signals[CANDIDATE_PAIR], 0, 755 g_signal_emit(session->media, purple_media_signals[CANDIDATE_PAIR], 0,
753 media->priv->local_candidate, 756 session->local_candidate,
754 media->priv->remote_candidate); 757 session->remote_candidate);
755 } 758 }
756 759
757 static void 760 static void
758 purple_media_src_pad_added(FsStream *stream, GstPad *srcpad, 761 purple_media_src_pad_added(FsStream *stream, GstPad *srcpad,
759 FsCodec *codec, PurpleMedia *media) 762 FsCodec *codec, PurpleMediaSession *session)
760 { 763 {
761 GstElement *pipeline = purple_media_get_audio_pipeline(media); 764 GstElement *pipeline = purple_media_get_pipeline(session->media);
762 GstPad *sinkpad = gst_element_get_static_pad(purple_media_get_audio_sink(media), "ghostsink"); 765 GstPad *sinkpad = gst_element_get_static_pad(session->sink, "ghostsink");
763 purple_debug_info("media", "connecting new src pad: %s\n", 766 purple_debug_info("media", "connecting new src pad: %s\n",
764 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure"); 767 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure");
765 gst_element_set_state(pipeline, GST_STATE_PLAYING); 768 gst_element_set_state(pipeline, GST_STATE_PLAYING);
766 } 769 }
767 770
768 static gboolean 771 static gboolean
769 purple_media_add_stream_internal(PurpleMedia *media, FsSession **session, GList **streams, 772 purple_media_add_stream_internal(PurpleMedia *media, const gchar *sess_id,
770 GstElement *src, const gchar *who, FsMediaType type, 773 const gchar *who, FsMediaType type,
771 FsStreamDirection type_direction, const gchar *transmitter) 774 FsStreamDirection type_direction,
772 { 775 const gchar *transmitter)
773 char *cname = NULL; 776 {
777 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
774 FsParticipant *participant = NULL; 778 FsParticipant *participant = NULL;
775 GList *l = NULL;
776 FsStream *stream = NULL; 779 FsStream *stream = NULL;
777 FsParticipant *p = NULL;
778 FsStreamDirection *direction = NULL; 780 FsStreamDirection *direction = NULL;
779 FsSession *s = NULL; 781
780 782 if (!session) {
781 if (!*session) {
782 GError *err = NULL; 783 GError *err = NULL;
783 *session = fs_conference_new_session(media->priv->conference, type, &err); 784 GList *codec_conf;
785
786 session = g_new0(PurpleMediaSession, 1);
787
788 session->session = fs_conference_new_session(media->priv->conference, type, &err);
784 789
785 if (err != NULL) { 790 if (err != NULL) {
786 purple_debug_error("media", "Error creating session: %s\n", err->message); 791 purple_debug_error("media", "Error creating session: %s\n", err->message);
787 g_error_free(err); 792 g_error_free(err);
788 purple_conv_present_error(who, 793 purple_conv_present_error(who,
789 purple_connection_get_account(purple_media_get_connection(media)), 794 purple_connection_get_account(purple_media_get_connection(media)),
790 _("Error creating session.")); 795 _("Error creating session."));
796 g_free(session);
791 return FALSE; 797 return FALSE;
792 } 798 }
793 799
794 if (src) { 800 /*
795 GstPad *sinkpad; 801 * None of these three worked for me. THEORA is known to
796 GstPad *srcpad; 802 * not work as of at least Farsight2 0.0.2
797 g_object_get(*session, "sink-pad", &sinkpad, NULL); 803 */
798 srcpad = gst_element_get_static_pad(src, "ghostsrc"); 804 codec_conf = g_list_prepend(NULL, fs_codec_new(FS_CODEC_ID_DISABLE,
799 purple_debug_info("media", "connecting pad: %s\n", 805 "THEORA", FS_MEDIA_TYPE_VIDEO, 90000));
800 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK 806 codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_DISABLE,
801 ? "success" : "failure"); 807 "MPV", FS_MEDIA_TYPE_VIDEO, 90000));
802 } 808 codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_DISABLE,
803 } 809 "H264", FS_MEDIA_TYPE_VIDEO, 90000));
804 810
805 for (l = media->priv->participants; l != NULL; l = g_list_next(l)) { 811 /* XXX: SPEEX has a latency of 5 or 6 seconds for me */
806 g_object_get(l->data, "cname", cname, NULL); 812 #if 0
807 if (!strcmp(cname, who)) { 813 /* SPEEX is added through the configuration */
808 g_free(cname); 814 codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_ANY,
809 participant = l->data; 815 "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000));
810 break; 816 codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_ANY,
811 } 817 "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000));
812 g_free(cname); 818 #endif
813 } 819
814 820 g_object_set(G_OBJECT(session->session), "local-codecs-config",
815 if (!participant) { 821 codec_conf, NULL);
816 participant = fs_conference_new_participant(media->priv->conference, (gchar*)who, NULL); 822
817 media->priv->participants = g_list_prepend(media->priv->participants, participant); 823 fs_codec_list_destroy(codec_conf);
818 } 824
819 825 session->id = g_strdup(sess_id);
820 for (l = *streams; l != NULL; l = g_list_next(l)) { 826 session->media = media;
821 g_object_get(l->data, "participant", &p, "direction", &direction, "session", &s, NULL); 827 session->type = type;
822 828
823 if (participant == p && *session == s) { 829 purple_media_add_session(media, session);
824 stream = l->data; 830 }
825 break; 831
826 } 832 participant = purple_media_add_participant(media, who);
827 } 833
834 stream = purple_media_session_get_stream(session, who);
828 835
829 if (!stream) { 836 if (!stream) {
830 stream = fs_session_new_stream(*session, participant, 837 stream = fs_session_new_stream(session->session, participant,
831 type_direction, transmitter, 0, NULL, NULL); 838 type_direction, transmitter, 0, NULL, NULL);
832 *streams = g_list_prepend(*streams, stream); 839 purple_media_insert_stream(session, who, stream);
833 /* callback for new local candidate (new local candidate retreived) */ 840 /* callback for new local candidate (new local candidate retreived) */
834 g_signal_connect(G_OBJECT(stream), 841 g_signal_connect(G_OBJECT(stream),
835 "new-local-candidate", G_CALLBACK(purple_media_new_local_candidate), media); 842 "new-local-candidate", G_CALLBACK(purple_media_new_local_candidate), session);
836 /* callback for source pad added (new stream source ready) */ 843 /* callback for source pad added (new stream source ready) */
837 g_signal_connect(G_OBJECT(stream), 844 g_signal_connect(G_OBJECT(stream),
838 "src-pad-added", G_CALLBACK(purple_media_src_pad_added), media); 845 "src-pad-added", G_CALLBACK(purple_media_src_pad_added), session);
839 /* callback for local candidates prepared (local candidates ready to send) */ 846 /* callback for local candidates prepared (local candidates ready to send) */
840 g_signal_connect(G_OBJECT(stream), 847 g_signal_connect(G_OBJECT(stream),
841 "local-candidates-prepared", 848 "local-candidates-prepared",
842 G_CALLBACK(purple_media_candidates_prepared), media); 849 G_CALLBACK(purple_media_candidates_prepared), session);
843 /* callback for new active candidate pair (established connection) */ 850 /* callback for new active candidate pair (established connection) */
844 g_signal_connect(G_OBJECT(stream), 851 g_signal_connect(G_OBJECT(stream),
845 "new-active-candidate-pair", 852 "new-active-candidate-pair",
846 G_CALLBACK(purple_media_candidate_pair_established), media); 853 G_CALLBACK(purple_media_candidate_pair_established), session);
847 } else if (*direction != type_direction) { 854 } else if (*direction != type_direction) {
848 /* change direction */ 855 /* change direction */
849 g_object_set(stream, "direction", type_direction, NULL); 856 g_object_set(stream, "direction", type_direction, NULL);
850 } 857 }
851 858
852 return TRUE; 859 return TRUE;
853 } 860 }
854 861
855 gboolean 862 gboolean
856 purple_media_add_stream(PurpleMedia *media, const gchar *who, 863 purple_media_add_stream(PurpleMedia *media, const gchar *sess_id, const gchar *who,
857 PurpleMediaStreamType type, 864 PurpleMediaStreamType type,
858 const gchar *transmitter) 865 const gchar *transmitter)
859 { 866 {
860 FsStreamDirection type_direction; 867 FsStreamDirection type_direction;
861 868
867 else if (type & PURPLE_MEDIA_RECV_AUDIO) 874 else if (type & PURPLE_MEDIA_RECV_AUDIO)
868 type_direction = FS_DIRECTION_RECV; 875 type_direction = FS_DIRECTION_RECV;
869 else 876 else
870 type_direction = FS_DIRECTION_NONE; 877 type_direction = FS_DIRECTION_NONE;
871 878
872 if (!purple_media_add_stream_internal(media, &media->priv->audio_session, 879 if (!purple_media_add_stream_internal(media, sess_id, who,
873 &media->priv->audio_streams,
874 media->priv->audio_src, who,
875 FS_MEDIA_TYPE_AUDIO, type_direction, 880 FS_MEDIA_TYPE_AUDIO, type_direction,
876 transmitter)) { 881 transmitter)) {
877 return FALSE; 882 return FALSE;
878 } 883 }
879 } 884 }
885 else if (type & PURPLE_MEDIA_RECV_VIDEO) 890 else if (type & PURPLE_MEDIA_RECV_VIDEO)
886 type_direction = FS_DIRECTION_RECV; 891 type_direction = FS_DIRECTION_RECV;
887 else 892 else
888 type_direction = FS_DIRECTION_NONE; 893 type_direction = FS_DIRECTION_NONE;
889 894
890 if (!purple_media_add_stream_internal(media, &media->priv->video_session, 895 if (!purple_media_add_stream_internal(media, sess_id, who,
891 &media->priv->video_streams,
892 media->priv->video_src, who,
893 FS_MEDIA_TYPE_VIDEO, type_direction, 896 FS_MEDIA_TYPE_VIDEO, type_direction,
894 transmitter)) { 897 transmitter)) {
895 return FALSE; 898 return FALSE;
896 } 899 }
897 } 900 }
898 return TRUE; 901 return TRUE;
899 } 902 }
900 903
901 void 904 void
902 purple_media_remove_stream(PurpleMedia *media, const gchar *who, PurpleMediaStreamType type) 905 purple_media_remove_stream(PurpleMedia *media, const gchar *sess_id, const gchar *who)
903 { 906 {
904 907
905 } 908 }
906 909
907 static FsStream * 910 PurpleMediaStreamType
908 purple_media_get_audio_stream(PurpleMedia *media, const gchar *name) 911 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
909 { 912 {
910 GList *streams = media->priv->audio_streams; 913 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
911 for (; streams; streams = streams->next) { 914 return session->type;
912 FsParticipant *participant;
913 gchar *cname;
914 g_object_get(streams->data, "participant", &participant, NULL);
915 g_object_get(participant, "cname", &cname, NULL);
916
917 if (!strcmp(cname, name)) {
918 return streams->data;
919 }
920 }
921
922 return NULL;
923 } 915 }
924 916
925 GList * 917 GList *
926 purple_media_get_local_audio_codecs(PurpleMedia *media) 918 purple_media_get_local_codecs(PurpleMedia *media, const gchar *sess_id)
927 { 919 {
928 GList *codecs; 920 GList *codecs;
929 g_object_get(G_OBJECT(media->priv->audio_session), "local-codecs", &codecs, NULL); 921 g_object_get(G_OBJECT(purple_media_get_session(media, sess_id)->session),
922 "local-codecs", &codecs, NULL);
930 return codecs; 923 return codecs;
931 } 924 }
932 925
933 GList * 926 GList *
934 purple_media_get_local_audio_candidates(PurpleMedia *media) 927 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *name)
935 { 928 {
936 return media->priv->local_candidates; 929 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
930 return purple_media_session_get_local_candidates(session, name);
937 } 931 }
938 932
939 GList * 933 GList *
940 purple_media_get_negotiated_audio_codecs(PurpleMedia *media) 934 purple_media_get_negotiated_codecs(PurpleMedia *media, const gchar *sess_id)
941 { 935 {
936 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
942 GList *codec_intersection; 937 GList *codec_intersection;
943 g_object_get(media->priv->audio_session, "negotiated-codecs", &codec_intersection, NULL); 938 g_object_get(session->session, "negotiated-codecs", &codec_intersection, NULL);
944 return codec_intersection; 939 return codec_intersection;
945 } 940 }
946 941
947 void 942 void
948 purple_media_add_remote_audio_candidates(PurpleMedia *media, const gchar *name, GList *remote_candidates) 943 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
949 { 944 const gchar *name, GList *remote_candidates)
950 FsStream *stream = purple_media_get_audio_stream(media, name); 945 {
946 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
947 FsStream *stream = purple_media_session_get_stream(session, name);
951 GList *candidates = remote_candidates; 948 GList *candidates = remote_candidates;
952 for (; candidates; candidates = candidates->next) 949 for (; candidates; candidates = candidates->next)
953 fs_stream_add_remote_candidate(stream, candidates->data, NULL); 950 fs_stream_add_remote_candidate(stream, candidates->data, NULL);
954 } 951 }
955 952
956 FsCandidate * 953 FsCandidate *
957 purple_media_get_local_candidate(PurpleMedia *media) 954 purple_media_get_local_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
958 { 955 {
959 return media->priv->local_candidate; 956 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
957 return session->local_candidate;
960 } 958 }
961 959
962 FsCandidate * 960 FsCandidate *
963 purple_media_get_remote_candidate(PurpleMedia *media) 961 purple_media_get_remote_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
964 { 962 {
965 return media->priv->remote_candidate; 963 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
966 } 964 return session->remote_candidate;
967 965 }
968 void 966
969 purple_media_set_remote_audio_codecs(PurpleMedia *media, const gchar *name, GList *codecs) 967 void
970 { 968 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, const gchar *name, GList *codecs)
971 fs_stream_set_remote_codecs(purple_media_get_audio_stream(media, name), codecs, NULL); 969 {
970 PurpleMediaSession *session = purple_media_get_session(media, sess_id);
971 FsStream *stream = purple_media_session_get_stream(session, name);
972 fs_stream_set_remote_codecs(stream, codecs, NULL);
972 } 973 }
973 974
974 #endif /* USE_VV */ 975 #endif /* USE_VV */