]> git.0d.be Git - empathy.git/blob - libempathy/empathy-call-handler.c
Allow handlers to be created for streams with initial audio and video
[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 <gst/farsight/fs-element-added-notifier.h>
31
32 #include "empathy-call-handler.h"
33 #include "empathy-dispatcher.h"
34 #include "empathy-marshal.h"
35 #include "empathy-utils.h"
36
37 G_DEFINE_TYPE(EmpathyCallHandler, empathy_call_handler, G_TYPE_OBJECT)
38
39 /* signal enum */
40 enum {
41   CONFERENCE_ADDED,
42   SRC_PAD_ADDED,
43   SINK_PAD_ADDED,
44   REQUEST_RESOURCE,
45   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   FsElementAddedNotifier *fsnotifier;
69 } EmpathyCallHandlerPriv;
70
71 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
72
73 static void
74 empathy_call_handler_dispose (GObject *object)
75 {
76   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
77
78   if (priv->dispose_has_run)
79     return;
80
81   priv->dispose_has_run = TRUE;
82
83   if (priv->contact != NULL)
84     g_object_unref (priv->contact);
85
86   priv->contact = NULL;
87
88   if (priv->tfchannel != NULL)
89     g_object_unref (priv->tfchannel);
90
91   priv->tfchannel = NULL;
92
93   if (priv->call != NULL)
94     {
95       empathy_tp_call_close (priv->call);
96       g_object_unref (priv->call);
97     }
98
99   priv->call = NULL;
100
101   if (priv->fsnotifier != NULL)
102     {
103       g_object_unref (priv->fsnotifier);
104     }
105   priv->fsnotifier = NULL;
106
107   /* release any references held by the object here */
108   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose)
109     G_OBJECT_CLASS (empathy_call_handler_parent_class)->dispose (object);
110 }
111
112 static void
113 empathy_call_handler_finalize (GObject *object)
114 {
115   /* free any data held directly by the object here */
116   if (G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize)
117     G_OBJECT_CLASS (empathy_call_handler_parent_class)->finalize (object);
118 }
119
120 static void
121 empathy_call_handler_init (EmpathyCallHandler *obj)
122 {
123   EmpathyCallHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
124     EMPATHY_TYPE_CALL_HANDLER, EmpathyCallHandlerPriv);
125
126   obj->priv = priv;
127 }
128
129 static void
130 empathy_call_handler_constructed (GObject *object)
131 {
132   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
133
134   if (priv->contact == NULL)
135     {
136       g_object_get (priv->call, "contact", &(priv->contact), NULL);
137     }
138 }
139
140 static void
141 empathy_call_handler_set_property (GObject *object,
142   guint property_id, const GValue *value, GParamSpec *pspec)
143 {
144   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
145
146   switch (property_id)
147     {
148       case PROP_CONTACT:
149         priv->contact = g_value_dup_object (value);
150         break;
151       case PROP_TP_CALL:
152         priv->call = g_value_dup_object (value);
153         break;
154       case PROP_INITIAL_AUDIO:
155         priv->initial_audio = g_value_get_boolean (value);
156         break;
157       case PROP_INITIAL_VIDEO:
158         priv->initial_video = g_value_get_boolean (value);
159         break;
160       default:
161         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
162     }
163 }
164
165 static void
166 empathy_call_handler_get_property (GObject *object,
167   guint property_id, GValue *value, GParamSpec *pspec)
168 {
169   EmpathyCallHandlerPriv *priv = GET_PRIV (object);
170
171   switch (property_id)
172     {
173       case PROP_CONTACT:
174         g_value_set_object (value, priv->contact);
175         break;
176       case PROP_TP_CALL:
177         g_value_set_object (value, priv->call);
178         break;
179       case PROP_INITIAL_AUDIO:
180         g_value_set_boolean (value, priv->initial_audio);
181         break;
182       case PROP_INITIAL_VIDEO:
183         g_value_set_boolean (value, priv->initial_video);
184         break;
185       default:
186         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
187     }
188 }
189
190
191 static void
192 empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
193 {
194   GObjectClass *object_class = G_OBJECT_CLASS (klass);
195   GParamSpec *param_spec;
196
197   g_type_class_add_private (klass, sizeof (EmpathyCallHandlerPriv));
198
199   object_class->constructed = empathy_call_handler_constructed;
200   object_class->set_property = empathy_call_handler_set_property;
201   object_class->get_property = empathy_call_handler_get_property;
202   object_class->dispose = empathy_call_handler_dispose;
203   object_class->finalize = empathy_call_handler_finalize;
204
205   param_spec = g_param_spec_object ("contact",
206     "contact", "The remote contact",
207     EMPATHY_TYPE_CONTACT,
208     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
209   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
210
211   param_spec = g_param_spec_object ("tp-call",
212     "tp-call", "The calls channel wrapper",
213     EMPATHY_TYPE_TP_CALL,
214     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
215   g_object_class_install_property (object_class, PROP_TP_CALL, param_spec);
216
217   param_spec = g_param_spec_boolean ("initial-audio",
218     "initial-audio", "Whether the call should start with audio",
219     TRUE,
220     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
221   g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
222       param_spec);
223
224   param_spec = g_param_spec_boolean ("initial-video",
225     "initial-video", "Whether the call should start with video",
226     FALSE,
227     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
228   g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
229     param_spec);
230
231   signals[CONFERENCE_ADDED] =
232     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
233       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
234       g_cclosure_marshal_VOID__OBJECT,
235       G_TYPE_NONE,
236       1, FS_TYPE_CONFERENCE);
237
238   signals[SRC_PAD_ADDED] =
239     g_signal_new ("src-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[SINK_PAD_ADDED] =
246     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
247       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
248       _empathy_marshal_VOID__OBJECT_UINT,
249       G_TYPE_NONE,
250       2, GST_TYPE_PAD, G_TYPE_UINT);
251
252   signals[REQUEST_RESOURCE] =
253     g_signal_new ("request-resource", G_TYPE_FROM_CLASS (klass),
254       G_SIGNAL_RUN_LAST, 0,
255       g_signal_accumulator_true_handled, NULL,
256       _empathy_marshal_BOOLEAN__UINT_UINT,
257       G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
258
259   signals[CLOSED] =
260     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
261       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
262       g_cclosure_marshal_VOID__VOID,
263       G_TYPE_NONE,
264       0);
265 }
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 EmpathyCallHandler *
275 empathy_call_handler_new_for_contact_with_streams (EmpathyContact *contact,
276     gboolean audio, gboolean video)
277 {
278   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
279     "contact", contact,
280     "initial-audio", audio,
281     "initial-video", video,
282     NULL));
283 }
284
285 EmpathyCallHandler *
286 empathy_call_handler_new_for_channel (EmpathyTpCall *call)
287 {
288   return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER,
289     "tp-call", call, NULL));
290 }
291
292 void
293 empathy_call_handler_bus_message (EmpathyCallHandler *handler,
294   GstBus *bus, GstMessage *message)
295 {
296   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
297
298   if (priv->tfchannel == NULL)
299     return;
300
301   tf_channel_bus_message (priv->tfchannel, message);
302 }
303
304 static void
305 conference_element_added (FsElementAddedNotifier *notifier,
306     GstBin *bin,
307     GstElement *element,
308     gpointer user_data)
309 {
310   GstElementFactory *factory;
311   const gchar *name;
312
313   factory = gst_element_get_factory (element);
314   name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory));
315
316   if (!tp_strdiff (name, "x264enc"))
317     {
318       /* Ensure that the encoder creates the baseline profile */
319       g_object_set (element,
320           "byte-stream", TRUE,
321           "bframes", 0,
322           "b-adapt", FALSE,
323           "cabac", FALSE,
324           "dct8x8", FALSE,
325           NULL);
326     }
327   else if (!tp_strdiff (name, "gstrtpbin"))
328     {
329       /* Lower the jitterbuffer latency to make it more suitable for video
330        * conferencing */
331       g_object_set (element, "latency", 100, NULL);
332     }
333 }
334
335 static void
336 empathy_call_handler_tf_channel_session_created_cb (TfChannel *tfchannel,
337   FsConference *conference, FsParticipant *participant,
338   EmpathyCallHandler *self)
339 {
340   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
341
342   priv->fsnotifier = fs_element_added_notifier_new ();
343   fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (conference));
344
345   g_signal_connect (priv->fsnotifier, "element-added",
346     G_CALLBACK (conference_element_added), NULL);
347
348   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
349     GST_ELEMENT (conference));
350 }
351
352 static void
353 empathy_call_handler_tf_stream_src_pad_added_cb (TfStream *stream,
354   GstPad *pad, FsCodec *codec, EmpathyCallHandler  *handler)
355 {
356   guint media_type;
357
358   g_object_get (stream, "media-type", &media_type, NULL);
359
360   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
361     pad, media_type);
362 }
363
364
365 static gboolean
366 empathy_call_handler_tf_stream_request_resource_cb (TfStream *stream,
367   guint direction, EmpathyTpCall *call)
368 {
369   gboolean ret;
370   guint media_type;
371
372   g_object_get (G_OBJECT (stream), "media-type", &media_type, NULL);
373
374   g_signal_emit (G_OBJECT (call),
375     signals[REQUEST_RESOURCE], 0, media_type, direction, &ret);
376
377   return ret;
378 }
379
380 static void
381 empathy_call_handler_tf_channel_stream_created_cb (TfChannel *tfchannel,
382   TfStream *stream, EmpathyCallHandler *handler)
383 {
384   guint media_type;
385   GstPad *spad;
386
387   g_signal_connect (stream, "src-pad-added",
388       G_CALLBACK (empathy_call_handler_tf_stream_src_pad_added_cb), handler);
389   g_signal_connect (stream, "request-resource",
390       G_CALLBACK (empathy_call_handler_tf_stream_request_resource_cb),
391         handler);
392
393   g_object_get (stream, "media-type", &media_type,
394     "sink-pad", &spad, NULL);
395
396   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
397     spad, media_type);
398
399   gst_object_unref (spad);
400 }
401
402 static void
403 empathy_call_handler_tf_channel_closed_cb (TfChannel *tfchannel,
404   EmpathyCallHandler *handler)
405 {
406   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
407 }
408
409 static GList *
410 empathy_call_handler_tf_channel_codec_config_get_defaults (FsCodec *codecs)
411 {
412   GList *l = NULL;
413   int i;
414
415   for (i = 0; codecs[i].encoding_name != NULL; i++)
416       l = g_list_append (l, fs_codec_copy (codecs + i));
417
418   return l;
419 }
420
421 static GList *
422 empathy_call_handler_tf_channel_codec_config_cb (TfChannel *channel,
423   guint stream_id, FsMediaType media_type, guint direction, gpointer user_data)
424 {
425   FsCodec audio_codecs[] = {
426     { FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000, },
427     { FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000, },
428
429     { FS_CODEC_ID_DISABLE, "DV",     FS_MEDIA_TYPE_AUDIO, },
430     { FS_CODEC_ID_DISABLE, "MPA",    FS_MEDIA_TYPE_AUDIO, },
431     { FS_CODEC_ID_DISABLE, "VORBIS", FS_MEDIA_TYPE_AUDIO, },
432     { FS_CODEC_ID_DISABLE, "MP3",    FS_MEDIA_TYPE_AUDIO, },
433     { 0, NULL, 0,}
434   };
435   FsCodec video_codecs[] = {
436     { FS_CODEC_ID_ANY, "H264",   FS_MEDIA_TYPE_VIDEO, },
437     { FS_CODEC_ID_ANY, "THEORA", FS_MEDIA_TYPE_VIDEO, },
438     { FS_CODEC_ID_ANY, "H263",   FS_MEDIA_TYPE_VIDEO, },
439
440     { FS_CODEC_ID_DISABLE, "DV",   FS_MEDIA_TYPE_VIDEO, },
441     { FS_CODEC_ID_DISABLE, "JPEG", FS_MEDIA_TYPE_VIDEO, },
442     { FS_CODEC_ID_DISABLE, "MPV",  FS_MEDIA_TYPE_VIDEO, },
443     { 0, NULL, 0}
444   };
445
446   switch (media_type)
447     {
448       case FS_MEDIA_TYPE_AUDIO:
449         return empathy_call_handler_tf_channel_codec_config_get_defaults
450           (audio_codecs);
451       case FS_MEDIA_TYPE_VIDEO:
452         return empathy_call_handler_tf_channel_codec_config_get_defaults
453           (video_codecs);
454     }
455
456   return NULL;
457 }
458
459 static void
460 empathy_call_handler_start_tpfs (EmpathyCallHandler *self)
461 {
462   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
463   TpChannel *channel;
464
465   g_object_get (priv->call, "channel", &channel, NULL);
466
467   g_assert (channel != NULL);
468
469   priv->tfchannel = tf_channel_new (channel);
470
471   /* Set up the telepathy farsight channel */
472   g_signal_connect (priv->tfchannel, "session-created",
473       G_CALLBACK (empathy_call_handler_tf_channel_session_created_cb), self);
474   g_signal_connect (priv->tfchannel, "stream-created",
475       G_CALLBACK (empathy_call_handler_tf_channel_stream_created_cb), self);
476   g_signal_connect (priv->tfchannel, "closed",
477       G_CALLBACK (empathy_call_handler_tf_channel_closed_cb), self);
478   g_signal_connect (priv->tfchannel, "stream-get-codec-config",
479       G_CALLBACK (empathy_call_handler_tf_channel_codec_config_cb), self);
480
481   g_object_unref (channel);
482 }
483
484 static void
485 empathy_call_handler_request_cb (EmpathyDispatchOperation *operation,
486   const GError *error, gpointer user_data)
487 {
488   EmpathyCallHandler *self = EMPATHY_CALL_HANDLER (user_data);
489   EmpathyCallHandlerPriv *priv = GET_PRIV (self);
490
491   if (error != NULL)
492     return;
493
494   priv->call = EMPATHY_TP_CALL (
495     empathy_dispatch_operation_get_channel_wrapper (operation));
496
497   g_object_ref (priv->call);
498
499   empathy_call_handler_start_tpfs (self);
500
501   empathy_tp_call_to (priv->call, priv->contact,
502     priv->initial_audio, priv->initial_video);
503
504   empathy_dispatch_operation_claim (operation);
505 }
506
507 void
508 empathy_call_handler_start_call (EmpathyCallHandler *handler)
509 {
510
511   EmpathyCallHandlerPriv *priv = GET_PRIV (handler);
512   EmpathyDispatcher *dispatcher;
513   TpConnection *connection;
514   GStrv allowed;
515   GValue *value;
516   GHashTable *request;
517
518   if (priv->call != NULL)
519     {
520       empathy_call_handler_start_tpfs (handler);
521       empathy_tp_call_accept_incoming_call (priv->call);
522       return;
523     }
524
525   g_assert (priv->contact != NULL);
526
527   dispatcher = empathy_dispatcher_dup_singleton ();
528   connection = empathy_contact_get_connection (priv->contact);
529   allowed = empathy_dispatcher_find_channel_class (dispatcher, connection,
530     TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_HANDLE_TYPE_CONTACT);
531
532   if (!tp_strv_contains ((const gchar * const *) allowed,
533       TP_IFACE_CHANNEL ".TargetHandle"))
534     return;
535
536   request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
537       (GDestroyNotify) tp_g_value_slice_free);
538
539   /* org.freedesktop.Telepathy.Channel.ChannelType */
540   value = tp_g_value_slice_new (G_TYPE_STRING);
541   g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
542   g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value);
543
544   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
545   value = tp_g_value_slice_new (G_TYPE_UINT);
546   g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
547   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value);
548
549   /* org.freedesktop.Telepathy.Channel.TargetHandle*/
550   value = tp_g_value_slice_new (G_TYPE_UINT);
551   g_value_set_uint (value, empathy_contact_get_handle (priv->contact));
552   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value);
553
554   empathy_dispatcher_create_channel (dispatcher, connection,
555     request, empathy_call_handler_request_cb, handler);
556
557   g_object_unref (dispatcher);
558 }
559