]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-streamed-media.c
individual-menu: remove link-contacts-activated signal
[empathy.git] / libempathy / empathy-tp-streamed-media.c
1 /*
2  * Copyright (C) 2007 Elliot Fairweather
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
20  *          Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <telepathy-glib/proxy-subclass.h>
28 #include <telepathy-glib/dbus.h>
29 #include <telepathy-glib/interfaces.h>
30 #include <telepathy-glib/util.h>
31
32 #include "empathy-tp-streamed-media.h"
33 #include "empathy-tp-contact-factory.h"
34 #include "empathy-utils.h"
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
37 #include "empathy-debug.h"
38
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpStreamedMedia)
40 typedef struct
41 {
42   gboolean dispose_has_run;
43   TpAccount *account;
44   TpChannel *channel;
45   EmpathyContact *contact;
46   gboolean is_incoming;
47   guint status;
48
49   EmpathyTpStreamedMediaStream *audio;
50   EmpathyTpStreamedMediaStream *video;
51 } EmpathyTpStreamedMediaPriv;
52
53 /* signal enum */
54 enum {
55   AUDIO_STREAM_ERROR,
56   VIDEO_STREAM_ERROR,
57   LAST_SIGNAL
58 };
59
60 static guint signals[LAST_SIGNAL] = {0};
61
62 enum
63 {
64   PROP_0,
65   PROP_ACCOUNT,
66   PROP_CHANNEL,
67   PROP_CONTACT,
68   PROP_STATUS,
69   PROP_AUDIO_STREAM,
70   PROP_VIDEO_STREAM
71 };
72
73 G_DEFINE_TYPE (EmpathyTpStreamedMedia, empathy_tp_streamed_media, G_TYPE_OBJECT)
74
75 static void
76 tp_streamed_media_add_stream (EmpathyTpStreamedMedia *call,
77                     guint stream_id,
78                     guint contact_handle,
79                     guint stream_type,
80                     guint stream_state,
81                     guint stream_direction)
82 {
83   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
84
85   switch (stream_type)
86     {
87       case TP_MEDIA_STREAM_TYPE_AUDIO:
88         DEBUG ("Audio stream - id: %d, state: %d, direction: %d",
89             stream_id, stream_state, stream_direction);
90         priv->audio->exists = TRUE;
91         priv->audio->id = stream_id;
92         priv->audio->state = stream_state;
93         priv->audio->direction = stream_direction;
94         g_object_notify (G_OBJECT (call), "audio-stream");
95         break;
96       case TP_MEDIA_STREAM_TYPE_VIDEO:
97         DEBUG ("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         DEBUG ("Unknown stream type: %d", stream_type);
107     }
108 }
109
110 static void
111 tp_streamed_media_stream_added_cb (TpChannel *channel,
112                          guint stream_id,
113                          guint contact_handle,
114                          guint stream_type,
115                          gpointer user_data,
116                          GObject *call)
117 {
118   DEBUG ("Stream added - stream id: %d, contact handle: %d, stream type: %d",
119       stream_id, contact_handle, stream_type);
120
121   tp_streamed_media_add_stream (EMPATHY_TP_STREAMED_MEDIA (call), stream_id, contact_handle,
122       stream_type, TP_MEDIA_STREAM_STATE_DISCONNECTED,
123       TP_MEDIA_STREAM_DIRECTION_NONE);
124 }
125
126 static void
127 tp_streamed_media_stream_removed_cb (TpChannel *channel,
128                            guint stream_id,
129                            gpointer user_data,
130                            GObject *call)
131 {
132   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
133
134   DEBUG ("Stream removed - stream id: %d", stream_id);
135
136   if (stream_id == priv->audio->id)
137     {
138       priv->audio->exists = FALSE;
139       g_object_notify (call, "audio-stream");
140     }
141   else if (stream_id == priv->video->id)
142     {
143       priv->video->exists = FALSE;
144       g_object_notify (call, "video-stream");
145     }
146 }
147
148 static void
149 tp_streamed_media_stream_state_changed_cb (TpChannel *proxy,
150                                  guint stream_id,
151                                  guint stream_state,
152                                  gpointer user_data,
153                                  GObject *call)
154 {
155   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
156
157   DEBUG ("Stream state changed - stream id: %d, state state: %d",
158       stream_id, stream_state);
159
160   if (stream_id == priv->audio->id)
161     {
162       priv->audio->state = stream_state;
163       g_object_notify (call, "audio-stream");
164     }
165   else if (stream_id == priv->video->id)
166     {
167       priv->video->state = stream_state;
168       g_object_notify (call, "video-stream");
169     }
170 }
171
172 static void
173 tp_streamed_media_stream_direction_changed_cb (TpChannel *channel,
174                                      guint stream_id,
175                                      guint stream_direction,
176                                      guint pending_flags,
177                                      gpointer user_data,
178                                      GObject *call)
179 {
180   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
181
182   DEBUG ("Stream direction changed - stream: %d, direction: %d",
183       stream_id, stream_direction);
184
185   if (stream_id == priv->audio->id)
186     {
187       priv->audio->direction = stream_direction;
188       g_object_notify (call, "audio-stream");
189     }
190   else if (stream_id == priv->video->id)
191     {
192       priv->video->direction = stream_direction;
193       g_object_notify (call, "video-stream");
194     }
195 }
196
197 static void
198 tp_streamed_media_request_streams_cb (TpChannel *channel,
199                             const GPtrArray *streams,
200                             const GError *error,
201                             gpointer user_data,
202                             GObject *call)
203 {
204   guint i;
205
206   if (error)
207     {
208       DEBUG ("Error requesting streams: %s", error->message);
209       return;
210     }
211
212   for (i = 0; i < streams->len; i++)
213     {
214       GValueArray *values;
215       guint stream_id;
216       guint contact_handle;
217       guint stream_type;
218       guint stream_state;
219       guint stream_direction;
220
221       values = g_ptr_array_index (streams, i);
222       stream_id = g_value_get_uint (g_value_array_get_nth (values, 0));
223       contact_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
224       stream_type = g_value_get_uint (g_value_array_get_nth (values, 2));
225       stream_state = g_value_get_uint (g_value_array_get_nth (values, 3));
226       stream_direction = g_value_get_uint (g_value_array_get_nth (values, 4));
227
228       tp_streamed_media_add_stream (EMPATHY_TP_STREAMED_MEDIA (call), stream_id, contact_handle,
229           stream_type, stream_state, stream_direction);
230   }
231 }
232
233 static void
234 tp_streamed_media_request_streams_for_capabilities (EmpathyTpStreamedMedia *call,
235                                           EmpathyCapabilities capabilities)
236 {
237   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
238   GArray *stream_types;
239   guint handle;
240   guint stream_type;
241
242   if (capabilities == EMPATHY_CAPABILITIES_UNKNOWN)
243       capabilities = EMPATHY_CAPABILITIES_AUDIO | EMPATHY_CAPABILITIES_VIDEO;
244
245   DEBUG ("Requesting new stream for capabilities %d",
246       capabilities);
247
248   stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
249   handle = empathy_contact_get_handle (priv->contact);
250
251   if (capabilities & EMPATHY_CAPABILITIES_AUDIO)
252     {
253       stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
254       g_array_append_val (stream_types, stream_type);
255     }
256   if (capabilities & EMPATHY_CAPABILITIES_VIDEO)
257     {
258       stream_type = TP_MEDIA_STREAM_TYPE_VIDEO;
259       g_array_append_val (stream_types, stream_type);
260     }
261
262   tp_cli_channel_type_streamed_media_call_request_streams (priv->channel, -1,
263       handle, stream_types, tp_streamed_media_request_streams_cb, NULL, NULL,
264       G_OBJECT (call));
265
266   g_array_unref (stream_types);
267 }
268
269 static void
270 tp_streamed_media_got_contact_cb (TpConnection            *connection,
271                         EmpathyContact          *contact,
272                         const GError            *error,
273                         gpointer                 user_data,
274                         GObject                 *call)
275 {
276   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
277
278   if (error)
279     {
280       DEBUG ("Error: %s", error->message);
281       return;
282     }
283
284   priv->contact = g_object_ref (contact);
285
286   if (priv->status < EMPATHY_TP_STREAMED_MEDIA_STATUS_PENDING)
287     {
288       priv->status = EMPATHY_TP_STREAMED_MEDIA_STATUS_PENDING;
289       g_object_notify (G_OBJECT (call), "status");
290     }
291
292   g_object_notify (G_OBJECT (call), "contact");
293 }
294
295 static void
296 tp_streamed_media_update_status (EmpathyTpStreamedMedia *call)
297 {
298   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
299   TpHandle self_handle;
300   const TpIntSet *set;
301   TpIntSetIter iter;
302
303   g_object_ref (call);
304
305   self_handle = tp_channel_group_get_self_handle (priv->channel);
306   set = tp_channel_group_get_members (priv->channel);
307   tp_intset_iter_init (&iter, set);
308   while (tp_intset_iter_next (&iter))
309     {
310       if (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_PENDING &&
311           ((priv->is_incoming && iter.element == self_handle) ||
312            (!priv->is_incoming && iter.element != self_handle)))
313         {
314           priv->status = EMPATHY_TP_STREAMED_MEDIA_STATUS_ACCEPTED;
315           g_object_notify (G_OBJECT (call), "status");
316         }
317     }
318
319   g_object_unref (call);
320 }
321
322 static void
323 tp_streamed_media_channel_invalidated_cb (TpChannel     *channel,
324                                 GQuark         domain,
325                                 gint           code,
326                                 gchar         *message,
327                                 EmpathyTpStreamedMedia *call)
328 {
329   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
330
331   DEBUG ("Channel invalidated: %s", message);
332   priv->status = EMPATHY_TP_STREAMED_MEDIA_STATUS_CLOSED;
333   g_object_notify (G_OBJECT (call), "status");
334 }
335
336 static void
337 tp_streamed_media_async_cb (TpProxy *proxy,
338                   const GError *error,
339                   gpointer user_data,
340                   GObject *call)
341 {
342   if (error)
343       DEBUG ("Error %s: %s", (gchar *) user_data, error->message);
344 }
345
346 static void
347 tp_streamed_media_stream_error_cb (TpChannel *channel,
348     guint stream_id,
349     guint error_code,
350     const gchar *msg,
351     gpointer user_data,
352     GObject *call)
353 {
354   EmpathyTpStreamedMedia *self = EMPATHY_TP_STREAMED_MEDIA (call);
355   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
356
357   DEBUG ("Stream error on stream %u: %s (code: %u)", stream_id, msg,
358       error_code);
359
360   if (priv->audio->id == stream_id)
361     {
362       g_signal_emit (call, signals[AUDIO_STREAM_ERROR], 0, error_code, msg);
363     }
364   else if (priv->video->id == stream_id)
365     {
366       g_signal_emit (call, signals[VIDEO_STREAM_ERROR], 0, error_code, msg);
367     }
368   else
369     {
370       DEBUG ("Unknown stream id: %u", stream_id);
371     }
372 }
373
374 static GObject *
375 tp_streamed_media_constructor (GType type,
376                      guint n_construct_params,
377                      GObjectConstructParam *construct_params)
378 {
379   GObject *object;
380   EmpathyTpStreamedMedia *call;
381   EmpathyTpStreamedMediaPriv *priv;
382
383   object = G_OBJECT_CLASS (empathy_tp_streamed_media_parent_class)->constructor (type,
384       n_construct_params, construct_params);
385
386   call = EMPATHY_TP_STREAMED_MEDIA (object);
387   priv = GET_PRIV (call);
388
389   /* Setup streamed media channel */
390   g_signal_connect (priv->channel, "invalidated",
391       G_CALLBACK (tp_streamed_media_channel_invalidated_cb), call);
392   tp_cli_channel_type_streamed_media_connect_to_stream_added (priv->channel,
393       tp_streamed_media_stream_added_cb, NULL, NULL, G_OBJECT (call), NULL);
394   tp_cli_channel_type_streamed_media_connect_to_stream_removed (priv->channel,
395       tp_streamed_media_stream_removed_cb, NULL, NULL, G_OBJECT (call), NULL);
396   tp_cli_channel_type_streamed_media_connect_to_stream_state_changed (priv->channel,
397       tp_streamed_media_stream_state_changed_cb, NULL, NULL, G_OBJECT (call), NULL);
398   tp_cli_channel_type_streamed_media_connect_to_stream_direction_changed (priv->channel,
399       tp_streamed_media_stream_direction_changed_cb, NULL, NULL, G_OBJECT (call), NULL);
400   tp_cli_channel_type_streamed_media_connect_to_stream_error (priv->channel,
401       tp_streamed_media_stream_error_cb, NULL, NULL, G_OBJECT (call), NULL);
402   tp_cli_channel_type_streamed_media_call_list_streams (priv->channel, -1,
403       tp_streamed_media_request_streams_cb, NULL, NULL, G_OBJECT (call));
404
405   /* Is the call incoming? */
406   priv->is_incoming = !tp_channel_get_requested (priv->channel);
407
408   /* Get the remote contact */
409   empathy_tp_contact_factory_get_from_handle (
410       tp_channel_borrow_connection (priv->channel),
411       tp_channel_get_handle (priv->channel, NULL), tp_streamed_media_got_contact_cb,
412       NULL, NULL, object);
413
414   /* Update status when members changes */
415   tp_streamed_media_update_status (call);
416   tp_g_signal_connect_object (priv->channel, "group-members-changed",
417       G_CALLBACK (tp_streamed_media_update_status), call, G_CONNECT_SWAPPED);
418
419   return object;
420 }
421 static void
422 tp_streamed_media_dispose (GObject *object)
423 {
424   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (object);
425
426   DEBUG ("Disposing: %p, %d", object, priv->dispose_has_run);
427
428   if (priv->dispose_has_run)
429     return;
430
431   priv->dispose_has_run = TRUE;
432
433   if (priv->channel != NULL)
434     {
435       g_signal_handlers_disconnect_by_func (priv->channel,
436         tp_streamed_media_channel_invalidated_cb, object);
437
438       g_object_unref (priv->channel);
439       priv->channel = NULL;
440     }
441
442   if (priv->contact != NULL)
443       g_object_unref (priv->contact);
444
445   tp_clear_object (&priv->account);
446
447   if (G_OBJECT_CLASS (empathy_tp_streamed_media_parent_class)->dispose)
448     G_OBJECT_CLASS (empathy_tp_streamed_media_parent_class)->dispose (object);
449 }
450
451 static void
452 tp_streamed_media_finalize (GObject *object)
453 {
454   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (object);
455
456   DEBUG ("Finalizing: %p", object);
457
458   g_slice_free (EmpathyTpStreamedMediaStream, priv->audio);
459   g_slice_free (EmpathyTpStreamedMediaStream, priv->video);
460
461   (G_OBJECT_CLASS (empathy_tp_streamed_media_parent_class)->finalize) (object);
462 }
463
464 static void
465 tp_streamed_media_set_property (GObject *object,
466                       guint prop_id,
467                       const GValue *value,
468                       GParamSpec *pspec)
469 {
470   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (object);
471
472   switch (prop_id)
473     {
474     case PROP_ACCOUNT:
475       priv->account = g_value_dup_object (value);
476       break;
477     case PROP_CHANNEL:
478       priv->channel = g_value_dup_object (value);
479       break;
480     default:
481       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
482       break;
483   }
484 }
485
486 static void
487 tp_streamed_media_get_property (GObject *object,
488                       guint prop_id,
489                       GValue *value,
490                       GParamSpec *pspec)
491 {
492   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (object);
493
494   switch (prop_id)
495     {
496     case PROP_ACCOUNT:
497       g_value_set_object (value, priv->channel);
498       break;
499     case PROP_CHANNEL:
500       g_value_set_object (value, priv->channel);
501       break;
502     case PROP_CONTACT:
503       g_value_set_object (value, priv->contact);
504       break;
505     case PROP_STATUS:
506       g_value_set_uint (value, priv->status);
507       break;
508     case PROP_AUDIO_STREAM:
509       g_value_set_pointer (value, priv->audio);
510       break;
511     case PROP_VIDEO_STREAM:
512       g_value_set_pointer (value, priv->video);
513       break;
514     default:
515       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
516       break;
517   }
518 }
519
520 static void
521 empathy_tp_streamed_media_class_init (EmpathyTpStreamedMediaClass *klass)
522 {
523   GObjectClass *object_class = G_OBJECT_CLASS (klass);
524
525   object_class->constructor = tp_streamed_media_constructor;
526   object_class->dispose = tp_streamed_media_dispose;
527   object_class->finalize = tp_streamed_media_finalize;
528   object_class->set_property = tp_streamed_media_set_property;
529   object_class->get_property = tp_streamed_media_get_property;
530
531   g_type_class_add_private (klass, sizeof (EmpathyTpStreamedMediaPriv));
532
533   g_object_class_install_property (object_class, PROP_ACCOUNT,
534       g_param_spec_object ("account", "TpAccount", "TpAccount",
535       TP_TYPE_ACCOUNT,
536       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
537       G_PARAM_STATIC_STRINGS));
538
539   g_object_class_install_property (object_class, PROP_CHANNEL,
540       g_param_spec_object ("channel", "channel", "channel",
541       TP_TYPE_CHANNEL,
542       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
543       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
544
545   g_object_class_install_property (object_class, PROP_CONTACT,
546       g_param_spec_object ("contact", "Call contact", "Call contact",
547       EMPATHY_TYPE_CONTACT,
548       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
549
550   g_object_class_install_property (object_class, PROP_STATUS,
551       g_param_spec_uint ("status", "Call status",
552       "Call status", 0, 255, 0, G_PARAM_READABLE | G_PARAM_STATIC_NICK |
553       G_PARAM_STATIC_BLURB));
554
555   g_object_class_install_property (object_class, PROP_AUDIO_STREAM,
556       g_param_spec_pointer ("audio-stream", "Audio stream data",
557       "Audio stream data",
558       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
559
560   g_object_class_install_property (object_class, PROP_VIDEO_STREAM,
561       g_param_spec_pointer ("video-stream", "Video stream data",
562       "Video stream data",
563       G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
564
565   signals[AUDIO_STREAM_ERROR] =
566     g_signal_new ("audio-stream-error",
567       G_TYPE_FROM_CLASS (klass),
568       G_SIGNAL_RUN_LAST, 0,
569       NULL, NULL,
570       g_cclosure_marshal_generic,
571       G_TYPE_NONE,
572       2, G_TYPE_UINT, G_TYPE_STRING);
573
574   signals[VIDEO_STREAM_ERROR] =
575     g_signal_new ("video-stream-error",
576       G_TYPE_FROM_CLASS (klass),
577       G_SIGNAL_RUN_LAST, 0,
578       NULL, NULL,
579       g_cclosure_marshal_generic,
580       G_TYPE_NONE,
581       2, G_TYPE_UINT, G_TYPE_STRING);
582 }
583
584 static void
585 empathy_tp_streamed_media_init (EmpathyTpStreamedMedia *call)
586 {
587   EmpathyTpStreamedMediaPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (call,
588     EMPATHY_TYPE_TP_STREAMED_MEDIA, EmpathyTpStreamedMediaPriv);
589
590   call->priv = priv;
591   priv->status = EMPATHY_TP_STREAMED_MEDIA_STATUS_READYING;
592   priv->contact = NULL;
593   priv->audio = g_slice_new0 (EmpathyTpStreamedMediaStream);
594   priv->video = g_slice_new0 (EmpathyTpStreamedMediaStream);
595   priv->audio->exists = FALSE;
596   priv->video->exists = FALSE;
597 }
598
599 EmpathyTpStreamedMedia *
600 empathy_tp_streamed_media_new (TpAccount *account,
601     TpChannel *channel)
602 {
603   g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
604   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
605
606   return g_object_new (EMPATHY_TYPE_TP_STREAMED_MEDIA,
607       "account", account,
608       "channel", channel,
609       NULL);
610 }
611
612 void
613 empathy_tp_streamed_media_accept_incoming_call (EmpathyTpStreamedMedia *call)
614 {
615   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
616   TpHandle self_handle;
617   GArray handles = {(gchar *) &self_handle, 1};
618
619   g_return_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call));
620   g_return_if_fail (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_PENDING);
621
622   if (!priv->is_incoming)
623     return;
624
625   DEBUG ("Accepting incoming call");
626
627   self_handle = tp_channel_group_get_self_handle (priv->channel);
628   tp_cli_channel_interface_group_call_add_members (priv->channel, -1,
629       &handles, NULL, NULL, NULL, NULL, NULL);
630 }
631
632 void
633 empathy_tp_streamed_media_close (EmpathyTpStreamedMedia *call)
634 {
635   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
636
637   g_return_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call));
638
639   if (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_CLOSED)
640       return;
641
642   DEBUG ("Closing channel");
643
644   tp_cli_channel_call_close (priv->channel, -1,
645       NULL, NULL, NULL, NULL);
646
647   priv->status = EMPATHY_TP_STREAMED_MEDIA_STATUS_CLOSED;
648   g_object_notify (G_OBJECT (call), "status");
649 }
650
651 void
652 empathy_tp_streamed_media_request_video_stream_direction (EmpathyTpStreamedMedia *call,
653                                                 gboolean is_sending)
654 {
655   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
656   guint new_direction;
657
658   g_return_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call));
659   g_return_if_fail (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_ACCEPTED);
660
661   DEBUG ("Requesting video stream direction - is_sending: %d", is_sending);
662
663   if (!priv->video->exists)
664     {
665       if (is_sending)
666           tp_streamed_media_request_streams_for_capabilities (call,
667               EMPATHY_CAPABILITIES_VIDEO);
668       return;
669     }
670
671   if (is_sending)
672       new_direction = priv->video->direction | TP_MEDIA_STREAM_DIRECTION_SEND;
673   else
674       new_direction = priv->video->direction & ~TP_MEDIA_STREAM_DIRECTION_SEND;
675
676   tp_cli_channel_type_streamed_media_call_request_stream_direction (priv->channel,
677       -1, priv->video->id, new_direction,
678       (tp_cli_channel_type_streamed_media_callback_for_request_stream_direction)
679       tp_streamed_media_async_cb, NULL, NULL, G_OBJECT (call));
680 }
681
682 void
683 empathy_tp_streamed_media_start_tone (EmpathyTpStreamedMedia *call, TpDTMFEvent event)
684 {
685   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
686
687   g_return_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call));
688   g_return_if_fail (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_ACCEPTED);
689
690   if (!priv->audio->exists)
691       return;
692
693   tp_cli_channel_interface_dtmf_call_start_tone (priv->channel, -1,
694       priv->audio->id, event,
695       (tp_cli_channel_interface_dtmf_callback_for_start_tone) tp_streamed_media_async_cb,
696       "starting tone", NULL, G_OBJECT (call));
697 }
698
699 void
700 empathy_tp_streamed_media_stop_tone (EmpathyTpStreamedMedia *call)
701 {
702   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
703
704   g_return_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call));
705   g_return_if_fail (priv->status == EMPATHY_TP_STREAMED_MEDIA_STATUS_ACCEPTED);
706
707   if (!priv->audio->exists)
708       return;
709
710   tp_cli_channel_interface_dtmf_call_stop_tone (priv->channel, -1,
711       priv->audio->id,
712       (tp_cli_channel_interface_dtmf_callback_for_stop_tone) tp_streamed_media_async_cb,
713       "stoping tone", NULL, G_OBJECT (call));
714 }
715
716 gboolean
717 empathy_tp_streamed_media_has_dtmf (EmpathyTpStreamedMedia *call)
718 {
719   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
720
721   g_return_val_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call), FALSE);
722
723   return tp_proxy_has_interface_by_id (priv->channel,
724       TP_IFACE_QUARK_CHANNEL_INTERFACE_DTMF);
725 }
726
727 /**
728  * empathy_tp_streamed_media_is_receiving_video:
729  * @call: the call
730  *
731  * Indicates if the call is receiving video or not.
732  *
733  * Returns: %TRUE if the call is currently receiving video, %FALSE otherwise.
734  */
735 gboolean
736 empathy_tp_streamed_media_is_receiving_video (EmpathyTpStreamedMedia *call)
737 {
738   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
739
740   g_return_val_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call), FALSE);
741
742   if (!priv->video->exists)
743     return FALSE;
744
745   return priv->video->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE ?
746       TRUE : FALSE;
747 }
748
749 /**
750  * empathy_tp_streamed_media_is_sending_video:
751  * @call: the call
752  *
753  * Indicates if the call is sending video or not.
754  *
755  * Returns: %TRUE if the call is currently sending video, %FALSE otherwise.
756  */
757 gboolean
758 empathy_tp_streamed_media_is_sending_video (EmpathyTpStreamedMedia *call)
759 {
760   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (call);
761
762   g_return_val_if_fail (EMPATHY_IS_TP_STREAMED_MEDIA (call), FALSE);
763
764   if (!priv->video->exists)
765     return FALSE;
766
767   return priv->video->direction & TP_MEDIA_STREAM_DIRECTION_SEND ?
768       TRUE : FALSE;
769 }
770
771 const gchar *
772 empathy_tp_streamed_media_get_connection_manager (EmpathyTpStreamedMedia *self)
773 {
774   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
775
776   return tp_account_get_connection_manager (priv->account);
777 }
778
779 gboolean
780 empathy_tp_streamed_media_has_initial_video (EmpathyTpStreamedMedia *self)
781 {
782   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
783   GHashTable *props;
784   gboolean initial_video;
785   gboolean valid;
786
787   if (priv->channel == NULL)
788     return FALSE;
789
790   g_object_get (priv->channel, "channel-properties", &props, NULL);
791
792   initial_video = tp_asv_get_boolean (props,
793     TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", &valid);
794   if (!valid)
795     initial_video = FALSE;
796
797   g_hash_table_unref (props);
798   return initial_video;
799 }
800
801 static void
802 leave_remove_members_cb (TpChannel *proxy,
803     const GError *error,
804     gpointer user_data,
805     GObject *weak_object)
806 {
807   EmpathyTpStreamedMedia *self = user_data;
808
809   if (error == NULL)
810     return;
811
812   DEBUG ("RemoveMembers failed (%s); closing the channel", error->message);
813   empathy_tp_streamed_media_close (self);
814 }
815
816 void
817 empathy_tp_streamed_media_leave (EmpathyTpStreamedMedia *self)
818 {
819   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
820   TpHandle self_handle;
821   GArray array = { (gchar *) &self_handle, 1 };
822
823   if (!tp_proxy_has_interface_by_id (priv->channel,
824         TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP))
825     {
826       empathy_tp_streamed_media_close (self);
827       return;
828     }
829
830   self_handle = tp_channel_group_get_self_handle (priv->channel);
831   if (self_handle == 0)
832     {
833       /* we are not member of the channel */
834       empathy_tp_streamed_media_close (self);
835       return;
836     }
837
838   tp_cli_channel_interface_group_call_remove_members (priv->channel, -1, &array,
839       "", leave_remove_members_cb, self, NULL, G_OBJECT (self));
840 }
841
842 EmpathyTpStreamedMediaStatus
843 empathy_tp_streamed_media_get_status (EmpathyTpStreamedMedia *self)
844 {
845   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
846
847   return priv->status;
848 }
849
850 TpAccount *
851 empathy_tp_streamed_media_get_account (EmpathyTpStreamedMedia *self)
852 {
853   EmpathyTpStreamedMediaPriv *priv = GET_PRIV (self);
854
855   return priv->account;
856 }