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