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