]> git.0d.be Git - empathy.git/blob - src/empathy-streamed-media-handler.c
Merge remote-tracking branch 'em/trivia'
[empathy.git] / src / empathy-streamed-media-handler.c
1 /*
2  * empathy-streamed-media-handler.c - Source for EmpathyStreamedMediaHandler
3  * Copyright (C) 2008-2011 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include <telepathy-glib/account-channel-request.h>
27 #include <telepathy-glib/util.h>
28 #include <telepathy-glib/interfaces.h>
29
30 #include <telepathy-farstream/telepathy-farstream.h>
31
32 #include <libempathy/empathy-utils.h>
33
34 #include <libempathy-gtk/empathy-call-utils.h>
35
36 #include "empathy-streamed-media-handler.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
39 #include <libempathy/empathy-debug.h>
40
41 G_DEFINE_TYPE(EmpathyStreamedMediaHandler, empathy_streamed_media_handler, G_TYPE_OBJECT)
42
43 /* signal enum */
44 enum {
45   CONFERENCE_ADDED,
46   SRC_PAD_ADDED,
47   SINK_PAD_ADDED,
48   START_RECEIVING,
49   CLOSED,
50   STREAM_CLOSED,
51   CANDIDATES_CHANGED,
52   LAST_SIGNAL
53 };
54
55 static guint signals[LAST_SIGNAL] = {0};
56
57 enum {
58   PROP_TP_STREAMED_MEDIA = 1,
59   PROP_GST_BUS,
60   PROP_CONTACT,
61   PROP_INITIAL_AUDIO,
62   PROP_INITIAL_VIDEO,
63   PROP_SEND_AUDIO_CODEC,
64   PROP_SEND_VIDEO_CODEC,
65   PROP_RECV_AUDIO_CODECS,
66   PROP_RECV_VIDEO_CODECS,
67   PROP_AUDIO_REMOTE_CANDIDATE,
68   PROP_VIDEO_REMOTE_CANDIDATE,
69   PROP_AUDIO_LOCAL_CANDIDATE,
70   PROP_VIDEO_LOCAL_CANDIDATE,
71 };
72
73 /* private structure */
74
75 typedef struct {
76   gboolean dispose_has_run;
77   EmpathyTpStreamedMedia *call;
78   EmpathyContact *contact;
79   TfChannel *tfchannel;
80   gboolean initial_audio;
81   gboolean initial_video;
82
83   FsCodec *send_audio_codec;
84   FsCodec *send_video_codec;
85   GList *recv_audio_codecs;
86   GList *recv_video_codecs;
87   FsCandidate *audio_remote_candidate;
88   FsCandidate *video_remote_candidate;
89   FsCandidate *audio_local_candidate;
90   FsCandidate *video_local_candidate;
91 } EmpathyStreamedMediaHandlerPriv;
92
93 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyStreamedMediaHandler)
94
95 static void
96 empathy_streamed_media_handler_dispose (GObject *object)
97 {
98   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
99
100   if (priv->dispose_has_run)
101     return;
102
103   priv->dispose_has_run = TRUE;
104
105   if (priv->contact != NULL)
106     g_object_unref (priv->contact);
107
108   priv->contact = NULL;
109
110   if (priv->tfchannel != NULL)
111     g_object_unref (priv->tfchannel);
112
113   priv->tfchannel = NULL;
114
115   if (priv->call != NULL)
116     {
117       empathy_tp_streamed_media_close (priv->call);
118       g_object_unref (priv->call);
119     }
120
121   priv->call = NULL;
122
123   /* release any references held by the object here */
124   if (G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->dispose)
125     G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->dispose (object);
126 }
127
128 static void
129 empathy_streamed_media_handler_finalize (GObject *object)
130 {
131   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
132
133   fs_codec_destroy (priv->send_audio_codec);
134   fs_codec_destroy (priv->send_video_codec);
135   fs_codec_list_destroy (priv->recv_audio_codecs);
136   fs_codec_list_destroy (priv->recv_video_codecs);
137   fs_candidate_destroy (priv->audio_remote_candidate);
138   fs_candidate_destroy (priv->video_remote_candidate);
139   fs_candidate_destroy (priv->audio_local_candidate);
140   fs_candidate_destroy (priv->video_local_candidate);
141
142   if (G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->finalize)
143     G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->finalize (object);
144 }
145
146 static void
147 empathy_streamed_media_handler_init (EmpathyStreamedMediaHandler *obj)
148 {
149   EmpathyStreamedMediaHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
150     EMPATHY_TYPE_STREAMED_MEDIA_HANDLER, EmpathyStreamedMediaHandlerPriv);
151
152   obj->priv = priv;
153 }
154
155 static void
156 empathy_streamed_media_handler_constructed (GObject *object)
157 {
158   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
159
160   if (priv->contact == NULL)
161     {
162       g_object_get (priv->call, "contact", &(priv->contact), NULL);
163     }
164 }
165
166 static void
167 empathy_streamed_media_handler_set_property (GObject *object,
168   guint property_id, const GValue *value, GParamSpec *pspec)
169 {
170   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
171
172   switch (property_id)
173     {
174       case PROP_CONTACT:
175         priv->contact = g_value_dup_object (value);
176         break;
177       case PROP_TP_STREAMED_MEDIA:
178         priv->call = g_value_dup_object (value);
179         break;
180       case PROP_INITIAL_AUDIO:
181         priv->initial_audio = g_value_get_boolean (value);
182         break;
183       case PROP_INITIAL_VIDEO:
184         priv->initial_video = g_value_get_boolean (value);
185         break;
186       default:
187         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
188     }
189 }
190
191 static void
192 empathy_streamed_media_handler_get_property (GObject *object,
193   guint property_id, GValue *value, GParamSpec *pspec)
194 {
195   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
196
197   switch (property_id)
198     {
199       case PROP_CONTACT:
200         g_value_set_object (value, priv->contact);
201         break;
202       case PROP_TP_STREAMED_MEDIA:
203         g_value_set_object (value, priv->call);
204         break;
205       case PROP_INITIAL_AUDIO:
206         g_value_set_boolean (value, priv->initial_audio);
207         break;
208       case PROP_INITIAL_VIDEO:
209         g_value_set_boolean (value, priv->initial_video);
210         break;
211       case PROP_SEND_AUDIO_CODEC:
212         g_value_set_boxed (value, priv->send_audio_codec);
213         break;
214       case PROP_SEND_VIDEO_CODEC:
215         g_value_set_boxed (value, priv->send_video_codec);
216         break;
217       case PROP_RECV_AUDIO_CODECS:
218         g_value_set_boxed (value, priv->recv_audio_codecs);
219         break;
220       case PROP_RECV_VIDEO_CODECS:
221         g_value_set_boxed (value, priv->recv_video_codecs);
222         break;
223       case PROP_AUDIO_REMOTE_CANDIDATE:
224         g_value_set_boxed (value, priv->audio_remote_candidate);
225         break;
226       case PROP_VIDEO_REMOTE_CANDIDATE:
227         g_value_set_boxed (value, priv->video_remote_candidate);
228         break;
229       case PROP_AUDIO_LOCAL_CANDIDATE:
230         g_value_set_boxed (value, priv->audio_local_candidate);
231         break;
232       case PROP_VIDEO_LOCAL_CANDIDATE:
233         g_value_set_boxed (value, priv->video_local_candidate);
234         break;
235       default:
236         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
237     }
238 }
239
240
241 static void
242 empathy_streamed_media_handler_class_init (EmpathyStreamedMediaHandlerClass *klass)
243 {
244   GObjectClass *object_class = G_OBJECT_CLASS (klass);
245   GParamSpec *param_spec;
246
247   g_type_class_add_private (klass, sizeof (EmpathyStreamedMediaHandlerPriv));
248
249   object_class->constructed = empathy_streamed_media_handler_constructed;
250   object_class->set_property = empathy_streamed_media_handler_set_property;
251   object_class->get_property = empathy_streamed_media_handler_get_property;
252   object_class->dispose = empathy_streamed_media_handler_dispose;
253   object_class->finalize = empathy_streamed_media_handler_finalize;
254
255   param_spec = g_param_spec_object ("contact",
256     "contact", "The remote contact",
257     EMPATHY_TYPE_CONTACT,
258     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
259   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
260
261   param_spec = g_param_spec_object ("tp-call",
262     "tp-call", "The calls channel wrapper",
263     EMPATHY_TYPE_TP_STREAMED_MEDIA,
264     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
265   g_object_class_install_property (object_class, PROP_TP_STREAMED_MEDIA, param_spec);
266
267   param_spec = g_param_spec_boolean ("initial-audio",
268     "initial-audio", "Whether the call should start with audio",
269     TRUE,
270     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
271   g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
272       param_spec);
273
274   param_spec = g_param_spec_boolean ("initial-video",
275     "initial-video", "Whether the call should start with video",
276     FALSE,
277     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
278   g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
279     param_spec);
280
281   param_spec = g_param_spec_boxed ("send-audio-codec",
282     "send audio codec", "Codec used to encode the outgoing video stream",
283     FS_TYPE_CODEC,
284     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
285   g_object_class_install_property (object_class, PROP_SEND_AUDIO_CODEC,
286     param_spec);
287
288   param_spec = g_param_spec_boxed ("send-video-codec",
289     "send video codec", "Codec used to encode the outgoing video stream",
290     FS_TYPE_CODEC,
291     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
292   g_object_class_install_property (object_class, PROP_SEND_VIDEO_CODEC,
293     param_spec);
294
295   param_spec = g_param_spec_boxed ("recv-audio-codecs",
296     "recvs audio codec", "Codecs used to decode the incoming audio stream",
297     FS_TYPE_CODEC_LIST,
298     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
299   g_object_class_install_property (object_class, PROP_RECV_AUDIO_CODECS,
300     param_spec);
301
302   param_spec = g_param_spec_boxed ("recv-video-codecs",
303     "recvs video codec", "Codecs used to decode the incoming video stream",
304     FS_TYPE_CODEC_LIST,
305     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
306   g_object_class_install_property (object_class, PROP_RECV_VIDEO_CODECS,
307     param_spec);
308
309   param_spec = g_param_spec_boxed ("audio-remote-candidate",
310     "audio remote candidate",
311     "Remote candidate used for the audio stream",
312     FS_TYPE_CANDIDATE,
313     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
314   g_object_class_install_property (object_class,
315       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
316
317   param_spec = g_param_spec_boxed ("video-remote-candidate",
318     "video remote candidate",
319     "Remote candidate used for the video stream",
320     FS_TYPE_CANDIDATE,
321     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
322   g_object_class_install_property (object_class,
323       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
324
325   param_spec = g_param_spec_boxed ("audio-local-candidate",
326     "audio local candidate",
327     "Local candidate used for the audio stream",
328     FS_TYPE_CANDIDATE,
329     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
330   g_object_class_install_property (object_class,
331       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
332
333   param_spec = g_param_spec_boxed ("video-local-candidate",
334     "video local candidate",
335     "Local candidate used for the video stream",
336     FS_TYPE_CANDIDATE,
337     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
338   g_object_class_install_property (object_class,
339       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
340
341   signals[CONFERENCE_ADDED] =
342     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
343       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
344       g_cclosure_marshal_generic,
345       G_TYPE_NONE,
346       1, FS_TYPE_CONFERENCE);
347
348   signals[SRC_PAD_ADDED] =
349     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
350       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
351       g_cclosure_marshal_generic,
352       G_TYPE_BOOLEAN,
353       2, GST_TYPE_PAD, G_TYPE_UINT);
354
355   signals[SINK_PAD_ADDED] =
356     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
357       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
358       g_cclosure_marshal_generic,
359       G_TYPE_BOOLEAN,
360       2, GST_TYPE_PAD, G_TYPE_UINT);
361
362   signals[START_RECEIVING] =
363     g_signal_new ("start-receiving", G_TYPE_FROM_CLASS (klass),
364       G_SIGNAL_RUN_LAST, 0,
365       g_signal_accumulator_true_handled, NULL,
366       g_cclosure_marshal_generic,
367       G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
368
369   signals[CLOSED] =
370     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
371       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
372       g_cclosure_marshal_generic,
373       G_TYPE_NONE,
374       0);
375
376   signals[STREAM_CLOSED] =
377     g_signal_new ("stream-closed", G_TYPE_FROM_CLASS (klass),
378       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
379       g_cclosure_marshal_generic,
380       G_TYPE_NONE, 1, TF_TYPE_CONTENT);
381
382   signals[CANDIDATES_CHANGED] =
383     g_signal_new ("candidates-changed", G_TYPE_FROM_CLASS (klass),
384       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
385       g_cclosure_marshal_generic,
386       G_TYPE_NONE, 1, G_TYPE_UINT);
387 }
388
389 /**
390  * empathy_streamed_media_handler_new_for_contact:
391  * @contact: an #EmpathyContact
392  *
393  * Creates a new #EmpathyStreamedMediaHandler with contact @contact.
394  *
395  * Return value: a new #EmpathyStreamedMediaHandler
396  */
397 EmpathyStreamedMediaHandler *
398 empathy_streamed_media_handler_new_for_contact (EmpathyContact *contact)
399 {
400   return EMPATHY_STREAMED_MEDIA_HANDLER (g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
401     "contact", contact, NULL));
402 }
403
404 EmpathyStreamedMediaHandler *
405 empathy_streamed_media_handler_new_for_channel (EmpathyTpStreamedMedia *call)
406 {
407   return EMPATHY_STREAMED_MEDIA_HANDLER (g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
408     "tp-call", call,
409     "initial-video", empathy_tp_streamed_media_is_receiving_video (call),
410     NULL));
411 }
412
413 static void
414 update_sending_codec (EmpathyStreamedMediaHandler *self,
415     FsCodec *codec,
416     FsSession *session)
417 {
418   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
419   FsMediaType type;
420
421   if (codec == NULL || session == NULL)
422     return;
423
424   g_object_get (session, "media-type", &type, NULL);
425
426   if (type == FS_MEDIA_TYPE_AUDIO)
427     {
428       priv->send_audio_codec = fs_codec_copy (codec);
429       g_object_notify (G_OBJECT (self), "send-audio-codec");
430     }
431   else if (type == FS_MEDIA_TYPE_VIDEO)
432     {
433       priv->send_video_codec = fs_codec_copy (codec);
434       g_object_notify (G_OBJECT (self), "send-video-codec");
435     }
436 }
437
438 static void
439 update_receiving_codec (EmpathyStreamedMediaHandler *self,
440     GList *codecs,
441     FsStream *stream)
442 {
443   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
444   FsSession *session;
445   FsMediaType type;
446
447   if (codecs == NULL || stream == NULL)
448     return;
449
450   g_object_get (stream, "session", &session, NULL);
451   if (session == NULL)
452     return;
453
454   g_object_get (session, "media-type", &type, NULL);
455
456   if (type == FS_MEDIA_TYPE_AUDIO)
457     {
458       priv->recv_audio_codecs = fs_codec_list_copy (codecs);
459       g_object_notify (G_OBJECT (self), "recv-audio-codecs");
460     }
461   else if (type == FS_MEDIA_TYPE_VIDEO)
462     {
463       priv->recv_video_codecs = fs_codec_list_copy (codecs);
464       g_object_notify (G_OBJECT (self), "recv-video-codecs");
465     }
466
467   g_object_unref (session);
468 }
469
470 static void
471 update_candidates (EmpathyStreamedMediaHandler *self,
472     FsCandidate *remote_candidate,
473     FsCandidate *local_candidate,
474     FsStream *stream)
475 {
476   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
477   FsSession *session;
478   FsMediaType type;
479
480   if (stream == NULL)
481     return;
482
483   g_object_get (stream, "session", &session, NULL);
484   if (session == NULL)
485     return;
486
487   g_object_get (session, "media-type", &type, NULL);
488
489   if (type == FS_MEDIA_TYPE_AUDIO)
490     {
491       if (remote_candidate != NULL)
492         {
493           fs_candidate_destroy (priv->audio_remote_candidate);
494           priv->audio_remote_candidate = fs_candidate_copy (remote_candidate);
495           g_object_notify (G_OBJECT (self), "audio-remote-candidate");
496         }
497
498       if (local_candidate != NULL)
499         {
500           fs_candidate_destroy (priv->audio_local_candidate);
501           priv->audio_local_candidate = fs_candidate_copy (local_candidate);
502           g_object_notify (G_OBJECT (self), "audio-local-candidate");
503         }
504
505       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
506           FS_MEDIA_TYPE_AUDIO);
507     }
508   else if (type == FS_MEDIA_TYPE_VIDEO)
509     {
510       if (remote_candidate != NULL)
511         {
512           fs_candidate_destroy (priv->video_remote_candidate);
513           priv->video_remote_candidate = fs_candidate_copy (remote_candidate);
514           g_object_notify (G_OBJECT (self), "video-remote-candidate");
515         }
516
517       if (local_candidate != NULL)
518         {
519           fs_candidate_destroy (priv->video_local_candidate);
520           priv->video_local_candidate = fs_candidate_copy (local_candidate);
521           g_object_notify (G_OBJECT (self), "video-local-candidate");
522         }
523
524       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
525           FS_MEDIA_TYPE_VIDEO);
526     }
527
528   g_object_unref (session);
529 }
530
531 void
532 empathy_streamed_media_handler_bus_message (EmpathyStreamedMediaHandler *handler,
533   GstBus *bus, GstMessage *message)
534 {
535   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
536   const GstStructure *s = gst_message_get_structure (message);
537
538   if (priv->tfchannel == NULL)
539     return;
540
541   if (s != NULL &&
542       gst_structure_has_name (s, "farstream-send-codec-changed"))
543     {
544       const GValue *val;
545       FsCodec *codec;
546       FsSession *session;
547
548       val = gst_structure_get_value (s, "codec");
549       codec = g_value_get_boxed (val);
550
551       val = gst_structure_get_value (s, "session");
552       session = g_value_get_object (val);
553
554       update_sending_codec (handler, codec, session);
555     }
556   else if (s != NULL &&
557       gst_structure_has_name (s, "farstream-recv-codecs-changed"))
558     {
559       const GValue *val;
560       GList *codecs;
561       FsStream *stream;
562
563       val = gst_structure_get_value (s, "codecs");
564       codecs = g_value_get_boxed (val);
565
566       val = gst_structure_get_value (s, "stream");
567       stream = g_value_get_object (val);
568
569       update_receiving_codec (handler, codecs, stream);
570     }
571   else if (s != NULL &&
572       gst_structure_has_name (s, "farstream-new-active-candidate-pair"))
573     {
574       const GValue *val;
575       FsCandidate *remote_candidate, *local_candidate;
576       FsStream *stream;
577
578       val = gst_structure_get_value (s, "remote-candidate");
579       remote_candidate = g_value_get_boxed (val);
580
581       val = gst_structure_get_value (s, "local-candidate");
582       local_candidate = g_value_get_boxed (val);
583
584       val = gst_structure_get_value (s, "stream");
585       stream = g_value_get_object (val);
586
587       update_candidates (handler, remote_candidate, local_candidate, stream);
588     }
589
590   tf_channel_bus_message (priv->tfchannel, message);
591 }
592
593 static void
594 empathy_streamed_media_handler_tf_channel_conference_added_cb (TfChannel *tfchannel,
595   FsConference *conference,
596   EmpathyStreamedMediaHandler *self)
597 {
598   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
599     GST_ELEMENT (conference));
600 }
601
602 static gboolean
603 src_pad_added_error_idle (gpointer data)
604 {
605   TfContent *content = data;
606
607   tf_content_error_literal (content, "Could not link sink");
608   g_object_unref (content);
609
610   return FALSE;
611 }
612
613 static void
614 empathy_streamed_media_handler_tf_content_src_pad_added_cb (TfContent *content,
615   guint handle,
616   FsStream *stream,
617   GstPad *pad,
618   FsCodec *codec,
619   EmpathyStreamedMediaHandler  *handler)
620 {
621   guint media_type;
622   gboolean retval;
623
624   g_object_get (content, "media-type", &media_type, NULL);
625
626   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
627       pad, media_type, &retval);
628
629   if (!retval)
630     g_idle_add (src_pad_added_error_idle, g_object_ref (content));
631 }
632
633
634 static gboolean
635 empathy_streamed_media_handler_tf_content_start_receiving_cb (TfContent *content,
636     guint *handles, guint handle_count, EmpathyTpStreamedMedia *call)
637 {
638   gboolean ret = FALSE;
639   guint media_type;
640
641   g_object_get (G_OBJECT (content), "media-type", &media_type, NULL);
642
643   g_signal_emit (G_OBJECT (call),
644     signals[START_RECEIVING], 0, media_type, &ret);
645
646   return ret;
647 }
648
649 static void
650 empathy_streamed_media_handler_tf_content_removed_cb (TfChannel *channel,
651     TfContent *content, EmpathyStreamedMediaHandler *handler)
652 {
653   g_signal_emit (handler, signals[STREAM_CLOSED], 0, content);
654 }
655
656 static void
657 empathy_streamed_media_handler_tf_channel_content_added_cb (TfChannel *tfchannel,
658   TfContent *content, EmpathyStreamedMediaHandler *handler)
659 {
660   guint media_type;
661   GstPad *spad;
662   gboolean retval;
663   FsSession *session;
664   FsCodec *codec;
665
666   g_signal_connect (content, "src-pad-added",
667       G_CALLBACK (empathy_streamed_media_handler_tf_content_src_pad_added_cb), handler);
668
669   g_signal_connect (content, "start-receiving",
670       G_CALLBACK (empathy_streamed_media_handler_tf_content_start_receiving_cb),
671         handler);
672
673   g_object_get (content, "media-type", &media_type,
674     "sink-pad", &spad, NULL);
675
676   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
677       spad, media_type, &retval);
678
679  if (!retval)
680    tf_content_error_literal (content, "Could not link source");
681
682  /* Get sending codec */
683  g_object_get (content, "fs-session", &session, NULL);
684  g_object_get (session, "current-send-codec", &codec, NULL);
685
686  update_sending_codec (handler, codec, session);
687
688  tp_clear_object (&session);
689  tp_clear_object (&codec);
690
691  gst_object_unref (spad);
692 }
693
694 static void
695 empathy_streamed_media_handler_tf_channel_closed_cb (TfChannel *tfchannel,
696   EmpathyStreamedMediaHandler *handler)
697 {
698   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
699 }
700
701 static void
702 empathy_streamed_media_handler_start_tpfs (GAsyncInitable *initable,
703     GAsyncResult *res, EmpathyStreamedMediaHandler *self)
704 {
705   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
706   GError *error = NULL;
707   GPtrArray *conferences;
708
709   if (g_async_initable_init_finish (initable, res, &error))
710     {
711       priv->tfchannel = TF_CHANNEL (initable);
712
713       /* Set up the telepathy farsight channel */
714       g_signal_connect (priv->tfchannel, "fs-conference-added",
715           G_CALLBACK (empathy_streamed_media_handler_tf_channel_conference_added_cb), self);
716       g_signal_connect (priv->tfchannel, "content-added",
717           G_CALLBACK (empathy_streamed_media_handler_tf_channel_content_added_cb), self);
718       g_signal_connect (priv->tfchannel, "content-removed",
719           G_CALLBACK (empathy_streamed_media_handler_tf_content_removed_cb),
720           self);
721       g_signal_connect (priv->tfchannel, "closed",
722           G_CALLBACK (empathy_streamed_media_handler_tf_channel_closed_cb), self);
723
724       g_object_get (priv->tfchannel, "fs-conferences", &conferences, NULL);
725       if (conferences)
726         {
727           if (conferences->len > 0)
728             empathy_streamed_media_handler_tf_channel_conference_added_cb (
729                 priv->tfchannel, g_ptr_array_index (conferences, 0), self);
730           g_ptr_array_unref (conferences);
731         }
732
733       empathy_tp_streamed_media_accept_incoming_call (priv->call);
734     }
735   else
736     {
737       g_warning ("Error creating tf channel");
738       g_clear_error (&error);
739     }
740   g_object_unref (self);
741 }
742
743 static void
744 empathy_streamed_media_handler_request_cb (GObject *source,
745     GAsyncResult *result,
746     gpointer user_data)
747 {
748   EmpathyStreamedMediaHandler *self = EMPATHY_STREAMED_MEDIA_HANDLER (user_data);
749   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
750   TpChannel *channel;
751   GError *error = NULL;
752   TpAccountChannelRequest *req = TP_ACCOUNT_CHANNEL_REQUEST (source);
753   TpAccount *account;
754
755   channel = tp_account_channel_request_create_and_handle_channel_finish (req,
756       result, NULL, &error);
757   if (channel == NULL)
758     {
759       DEBUG ("Failed to create the channel: %s", error->message);
760       g_error_free (error);
761       return;
762     }
763
764   account = tp_account_channel_request_get_account (req);
765
766   priv->call = empathy_tp_streamed_media_new (account, channel);
767
768   g_object_notify (G_OBJECT (self), "tp-call");
769
770
771   tf_channel_new_async (channel,
772       (GAsyncReadyCallback) empathy_streamed_media_handler_start_tpfs,
773       g_object_ref (self));
774
775   g_object_unref (channel);
776 }
777
778 void
779 empathy_streamed_media_handler_start_call (EmpathyStreamedMediaHandler *handler,
780     gint64 timestamp)
781 {
782   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
783   TpAccountChannelRequest *req;
784   TpAccount *account;
785   GHashTable *request;
786
787   if (priv->call != NULL)
788     {
789       TpChannel *channel;
790
791       g_object_get (priv->call, "channel", &channel, NULL);
792
793       tf_channel_new_async (channel,
794           (GAsyncReadyCallback)  empathy_streamed_media_handler_start_tpfs,
795           g_object_ref (handler));
796       g_object_unref (channel);
797       return;
798     }
799
800   /* No TpStreamedMedia object (we are redialing). Request a new media channel that
801    * will be used to create a new EmpathyTpStreamedMedia. */
802   g_assert (priv->contact != NULL);
803
804   account = empathy_contact_get_account (priv->contact);
805   request = empathy_call_create_streamed_media_request (
806       empathy_contact_get_id (priv->contact),
807       priv->initial_audio, priv->initial_video);
808
809   req = tp_account_channel_request_new (account, request, timestamp);
810
811   tp_account_channel_request_create_and_handle_channel_async (req, NULL,
812       empathy_streamed_media_handler_request_cb, handler);
813
814   g_object_unref (req);
815   g_hash_table_unref (request);
816 }
817
818 /**
819  * empathy_streamed_media_handler_stop_call:
820  * @handler: an #EmpathyStreamedMediaHandler
821  *
822  * Closes the #EmpathyStreamedMediaHandler's call and frees its resources.
823  */
824 void
825 empathy_streamed_media_handler_stop_call (EmpathyStreamedMediaHandler *handler)
826 {
827   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
828
829   if (priv->call != NULL)
830     {
831       empathy_tp_streamed_media_leave (priv->call);
832       g_object_unref (priv->call);
833     }
834
835   priv->call = NULL;
836 }
837
838 /**
839  * empathy_streamed_media_handler_has_initial_video:
840  * @handler: an #EmpathyStreamedMediaHandler
841  *
842  * Return %TRUE if the call managed by this #EmpathyStreamedMediaHandler was
843  * created with video enabled
844  *
845  * Return value: %TRUE if the call was created as a video conversation.
846  */
847 gboolean
848 empathy_streamed_media_handler_has_initial_video (EmpathyStreamedMediaHandler *handler)
849 {
850   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
851
852   return priv->initial_video;
853 }
854
855 FsCodec *
856 empathy_streamed_media_handler_get_send_audio_codec (EmpathyStreamedMediaHandler *self)
857 {
858   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
859
860   return priv->send_audio_codec;
861 }
862
863 FsCodec *
864 empathy_streamed_media_handler_get_send_video_codec (EmpathyStreamedMediaHandler *self)
865 {
866   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
867
868   return priv->send_video_codec;
869 }
870
871 GList *
872 empathy_streamed_media_handler_get_recv_audio_codecs (EmpathyStreamedMediaHandler *self)
873 {
874   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
875
876   return priv->recv_audio_codecs;
877 }
878
879 GList *
880 empathy_streamed_media_handler_get_recv_video_codecs (EmpathyStreamedMediaHandler *self)
881 {
882   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
883
884   return priv->recv_video_codecs;
885 }
886
887 FsCandidate *
888 empathy_streamed_media_handler_get_audio_remote_candidate (
889     EmpathyStreamedMediaHandler *self)
890 {
891   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
892
893   return priv->audio_remote_candidate;
894 }
895
896 FsCandidate *
897 empathy_streamed_media_handler_get_audio_local_candidate (
898     EmpathyStreamedMediaHandler *self)
899 {
900   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
901
902   return priv->audio_local_candidate;
903 }
904
905 FsCandidate *
906 empathy_streamed_media_handler_get_video_remote_candidate (
907     EmpathyStreamedMediaHandler *self)
908 {
909   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
910
911   return priv->video_remote_candidate;
912 }
913
914 FsCandidate *
915 empathy_streamed_media_handler_get_video_local_candidate (
916     EmpathyStreamedMediaHandler *self)
917 {
918   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
919
920   return priv->video_local_candidate;
921 }