Mercurial > pidgin
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 */ |