2 * empathy-call-handler.c - Source for EmpathyCallHandler
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
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.
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.
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
25 #include <telepathy-glib/util.h>
26 #include <telepathy-glib/interfaces.h>
28 #include <telepathy-farsight/channel.h>
29 #include <telepathy-farsight/stream.h>
31 #include "empathy-call-handler.h"
32 #include "empathy-dispatcher.h"
33 #include "empathy-marshal.h"
34 #include "empathy-utils.h"
36 G_DEFINE_TYPE(EmpathyCallHandler
, empathy_call_handler
, G_TYPE_OBJECT
)
49 static guint signals
[LAST_SIGNAL
] = {0};
59 /* private structure */
62 gboolean dispose_has_run
;
64 EmpathyContact
*contact
;
66 gboolean initial_audio
;
67 gboolean initial_video
;
68 } EmpathyCallHandlerPriv
;
70 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
73 empathy_call_handler_dispose (GObject
*object
)
75 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
77 if (priv
->dispose_has_run
)
80 priv
->dispose_has_run
= TRUE
;
82 if (priv
->contact
!= NULL
)
83 g_object_unref (priv
->contact
);
87 if (priv
->tfchannel
!= NULL
)
88 g_object_unref (priv
->tfchannel
);
90 priv
->tfchannel
= NULL
;
92 if (priv
->call
!= NULL
)
94 empathy_tp_call_close (priv
->call
);
95 g_object_unref (priv
->call
);
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
);
106 empathy_call_handler_finalize (GObject
*object
)
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
);
114 empathy_call_handler_init (EmpathyCallHandler
*obj
)
116 EmpathyCallHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (obj
,
117 EMPATHY_TYPE_CALL_HANDLER
, EmpathyCallHandlerPriv
);
123 empathy_call_handler_constructed (GObject
*object
)
125 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
127 if (priv
->contact
== NULL
)
129 g_object_get (priv
->call
, "contact", &(priv
->contact
), NULL
);
134 empathy_call_handler_set_property (GObject
*object
,
135 guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
137 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
142 priv
->contact
= g_value_dup_object (value
);
145 priv
->call
= g_value_dup_object (value
);
147 case PROP_INITIAL_AUDIO
:
148 priv
->initial_audio
= g_value_get_boolean (value
);
150 case PROP_INITIAL_VIDEO
:
151 priv
->initial_video
= g_value_get_boolean (value
);
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
159 empathy_call_handler_get_property (GObject
*object
,
160 guint property_id
, GValue
*value
, GParamSpec
*pspec
)
162 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
167 g_value_set_object (value
, priv
->contact
);
170 g_value_set_object (value
, priv
->call
);
172 case PROP_INITIAL_AUDIO
:
173 g_value_set_boolean (value
, priv
->initial_audio
);
175 case PROP_INITIAL_VIDEO
:
176 g_value_set_boolean (value
, priv
->initial_video
);
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
185 empathy_call_handler_class_init (EmpathyCallHandlerClass
*klass
)
187 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
188 GParamSpec
*param_spec
;
190 g_type_class_add_private (klass
, sizeof (EmpathyCallHandlerPriv
));
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
;
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
);
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
);
210 param_spec
= g_param_spec_boolean ("initial-audio",
211 "initial-audio", "Whether the call should start with audio",
213 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
214 g_object_class_install_property (object_class
, PROP_INITIAL_AUDIO
,
217 param_spec
= g_param_spec_boolean ("initial-video",
218 "initial-video", "Whether the call should start with video",
220 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
221 g_object_class_install_property (object_class
, PROP_INITIAL_VIDEO
,
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
,
229 1, FS_TYPE_CONFERENCE
);
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
,
236 2, GST_TYPE_PAD
, G_TYPE_UINT
);
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
,
243 2, GST_TYPE_PAD
, G_TYPE_UINT
);
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
);
253 g_signal_new ("closed", G_TYPE_FROM_CLASS (klass
),
254 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
255 g_cclosure_marshal_VOID__VOID
,
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
);
267 * empathy_call_handler_new_for_contact:
268 * @contact: an #EmpathyContact
270 * Creates a new #EmpathyCallHandler with contact @contact.
272 * Return value: a new #EmpathyCallHandler
275 empathy_call_handler_new_for_contact (EmpathyContact
*contact
)
277 return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER
,
278 "contact", contact
, NULL
));
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
287 * Creates a new #EmpathyCallHandler with contact @contact.
289 * Return value: a new #EmpathyCallHandler
292 empathy_call_handler_new_for_contact_with_streams (EmpathyContact
*contact
,
293 gboolean audio
, gboolean video
)
295 return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER
,
297 "initial-audio", audio
,
298 "initial-video", video
,
303 empathy_call_handler_new_for_channel (EmpathyTpCall
*call
)
305 return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER
,
307 "initial-video", empathy_tp_call_is_receiving_video (call
),
312 empathy_call_handler_bus_message (EmpathyCallHandler
*handler
,
313 GstBus
*bus
, GstMessage
*message
)
315 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
317 if (priv
->tfchannel
== NULL
)
320 tf_channel_bus_message (priv
->tfchannel
, message
);
324 empathy_call_handler_tf_channel_session_created_cb (TfChannel
*tfchannel
,
325 FsConference
*conference
, FsParticipant
*participant
,
326 EmpathyCallHandler
*self
)
328 g_signal_emit (G_OBJECT (self
), signals
[CONFERENCE_ADDED
], 0,
329 GST_ELEMENT (conference
));
333 empathy_call_handler_tf_stream_src_pad_added_cb (TfStream
*stream
,
334 GstPad
*pad
, FsCodec
*codec
, EmpathyCallHandler
*handler
)
338 g_object_get (stream
, "media-type", &media_type
, NULL
);
340 g_signal_emit (G_OBJECT (handler
), signals
[SRC_PAD_ADDED
], 0,
346 empathy_call_handler_tf_stream_request_resource_cb (TfStream
*stream
,
347 guint direction
, EmpathyTpCall
*call
)
352 g_object_get (G_OBJECT (stream
), "media-type", &media_type
, NULL
);
354 g_signal_emit (G_OBJECT (call
),
355 signals
[REQUEST_RESOURCE
], 0, media_type
, direction
, &ret
);
361 empathy_call_handler_tf_stream_closed_cb (TfStream
*stream
,
362 EmpathyCallHandler
*handler
)
364 g_signal_emit (handler
, signals
[STREAM_CLOSED
], 0, stream
);
368 empathy_call_handler_tf_channel_stream_created_cb (TfChannel
*tfchannel
,
369 TfStream
*stream
, EmpathyCallHandler
*handler
)
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
),
379 g_signal_connect (stream
, "closed",
380 G_CALLBACK (empathy_call_handler_tf_stream_closed_cb
), handler
);
382 g_object_get (stream
, "media-type", &media_type
,
383 "sink-pad", &spad
, NULL
);
385 g_signal_emit (G_OBJECT (handler
), signals
[SINK_PAD_ADDED
], 0,
388 gst_object_unref (spad
);
392 empathy_call_handler_tf_channel_closed_cb (TfChannel
*tfchannel
,
393 EmpathyCallHandler
*handler
)
395 g_signal_emit (G_OBJECT (handler
), signals
[CLOSED
], 0);
399 empathy_call_handler_tf_channel_codec_config_cb (TfChannel
*channel
,
400 guint stream_id
, FsMediaType media_type
, guint direction
, gpointer user_data
)
402 gchar
*filename
= empathy_file_lookup ("codec-preferences", "data");
404 GError
*error
= NULL
;
406 codecs
= fs_codec_list_from_keyfile (filename
, &error
);
411 g_warning ("No codec-preferences file: %s",
412 error
? error
->message
: "No error message");
414 g_clear_error (&error
);
420 empathy_call_handler_start_tpfs (EmpathyCallHandler
*self
)
422 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
425 g_object_get (priv
->call
, "channel", &channel
, NULL
);
427 g_assert (channel
!= NULL
);
429 priv
->tfchannel
= tf_channel_new (channel
);
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
);
441 g_object_unref (channel
);
445 empathy_call_handler_request_cb (EmpathyDispatchOperation
*operation
,
446 const GError
*error
, gpointer user_data
)
448 EmpathyCallHandler
*self
= EMPATHY_CALL_HANDLER (user_data
);
449 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
454 priv
->call
= EMPATHY_TP_CALL (
455 empathy_dispatch_operation_get_channel_wrapper (operation
));
457 g_object_ref (priv
->call
);
458 g_object_notify (G_OBJECT (self
), "tp-call");
460 empathy_call_handler_start_tpfs (self
);
462 empathy_tp_call_to (priv
->call
, priv
->contact
,
463 priv
->initial_audio
, priv
->initial_video
);
465 empathy_dispatch_operation_claim (operation
);
469 empathy_call_handler_start_call (EmpathyCallHandler
*handler
)
472 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
473 EmpathyDispatcher
*dispatcher
;
474 TpConnection
*connection
;
479 if (priv
->call
!= NULL
)
481 empathy_call_handler_start_tpfs (handler
);
482 empathy_tp_call_accept_incoming_call (priv
->call
);
486 g_assert (priv
->contact
!= NULL
);
488 dispatcher
= empathy_dispatcher_dup_singleton ();
489 connection
= empathy_contact_get_connection (priv
->contact
);
490 classes
= empathy_dispatcher_find_requestable_channel_classes
491 (dispatcher
, connection
, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA
,
492 TP_HANDLE_TYPE_CONTACT
, NULL
);
497 g_list_free (classes
);
499 request
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
,
500 (GDestroyNotify
) tp_g_value_slice_free
);
502 /* org.freedesktop.Telepathy.Channel.ChannelType */
503 value
= tp_g_value_slice_new (G_TYPE_STRING
);
504 g_value_set_string (value
, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA
);
505 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".ChannelType", value
);
507 /* org.freedesktop.Telepathy.Channel.TargetHandleType */
508 value
= tp_g_value_slice_new (G_TYPE_UINT
);
509 g_value_set_uint (value
, TP_HANDLE_TYPE_CONTACT
);
510 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".TargetHandleType", value
);
512 /* org.freedesktop.Telepathy.Channel.TargetHandle*/
513 value
= tp_g_value_slice_new (G_TYPE_UINT
);
514 g_value_set_uint (value
, empathy_contact_get_handle (priv
->contact
));
515 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".TargetHandle", value
);
517 empathy_dispatcher_create_channel (dispatcher
, connection
,
518 request
, empathy_call_handler_request_cb
, handler
);
520 g_object_unref (dispatcher
);
524 * empathy_call_handler_stop_call:
525 * @handler: an #EmpathyCallHandler
527 * Closes the #EmpathyCallHandler's call and frees its resources.
530 empathy_call_handler_stop_call (EmpathyCallHandler
*handler
)
532 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
534 if (priv
->call
!= NULL
)
536 empathy_tp_call_close (priv
->call
);
537 g_object_unref (priv
->call
);
544 * empathy_call_handler_has_initial_video:
545 * @handler: an #EmpathyCallHandler
547 * Return %TRUE if the call managed by this #EmpathyCallHandler was
548 * created with video enabled
550 * Return value: %TRUE if the call was created as a video conversation.
553 empathy_call_handler_has_initial_video (EmpathyCallHandler
*handler
)
555 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
557 return priv
->initial_video
;