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