Port EmpathyCallHandler to new API
[empathy.git] / libempathy / empathy-call-handler.c
1 /*
2  * empathy-call-handler.c - Source for EmpathyCallHandler
3  * Copyright (C) 2008 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
27 #include <telepathy-farsight/channel.h>
28 #include <telepathy-farsight/stream.h>
29
30 #include "empathy-call-handler.h"
31 #include "empathy-dispatcher.h"
32 #include "empathy-marshal.h"
33 #include "empathy-utils.h"
34
35 G_DEFINE_TYPE(EmpathyCallHandler, empathy_call_handler, G_TYPE_OBJECT)
36
37 /* signal enum */
38 enum {
39   CONFERENCE_ADDED,
40   SRC_PAD_ADDED,
41   SINK_PAD_ADDED,
42   REQUEST_RESOURCE,
43   CLOSED,
44   LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL] = {0};
48
49 enum {
50   PROP_TP_CALL = 1,
51   PROP_GST_BUS,
52   PROP_CONTACT
53 };
54
55 /* private structure */
56
57 typedef struct {
58   gboolean dispose_has_run;
59   EmpathyTpCall *call;
60   EmpathyContact *contact;
61   TfChannel *tfchannel;
62 } EmpathyCallHandlerPriv;
63
64 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
65
66 static void
67 empathy_call_handler_dispose (GObject *object)
68 {
69   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
70
71   if (priv->dispose_has_run)
72     return;
73
74   priv->dispose_has_run = TRUE;
75
76   if (priv->contact != NULL)
77     g_object_unref (priv->contact);
78
79   priv->contact = NULL;
80
81   if (priv->tfchannel != NULL)
82     g_object_unref (priv->tfchannel);
83
84   priv->tfchannel = NULL;
85
86   if (priv->call != NULL)
87     {
88       empathy_tp_call_close (priv->call);
89       g_object_unref (priv->call);
90     }
91
92   priv->call = NULL;
93
94   /* release any references held by the object here */
95   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose)
96     G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose (object);
97 }
98
99 static void
100 empathy_call_handler_finalize (GObject *object)
101 {
102   /* free any data held directly by the object here */
103   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize)
104     G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize (object);
105 }
106
107 static void
108 empathy_call_handler_init (EmpathyCallHandler *obj)
109 {
110   EmpathyCallHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
111     EMPATHY_TYPE_CALL_HANDLER, EmpathyCallHandlerPriv);
112
113   obj->priv = priv;
114 }
115
116 static void
117 empathy_call_handler_set_property (GObject *object,
118   guint property_id, const GValue *value, GParamSpec *pspec)
119 {
120   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
121
122   switch (property_id)
123     {
124       case PROP_CONTACT:
125         priv->contact = g_value_dup_object (value);
126         break;
127       case PROP_TP_CALL:
128         priv->call = g_value_dup_object (value);
129         break;
130       default:
131         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
132     }
133 }
134
135 static void
136 empathy_call_handler_get_property (GObject *object,
137   guint property_id, GValue *value, GParamSpec *pspec)
138 {
139   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
140
141   switch (property_id)
142     {
143       case PROP_CONTACT:
144         g_value_set_object (value, priv->contact);
145         break;
146       case PROP_TP_CALL:
147         g_value_set_object (value, priv->call);
148         break;
149       default:
150         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151     }
152 }
153
154
155 static void
156 empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
157 {
158   GObjectClass *object_class = G_OBJECT_CLASS (klass);
159   GParamSpec *param_spec;
160
161   g_type_class_add_private (klass, sizeof (EmpathyCallHandlerPriv));
162
163   object_class->set_property = empathy_call_handler_set_property;
164   object_class->get_property = empathy_call_handler_get_property;
165   object_class->dispose = empathy_call_handler_dispose;
166   object_class->finalize = empathy_call_handler_finalize;
167
168   param_spec = g_param_spec_object ("contact",
169     "contact", "The remote contact",
170     EMPATHY_TYPE_CONTACT,
171     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
172   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
173
174   param_spec = g_param_spec_object ("tp-call",
175     "tp-call", "The calls channel wrapper",
176     EMPATHY_TYPE_TP_CALL,
177     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
178   g_object_class_install_property (object_class, PROP_TP_CALL, param_spec);
179
180   signals[CONFERENCE_ADDED] =
181     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
182       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
183       g_cclosure_marshal_VOID__OBJECT,
184       G_TYPE_NONE,
185       1, FS_TYPE_CONFERENCE);
186
187   signals[SRC_PAD_ADDED] =
188     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
189       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
190       _empathy_marshal_VOID__OBJECT_UINT,
191       G_TYPE_NONE,
192       2, GST_TYPE_PAD, G_TYPE_UINT);
193
194   signals[SINK_PAD_ADDED] =
195     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
196       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
197       _empathy_marshal_VOID__OBJECT_UINT,
198       G_TYPE_NONE,
199       2, GST_TYPE_PAD, G_TYPE_UINT);
200
201   signals[REQUEST_RESOURCE] =
202     g_signal_new ("request-resource", G_TYPE_FROM_CLASS (klass),
203       G_SIGNAL_RUN_LAST, 0,
204       g_signal_accumulator_true_handled, NULL,
205       _empathy_marshal_BOOLEAN__UINT_UINT,
206       G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
207
208   signals[CLOSED] =
209     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
210       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
211       g_cclosure_marshal_VOID__VOID,
212       G_TYPE_NONE,
213       0);
214 }
215
216 EmpathyCallHandler *
217 empathy_call_handler_new_for_contact (EmpathyContact *contact)
218 {
219   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
220     "contact", contact, NULL));
221 }
222
223 EmpathyCallHandler *
224 empathy_call_handler_new_for_channel (EmpathyTpCall *call)
225 {
226   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
227     "tp-call", call, NULL));
228 }
229
230 void
231 empathy_call_handler_bus_message (EmpathyCallHandler *handler,
232   GstBus *bus, GstMessage *message)
233 {
234   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
235
236   if (priv->tfchannel == NULL)
237     return;
238
239   tf_channel_bus_message (priv->tfchannel, message);
240 }
241
242 static void
243 empathy_call_handler_tf_channel_session_created_cb (TfChannel *tfchannel,
244   FsConference *conference, FsParticipant *participant,
245   EmpathyCallHandler *self)
246 {
247   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
248     GST_ELEMENT (conference));
249 }
250
251 static void
252 empathy_call_handler_tf_stream_src_pad_added_cb (TfStream *stream,
253   GstPad *pad, FsCodec *codec, EmpathyCallHandler  *handler)
254 {
255   guint media_type;
256
257   g_object_get (stream, "media-type", &media_type, NULL);
258
259   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
260     pad, media_type);
261 }
262
263
264 static gboolean
265 empathy_call_handler_tf_stream_request_resource_cb (TfStream *stream,
266   guint direction, EmpathyTpCall *call)
267 {
268   gboolean ret;
269   guint media_type;
270
271   g_object_get (G_OBJECT (stream), "media-type", &media_type, NULL);
272
273   g_signal_emit (G_OBJECT (call),
274     signals[REQUEST_RESOURCE], 0, media_type, direction, &ret);
275
276   return ret;
277 }
278
279 static void
280 empathy_call_handler_tf_channel_stream_created_cb (TfChannel *tfchannel,
281   TfStream *stream, EmpathyCallHandler *handler)
282 {
283   guint media_type;
284   GstPad *spad;
285
286   g_signal_connect (stream, "src-pad-added",
287       G_CALLBACK (empathy_call_handler_tf_stream_src_pad_added_cb), handler);
288   g_signal_connect (stream, "request-resource",
289       G_CALLBACK (empathy_call_handler_tf_stream_request_resource_cb),
290         handler);
291
292   g_object_get (stream, "media-type", &media_type,
293     "sink-pad", &spad, NULL);
294
295   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
296     spad, media_type);
297
298   gst_object_unref (spad);
299 }
300
301 static void
302 empathy_call_handler_tf_channel_closed_cb (TfChannel *tfchannel,
303   EmpathyCallHandler *handler)
304 {
305   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
306 }
307
308 static GList *
309 empathy_call_handler_tf_channel_codec_config_get_defaults (FsCodec *codecs)
310 {
311   GList *l = NULL;
312   int i;
313
314   for (i = 0; codecs[i].encoding_name != NULL; i++)
315       l = g_list_append (l, fs_codec_copy (codecs + i));
316
317   return l;
318 }
319
320 static GList *
321 empathy_call_handler_tf_channel_codec_config_cb (TfChannel *channel,
322   guint stream_id, FsMediaType media_type, guint direction, gpointer user_data)
323 {
324   FsCodec audio_codecs[] = {
325     { FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000, },
326     { FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000, },
327
328     { FS_CODEC_ID_DISABLE, "DV",     FS_MEDIA_TYPE_AUDIO, },
329     { FS_CODEC_ID_DISABLE, "MPA",    FS_MEDIA_TYPE_AUDIO, },
330     { FS_CODEC_ID_DISABLE, "VORBIS", FS_MEDIA_TYPE_AUDIO, },
331     { FS_CODEC_ID_DISABLE, "MP3",    FS_MEDIA_TYPE_AUDIO, },
332     { 0, NULL, 0,}
333   };
334   FsCodec video_codecs[] = {
335     { FS_CODEC_ID_ANY, "H264",   FS_MEDIA_TYPE_VIDEO, },
336     { FS_CODEC_ID_ANY, "THEORA", FS_MEDIA_TYPE_VIDEO, },
337     { FS_CODEC_ID_ANY, "H263",   FS_MEDIA_TYPE_VIDEO, },
338
339     { FS_CODEC_ID_DISABLE, "DV",   FS_MEDIA_TYPE_VIDEO, },
340     { FS_CODEC_ID_DISABLE, "JPEG", FS_MEDIA_TYPE_VIDEO, },
341     { FS_CODEC_ID_DISABLE, "MPV",  FS_MEDIA_TYPE_VIDEO, },
342     { 0, NULL, 0}
343   };
344
345   switch (media_type)
346     {
347       case FS_MEDIA_TYPE_AUDIO:
348         return empathy_call_handler_tf_channel_codec_config_get_defaults
349           (audio_codecs);
350       case FS_MEDIA_TYPE_VIDEO:
351         return empathy_call_handler_tf_channel_codec_config_get_defaults
352           (video_codecs);
353     }
354
355   return NULL;
356 }
357
358 static void
359 empathy_call_handler_start_tpfs (EmpathyCallHandler *self)
360 {
361   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
362   TpChannel *channel;
363
364   g_object_get (priv->call, "channel", &channel, NULL);
365
366   g_assert (channel != NULL);
367
368   priv->tfchannel = tf_channel_new (channel);
369
370   /* Set up the telepathy farsight channel */
371   g_signal_connect (priv->tfchannel, "session-created",
372       G_CALLBACK (empathy_call_handler_tf_channel_session_created_cb), self);
373   g_signal_connect (priv->tfchannel, "stream-created",
374       G_CALLBACK (empathy_call_handler_tf_channel_stream_created_cb), self);
375   g_signal_connect (priv->tfchannel, "closed",
376       G_CALLBACK (empathy_call_handler_tf_channel_closed_cb), self);
377   g_signal_connect (priv->tfchannel, "stream-get-codec-config",
378       G_CALLBACK (empathy_call_handler_tf_channel_codec_config_cb), self);
379
380   g_object_unref (channel);
381 }
382
383 static void
384 empathy_call_handler_request_cb (EmpathyDispatchOperation *operation,
385   const GError *error, gpointer user_data)
386 {
387   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
388   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
389
390   if (error != NULL)
391     return;
392
393   priv->call = EMPATHY_TP_CALL (
394     empathy_dispatch_operation_get_channel_wrapper (operation));
395
396   g_object_ref (priv->call);
397
398   empathy_call_handler_start_tpfs (self);
399
400   empathy_tp_call_to (priv->call, priv->contact);
401
402   empathy_dispatch_operation_claim (operation);
403 }
404
405 void
406 empathy_call_handler_start_call (EmpathyCallHandler *handler)
407 {
408
409   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
410   EmpathyDispatcher *dispatcher;
411   TpConnection *connection;
412   GStrv allowed;
413   GValue *value;
414   GHashTable *request;
415
416   if (priv->call != NULL)
417     {
418       empathy_call_handler_start_tpfs (handler);
419       empathy_tp_call_accept_incoming_call (priv->call);
420       return;
421     }
422
423   g_assert (priv->contact != NULL);
424
425   dispatcher = empathy_dispatcher_dup_singleton ();
426   connection = empathy_contact_get_connection (priv->contact);
427   allowed = empathy_dispatcher_find_channel_class (dispatcher, connection,
428     TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_HANDLE_TYPE_CONTACT);
429
430   if (!tp_strv_contains ((const gchar * const *) allowed,
431       TP_IFACE_CHANNEL ".TargetHandle"))
432     return;
433
434   request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
435       (GDestroyNotify) tp_g_value_slice_free);
436
437   /* org.freedesktop.Telepathy.Channel.ChannelType */
438   value = tp_g_value_slice_new (G_TYPE_STRING);
439   g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
440   g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value);
441
442   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
443   value = tp_g_value_slice_new (G_TYPE_UINT);
444   g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
445   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value);
446
447   /* org.freedesktop.Telepathy.Channel.TargetHandle*/
448   value = tp_g_value_slice_new (G_TYPE_UINT);
449   g_value_set_uint (value, empathy_contact_get_handle (priv->contact));
450   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value);
451
452   empathy_dispatcher_create_channel (dispatcher, connection,
453     request, empathy_call_handler_request_cb, handler);
454
455   g_object_unref (dispatcher);
456 }
457