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