]> git.0d.be Git - empathy.git/blob - libempathy/empathy-call-handler.c
Add human-readable names for Yahoo! Japan and Facebook Chat
[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-dispatcher.h"
33 #include "empathy-marshal.h"
34 #include "empathy-utils.h"
35
36 G_DEFINE_TYPE(EmpathyCallHandler, empathy_call_handler, G_TYPE_OBJECT)
37
38 /* signal enum */
39 enum {
40   CONFERENCE_ADDED,
41   SRC_PAD_ADDED,
42   SINK_PAD_ADDED,
43   REQUEST_RESOURCE,
44   CLOSED,
45   LAST_SIGNAL
46 };
47
48 static guint signals[LAST_SIGNAL] = {0};
49
50 enum {
51   PROP_TP_CALL = 1,
52   PROP_GST_BUS,
53   PROP_CONTACT,
54   PROP_INITIAL_AUDIO,
55   PROP_INITIAL_VIDEO
56 };
57
58 /* private structure */
59
60 typedef struct {
61   gboolean dispose_has_run;
62   EmpathyTpCall *call;
63   EmpathyContact *contact;
64   TfChannel *tfchannel;
65   gboolean initial_audio;
66   gboolean initial_video;
67 } EmpathyCallHandlerPriv;
68
69 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
70
71 static void
72 empathy_call_handler_dispose (GObject *object)
73 {
74   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
75
76   if (priv->dispose_has_run)
77     return;
78
79   priv->dispose_has_run = TRUE;
80
81   if (priv->contact != NULL)
82     g_object_unref (priv->contact);
83
84   priv->contact = NULL;
85
86   if (priv->tfchannel != NULL)
87     g_object_unref (priv->tfchannel);
88
89   priv->tfchannel = NULL;
90
91   if (priv->call != NULL)
92     {
93       empathy_tp_call_close (priv->call);
94       g_object_unref (priv->call);
95     }
96
97   priv->call = NULL;
98
99   /* release any references held by the object here */
100   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose)
101     G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose (object);
102 }
103
104 static void
105 empathy_call_handler_finalize (GObject *object)
106 {
107   /* free any data held directly by the object here */
108   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize)
109     G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize (object);
110 }
111
112 static void
113 empathy_call_handler_init (EmpathyCallHandler *obj)
114 {
115   EmpathyCallHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
116     EMPATHY_TYPE_CALL_HANDLER, EmpathyCallHandlerPriv);
117
118   obj->priv = priv;
119 }
120
121 static void
122 empathy_call_handler_constructed (GObject *object)
123 {
124   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
125
126   if (priv->contact == NULL)
127     {
128       g_object_get (priv->call, "contact", &(priv->contact), NULL);
129     }
130 }
131
132 static void
133 empathy_call_handler_set_property (GObject *object,
134   guint property_id, const GValue *value, GParamSpec *pspec)
135 {
136   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
137
138   switch (property_id)
139     {
140       case PROP_CONTACT:
141         priv->contact = g_value_dup_object (value);
142         break;
143       case PROP_TP_CALL:
144         priv->call = g_value_dup_object (value);
145         break;
146       case PROP_INITIAL_AUDIO:
147         priv->initial_audio = g_value_get_boolean (value);
148         break;
149       case PROP_INITIAL_VIDEO:
150         priv->initial_video = g_value_get_boolean (value);
151         break;
152       default:
153         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
154     }
155 }
156
157 static void
158 empathy_call_handler_get_property (GObject *object,
159   guint property_id, GValue *value, GParamSpec *pspec)
160 {
161   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
162
163   switch (property_id)
164     {
165       case PROP_CONTACT:
166         g_value_set_object (value, priv->contact);
167         break;
168       case PROP_TP_CALL:
169         g_value_set_object (value, priv->call);
170         break;
171       case PROP_INITIAL_AUDIO:
172         g_value_set_boolean (value, priv->initial_audio);
173         break;
174       case PROP_INITIAL_VIDEO:
175         g_value_set_boolean (value, priv->initial_video);
176         break;
177       default:
178         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179     }
180 }
181
182
183 static void
184 empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
185 {
186   GObjectClass *object_class = G_OBJECT_CLASS (klass);
187   GParamSpec *param_spec;
188
189   g_type_class_add_private (klass, sizeof (EmpathyCallHandlerPriv));
190
191   object_class->constructed = empathy_call_handler_constructed;
192   object_class->set_property = empathy_call_handler_set_property;
193   object_class->get_property = empathy_call_handler_get_property;
194   object_class->dispose = empathy_call_handler_dispose;
195   object_class->finalize = empathy_call_handler_finalize;
196
197   param_spec = g_param_spec_object ("contact",
198     "contact", "The remote contact",
199     EMPATHY_TYPE_CONTACT,
200     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
201   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
202
203   param_spec = g_param_spec_object ("tp-call",
204     "tp-call", "The calls channel wrapper",
205     EMPATHY_TYPE_TP_CALL,
206     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
207   g_object_class_install_property (object_class, PROP_TP_CALL, param_spec);
208
209   param_spec = g_param_spec_boolean ("initial-audio",
210     "initial-audio", "Whether the call should start with audio",
211     TRUE,
212     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
213   g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
214       param_spec);
215
216   param_spec = g_param_spec_boolean ("initial-video",
217     "initial-video", "Whether the call should start with video",
218     FALSE,
219     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
220   g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
221     param_spec);
222
223   signals[CONFERENCE_ADDED] =
224     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
225       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
226       g_cclosure_marshal_VOID__OBJECT,
227       G_TYPE_NONE,
228       1, FS_TYPE_CONFERENCE);
229
230   signals[SRC_PAD_ADDED] =
231     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
232       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
233       _empathy_marshal_VOID__OBJECT_UINT,
234       G_TYPE_NONE,
235       2, GST_TYPE_PAD, G_TYPE_UINT);
236
237   signals[SINK_PAD_ADDED] =
238     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
239       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
240       _empathy_marshal_VOID__OBJECT_UINT,
241       G_TYPE_NONE,
242       2, GST_TYPE_PAD, G_TYPE_UINT);
243
244   signals[REQUEST_RESOURCE] =
245     g_signal_new ("request-resource", G_TYPE_FROM_CLASS (klass),
246       G_SIGNAL_RUN_LAST, 0,
247       g_signal_accumulator_true_handled, NULL,
248       _empathy_marshal_BOOLEAN__UINT_UINT,
249       G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
250
251   signals[CLOSED] =
252     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
253       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
254       g_cclosure_marshal_VOID__VOID,
255       G_TYPE_NONE,
256       0);
257 }
258
259 /**
260  * empathy_call_handler_new_for_contact:
261  * @contact: an #EmpathyContact
262  *
263  * Creates a new #EmpathyCallHandler with contact @contact.
264  *
265  * Return value: a new #EmpathyCallHandler
266  */
267 EmpathyCallHandler *
268 empathy_call_handler_new_for_contact (EmpathyContact *contact)
269 {
270   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
271     "contact", contact, NULL));
272 }
273
274 /**
275  * empathy_call_handler_new_for_contact_with_streams:
276  * @contact: an #EmpathyContact
277  * @audio: if %TRUE the call will be started with audio
278  * @video: if %TRUE the call will be started with video
279  *
280  * Creates a new #EmpathyCallHandler with contact @contact.
281  *
282  * Return value: a new #EmpathyCallHandler
283  */
284 EmpathyCallHandler *
285 empathy_call_handler_new_for_contact_with_streams (EmpathyContact *contact,
286     gboolean audio, gboolean video)
287 {
288   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
289     "contact", contact,
290     "initial-audio", audio,
291     "initial-video", video,
292     NULL));
293 }
294
295 EmpathyCallHandler *
296 empathy_call_handler_new_for_channel (EmpathyTpCall *call)
297 {
298   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
299     "tp-call", call,
300     "initial-video", empathy_tp_call_is_receiving_video (call),
301     NULL));
302 }
303
304 void
305 empathy_call_handler_bus_message (EmpathyCallHandler *handler,
306   GstBus *bus, GstMessage *message)
307 {
308   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
309
310   if (priv->tfchannel == NULL)
311     return;
312
313   tf_channel_bus_message (priv->tfchannel, message);
314 }
315
316 static void
317 empathy_call_handler_tf_channel_session_created_cb (TfChannel *tfchannel,
318   FsConference *conference, FsParticipant *participant,
319   EmpathyCallHandler *self)
320 {
321   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
322     GST_ELEMENT (conference));
323 }
324
325 static void
326 empathy_call_handler_tf_stream_src_pad_added_cb (TfStream *stream,
327   GstPad *pad, FsCodec *codec, EmpathyCallHandler  *handler)
328 {
329   guint media_type;
330
331   g_object_get (stream, "media-type", &media_type, NULL);
332
333   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
334     pad, media_type);
335 }
336
337
338 static gboolean
339 empathy_call_handler_tf_stream_request_resource_cb (TfStream *stream,
340   guint direction, EmpathyTpCall *call)
341 {
342   gboolean ret;
343   guint media_type;
344
345   g_object_get (G_OBJECT (stream), "media-type", &media_type, NULL);
346
347   g_signal_emit (G_OBJECT (call),
348     signals[REQUEST_RESOURCE], 0, media_type, direction, &ret);
349
350   return ret;
351 }
352
353 static void
354 empathy_call_handler_tf_channel_stream_created_cb (TfChannel *tfchannel,
355   TfStream *stream, EmpathyCallHandler *handler)
356 {
357   guint media_type;
358   GstPad *spad;
359
360   g_signal_connect (stream, "src-pad-added",
361       G_CALLBACK (empathy_call_handler_tf_stream_src_pad_added_cb), handler);
362   g_signal_connect (stream, "request-resource",
363       G_CALLBACK (empathy_call_handler_tf_stream_request_resource_cb),
364         handler);
365
366   g_object_get (stream, "media-type", &media_type,
367     "sink-pad", &spad, NULL);
368
369   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
370     spad, media_type);
371
372   gst_object_unref (spad);
373 }
374
375 static void
376 empathy_call_handler_tf_channel_closed_cb (TfChannel *tfchannel,
377   EmpathyCallHandler *handler)
378 {
379   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
380 }
381
382 static GList *
383 empathy_call_handler_tf_channel_codec_config_cb (TfChannel *channel,
384   guint stream_id, FsMediaType media_type, guint direction, gpointer user_data)
385 {
386   gchar *filename = empathy_file_lookup ("codec-preferences", "data");
387   GList *codecs;
388   GError *error = NULL;
389
390   codecs = fs_codec_list_from_keyfile (filename, &error);
391   g_free (filename);
392
393   if (!codecs)
394     {
395       g_warning ("No codec-preferences file: %s",
396           error ? error->message : "No error message");
397     }
398   g_clear_error (&error);
399
400   return codecs;
401 }
402
403 static void
404 empathy_call_handler_start_tpfs (EmpathyCallHandler *self)
405 {
406   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
407   TpChannel *channel;
408
409   g_object_get (priv->call, "channel", &channel, NULL);
410
411   g_assert (channel != NULL);
412
413   priv->tfchannel = tf_channel_new (channel);
414
415   /* Set up the telepathy farsight channel */
416   g_signal_connect (priv->tfchannel, "session-created",
417       G_CALLBACK (empathy_call_handler_tf_channel_session_created_cb), self);
418   g_signal_connect (priv->tfchannel, "stream-created",
419       G_CALLBACK (empathy_call_handler_tf_channel_stream_created_cb), self);
420   g_signal_connect (priv->tfchannel, "closed",
421       G_CALLBACK (empathy_call_handler_tf_channel_closed_cb), self);
422   g_signal_connect (priv->tfchannel, "stream-get-codec-config",
423       G_CALLBACK (empathy_call_handler_tf_channel_codec_config_cb), self);
424
425   g_object_unref (channel);
426 }
427
428 static void
429 empathy_call_handler_request_cb (EmpathyDispatchOperation *operation,
430   const GError *error, gpointer user_data)
431 {
432   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
433   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
434
435   if (error != NULL)
436     return;
437
438   priv->call = EMPATHY_TP_CALL (
439     empathy_dispatch_operation_get_channel_wrapper (operation));
440
441   g_object_ref (priv->call);
442
443   empathy_call_handler_start_tpfs (self);
444
445   empathy_tp_call_to (priv->call, priv->contact,
446     priv->initial_audio, priv->initial_video);
447
448   empathy_dispatch_operation_claim (operation);
449 }
450
451 void
452 empathy_call_handler_start_call (EmpathyCallHandler *handler)
453 {
454
455   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
456   EmpathyDispatcher *dispatcher;
457   TpConnection *connection;
458   GList *classes;
459   GValue *value;
460   GHashTable *request;
461
462   if (priv->call != NULL)
463     {
464       empathy_call_handler_start_tpfs (handler);
465       empathy_tp_call_accept_incoming_call (priv->call);
466       return;
467     }
468
469   g_assert (priv->contact != NULL);
470
471   dispatcher = empathy_dispatcher_dup_singleton ();
472   connection = empathy_contact_get_connection (priv->contact);
473   classes = empathy_dispatcher_find_requestable_channel_classes
474     (dispatcher, connection, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
475      TP_HANDLE_TYPE_CONTACT, NULL);
476
477   if (classes == NULL)
478     return;
479
480   g_list_free (classes);
481
482   request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
483       (GDestroyNotify) tp_g_value_slice_free);
484
485   /* org.freedesktop.Telepathy.Channel.ChannelType */
486   value = tp_g_value_slice_new (G_TYPE_STRING);
487   g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
488   g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value);
489
490   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
491   value = tp_g_value_slice_new (G_TYPE_UINT);
492   g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
493   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value);
494
495   /* org.freedesktop.Telepathy.Channel.TargetHandle*/
496   value = tp_g_value_slice_new (G_TYPE_UINT);
497   g_value_set_uint (value, empathy_contact_get_handle (priv->contact));
498   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value);
499
500   empathy_dispatcher_create_channel (dispatcher, connection,
501     request, empathy_call_handler_request_cb, handler);
502
503   g_object_unref (dispatcher);
504 }
505
506 /**
507  * empathy_call_handler_stop_call:
508  * @handler: an #EmpathyCallHandler
509  *
510  * Closes the #EmpathyCallHandler's call and frees its resources.
511  */
512 void
513 empathy_call_handler_stop_call (EmpathyCallHandler *handler)
514 {
515   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
516
517   if (priv->call != NULL)
518     {
519       empathy_tp_call_close (priv->call);
520       g_object_unref (priv->call);
521     }
522
523   priv->call = NULL;
524 }
525
526 /**
527  * empathy_call_handler_has_initial_video:
528  * @handler: an #EmpathyCallHandler
529  *
530  * Return %TRUE if the call managed by this #EmpathyCallHandler was
531  * created with video enabled
532  *
533  * Return value: %TRUE if the call was created as a video conversation.
534  */
535 gboolean
536 empathy_call_handler_has_initial_video (EmpathyCallHandler *handler)
537 {
538   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
539
540   return priv->initial_video;
541 }
542