]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-call.c
Do not take McAccount as construct param, it can be found from the TpChannel
[empathy.git] / libempathy / empathy-tp-call.c
1 /*
2  *  Copyright (C) 2007 Elliot Fairweather
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  *  Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
19  */
20
21 #include <string.h>
22 #include <dbus/dbus-glib.h>
23
24 #include <telepathy-glib/proxy-subclass.h>
25 #include <telepathy-glib/dbus.h>
26
27 #include <extensions/extensions.h>
28 #include <libempathy/empathy-contact-factory.h>
29 #include <libempathy/empathy-debug.h>
30 #include <libempathy/empathy-tp-group.h>
31 #include <libempathy/empathy-utils.h>
32
33 #include "empathy-tp-call.h"
34
35 #define DEBUG_DOMAIN "TpCall"
36
37 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE \
38     ((object), EMPATHY_TYPE_TP_CALL, EmpathyTpCallPriv))
39
40 #define STREAM_ENGINE_BUS_NAME "org.freedesktop.Telepathy.StreamEngine"
41 #define STREAM_ENGINE_OBJECT_PATH "/org/freedesktop/Telepathy/StreamEngine"
42
43 typedef struct _EmpathyTpCallPriv EmpathyTpCallPriv;
44
45 struct _EmpathyTpCallPriv
46 {
47   TpChannel *channel;
48   TpProxy *stream_engine;
49   TpDBusDaemon *dbus_daemon;
50   EmpathyTpGroup *group;
51   EmpathyContact *contact;
52   gboolean is_incoming;
53   guint status;
54   gboolean stream_engine_running;
55
56   EmpathyTpCallStream *audio;
57   EmpathyTpCallStream *video;
58 };
59
60 enum
61 {
62   PROP_0,
63   PROP_CHANNEL,
64   PROP_CONTACT,
65   PROP_IS_INCOMING,
66   PROP_STATUS,
67   PROP_AUDIO_STREAM,
68   PROP_VIDEO_STREAM
69 };
70
71 G_DEFINE_TYPE (EmpathyTpCall, empathy_tp_call, G_TYPE_OBJECT)
72
73 static void
74 tp_call_add_stream (EmpathyTpCall *call,
75                     guint stream_id,
76                     guint contact_handle,
77                     guint stream_type,
78                     guint stream_state,
79                     guint stream_direction)
80 {
81   EmpathyTpCallPriv *priv = GET_PRIV (call);
82
83   switch (stream_type)
84     {
85       case TP_MEDIA_STREAM_TYPE_AUDIO:
86         empathy_debug (DEBUG_DOMAIN,
87             "Audio stream - id: %d, state: %d, direction: %d",
88             stream_id, stream_state, stream_direction);
89         priv->audio->exists = TRUE;
90         priv->audio->id = stream_id;
91         priv->audio->state = stream_state;
92         priv->audio->direction = stream_direction;
93         g_object_notify (G_OBJECT (call), "audio-stream");
94         break;
95       case TP_MEDIA_STREAM_TYPE_VIDEO:
96         empathy_debug (DEBUG_DOMAIN,
97             "Video stream - id: %d, state: %d, direction: %d",
98             stream_id, stream_state, stream_direction);
99         priv->video->exists = TRUE;
100         priv->video->id = stream_id;
101         priv->video->state = stream_state;
102         priv->video->direction = stream_direction;
103         g_object_notify (G_OBJECT (call), "video-stream");
104         break;
105       default:
106         empathy_debug (DEBUG_DOMAIN, "Unknown stream type: %d",
107             stream_type);
108     }
109 }
110
111 static void
112 tp_call_stream_added_cb (TpChannel *channel,
113                          guint stream_id,
114                          guint contact_handle,
115                          guint stream_type,
116                          gpointer user_data,
117                          GObject *call)
118 {
119   empathy_debug (DEBUG_DOMAIN,
120       "Stream added - stream id: %d, contact handle: %d, stream type: %d",
121       stream_id, contact_handle, stream_type);
122
123   tp_call_add_stream (EMPATHY_TP_CALL (call), stream_id, contact_handle,
124       stream_type, TP_MEDIA_STREAM_STATE_DISCONNECTED,
125       TP_MEDIA_STREAM_DIRECTION_NONE);
126 }
127
128 static void
129 tp_call_stream_removed_cb (TpChannel *channel,
130                            guint stream_id,
131                            gpointer user_data,
132                            GObject *call)
133 {
134   EmpathyTpCallPriv *priv = GET_PRIV (call);
135
136   empathy_debug (DEBUG_DOMAIN, "Stream removed - stream id: %d", stream_id);
137
138   if (stream_id == priv->audio->id)
139     {
140       priv->audio->exists = FALSE;
141       g_object_notify (call, "audio-stream");
142     }
143   else if (stream_id == priv->video->id)
144     {
145       priv->video->exists = FALSE;
146       g_object_notify (call, "video-stream");
147     }
148 }
149
150 static void
151 tp_call_stream_state_changed_cb (TpChannel *proxy,
152                                  guint stream_id,
153                                  guint stream_state,
154                                  gpointer user_data,
155                                  GObject *call)
156 {
157   EmpathyTpCallPriv *priv = GET_PRIV (call);
158
159   empathy_debug (DEBUG_DOMAIN,
160       "Stream state changed - stream id: %d, state state: %d",
161       stream_id, stream_state);
162
163   if (stream_id == priv->audio->id)
164     {
165       priv->audio->state = stream_state;
166       g_object_notify (call, "audio-stream");
167     }
168   else if (stream_id == priv->video->id)
169     {
170       priv->video->state = stream_state;
171       g_object_notify (call, "video-stream");
172     }
173 }
174
175 static void
176 tp_call_stream_direction_changed_cb (TpChannel *channel,
177                                      guint stream_id,
178                                      guint stream_direction,
179                                      guint pending_flags,
180                                      gpointer user_data,
181                                      GObject *call)
182 {
183   EmpathyTpCallPriv *priv = GET_PRIV (call);
184
185   empathy_debug (DEBUG_DOMAIN,
186       "Stream direction changed - stream: %d, direction: %d",
187       stream_id, stream_direction);
188
189   if (stream_id == priv->audio->id)
190     {
191       priv->audio->direction = stream_direction;
192       g_object_notify (call, "audio-stream");
193     }
194   else if (stream_id == priv->video->id)
195     {
196       priv->video->direction = stream_direction;
197       g_object_notify (call, "video-stream");
198     }
199 }
200
201 static void
202 tp_call_request_streams_cb (TpChannel *channel,
203                             const GPtrArray *streams,
204                             const GError *error,
205                             gpointer user_data,
206                             GObject *call)
207 {
208   guint i;
209
210   if (error)
211     {
212       empathy_debug (DEBUG_DOMAIN, "Error requesting streams: %s", error->message);
213       return;
214     }
215
216   for (i = 0; i < streams->len; i++)
217     {
218       GValueArray *values;
219       guint stream_id;
220       guint contact_handle;
221       guint stream_type;
222       guint stream_state;
223       guint stream_direction;
224
225       values = g_ptr_array_index (streams, i);
226       stream_id = g_value_get_uint (g_value_array_get_nth (values, 0));
227       contact_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
228       stream_type = g_value_get_uint (g_value_array_get_nth (values, 2));
229       stream_state = g_value_get_uint (g_value_array_get_nth (values, 3));
230       stream_direction = g_value_get_uint (g_value_array_get_nth (values, 4));
231
232       tp_call_add_stream (EMPATHY_TP_CALL (call), stream_id, contact_handle,
233           stream_type, stream_state, stream_direction);
234   }
235 }
236
237 static void
238 tp_call_request_streams_for_capabilities (EmpathyTpCall *call,
239                                           EmpathyCapabilities capabilities)
240 {
241   EmpathyTpCallPriv *priv = GET_PRIV (call);
242   GArray *stream_types;
243   guint handle;
244   guint stream_type;
245
246   empathy_debug (DEBUG_DOMAIN, "Requesting new stream for capabilities %d",
247       capabilities);
248
249   stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
250   handle = empathy_contact_get_handle (priv->contact);
251
252   if (capabilities & EMPATHY_CAPABILITIES_AUDIO)
253     {
254       stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
255       g_array_append_val (stream_types, stream_type);
256     }
257   if (capabilities & EMPATHY_CAPABILITIES_VIDEO)
258     {
259       stream_type = TP_MEDIA_STREAM_TYPE_VIDEO;
260       g_array_append_val (stream_types, stream_type);
261     }
262
263   tp_cli_channel_type_streamed_media_call_request_streams (priv->channel, -1,
264       handle, stream_types, tp_call_request_streams_cb, NULL, NULL,
265       G_OBJECT (call));
266
267   g_array_free (stream_types, TRUE);
268 }
269
270 static void
271 tp_call_request_streams (EmpathyTpCall *call)
272 {
273   EmpathyTpCallPriv *priv = GET_PRIV (call);
274   EmpathyCapabilities capabilities;
275   TpConnection *connection;
276
277   empathy_debug (DEBUG_DOMAIN,
278       "Requesting appropriate audio/video streams from contact");
279
280   /* FIXME: SIP don't have capabilities interface but we know it supports
281    *        only audio and not video. */
282   g_object_get (priv->channel, "connection", &connection, NULL);
283   if (!tp_proxy_has_interface_by_id (connection,
284            TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES))
285       capabilities = EMPATHY_CAPABILITIES_AUDIO;
286   else
287       capabilities = empathy_contact_get_capabilities (priv->contact);
288
289   tp_call_request_streams_for_capabilities (call, capabilities);
290   g_object_unref (connection);
291 }
292
293 static void
294 tp_call_group_ready_cb (EmpathyTpCall *call)
295 {
296   EmpathyTpCallPriv  *priv = GET_PRIV (call);
297   EmpathyPendingInfo *invitation;
298
299   invitation = empathy_tp_group_get_invitation (priv->group, &priv->contact);
300   priv->is_incoming = (invitation != NULL);
301   priv->status = EMPATHY_TP_CALL_STATUS_PENDING;
302
303   if (!priv->is_incoming)
304       tp_call_request_streams (call);
305
306   g_object_notify (G_OBJECT (call), "is-incoming");
307   g_object_notify (G_OBJECT (call), "contact");
308   g_object_notify (G_OBJECT (call), "status");
309 }
310
311 static void
312 tp_call_member_added_cb (EmpathyTpGroup *group,
313                          EmpathyContact *contact,
314                          EmpathyContact *actor,
315                          guint reason,
316                          const gchar *message,
317                          EmpathyTpCall *call)
318 {
319   EmpathyTpCallPriv *priv = GET_PRIV (call);
320
321   if (priv->status == EMPATHY_TP_CALL_STATUS_PENDING &&
322       ((priv->is_incoming && contact != priv->contact) ||
323        (!priv->is_incoming && contact == priv->contact)))
324     {
325       priv->status = EMPATHY_TP_CALL_STATUS_ACCEPTED;
326       g_object_notify (G_OBJECT (call), "status");
327     }
328 }
329
330 static void
331 tp_call_channel_invalidated_cb (TpChannel     *channel,
332                                 GQuark         domain,
333                                 gint           code,
334                                 gchar         *message,
335                                 EmpathyTpCall *call)
336 {
337   EmpathyTpCallPriv *priv = GET_PRIV (call);
338
339   empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message);
340   priv->status = EMPATHY_TP_CALL_STATUS_CLOSED;
341   g_object_notify (G_OBJECT (call), "status");
342 }
343
344 static void
345 tp_call_async_cb (TpProxy *proxy,
346                   const GError *error,
347                   gpointer user_data,
348                   GObject *call)
349 {
350   if (error)
351     {
352       empathy_debug (DEBUG_DOMAIN, "Error %s: %s",
353           user_data, error->message);
354     }
355 }
356
357 static void
358 tp_call_close_channel (EmpathyTpCall *call)
359 {
360   EmpathyTpCallPriv *priv = GET_PRIV (call);
361
362   if (priv->status == EMPATHY_TP_CALL_STATUS_CLOSED)
363       return;
364
365   empathy_debug (DEBUG_DOMAIN, "Closing channel");
366
367   tp_cli_channel_call_close (priv->channel, -1,
368       (tp_cli_channel_callback_for_close) tp_call_async_cb,
369       "closing channel", NULL, G_OBJECT (call));
370
371   priv->status = EMPATHY_TP_CALL_STATUS_CLOSED;
372   g_object_notify (G_OBJECT (call), "status");
373 }
374
375 static void
376 tp_call_stream_engine_invalidated_cb (TpProxy       *stream_engine,
377                                       GQuark         domain,
378                                       gint           code,
379                                       gchar         *message,
380                                       EmpathyTpCall *call)
381 {
382   empathy_debug (DEBUG_DOMAIN, "Stream engine proxy invalidated: %s",
383       message);
384   tp_call_close_channel (call);
385 }
386
387 static void
388 tp_call_stream_engine_watch_name_owner_cb (TpDBusDaemon *daemon,
389                                            const gchar *name,
390                                            const gchar *new_owner,
391                                            gpointer call)
392 {
393   EmpathyTpCallPriv *priv = GET_PRIV (call);
394
395   /* G_STR_EMPTY(new_owner) means either stream-engine has not started yet or
396    * has crashed. We want to close the channel if stream-engine has crashed.
397    * */
398   empathy_debug (DEBUG_DOMAIN,
399                  "Watch SE: name='%s' SE running='%s' new_owner='%s'",
400                  name, priv->stream_engine_running ? "yes" : "no",
401                  new_owner ? new_owner : "none");
402   if (priv->stream_engine_running && G_STR_EMPTY (new_owner))
403     {
404       empathy_debug (DEBUG_DOMAIN, "Stream engine falled off the bus");
405       tp_call_close_channel (call);
406       return;
407     }
408
409   priv->stream_engine_running = !G_STR_EMPTY (new_owner);
410 }
411
412 static void
413 tp_call_stream_engine_handle_channel (EmpathyTpCall *call)
414 {
415   EmpathyTpCallPriv *priv = GET_PRIV (call);
416   gchar *channel_type;
417   gchar *object_path;
418   guint handle_type;
419   guint handle;
420   TpProxy *connection;
421
422   empathy_debug (DEBUG_DOMAIN, "Revving up the stream engine");
423
424   priv->stream_engine = g_object_new (TP_TYPE_PROXY,
425       "bus-name", STREAM_ENGINE_BUS_NAME,
426       "dbus-connection", tp_get_bus (),
427       "object-path", STREAM_ENGINE_OBJECT_PATH,
428        NULL);
429   tp_proxy_add_interface_by_id (priv->stream_engine,
430       EMP_IFACE_QUARK_STREAM_ENGINE);
431   tp_proxy_add_interface_by_id (priv->stream_engine,
432       EMP_IFACE_QUARK_CHANNEL_HANDLER);
433
434   g_signal_connect (priv->stream_engine, "invalidated",
435       G_CALLBACK (tp_call_stream_engine_invalidated_cb),
436       call);
437   
438   /* FIXME: dbus daemon should be unique */
439   priv->dbus_daemon = tp_dbus_daemon_new (tp_get_bus ());
440   tp_dbus_daemon_watch_name_owner (priv->dbus_daemon, STREAM_ENGINE_BUS_NAME,
441       tp_call_stream_engine_watch_name_owner_cb,
442       call, NULL);
443
444   g_object_get (priv->channel,
445       "connection", &connection,
446       "channel-type", &channel_type,
447       "object-path", &object_path,
448       "handle_type", &handle_type,
449       "handle", &handle,
450       NULL);
451
452   emp_cli_channel_handler_call_handle_channel (priv->stream_engine, -1,
453         connection->bus_name,
454         connection->object_path,
455         channel_type, object_path, handle_type, handle,
456         tp_call_async_cb, "calling handle channel", NULL,
457         G_OBJECT (call));
458
459   g_object_unref (connection);
460   g_free (channel_type);
461   g_free (object_path);
462 }
463
464 static GObject *
465 tp_call_constructor (GType type,
466                      guint n_construct_params,
467                      GObjectConstructParam *construct_params)
468 {
469   GObject *object;
470   EmpathyTpCall *call;
471   EmpathyTpCallPriv *priv;
472
473   object = G_OBJECT_CLASS (empathy_tp_call_parent_class)->constructor (type,
474       n_construct_params, construct_params);
475
476   call = EMPATHY_TP_CALL (object);
477   priv = GET_PRIV (call);
478
479   /* Setup streamed media channel */
480   g_signal_connect (priv->channel, "invalidated",
481       G_CALLBACK (tp_call_channel_invalidated_cb), call);
482   tp_cli_channel_type_streamed_media_connect_to_stream_added (priv->channel,
483       tp_call_stream_added_cb, NULL, NULL, G_OBJECT (call), NULL);
484   tp_cli_channel_type_streamed_media_connect_to_stream_removed (priv->channel,
485       tp_call_stream_removed_cb, NULL, NULL, G_OBJECT (call), NULL);
486   tp_cli_channel_type_streamed_media_connect_to_stream_state_changed (priv->channel,
487       tp_call_stream_state_changed_cb, NULL, NULL, G_OBJECT (call), NULL);
488   tp_cli_channel_type_streamed_media_connect_to_stream_direction_changed (priv->channel,
489       tp_call_stream_direction_changed_cb, NULL, NULL, G_OBJECT (call), NULL);
490   tp_cli_channel_type_streamed_media_call_list_streams (priv->channel, -1,
491       tp_call_request_streams_cb, NULL, NULL, G_OBJECT (call));
492
493   /* Setup group interface */
494   priv->group = empathy_tp_group_new (priv->channel);
495
496   g_signal_connect (G_OBJECT (priv->group), "member-added",
497       G_CALLBACK (tp_call_member_added_cb), call);
498
499   if (empathy_tp_group_is_ready (priv->group))
500       tp_call_group_ready_cb (call);
501   else
502       g_signal_connect_swapped (priv->group, "ready",
503           G_CALLBACK (tp_call_group_ready_cb), call);
504
505   /* Start stream engine */
506   tp_call_stream_engine_handle_channel (call);
507
508   return object;
509 }
510
511 static void 
512 tp_call_finalize (GObject *object)
513 {
514   EmpathyTpCallPriv *priv = GET_PRIV (object);
515
516   empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
517
518   g_slice_free (EmpathyTpCallStream, priv->audio);
519   g_slice_free (EmpathyTpCallStream, priv->video);
520   g_object_unref (priv->group);
521
522   if (priv->channel != NULL)
523     {
524       g_signal_handlers_disconnect_by_func (priv->channel,
525           tp_call_channel_invalidated_cb, object);
526       g_object_unref (priv->channel);
527     }
528
529   if (priv->stream_engine != NULL)
530     {
531       g_signal_handlers_disconnect_by_func (priv->stream_engine,
532           tp_call_stream_engine_invalidated_cb, object);
533       g_object_unref (priv->stream_engine);
534     }
535
536   if (priv->contact != NULL)
537       g_object_unref (priv->contact);
538
539   if (priv->dbus_daemon != NULL)
540     {
541       tp_dbus_daemon_cancel_name_owner_watch (priv->dbus_daemon,
542           STREAM_ENGINE_BUS_NAME,
543           tp_call_stream_engine_watch_name_owner_cb,
544           object);
545       g_object_unref (priv->dbus_daemon);
546     }
547
548   (G_OBJECT_CLASS (empathy_tp_call_parent_class)->finalize) (object);
549 }
550
551 static void 
552 tp_call_set_property (GObject *object,
553                       guint prop_id,
554                       const GValue *value,
555                       GParamSpec *pspec)
556 {
557   EmpathyTpCallPriv *priv = GET_PRIV (object);
558
559   switch (prop_id)
560     {
561     case PROP_CHANNEL:
562       priv->channel = g_value_dup_object (value);
563       break;
564     default:
565       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
566       break;
567   }
568 }
569
570 static void
571 tp_call_get_property (GObject *object,
572                       guint prop_id,
573                       GValue *value,
574                       GParamSpec *pspec)
575 {
576   EmpathyTpCallPriv *priv = GET_PRIV (object);
577
578   switch (prop_id)
579     {
580     case PROP_CHANNEL:
581       g_value_set_object (value, priv->channel);
582       break;
583     case PROP_CONTACT:
584       g_value_set_object (value, priv->contact);
585       break;
586     case PROP_IS_INCOMING:
587       g_value_set_boolean (value, priv->is_incoming);
588       break;
589     case PROP_STATUS:
590       g_value_set_uint (value, priv->status);
591       break;
592     case PROP_AUDIO_STREAM:
593       g_value_set_pointer (value, priv->audio);
594       break;
595     case PROP_VIDEO_STREAM:
596       g_value_set_pointer (value, priv->video);
597       break;
598     default:
599       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
600       break;
601   }
602 }
603
604 static void
605 empathy_tp_call_class_init (EmpathyTpCallClass *klass)
606 {
607   GObjectClass *object_class = G_OBJECT_CLASS (klass);
608
609   emp_cli_init ();
610
611   object_class->constructor = tp_call_constructor;
612   object_class->finalize = tp_call_finalize;
613   object_class->set_property = tp_call_set_property;
614   object_class->get_property = tp_call_get_property;
615
616   g_type_class_add_private (klass, sizeof (EmpathyTpCallPriv));
617
618   g_object_class_install_property (object_class, PROP_CHANNEL,
619       g_param_spec_object ("channel", "channel", "channel",
620       TELEPATHY_CHAN_TYPE,
621       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
622       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
623   g_object_class_install_property (object_class, PROP_CONTACT,
624       g_param_spec_object ("contact", "Call contact", "Call contact",
625       EMPATHY_TYPE_CONTACT,
626       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
627   g_object_class_install_property (object_class, PROP_IS_INCOMING,
628       g_param_spec_boolean ("is-incoming", "Is media stream incoming",
629       "Is media stream incoming", FALSE, G_PARAM_READABLE |
630       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
631   g_object_class_install_property (object_class, PROP_STATUS,
632       g_param_spec_uint ("status", "Call status",
633       "Call status", 0, 255, 0, G_PARAM_READABLE | G_PARAM_STATIC_NICK |
634       G_PARAM_STATIC_BLURB));
635   g_object_class_install_property (object_class, PROP_AUDIO_STREAM,
636       g_param_spec_pointer ("audio-stream", "Audio stream data",
637       "Audio stream data",
638       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
639   g_object_class_install_property (object_class, PROP_VIDEO_STREAM,
640       g_param_spec_pointer ("video-stream", "Video stream data",
641       "Video stream data",
642       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
643 }
644
645 static void
646 empathy_tp_call_init (EmpathyTpCall *call)
647 {
648   EmpathyTpCallPriv *priv = GET_PRIV (call);
649
650   priv->status = EMPATHY_TP_CALL_STATUS_READYING;
651   priv->contact = NULL;
652   priv->stream_engine_running = FALSE;
653   priv->audio = g_slice_new0 (EmpathyTpCallStream);
654   priv->video = g_slice_new0 (EmpathyTpCallStream);
655   priv->audio->exists = FALSE;
656   priv->video->exists = FALSE;
657 }
658
659 EmpathyTpCall *
660 empathy_tp_call_new (TpChannel *channel)
661 {
662   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
663
664   return g_object_new (EMPATHY_TYPE_TP_CALL,
665       "channel", channel,
666       NULL);
667 }
668
669 void
670 empathy_tp_call_accept_incoming_call (EmpathyTpCall *call)
671 {
672   EmpathyTpCallPriv *priv = GET_PRIV (call);
673   EmpathyContact *self_contact;
674
675   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
676   g_return_if_fail (priv->status == EMPATHY_TP_CALL_STATUS_PENDING);
677
678   empathy_debug (DEBUG_DOMAIN, "Accepting incoming call");
679
680   self_contact = empathy_tp_group_get_self_contact (priv->group);
681   empathy_tp_group_add_member (priv->group, self_contact, NULL);
682   g_object_unref (self_contact);
683 }
684
685 void
686 empathy_tp_call_request_video_stream_direction (EmpathyTpCall *call,
687                                                 gboolean is_sending)
688 {
689   EmpathyTpCallPriv *priv = GET_PRIV (call);
690   guint new_direction;
691
692   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
693   g_return_if_fail (priv->status == EMPATHY_TP_CALL_STATUS_ACCEPTED);
694
695   empathy_debug (DEBUG_DOMAIN,
696       "Requesting video stream direction - is_sending: %d", is_sending);
697
698   if (!priv->video->exists)
699     {
700       tp_call_request_streams_for_capabilities (call, EMPATHY_CAPABILITIES_VIDEO);
701       return;
702     }
703
704   if (is_sending)
705       new_direction = priv->video->direction | TP_MEDIA_STREAM_DIRECTION_SEND;
706   else
707       new_direction = priv->video->direction & ~TP_MEDIA_STREAM_DIRECTION_SEND;
708
709   tp_cli_channel_type_streamed_media_call_request_stream_direction (priv->channel,
710       -1, priv->video->id, new_direction,
711       (tp_cli_channel_type_streamed_media_callback_for_request_stream_direction)
712       tp_call_async_cb, NULL, NULL, G_OBJECT (call));
713 }
714
715 void
716 empathy_tp_call_add_preview_video (EmpathyTpCall *call,
717                                    guint preview_video_socket_id)
718 {
719   EmpathyTpCallPriv *priv = GET_PRIV (call);
720
721   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
722   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
723
724   empathy_debug (DEBUG_DOMAIN, "Adding preview video");
725
726   emp_cli_stream_engine_call_add_preview_window (priv->stream_engine, -1,
727       preview_video_socket_id,
728       tp_call_async_cb,
729       "adding preview window", NULL,
730       G_OBJECT (call));
731 }
732
733 void
734 empathy_tp_call_remove_preview_video (EmpathyTpCall *call,
735                                       guint preview_video_socket_id)
736 {
737   EmpathyTpCallPriv *priv = GET_PRIV (call);
738
739   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
740   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
741
742   empathy_debug (DEBUG_DOMAIN, "Removing preview video");
743
744   emp_cli_stream_engine_call_remove_preview_window (priv->stream_engine, -1,
745       preview_video_socket_id,
746       tp_call_async_cb,
747       "removing preview window", NULL,
748       G_OBJECT (call));
749 }
750
751 void
752 empathy_tp_call_add_output_video (EmpathyTpCall *call,
753                                   guint output_video_socket_id)
754 {
755   EmpathyTpCallPriv *priv = GET_PRIV (call);
756
757   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
758   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
759
760   empathy_debug (DEBUG_DOMAIN, "Adding output video - socket: %d",
761       output_video_socket_id);
762
763   emp_cli_stream_engine_call_set_output_window (priv->stream_engine, -1,
764       dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
765       priv->video->id, output_video_socket_id,
766       tp_call_async_cb,
767       "setting output window", NULL,
768       G_OBJECT (call));
769 }
770
771 void
772 empathy_tp_call_set_output_volume (EmpathyTpCall *call,
773                                    guint volume)
774 {
775   EmpathyTpCallPriv *priv = GET_PRIV (call);
776
777   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
778   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
779
780   empathy_debug (DEBUG_DOMAIN, "Setting output volume: %d", volume);
781
782   emp_cli_stream_engine_call_set_output_volume (priv->stream_engine, -1,
783       dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
784       priv->audio->id, volume,
785       tp_call_async_cb,
786       "setting output volume", NULL,
787       G_OBJECT (call));
788 }
789
790 void
791 empathy_tp_call_mute_output (EmpathyTpCall *call,
792                              gboolean is_muted)
793 {
794   EmpathyTpCallPriv *priv = GET_PRIV (call);
795
796   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
797   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
798
799   empathy_debug (DEBUG_DOMAIN, "Setting output mute: %d", is_muted);
800
801   emp_cli_stream_engine_call_mute_output (priv->stream_engine, -1,
802       dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
803       priv->audio->id, is_muted,
804       tp_call_async_cb,
805       "muting output", NULL,
806       G_OBJECT (call));
807 }
808
809 void
810 empathy_tp_call_mute_input (EmpathyTpCall *call,
811                             gboolean is_muted)
812 {
813   EmpathyTpCallPriv *priv = GET_PRIV (call);
814
815   g_return_if_fail (EMPATHY_IS_TP_CALL (call));
816   g_return_if_fail (priv->status != EMPATHY_TP_CALL_STATUS_CLOSED);
817
818   empathy_debug (DEBUG_DOMAIN, "Setting input mute: %d", is_muted);
819
820   emp_cli_stream_engine_call_mute_input (priv->stream_engine, -1,
821       dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
822       priv->audio->id, is_muted,
823       tp_call_async_cb,
824       "muting input", NULL,
825       G_OBJECT (call));
826 }
827