]> git.0d.be Git - empathy.git/blob - libempathy/empathy-call-handler.c
Wait for handle instead of id before request a channel
[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   CLOSED,
43   LAST_SIGNAL
44 };
45
46 static guint signals[LAST_SIGNAL] = {0};
47
48 enum {
49   PROP_TP_CALL = 1,
50   PROP_GST_BUS,
51   PROP_CONTACT
52 };
53
54 /* private structure */
55
56 typedef struct {
57   gboolean dispose_has_run;
58   EmpathyTpCall *call;
59   EmpathyContact *contact;
60   TfChannel *tfchannel;
61   GstBus *bus;
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       case PROP_GST_BUS:
131         priv->bus = g_value_dup_object (value);
132         break;
133       default:
134         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
135     }
136 }
137
138 static void
139 empathy_call_handler_get_property (GObject *object,
140   guint property_id, GValue *value, GParamSpec *pspec)
141 {
142   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
143
144   switch (property_id)
145     {
146       case PROP_CONTACT:
147         g_value_set_object (value, priv->contact);
148         break;
149       case PROP_TP_CALL:
150         g_value_set_object (value, priv->call);
151         break;
152       case PROP_GST_BUS:
153         g_value_set_object (value, priv->bus);
154         break;
155       default:
156         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157     }
158 }
159
160
161 static void
162 empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
163 {
164   GObjectClass *object_class = G_OBJECT_CLASS (klass);
165   GParamSpec *param_spec;
166
167   g_type_class_add_private (klass, sizeof (EmpathyCallHandlerPriv));
168
169   object_class->set_property = empathy_call_handler_set_property;
170   object_class->get_property = empathy_call_handler_get_property;
171   object_class->dispose = empathy_call_handler_dispose;
172   object_class->finalize = empathy_call_handler_finalize;
173
174   param_spec = g_param_spec_object ("contact",
175     "contact", "The remote contact",
176     EMPATHY_TYPE_CONTACT,
177     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
178   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
179
180   param_spec = g_param_spec_object ("gst-bus",
181     "gst-bus", "The gstreamer bus",
182     GST_TYPE_BUS,
183     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
184   g_object_class_install_property (object_class, PROP_GST_BUS, param_spec);
185
186   param_spec = g_param_spec_object ("tp-call",
187     "tp-call", "The calls channel wrapper",
188     EMPATHY_TYPE_TP_CALL,
189     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
190   g_object_class_install_property (object_class, PROP_TP_CALL, param_spec);
191
192   signals[CONFERENCE_ADDED] =
193     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
194       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
195       g_cclosure_marshal_VOID__OBJECT,
196       G_TYPE_NONE,
197       1, FS_TYPE_CONFERENCE);
198
199   signals[SRC_PAD_ADDED] =
200     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
201       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
202       _empathy_marshal_VOID__OBJECT_UINT,
203       G_TYPE_NONE,
204       2, GST_TYPE_PAD, G_TYPE_UINT);
205
206   signals[SINK_PAD_ADDED] =
207     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
208       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
209       _empathy_marshal_VOID__OBJECT_UINT,
210       G_TYPE_NONE,
211       2, GST_TYPE_PAD, G_TYPE_UINT);
212
213   signals[CLOSED] =
214     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
215       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
216       g_cclosure_marshal_VOID__VOID,
217       G_TYPE_NONE,
218       0);
219 }
220
221 EmpathyCallHandler *
222 empathy_call_handler_new_for_contact (EmpathyContact *contact)
223 {
224   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
225     "contact", contact, NULL));
226 }
227
228 EmpathyCallHandler *
229 empathy_call_handler_new_for_channel (EmpathyTpCall *call)
230 {
231   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
232     "tp-call", call, NULL));
233 }
234
235 static gboolean
236 empathy_call_handler_pipeline_bus_watch (GstBus *bus, GstMessage *message,
237   gpointer user_data)
238 {
239   EmpathyCallHandler *handler = EMPATHY_CALL_HANDLER (user_data);
240   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
241
242   g_assert (priv->tfchannel != NULL);
243
244   tf_channel_bus_message (priv->tfchannel, message);
245
246   return TRUE;
247 }
248
249 static void
250 empathy_call_handler_tf_channel_session_created_cb (TfChannel *tfchannel,
251   FsConference *conference, FsParticipant *participant,
252   EmpathyCallHandler *self)
253 {
254   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
255
256   gst_bus_add_watch (priv->bus, empathy_call_handler_pipeline_bus_watch, self);
257
258   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
259     GST_ELEMENT (conference));
260 }
261
262 static void
263 empathy_call_handler_tf_stream_src_pad_added_cb (TfStream *stream,
264   GstPad *pad, FsCodec *codec, EmpathyCallHandler  *handler)
265 {
266   guint media_type;
267
268   g_object_get (stream, "media-type", &media_type, NULL);
269
270   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
271     pad, media_type);
272 }
273
274
275 static gboolean
276 empathy_call_handler_tf_stream_request_resource_cb (TfStream *stream,
277   guint direction, EmpathyTpCall *call)
278 {
279   return TRUE;
280 }
281
282 static void
283 empathy_call_handler_tf_channel_stream_created_cb (TfChannel *tfchannel,
284   TfStream *stream, EmpathyCallHandler *handler)
285 {
286   guint media_type;
287   GstPad *spad;
288
289   g_signal_connect (stream, "src-pad-added",
290       G_CALLBACK (empathy_call_handler_tf_stream_src_pad_added_cb), handler);
291   g_signal_connect (stream, "request-resource",
292       G_CALLBACK (empathy_call_handler_tf_stream_request_resource_cb),
293         handler);
294
295   g_object_get (stream, "media-type", &media_type,
296     "sink-pad", &spad, NULL);
297
298   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
299     spad, media_type);
300
301   gst_object_unref (spad);
302 }
303
304 static void
305 empathy_call_handler_tf_channel_closed_cb (TfChannel *tfchannel,
306   EmpathyCallHandler *handler)
307 {
308   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
309 }
310
311 static void
312 empathy_call_handler_start_tpfs (EmpathyCallHandler *self)
313 {
314   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
315   TpChannel *channel;
316
317   g_object_get (priv->call, "channel", &channel, NULL);
318
319   g_assert (channel != NULL);
320
321   priv->tfchannel = tf_channel_new (channel);
322
323   /* Set up the telepathy farsight channel */
324   g_signal_connect (priv->tfchannel, "session-created",
325       G_CALLBACK (empathy_call_handler_tf_channel_session_created_cb), self);
326   g_signal_connect (priv->tfchannel, "stream-created",
327       G_CALLBACK (empathy_call_handler_tf_channel_stream_created_cb), self);
328   g_signal_connect (priv->tfchannel, "closed",
329       G_CALLBACK (empathy_call_handler_tf_channel_closed_cb), self);
330
331   g_object_unref (channel);
332 }
333
334 static void
335 empathy_call_handler_request_cb (EmpathyDispatchOperation *operation,
336   const GError *error, gpointer user_data)
337 {
338   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
339   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
340
341   if (error != NULL)
342     return;
343
344   priv->call = EMPATHY_TP_CALL (
345     empathy_dispatch_operation_get_channel_wrapper (operation));
346
347   g_object_ref (priv->call);
348
349   empathy_call_handler_start_tpfs (self);
350
351   empathy_tp_call_to (priv->call, priv->contact);
352
353   empathy_dispatch_operation_claim (operation);
354 }
355
356 static void
357 empathy_call_handler_contact_ready_cb (EmpathyContact *contact,
358   const GError *error, gpointer user_data, GObject *object)
359 {
360   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (object);
361   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
362   EmpathyDispatcher *dispatcher;
363   McAccount *account;
364   GStrv allowed;
365   GValue *value;
366   GHashTable *request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
367       (GDestroyNotify) tp_g_value_slice_free);
368
369   g_assert (priv->contact != NULL);
370
371   dispatcher = empathy_dispatcher_dup_singleton ();
372   account = empathy_contact_get_account (priv->contact);
373   allowed = empathy_dispatcher_find_channel_class (dispatcher, account,
374     TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_HANDLE_TYPE_CONTACT);
375
376   if (!tp_strv_contains ((const gchar * const *)allowed,
377       TP_IFACE_CHANNEL ".TargetHandle"))
378     g_assert_not_reached ();
379
380   /* org.freedesktop.Telepathy.Channel.ChannelType */
381   value = tp_g_value_slice_new (G_TYPE_STRING);
382   g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
383   g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value);
384
385   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
386   value = tp_g_value_slice_new (G_TYPE_UINT);
387   g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
388   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value);
389
390   /* org.freedesktop.Telepathy.Channel.TargetHandle*/
391   value = tp_g_value_slice_new (G_TYPE_UINT);
392   g_value_set_uint (value, empathy_contact_get_handle (priv->contact));
393   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value);
394
395   empathy_dispatcher_create_channel (dispatcher, account,
396     request, empathy_call_handler_request_cb, self);
397
398   g_object_unref (dispatcher);
399 }
400
401 void
402 empathy_call_handler_start_call (EmpathyCallHandler *handler)
403 {
404
405   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
406
407   if (priv->call == NULL)
408     {
409       empathy_contact_call_when_ready (priv->contact,
410         EMPATHY_CONTACT_READY_HANDLE,
411         empathy_call_handler_contact_ready_cb, NULL, NULL, G_OBJECT (handler));
412     }
413   else
414     {
415       empathy_call_handler_start_tpfs (handler);
416       empathy_tp_call_accept_incoming_call (priv->call);
417     }
418 }
419
420 void
421 empathy_call_handler_set_bus (EmpathyCallHandler *handler, GstBus *bus)
422 {
423   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
424
425   g_assert (priv->bus == NULL);
426
427   priv->bus = g_object_ref (bus);
428 }
429