1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Elliot Fairweather
4 * Copyright (C) 2007 Collabora Ltd.
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
20 * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
21 * Xavier Claessens <xclaesse@gmail.com>
26 #include <libtelepathy/tp-chan-type-streamed-media-gen.h>
27 #include <libtelepathy/tp-helpers.h>
28 #include <libtelepathy/tp-conn.h>
30 #include <libmissioncontrol/mission-control.h>
32 #include "empathy-tp-call.h"
33 #include "empathy-tp-group.h"
34 #include "empathy-utils.h"
35 #include "empathy-debug.h"
36 #include "empathy-enum-types.h"
37 #include "tp-stream-engine-gen.h"
39 #define DEBUG_DOMAIN "TpCall"
41 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_TP_CALL, EmpathyTpCallPriv))
43 #define STREAM_ENGINE_BUS_NAME "org.freedesktop.Telepathy.StreamEngine"
44 #define STREAM_ENGINE_OBJECT_PATH "/org/freedesktop/Telepathy/StreamEngine"
45 #define STREAM_ENGINE_INTERFACE "org.freedesktop.Telepathy.StreamEngine"
46 #define CHANNEL_HANDLER_INTERFACE "org.freedesktop.Telepathy.ChannelHandler"
48 typedef struct _EmpathyTpCallPriv EmpathyTpCallPriv;
50 struct _EmpathyTpCallPriv {
52 DBusGProxy *streamed_iface;
53 DBusGProxy *se_ch_proxy;
56 EmpathyTpGroup *group;
57 EmpathyContact *contact;
58 EmpathyTpCallStatus status;
64 static void empathy_tp_call_class_init (EmpathyTpCallClass *klass);
65 static void empathy_tp_call_init (EmpathyTpCall *call);
79 static guint signals[LAST_SIGNAL];
81 G_DEFINE_TYPE (EmpathyTpCall, empathy_tp_call, G_TYPE_OBJECT)
84 tp_call_set_status (EmpathyTpCall *call,
85 EmpathyTpCallStatus status)
87 EmpathyTpCallPriv *priv = GET_PRIV (call);
89 priv->status = status;
90 g_object_notify (G_OBJECT (call), "status");
94 tp_call_set_property (GObject *object,
99 EmpathyTpCallPriv *priv = GET_PRIV (object);
103 priv->account = g_object_ref (g_value_get_object (value));
106 priv->tp_chan = g_object_ref (g_value_get_object (value));
109 tp_call_set_status (EMPATHY_TP_CALL (object),
110 g_value_get_enum (value));
113 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
119 tp_call_get_property (GObject *object,
124 EmpathyTpCallPriv *priv = GET_PRIV (object);
128 g_value_set_object (value, priv->account);
131 g_value_set_object (value, priv->tp_chan);
134 g_value_set_enum (value, priv->status);
137 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143 tp_call_destroy_cb (TpChan *call_chan,
146 EmpathyTpCallPriv *priv = GET_PRIV (call);
148 empathy_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
150 g_object_unref (priv->tp_chan);
151 priv->tp_chan = NULL;
152 priv->streamed_iface = NULL;
154 g_signal_emit (call, signals[DESTROY], 0);
158 tp_call_closed_cb (TpChan *call_chan,
161 EmpathyTpCallPriv *priv = GET_PRIV (call);
163 /* The channel is closed, do just like if the proxy was destroyed */
164 g_signal_handlers_disconnect_by_func (priv->tp_chan,
167 tp_call_destroy_cb (call_chan, call);
171 tp_call_stream_added_cb (DBusGProxy *streamed_iface,
173 guint contact_handle,
177 EmpathyTpCallPriv *priv = GET_PRIV (call);
179 empathy_debug (DEBUG_DOMAIN, "Stream added: id=%d, stream_type=%d",
180 stream_id, stream_type);
182 switch (stream_type) {
183 case TP_MEDIA_STREAM_TYPE_AUDIO:
184 priv->audio_stream = stream_id;
186 case TP_MEDIA_STREAM_TYPE_VIDEO:
187 priv->video_stream = stream_id;
190 empathy_debug (DEBUG_DOMAIN, "Unknown stream type: %d", stream_type);
196 tp_call_stream_removed_cb (DBusGProxy *streamed_iface,
200 EmpathyTpCallPriv *priv = GET_PRIV (call);
202 empathy_debug (DEBUG_DOMAIN, "Stream removed: %d", stream_id);
204 if (stream_id == priv->audio_stream) {
205 priv->audio_stream = 0;
207 else if (stream_id == priv->video_stream) {
208 priv->video_stream = 0;
213 tp_call_list_streams_cb (DBusGProxy *proxy,
221 empathy_debug (DEBUG_DOMAIN, "Failed to list streams: %s",
226 for (i = 0; i < streams->len; i++) {
229 guint contact_handle;
232 values = g_ptr_array_index (streams, i);
233 stream_id = g_value_get_uint (g_value_array_get_nth (values, 0));
234 contact_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
235 stream_type = g_value_get_uint (g_value_array_get_nth (values, 2));
237 tp_call_stream_added_cb (proxy,
246 tp_call_member_added_cb (EmpathyTpGroup *group,
247 EmpathyContact *contact,
248 EmpathyContact *actor,
250 const gchar *message,
253 EmpathyTpCallPriv *priv = GET_PRIV (call);
255 empathy_debug (DEBUG_DOMAIN, "Members added %s (%d)",
256 empathy_contact_get_id (contact),
257 empathy_contact_get_handle (contact));
259 if (!priv->contact) {
260 if (!empathy_contact_is_user (contact)) {
261 priv->is_incoming = TRUE;
262 priv->contact = g_object_ref (contact);
263 tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RINGING);
268 /* We already have the other contact, that means we now have 2 members,
269 * so we can start the call */
270 tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RUNNING);
274 tp_call_remote_pending_cb (EmpathyTpGroup *group,
275 EmpathyContact *contact,
276 EmpathyContact *actor,
278 const gchar *message,
281 EmpathyTpCallPriv *priv = GET_PRIV (call);
283 empathy_debug (DEBUG_DOMAIN, "Remote pending: %s (%d)",
284 empathy_contact_get_id (contact),
285 empathy_contact_get_handle (contact));
287 if (!priv->contact) {
288 priv->is_incoming = FALSE;
289 priv->contact = g_object_ref (contact);
290 tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RINGING);
295 tp_call_async_cb (DBusGProxy *proxy,
300 empathy_debug (DEBUG_DOMAIN, "Failed to %s: %s",
307 tp_call_constructor (GType type,
309 GObjectConstructParam *props)
312 EmpathyTpCallPriv *priv;
316 call = G_OBJECT_CLASS (empathy_tp_call_parent_class)->constructor (type, n_props, props);
317 priv = GET_PRIV (call);
319 priv->group = empathy_tp_group_new (priv->account, priv->tp_chan);
320 priv->streamed_iface = tp_chan_get_interface (priv->tp_chan,
321 TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
323 /* Connect signals */
324 dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamAdded",
325 G_CALLBACK (tp_call_stream_added_cb),
327 dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamRemoved",
328 G_CALLBACK (tp_call_stream_removed_cb),
330 dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed",
331 G_CALLBACK (tp_call_closed_cb),
333 g_signal_connect (priv->tp_chan, "destroy",
334 G_CALLBACK (tp_call_destroy_cb),
336 g_signal_connect (priv->group, "member-added",
337 G_CALLBACK (tp_call_member_added_cb),
339 g_signal_connect (priv->group, "remote-pending",
340 G_CALLBACK (tp_call_remote_pending_cb),
343 /* Start stream engine */
344 mc = empathy_mission_control_new ();
345 tp_conn = mission_control_get_connection (mc, priv->account, NULL);
346 priv->se_ch_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
347 STREAM_ENGINE_BUS_NAME,
348 STREAM_ENGINE_OBJECT_PATH,
349 CHANNEL_HANDLER_INTERFACE);
350 priv->se_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
351 STREAM_ENGINE_BUS_NAME,
352 STREAM_ENGINE_OBJECT_PATH,
353 STREAM_ENGINE_INTERFACE);
354 org_freedesktop_Telepathy_ChannelHandler_handle_channel_async (priv->se_ch_proxy,
355 dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn)),
356 dbus_g_proxy_get_path (DBUS_G_PROXY (tp_conn)),
358 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
359 priv->tp_chan->handle_type,
360 priv->tp_chan->handle,
363 g_object_unref (tp_conn);
367 tp_chan_type_streamed_media_list_streams_async (priv->streamed_iface,
368 tp_call_list_streams_cb,
375 tp_call_finalize (GObject *object)
377 EmpathyTpCallPriv *priv = GET_PRIV (object);
379 empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
382 GError *error = NULL;
384 g_signal_handlers_disconnect_by_func (priv->tp_chan,
387 empathy_debug (DEBUG_DOMAIN, "Closing channel...");
388 if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) {
389 empathy_debug (DEBUG_DOMAIN,
390 "Error closing text channel: %s",
391 error ? error->message : "No error given");
392 g_clear_error (&error);
394 g_object_unref (priv->tp_chan);
397 g_object_unref (priv->group);
398 g_object_unref (priv->contact);
399 g_object_unref (priv->account);
400 g_object_unref (priv->se_ch_proxy);
401 g_object_unref (priv->se_proxy);
403 G_OBJECT_CLASS (empathy_tp_call_parent_class)->finalize (object);
407 empathy_tp_call_class_init (EmpathyTpCallClass *klass)
409 GObjectClass *object_class = G_OBJECT_CLASS (klass);
411 object_class->constructor = tp_call_constructor;
412 object_class->finalize = tp_call_finalize;
413 object_class->set_property = tp_call_set_property;
414 object_class->get_property = tp_call_get_property;
416 /* Construct-only properties */
417 g_object_class_install_property (object_class,
419 g_param_spec_object ("account",
421 "The account associated with the channel",
424 G_PARAM_CONSTRUCT_ONLY));
425 g_object_class_install_property (object_class,
427 g_param_spec_object ("tp-chan",
429 "The media channel for the call",
432 G_PARAM_CONSTRUCT_ONLY));
434 /* Normal properties */
435 g_object_class_install_property (object_class,
437 g_param_spec_enum ("status",
439 "The status of the call",
440 EMPATHY_TYPE_TP_CALL_STATUS,
441 EMPATHY_TP_CALL_STATUS_PREPARING,
446 g_signal_new ("destroy",
447 G_TYPE_FROM_CLASS (klass),
451 g_cclosure_marshal_VOID__VOID,
456 g_type_class_add_private (klass, sizeof (EmpathyTpCallPriv));
460 empathy_tp_call_init (EmpathyTpCall *call)
465 empathy_tp_call_new (McAccount *account, TpChan *channel)
467 return g_object_new (EMPATHY_TYPE_TP_CALL,
474 empathy_tp_call_is_incoming (EmpathyTpCall *call)
476 EmpathyTpCallPriv *priv = GET_PRIV (call);
478 return priv->is_incoming;
482 empathy_tp_call_get_status (EmpathyTpCall *call)
484 EmpathyTpCallPriv *priv = GET_PRIV (call);
490 empathy_tp_call_get_contact (EmpathyTpCall *call)
492 EmpathyTpCallPriv *priv = GET_PRIV (call);
494 return priv->contact;
498 empathy_tp_call_accept (EmpathyTpCall *call)
500 EmpathyTpCallPriv *priv = GET_PRIV (call);
501 EmpathyContact *contact;
503 contact = empathy_tp_group_get_self_contact (priv->group);
504 empathy_tp_group_add_member (priv->group, contact, "");
508 empathy_tp_call_invite (EmpathyTpCall *call,
509 EmpathyContact *contact)
511 EmpathyTpCallPriv *priv = GET_PRIV (call);
513 empathy_tp_group_add_member (priv->group, contact, "you're welcome");
517 empathy_tp_call_request_streams (EmpathyTpCall *call,
521 EmpathyTpCallPriv *priv = GET_PRIV (call);
522 GArray *stream_types;
526 empathy_debug (DEBUG_DOMAIN, "Requesting streams for audio=%s video=%s",
527 audio ? "Yes" : "No",
528 video ? "Yes" : "No");
530 stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
532 type = TP_MEDIA_STREAM_TYPE_AUDIO;
533 g_array_append_val (stream_types, type);
536 type = TP_MEDIA_STREAM_TYPE_VIDEO;
537 g_array_append_val (stream_types, type);
540 handle = empathy_contact_get_handle (priv->contact);
541 tp_chan_type_streamed_media_request_streams_async (priv->streamed_iface,
544 tp_call_list_streams_cb,
547 g_array_free (stream_types, TRUE);
551 empathy_tp_call_send_video (EmpathyTpCall *call,
554 EmpathyTpCallPriv *priv = GET_PRIV (call);
557 if (!priv->video_stream) {
562 new_direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
564 new_direction = TP_MEDIA_STREAM_DIRECTION_RECEIVE;
567 tp_chan_type_streamed_media_request_stream_direction_async (priv->streamed_iface,
571 "request stream direction");
575 empathy_tp_call_add_preview_window (EmpathyTpCall *call,
578 EmpathyTpCallPriv *priv = GET_PRIV (call);
580 org_freedesktop_Telepathy_StreamEngine_add_preview_window_async (priv->se_proxy,
583 "add preview window");
587 empathy_tp_call_remove_preview_window (EmpathyTpCall *call,
590 EmpathyTpCallPriv *priv = GET_PRIV (call);
592 org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async (priv->se_proxy,
595 "remove preview window");
599 empathy_tp_call_set_output_window (EmpathyTpCall *call,
602 EmpathyTpCallPriv *priv = GET_PRIV (call);
604 org_freedesktop_Telepathy_StreamEngine_set_output_window_async (priv->se_proxy,
605 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
609 "set output window");
613 empathy_tp_call_set_output_volume (EmpathyTpCall *call,
616 EmpathyTpCallPriv *priv = GET_PRIV (call);
618 org_freedesktop_Telepathy_StreamEngine_set_output_volume_async (priv->se_proxy,
619 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
623 "set output volume");
628 empathy_tp_call_mute_output (EmpathyTpCall *call,
631 EmpathyTpCallPriv *priv = GET_PRIV (call);
633 org_freedesktop_Telepathy_StreamEngine_mute_output_async (priv->se_proxy,
634 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
643 empathy_tp_call_mute_input (EmpathyTpCall *call,
646 EmpathyTpCallPriv *priv = GET_PRIV (call);
648 org_freedesktop_Telepathy_StreamEngine_mute_input_async (priv->se_proxy,
649 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),