]> git.0d.be Git - empathy.git/blob - src/empathy-call-handler.c
Remove stray debug
[empathy.git] / src / empathy-call-handler.c
1 /*
2  * empathy-call-handler.c - Source for EmpathyCallHandler
3  * Copyright (C) 2008-2009 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
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-channel-request.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/interfaces.h>
28
29 #include <telepathy-yell/telepathy-yell.h>
30
31 #include <telepathy-farstream/telepathy-farstream.h>
32
33 #include <libempathy/empathy-utils.h>
34 #include <libempathy/empathy-tp-contact-factory.h>
35
36 #include "empathy-call-handler.h"
37 #include "empathy-call-factory.h"
38 #include "src-marshal.h"
39
40 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
41 #include <libempathy/empathy-debug.h>
42
43 G_DEFINE_TYPE(EmpathyCallHandler, empathy_call_handler, G_TYPE_OBJECT)
44
45 /* signal enum */
46 enum {
47   CONFERENCE_ADDED,
48   CONFERENCE_REMOVED,
49   SRC_PAD_ADDED,
50   SINK_PAD_ADDED,
51   SINK_PAD_REMOVED,
52   CLOSED,
53   CANDIDATES_CHANGED,
54   LAST_SIGNAL
55 };
56
57 static guint signals[LAST_SIGNAL] = {0};
58
59 enum {
60   PROP_CALL_CHANNEL = 1,
61   PROP_GST_BUS,
62   PROP_CONTACT,
63   PROP_MEMBERS,
64   PROP_INITIAL_AUDIO,
65   PROP_INITIAL_VIDEO,
66   PROP_SEND_AUDIO_CODEC,
67   PROP_SEND_VIDEO_CODEC,
68   PROP_RECV_AUDIO_CODECS,
69   PROP_RECV_VIDEO_CODECS,
70   PROP_AUDIO_REMOTE_CANDIDATE,
71   PROP_VIDEO_REMOTE_CANDIDATE,
72   PROP_AUDIO_LOCAL_CANDIDATE,
73   PROP_VIDEO_LOCAL_CANDIDATE,
74 };
75
76 /* private structure */
77
78 typedef struct {
79   TpyCallChannel *call;
80
81   EmpathyContact *contact;
82   /* GArray of TpContacts */
83   GArray *members;
84   TfChannel *tfchannel;
85   gboolean initial_audio;
86   gboolean initial_video;
87
88   FsCodec *send_audio_codec;
89   FsCodec *send_video_codec;
90   GList *recv_audio_codecs;
91   GList *recv_video_codecs;
92   FsCandidate *audio_remote_candidate;
93   FsCandidate *video_remote_candidate;
94   FsCandidate *audio_local_candidate;
95   FsCandidate *video_local_candidate;
96 } EmpathyCallHandlerPriv;
97
98 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
99
100 static void
101 empathy_call_handler_dispose (GObject *object)
102 {
103   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
104
105   tp_clear_object (&priv->tfchannel);
106   tp_clear_object (&priv->call);
107   tp_clear_object (&priv->contact);
108
109   tp_clear_pointer (&priv->members, g_array_unref);
110
111   G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose (object);
112 }
113
114 static void
115 empathy_call_handler_finalize (GObject *object)
116 {
117   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
118
119   fs_codec_destroy (priv->send_audio_codec);
120   fs_codec_destroy (priv->send_video_codec);
121   fs_codec_list_destroy (priv->recv_audio_codecs);
122   fs_codec_list_destroy (priv->recv_video_codecs);
123   fs_candidate_destroy (priv->audio_remote_candidate);
124   fs_candidate_destroy (priv->video_remote_candidate);
125   fs_candidate_destroy (priv->audio_local_candidate);
126   fs_candidate_destroy (priv->video_local_candidate);
127
128   G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize (object);
129 }
130
131 static void
132 empathy_call_handler_init (EmpathyCallHandler *obj)
133 {
134   EmpathyCallHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
135     EMPATHY_TYPE_CALL_HANDLER, EmpathyCallHandlerPriv);
136
137   obj->priv = priv;
138 }
139
140 static void
141 on_get_contacts_cb (TpConnection *connection,
142     guint n_contacts,
143     EmpathyContact * const * contacts,
144     guint n_failed,
145     const TpHandle *failed,
146     const GError *error,
147     gpointer user_data,
148     GObject *weak_object)
149 {
150   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (weak_object);
151   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
152   guint i;
153
154   if (n_failed > 0)
155     g_warning ("Failed to get %d EmpathyContacts: %s",
156         n_failed, error->message);
157
158   priv->members = g_array_sized_new (FALSE, TRUE,
159       sizeof (EmpathyContact *), n_contacts);
160
161   for (i = 0; i < n_contacts; i++)
162     g_object_ref (contacts[i]);
163
164   g_array_append_vals (priv->members, contacts, n_contacts);
165
166   g_object_notify (G_OBJECT (self), "members");
167 }
168
169 static void
170 on_members_changed_cb (TpyCallChannel *call,
171     GHashTable *members,
172     EmpathyCallHandler *self)
173 {
174   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
175   GHashTableIter iter;
176   gpointer key, value;
177   TpHandle *handles;
178   guint n_handles;
179   guint i = 0;
180
181   if (members == NULL)
182     return;
183
184   n_handles = g_hash_table_size (members);
185   if (n_handles == 0)
186     return;
187
188   handles = g_new0 (TpHandle, n_handles);
189
190   g_hash_table_iter_init (&iter, members);
191   while (g_hash_table_iter_next (&iter, &key, &value))
192     handles[i++] = GPOINTER_TO_UINT (key);
193
194   empathy_tp_contact_factory_get_from_handles (
195       tp_channel_borrow_connection (TP_CHANNEL (priv->call)),
196       n_handles, handles,
197       on_get_contacts_cb,
198       NULL, NULL, G_OBJECT (self));
199
200   g_free (handles);
201 }
202
203 static void
204 empathy_call_handler_constructed (GObject *object)
205 {
206   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (object);
207   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
208 //  GHashTable *members;
209
210   g_signal_connect (priv->call, "members-changed",
211       G_CALLBACK (on_members_changed_cb), object);
212
213 /* FIXME
214   g_object_get (priv->call, "members", &members, NULL);
215
216   if (members)
217     on_members_changed_cb (priv->call, members, self);
218 */
219 }
220
221 static void
222 empathy_call_handler_set_property (GObject *object,
223   guint property_id, const GValue *value, GParamSpec *pspec)
224 {
225   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
226
227   switch (property_id)
228     {
229       case PROP_CONTACT:
230         priv->contact = g_value_dup_object (value);
231         break;
232       case PROP_MEMBERS:
233         priv->members = g_value_get_boxed (value);
234         break;
235       case PROP_CALL_CHANNEL:
236         priv->call = g_value_dup_object (value);
237         break;
238       case PROP_INITIAL_AUDIO:
239         priv->initial_audio = g_value_get_boolean (value);
240         break;
241       case PROP_INITIAL_VIDEO:
242         priv->initial_video = g_value_get_boolean (value);
243         break;
244       default:
245         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
246     }
247 }
248
249 static void
250 empathy_call_handler_get_property (GObject *object,
251   guint property_id, GValue *value, GParamSpec *pspec)
252 {
253   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
254
255   switch (property_id)
256     {
257       case PROP_CONTACT:
258         g_value_set_object (value, priv->contact);
259         break;
260       case PROP_MEMBERS:
261         g_value_set_boxed (value, priv->members);
262         break;
263       case PROP_CALL_CHANNEL:
264         g_value_set_object (value, priv->call);
265         break;
266       case PROP_INITIAL_AUDIO:
267         g_value_set_boolean (value, priv->initial_audio);
268         break;
269       case PROP_INITIAL_VIDEO:
270         g_value_set_boolean (value, priv->initial_video);
271         break;
272       case PROP_SEND_AUDIO_CODEC:
273         g_value_set_boxed (value, priv->send_audio_codec);
274         break;
275       case PROP_SEND_VIDEO_CODEC:
276         g_value_set_boxed (value, priv->send_video_codec);
277         break;
278       case PROP_RECV_AUDIO_CODECS:
279         g_value_set_boxed (value, priv->recv_audio_codecs);
280         break;
281       case PROP_RECV_VIDEO_CODECS:
282         g_value_set_boxed (value, priv->recv_video_codecs);
283         break;
284       case PROP_AUDIO_REMOTE_CANDIDATE:
285         g_value_set_boxed (value, priv->audio_remote_candidate);
286         break;
287       case PROP_VIDEO_REMOTE_CANDIDATE:
288         g_value_set_boxed (value, priv->video_remote_candidate);
289         break;
290       case PROP_AUDIO_LOCAL_CANDIDATE:
291         g_value_set_boxed (value, priv->audio_local_candidate);
292         break;
293       case PROP_VIDEO_LOCAL_CANDIDATE:
294         g_value_set_boxed (value, priv->video_local_candidate);
295         break;
296       default:
297         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
298     }
299 }
300
301
302 static void
303 empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
304 {
305   GObjectClass *object_class = G_OBJECT_CLASS (klass);
306   GParamSpec *param_spec;
307
308   g_type_class_add_private (klass, sizeof (EmpathyCallHandlerPriv));
309
310   object_class->constructed = empathy_call_handler_constructed;
311   object_class->set_property = empathy_call_handler_set_property;
312   object_class->get_property = empathy_call_handler_get_property;
313   object_class->dispose = empathy_call_handler_dispose;
314   object_class->finalize = empathy_call_handler_finalize;
315
316   param_spec = g_param_spec_object ("target-contact",
317     "TargetContact", "The contact",
318     EMPATHY_TYPE_CONTACT,
319     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
320   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
321
322   param_spec = g_param_spec_boxed ("members",
323     "call members", "The call participants",
324     G_TYPE_ARRAY,
325     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
326   g_object_class_install_property (object_class, PROP_MEMBERS, param_spec);
327
328   param_spec = g_param_spec_object ("call-channel",
329     "call channel", "The call channel",
330     TPY_TYPE_CALL_CHANNEL,
331     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
332   g_object_class_install_property (object_class, PROP_CALL_CHANNEL, param_spec);
333
334   param_spec = g_param_spec_boolean ("initial-audio",
335     "initial-audio", "Whether the call should start with audio",
336     TRUE,
337     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
338   g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
339       param_spec);
340
341   param_spec = g_param_spec_boolean ("initial-video",
342     "initial-video", "Whether the call should start with video",
343     FALSE,
344     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
345   g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
346     param_spec);
347
348   param_spec = g_param_spec_boxed ("send-audio-codec",
349     "send audio codec", "Codec used to encode the outgoing video stream",
350     FS_TYPE_CODEC,
351     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
352   g_object_class_install_property (object_class, PROP_SEND_AUDIO_CODEC,
353     param_spec);
354
355   param_spec = g_param_spec_boxed ("send-video-codec",
356     "send video codec", "Codec used to encode the outgoing video stream",
357     FS_TYPE_CODEC,
358     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
359   g_object_class_install_property (object_class, PROP_SEND_VIDEO_CODEC,
360     param_spec);
361
362   param_spec = g_param_spec_boxed ("recv-audio-codecs",
363     "recvs audio codec", "Codecs used to decode the incoming audio stream",
364     FS_TYPE_CODEC_LIST,
365     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
366   g_object_class_install_property (object_class, PROP_RECV_AUDIO_CODECS,
367     param_spec);
368
369   param_spec = g_param_spec_boxed ("recv-video-codecs",
370     "recvs video codec", "Codecs used to decode the incoming video stream",
371     FS_TYPE_CODEC_LIST,
372     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
373   g_object_class_install_property (object_class, PROP_RECV_VIDEO_CODECS,
374     param_spec);
375
376   param_spec = g_param_spec_boxed ("audio-remote-candidate",
377     "audio remote candidate",
378     "Remote candidate used for the audio stream",
379     FS_TYPE_CANDIDATE,
380     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
381   g_object_class_install_property (object_class,
382       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
383
384   param_spec = g_param_spec_boxed ("video-remote-candidate",
385     "video remote candidate",
386     "Remote candidate used for the video stream",
387     FS_TYPE_CANDIDATE,
388     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
389   g_object_class_install_property (object_class,
390       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
391
392   param_spec = g_param_spec_boxed ("audio-local-candidate",
393     "audio local candidate",
394     "Local candidate used for the audio stream",
395     FS_TYPE_CANDIDATE,
396     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
397   g_object_class_install_property (object_class,
398       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
399
400   param_spec = g_param_spec_boxed ("video-local-candidate",
401     "video local candidate",
402     "Local candidate used for the video stream",
403     FS_TYPE_CANDIDATE,
404     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
405   g_object_class_install_property (object_class,
406       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
407
408   signals[CONFERENCE_ADDED] =
409     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
410       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
411       g_cclosure_marshal_VOID__OBJECT,
412       G_TYPE_NONE,
413       1, FS_TYPE_CONFERENCE);
414
415   signals[CONFERENCE_REMOVED] =
416     g_signal_new ("conference-removed", G_TYPE_FROM_CLASS (klass),
417       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
418       g_cclosure_marshal_VOID__OBJECT,
419       G_TYPE_NONE,
420       1, FS_TYPE_CONFERENCE);
421
422   signals[SRC_PAD_ADDED] =
423     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
424       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
425       _src_marshal_BOOLEAN__OBJECT_UINT,
426       G_TYPE_BOOLEAN,
427       2, GST_TYPE_PAD, G_TYPE_UINT);
428
429   signals[SINK_PAD_ADDED] =
430     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
431       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
432       _src_marshal_BOOLEAN__OBJECT_UINT,
433       G_TYPE_BOOLEAN,
434       2, GST_TYPE_PAD, G_TYPE_UINT);
435
436   signals[SINK_PAD_REMOVED] =
437     g_signal_new ("sink-pad-removed", G_TYPE_FROM_CLASS (klass),
438       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
439       _src_marshal_BOOLEAN__OBJECT_UINT,
440       G_TYPE_BOOLEAN,
441       2, GST_TYPE_PAD, G_TYPE_UINT);
442
443   signals[CLOSED] =
444     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
445       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
446       g_cclosure_marshal_VOID__VOID,
447       G_TYPE_NONE,
448       0);
449
450   signals[CANDIDATES_CHANGED] =
451     g_signal_new ("candidates-changed", G_TYPE_FROM_CLASS (klass),
452       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
453       g_cclosure_marshal_VOID__UINT,
454       G_TYPE_NONE, 1, G_TYPE_UINT);
455 }
456
457 EmpathyCallHandler *
458 empathy_call_handler_new_for_channel (TpyCallChannel *call,
459   EmpathyContact *contact)
460 {
461   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
462     "call-channel", call,
463     "initial-video", tpy_call_channel_has_initial_video (call),
464     "target-contact", contact,
465     NULL));
466 }
467
468 static void
469 update_sending_codec (EmpathyCallHandler *self,
470     FsCodec *codec,
471     FsSession *session)
472 {
473   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
474   FsMediaType type;
475
476   if (codec == NULL || session == NULL)
477     return;
478
479   g_object_get (session, "media-type", &type, NULL);
480
481   if (type == FS_MEDIA_TYPE_AUDIO)
482     {
483       priv->send_audio_codec = fs_codec_copy (codec);
484       g_object_notify (G_OBJECT (self), "send-audio-codec");
485     }
486   else if (type == FS_MEDIA_TYPE_VIDEO)
487     {
488       priv->send_video_codec = fs_codec_copy (codec);
489       g_object_notify (G_OBJECT (self), "send-video-codec");
490     }
491 }
492
493 static void
494 update_receiving_codec (EmpathyCallHandler *self,
495     GList *codecs,
496     FsStream *stream)
497 {
498   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
499   FsSession *session;
500   FsMediaType type;
501
502   if (codecs == NULL || stream == NULL)
503     return;
504
505   g_object_get (stream, "session", &session, NULL);
506   if (session == NULL)
507     return;
508
509   g_object_get (session, "media-type", &type, NULL);
510
511   if (type == FS_MEDIA_TYPE_AUDIO)
512     {
513       priv->recv_audio_codecs = fs_codec_list_copy (codecs);
514       g_object_notify (G_OBJECT (self), "recv-audio-codecs");
515     }
516   else if (type == FS_MEDIA_TYPE_VIDEO)
517     {
518       priv->recv_video_codecs = fs_codec_list_copy (codecs);
519       g_object_notify (G_OBJECT (self), "recv-video-codecs");
520     }
521
522   g_object_unref (session);
523 }
524
525 static void
526 update_candidates (EmpathyCallHandler *self,
527     FsCandidate *remote_candidate,
528     FsCandidate *local_candidate,
529     FsStream *stream)
530 {
531   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
532   FsSession *session;
533   FsMediaType type;
534
535   if (stream == NULL)
536     return;
537
538   g_object_get (stream, "session", &session, NULL);
539   if (session == NULL)
540     return;
541
542   g_object_get (session, "media-type", &type, NULL);
543
544   if (type == FS_MEDIA_TYPE_AUDIO)
545     {
546       if (remote_candidate != NULL)
547         {
548           fs_candidate_destroy (priv->audio_remote_candidate);
549           priv->audio_remote_candidate = fs_candidate_copy (remote_candidate);
550           g_object_notify (G_OBJECT (self), "audio-remote-candidate");
551         }
552
553       if (local_candidate != NULL)
554         {
555           fs_candidate_destroy (priv->audio_local_candidate);
556           priv->audio_local_candidate = fs_candidate_copy (local_candidate);
557           g_object_notify (G_OBJECT (self), "audio-local-candidate");
558         }
559
560       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
561           FS_MEDIA_TYPE_AUDIO);
562     }
563   else if (type == FS_MEDIA_TYPE_VIDEO)
564     {
565       if (remote_candidate != NULL)
566         {
567           fs_candidate_destroy (priv->video_remote_candidate);
568           priv->video_remote_candidate = fs_candidate_copy (remote_candidate);
569           g_object_notify (G_OBJECT (self), "video-remote-candidate");
570         }
571
572       if (local_candidate != NULL)
573         {
574           fs_candidate_destroy (priv->video_local_candidate);
575           priv->video_local_candidate = fs_candidate_copy (local_candidate);
576           g_object_notify (G_OBJECT (self), "video-local-candidate");
577         }
578
579       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
580           FS_MEDIA_TYPE_VIDEO);
581     }
582
583   g_object_unref (session);
584 }
585
586 void
587 empathy_call_handler_bus_message (EmpathyCallHandler *handler,
588   GstBus *bus, GstMessage *message)
589 {
590   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
591   const GstStructure *s = gst_message_get_structure (message);
592
593   if (priv->tfchannel == NULL)
594     return;
595
596   if (s != NULL &&
597       gst_structure_has_name (s, "farsight-send-codec-changed"))
598     {
599       const GValue *val;
600       FsCodec *codec;
601       FsSession *session;
602
603       g_print ("empathy_call_handler_bus_message: farsight-send-codec-changed\n");
604
605       val = gst_structure_get_value (s, "codec");
606       codec = g_value_get_boxed (val);
607
608       val = gst_structure_get_value (s, "session");
609       session = g_value_get_object (val);
610
611       update_sending_codec (handler, codec, session);
612     }
613   else if (s != NULL &&
614       gst_structure_has_name (s, "farsight-recv-codecs-changed"))
615     {
616       const GValue *val;
617       GList *codecs;
618       FsStream *stream;
619
620       g_print ("empathy_call_handler_bus_message: farsight-recv-codecs-changed\n");
621
622       val = gst_structure_get_value (s, "codecs");
623       codecs = g_value_get_boxed (val);
624
625       val = gst_structure_get_value (s, "stream");
626       stream = g_value_get_object (val);
627
628       update_receiving_codec (handler, codecs, stream);
629     }
630   else if (s != NULL &&
631       gst_structure_has_name (s, "farsight-new-active-candidate-pair"))
632     {
633       const GValue *val;
634       FsCandidate *remote_candidate, *local_candidate;
635       FsStream *stream;
636
637       g_print ("empathy_call_handler_bus_message: farsight-new-active-candidate-pair\n");
638
639       val = gst_structure_get_value (s, "remote-candidate");
640       remote_candidate = g_value_get_boxed (val);
641
642       val = gst_structure_get_value (s, "local-candidate");
643       local_candidate = g_value_get_boxed (val);
644
645       val = gst_structure_get_value (s, "stream");
646       stream = g_value_get_object (val);
647
648       update_candidates (handler, remote_candidate, local_candidate, stream);
649     }
650
651   tf_channel_bus_message (priv->tfchannel, message);
652 }
653
654 static void
655 on_tf_channel_conference_added_cb (TfChannel *tfchannel,
656   GstElement *conference,
657   EmpathyCallHandler *self)
658 {
659   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
660     conference);
661 }
662
663 static void
664 on_tf_channel_conference_removed_cb (TfChannel *tfchannel,
665   FsConference *conference,
666   EmpathyCallHandler *self)
667 {
668   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_REMOVED], 0,
669     GST_ELEMENT (conference));
670 }
671
672 static gboolean
673 src_pad_added_error_idle (gpointer data)
674 {
675   TfContent *content = data;
676
677   tf_content_error (content, 0 /* FIXME */,
678       "Could not link sink", NULL);
679   g_object_unref (content);
680
681   return FALSE;
682 }
683
684 static void
685 on_tf_content_src_pad_added_cb (TfContent *content,
686   guint handle,
687   FsStream *stream,
688   GstPad *pad,
689   FsCodec *codec,
690   EmpathyCallHandler *handler)
691 {
692   guint media_type;
693   gboolean retval;
694
695   g_object_get (content, "media-type", &media_type, NULL);
696
697   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
698       pad, media_type, &retval);
699
700   if (!retval)
701     g_idle_add (src_pad_added_error_idle, g_object_ref (content));
702 }
703
704 static void
705 on_tf_channel_content_added_cb (TfChannel *tfchannel,
706   TfContent *content,
707   EmpathyCallHandler *handler)
708 {
709   FsMediaType mtype;
710   GstPad *spad;
711   FsSession *session;
712 //  FsStream *fs_stream;
713   FsCodec *codec;
714 //  GList *codecs;
715   gboolean retval;
716
717   g_signal_connect (content, "src-pad-added",
718       G_CALLBACK (on_tf_content_src_pad_added_cb), handler);
719 #if 0
720   g_signal_connect (content, "start-sending",
721       G_CALLBACK (on_tf_content_start_sending_cb), handler);
722   g_signal_connect (content, "stop-sending",
723       G_CALLBACK (on_tf_content_stop_sending_cb), handler);
724 #endif
725
726   g_object_get (content, "media-type", &mtype,
727     "sink-pad", &spad, NULL);
728
729   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
730       spad, mtype, &retval);
731
732  if (!retval)
733       tf_content_error (content, 0 /* FIXME */,
734           "Could not link source", NULL);
735
736  /* Get sending codec */
737  g_object_get (content, "fs-session", &session, NULL);
738  g_object_get (session, "current-send-codec", &codec, NULL);
739
740  update_sending_codec (handler, codec, session);
741
742  tp_clear_object (&session);
743  tp_clear_object (&codec);
744
745  /* Get receiving codec */
746 /* FIXME
747  g_object_get (content, "fs-stream", &fs_stream, NULL);
748  g_object_get (fs_stream, "current-recv-codecs", &codecs, NULL);
749
750  update_receiving_codec (handler, codecs, fs_stream);
751
752  fs_codec_list_destroy (codecs);
753  tp_clear_object (&fs_stream);
754 */
755
756  gst_object_unref (spad);
757 }
758
759 static void
760 on_tf_channel_content_removed_cb (TfChannel *tfchannel,
761   TfContent *content,
762   EmpathyCallHandler *handler)
763 {
764   FsMediaType mtype;
765   GstPad *spad;
766   gboolean retval;
767
768   g_print ("removing content\n");
769
770   g_object_get (content, "media-type", &mtype,
771     "sink-pad", &spad, NULL);
772
773   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_REMOVED], 0,
774       spad, mtype, &retval);
775
776   if (!retval)
777     {
778       g_warning ("Could not remove content!");
779
780       tf_content_error (content, 0 /* FIXME */,
781           "Could not link source", NULL);
782     }
783 }
784
785 static void
786 on_tf_channel_closed_cb (TfChannel *tfchannel,
787     EmpathyCallHandler *handler)
788 {
789   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
790 }
791
792 static void
793 on_tf_channel_ready (GObject *source,
794     GAsyncResult *result,
795     gpointer user_data)
796 {
797   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
798   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
799   GError *error = NULL;
800
801   priv->tfchannel = TF_CHANNEL (g_async_initable_new_finish (
802       G_ASYNC_INITABLE (source), result, NULL));
803
804   if (priv->tfchannel == NULL)
805     {
806       g_warning ("Failed to create Farstream channel: %s", error->message);
807       g_error_free (error);
808       return;
809     }
810
811   /* Set up the telepathy farstream channel */
812   g_signal_connect (priv->tfchannel, "closed",
813       G_CALLBACK (on_tf_channel_closed_cb), self);
814   g_signal_connect (priv->tfchannel, "fs-conference-added",
815       G_CALLBACK (on_tf_channel_conference_added_cb), self);
816   g_signal_connect (priv->tfchannel, "fs-conference-removed",
817       G_CALLBACK (on_tf_channel_conference_removed_cb), self);
818   g_signal_connect (priv->tfchannel, "content-added",
819       G_CALLBACK (on_tf_channel_content_added_cb), self);
820   g_signal_connect (priv->tfchannel, "content-removed",
821       G_CALLBACK (on_tf_channel_content_removed_cb), self);
822 }
823
824 static void
825 empathy_call_handler_start_tpfs (EmpathyCallHandler *self)
826 {
827   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
828
829   tf_channel_new_async (TP_CHANNEL (priv->call),
830       on_tf_channel_ready, self);
831 }
832
833 #if 0
834 static void
835 empathy_call_handler_request_cb (GObject *source,
836     GAsyncResult *result,
837     gpointer user_data)
838 {
839   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
840   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
841   TpChannel *channel;
842   GError *error = NULL;
843   TpAccountChannelRequest *req = TP_ACCOUNT_CHANNEL_REQUEST (source);
844
845   channel = tp_account_channel_request_create_and_handle_channel_finish (req,
846       result, NULL, &error);
847   if (channel == NULL)
848     {
849       DEBUG ("Failed to create the channel: %s", error->message);
850       g_error_free (error);
851       return;
852     }
853
854   if (!TPY_IS_CALL_CHANNEL (channel))
855     {
856       DEBUG ("The channel is not a Call channel!");
857       return;
858     }
859
860   priv->call = TPY_CALL_CHANNEL (channel);
861
862   g_object_notify (G_OBJECT (self), "call-channel");
863
864   empathy_call_handler_start_tpfs (self);
865 }
866 #endif
867
868 static void
869 on_call_accepted_cb (GObject *source_object,
870     GAsyncResult *res,
871     gpointer user_data)
872 {
873   TpyCallChannel *call = TPY_CALL_CHANNEL (source_object);
874   GError *error = NULL;
875
876   if (!tpy_call_channel_accept_finish (call, res, &error))
877     {
878       g_warning ("could not accept Call: %s", error->message);
879       g_error_free (error);
880     }
881 }
882
883 void
884 empathy_call_handler_start_call (EmpathyCallHandler *handler,
885     gint64 timestamp)
886 {
887   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
888 /*TpAccountChannelRequest *req;
889   TpAccount *account;
890   GHashTable *request;*/
891
892   if (priv->call != NULL)
893     {
894       empathy_call_handler_start_tpfs (handler);
895       tpy_call_channel_accept_async (priv->call, on_call_accepted_cb, NULL);
896       return;
897     }
898   else
899     {
900       g_warning ("No Call channel!");
901     }
902
903 #if 0
904   /* No TpyCallChannel (we are redialing). Request a new call channel */
905   g_assert (priv->contact != NULL);
906
907   account = empathy_contact_get_account (priv->contact);
908   request = empathy_call_create_call_request (priv->contact,
909       priv->initial_audio, priv->initial_video);
910
911   req = tp_account_channel_request_new (account, request, timestamp);
912
913   tp_account_channel_request_create_and_handle_channel_async (req, NULL,
914       empathy_call_handler_request_cb, handler);
915
916   g_object_unref (req);
917   g_hash_table_unref (request);
918 #endif
919 }
920
921 /**
922  * empathy_call_handler_stop_call:
923  * @handler: an #EmpathyCallHandler
924  *
925  * Closes the #EmpathyCallHandler's call and frees its resources.
926  */
927 void
928 empathy_call_handler_stop_call (EmpathyCallHandler *handler)
929 {
930   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
931
932   if (priv->call != NULL)
933     {
934       tpy_call_channel_hangup_async (priv->call,
935           TPY_CALL_STATE_CHANGE_REASON_USER_REQUESTED,
936           "", "", NULL, NULL);
937       tp_channel_close_async (TP_CHANNEL (priv->call),
938         NULL, NULL);
939       tp_clear_object (&priv->call);
940       tp_clear_object (&priv->tfchannel);
941     }
942 }
943
944 /**
945  * empathy_call_handler_has_initial_video:
946  * @handler: an #EmpathyCallHandler
947  *
948  * Return %TRUE if the call managed by this #EmpathyCallHandler was
949  * created with video enabled
950  *
951  * Return value: %TRUE if the call was created as a video conversation.
952  */
953 gboolean
954 empathy_call_handler_has_initial_video (EmpathyCallHandler *handler)
955 {
956   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
957
958   return priv->initial_video;
959 }
960
961 FsCodec *
962 empathy_call_handler_get_send_audio_codec (EmpathyCallHandler *self)
963 {
964   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
965
966   return priv->send_audio_codec;
967 }
968
969 FsCodec *
970 empathy_call_handler_get_send_video_codec (EmpathyCallHandler *self)
971 {
972   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
973
974   return priv->send_video_codec;
975 }
976
977 GList *
978 empathy_call_handler_get_recv_audio_codecs (EmpathyCallHandler *self)
979 {
980   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
981
982   return priv->recv_audio_codecs;
983 }
984
985 GList *
986 empathy_call_handler_get_recv_video_codecs (EmpathyCallHandler *self)
987 {
988   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
989
990   return priv->recv_video_codecs;
991 }
992
993 FsCandidate *
994 empathy_call_handler_get_audio_remote_candidate (
995     EmpathyCallHandler *self)
996 {
997   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
998
999   return priv->audio_remote_candidate;
1000 }
1001
1002 FsCandidate *
1003 empathy_call_handler_get_audio_local_candidate (
1004     EmpathyCallHandler *self)
1005 {
1006   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
1007
1008   return priv->audio_local_candidate;
1009 }
1010
1011 FsCandidate *
1012 empathy_call_handler_get_video_remote_candidate (
1013     EmpathyCallHandler *self)
1014 {
1015   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
1016
1017   return priv->video_remote_candidate;
1018 }
1019
1020 FsCandidate *
1021 empathy_call_handler_get_video_local_candidate (
1022     EmpathyCallHandler *self)
1023 {
1024   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
1025
1026   return priv->video_local_candidate;
1027 }