~ubuntu-branches/ubuntu/natty/empathy/natty-updates

« back to all changes in this revision

Viewing changes to libempathy/empathy-tp-call.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2008-03-10 16:39:07 UTC
  • mfrom: (1.1.13 upstream)
  • Revision ID: james.westby@ubuntu.com-20080310163907-tv41g2zmf0qqgi85
Tags: 0.22.0-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
1
/*
3
 
 * Copyright (C) 2007 Elliot Fairweather
4
 
 * Copyright (C) 2007 Collabora Ltd.
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
 
 * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
21
 
 *          Xavier Claessens <xclaesse@gmail.com>
 
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>
22
19
 */
23
20
 
24
 
#include "config.h"
 
21
#include <string.h>
 
22
#include <dbus/dbus-glib.h>
25
23
 
26
24
#include <libtelepathy/tp-chan-type-streamed-media-gen.h>
 
25
#include <libtelepathy/tp-connmgr.h>
27
26
#include <libtelepathy/tp-helpers.h>
28
 
#include <libtelepathy/tp-conn.h>
29
 
 
30
 
#include <libmissioncontrol/mission-control.h>
 
27
#include <telepathy-glib/proxy-subclass.h>
 
28
#include <telepathy-glib/dbus.h>
 
29
 
 
30
#include <libmissioncontrol/mc-account.h>
 
31
 
 
32
#include <extensions/extensions.h>
 
33
#include <libempathy/empathy-contact-factory.h>
 
34
#include <libempathy/empathy-debug.h>
 
35
#include <libempathy/empathy-tp-group.h>
 
36
#include <libempathy/empathy-utils.h>
31
37
 
32
38
#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"
38
39
 
39
40
#define DEBUG_DOMAIN "TpCall"
40
41
 
41
 
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_TP_CALL, EmpathyTpCallPriv))
 
42
#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE \
 
43
    ((object), EMPATHY_TYPE_TP_CALL, EmpathyTpCallPriv))
42
44
 
43
45
#define STREAM_ENGINE_BUS_NAME "org.freedesktop.Telepathy.StreamEngine"
44
46
#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"
47
47
 
48
48
typedef struct _EmpathyTpCallPriv EmpathyTpCallPriv;
49
49
 
50
 
struct _EmpathyTpCallPriv {
51
 
        TpChan              *tp_chan;
52
 
        DBusGProxy          *streamed_iface;
53
 
        DBusGProxy          *se_ch_proxy;
54
 
        DBusGProxy          *se_proxy;
55
 
        McAccount           *account;
56
 
        EmpathyTpGroup      *group;
57
 
        EmpathyContact      *contact;
58
 
        EmpathyTpCallStatus  status;
59
 
        gboolean             is_incoming;
60
 
        guint                audio_stream;
61
 
        guint                video_stream;
62
 
};
63
 
 
64
 
static void empathy_tp_call_class_init (EmpathyTpCallClass *klass);
65
 
static void empathy_tp_call_init       (EmpathyTpCall      *call);
66
 
 
67
 
enum {
68
 
        PROP_0,
69
 
        PROP_ACCOUNT,
70
 
        PROP_TP_CHAN,
71
 
        PROP_STATUS
72
 
};
73
 
 
74
 
enum {
75
 
        DESTROY,
76
 
        LAST_SIGNAL
 
50
struct _EmpathyTpCallPriv
 
51
{
 
52
  TpConn *connection;
 
53
  TpChan *channel;
 
54
  TpProxy *stream_engine;
 
55
  TpDBusDaemon *dbus_daemon;
 
56
  EmpathyTpGroup *group;
 
57
  EmpathyContact *contact;
 
58
  gboolean is_incoming;
 
59
  guint status;
 
60
 
 
61
  EmpathyTpCallStream *audio;
 
62
  EmpathyTpCallStream *video;
 
63
};
 
64
 
 
65
enum
 
66
{
 
67
  STATUS_CHANGED_SIGNAL,
 
68
  RECEIVING_VIDEO_SIGNAL,
 
69
  SENDING_VIDEO_SIGNAL,
 
70
  LAST_SIGNAL
 
71
};
 
72
 
 
73
enum
 
74
{
 
75
  PROP_0,
 
76
  PROP_CONNECTION,
 
77
  PROP_CHANNEL,
 
78
  PROP_CONTACT,
 
79
  PROP_IS_INCOMING,
 
80
  PROP_STATUS,
 
81
  PROP_AUDIO_STREAM,
 
82
  PROP_VIDEO_STREAM
77
83
};
78
84
 
79
85
static guint signals[LAST_SIGNAL];
81
87
G_DEFINE_TYPE (EmpathyTpCall, empathy_tp_call, G_TYPE_OBJECT)
82
88
 
83
89
static void
84
 
tp_call_set_status (EmpathyTpCall       *call,
85
 
                    EmpathyTpCallStatus  status)
86
 
{
87
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
88
 
 
89
 
        priv->status = status;
90
 
        g_object_notify (G_OBJECT (call), "status");
91
 
}
92
 
 
93
 
static void
94
 
tp_call_set_property (GObject      *object,
95
 
                      guint         prop_id,
96
 
                      const GValue *value,
97
 
                      GParamSpec   *pspec)
98
 
{
99
 
        EmpathyTpCallPriv *priv = GET_PRIV (object);
100
 
 
101
 
        switch (prop_id) {
102
 
        case PROP_ACCOUNT:
103
 
                priv->account = g_object_ref (g_value_get_object (value));
104
 
                break;
105
 
        case PROP_TP_CHAN:
106
 
                priv->tp_chan = g_object_ref (g_value_get_object (value));
107
 
                break;
108
 
        case PROP_STATUS:
109
 
                tp_call_set_status (EMPATHY_TP_CALL (object),
110
 
                                    g_value_get_enum (value));
111
 
                break;
112
 
        default:
113
 
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114
 
                break;
115
 
        }
116
 
}
117
 
 
118
 
static void
119
 
tp_call_get_property (GObject    *object,
120
 
                      guint       prop_id,
121
 
                      GValue     *value,
122
 
                      GParamSpec *pspec)
123
 
{
124
 
  EmpathyTpCallPriv *priv = GET_PRIV (object);
125
 
 
126
 
        switch (prop_id) {
127
 
        case PROP_ACCOUNT:
128
 
                g_value_set_object (value, priv->account);
129
 
                break;
130
 
        case PROP_TP_CHAN:
131
 
                g_value_set_object (value, priv->tp_chan);
132
 
                break;
133
 
        case PROP_STATUS:
134
 
                g_value_set_enum (value, priv->status);
135
 
                break;
136
 
        default:
137
 
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138
 
                break;
139
 
        }
140
 
}
141
 
 
142
 
static void
143
 
tp_call_destroy_cb (TpChan        *call_chan,
144
 
                    EmpathyTpCall *call)
145
 
{
146
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
147
 
 
148
 
        empathy_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
149
 
 
150
 
        g_object_unref  (priv->tp_chan);
151
 
        priv->tp_chan = NULL;
152
 
        priv->streamed_iface = NULL;
153
 
 
154
 
        g_signal_emit (call, signals[DESTROY], 0);
155
 
}
156
 
 
157
 
static void
158
 
tp_call_closed_cb (TpChan        *call_chan,
159
 
                   EmpathyTpCall *call)
160
 
{
161
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
162
 
 
163
 
        /* The channel is closed, do just like if the proxy was destroyed */
164
 
        g_signal_handlers_disconnect_by_func (priv->tp_chan,
165
 
                                              tp_call_destroy_cb,
166
 
                                              call);
167
 
        tp_call_destroy_cb (call_chan, call);
168
 
}
169
 
 
170
 
static void
171
 
tp_call_stream_added_cb (DBusGProxy    *streamed_iface,
172
 
                         guint          stream_id,
173
 
                         guint          contact_handle,
174
 
                         guint          stream_type,
175
 
                         EmpathyTpCall *call)
176
 
{
177
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
178
 
 
179
 
        empathy_debug (DEBUG_DOMAIN, "Stream added: id=%d, stream_type=%d",
180
 
                       stream_id, stream_type);
181
 
 
182
 
        switch (stream_type) {
183
 
        case TP_MEDIA_STREAM_TYPE_AUDIO:
184
 
                priv->audio_stream = stream_id;
185
 
                break;
186
 
        case TP_MEDIA_STREAM_TYPE_VIDEO:
187
 
                priv->video_stream = stream_id;
188
 
                break;
189
 
        default:
190
 
                empathy_debug (DEBUG_DOMAIN, "Unknown stream type: %d", stream_type);
191
 
        }
192
 
}
193
 
 
194
 
 
195
 
static void
196
 
tp_call_stream_removed_cb (DBusGProxy    *streamed_iface,
197
 
                           guint          stream_id,
198
 
                           EmpathyTpCall *call)
199
 
{
200
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
201
 
 
202
 
        empathy_debug (DEBUG_DOMAIN, "Stream removed: %d", stream_id);
203
 
 
204
 
        if (stream_id == priv->audio_stream) {
205
 
                priv->audio_stream = 0;
206
 
        }
207
 
        else if (stream_id == priv->video_stream) {
208
 
                priv->video_stream = 0;
209
 
        }
210
 
}
211
 
 
212
 
static void
213
 
tp_call_list_streams_cb (DBusGProxy *proxy,
214
 
                         GPtrArray  *streams,
215
 
                         GError     *error,
216
 
                         gpointer    user_data)
217
 
{
218
 
        guint i;
219
 
 
220
 
        if (error) {
221
 
                empathy_debug (DEBUG_DOMAIN, "Failed to list streams: %s",
222
 
                               error->message);
223
 
                return;
224
 
        }
225
 
 
226
 
        for (i = 0; i < streams->len; i++) {
227
 
                GValueArray *values;
228
 
                guint        stream_id;
229
 
                guint        contact_handle;
230
 
                guint        stream_type;
231
 
 
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));
236
 
 
237
 
                tp_call_stream_added_cb (proxy,
238
 
                                         stream_id,
239
 
                                         contact_handle,
240
 
                                         stream_type,
241
 
                                         user_data);
242
 
        }
 
90
tp_call_stream_state_changed_cb (DBusGProxy *channel,
 
91
                                 guint stream_id,
 
92
                                 guint stream_state,
 
93
                                 EmpathyTpCall *call)
 
94
{
 
95
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
96
 
 
97
  empathy_debug (DEBUG_DOMAIN,
 
98
      "Stream state changed - stream id: %d, state state: %d",
 
99
      stream_id, stream_state);
 
100
 
 
101
  if (stream_id == priv->audio->id)
 
102
    {
 
103
      priv->audio->state = stream_state;
 
104
    }
 
105
  else if (stream_id == priv->video->id)
 
106
    {
 
107
      priv->video->state = stream_state;
 
108
      if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED)
 
109
      {
 
110
        if (priv->video->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE)
 
111
          {
 
112
            empathy_debug (DEBUG_DOMAIN, "RECEIVING");
 
113
            g_signal_emit (call, signals[RECEIVING_VIDEO_SIGNAL], 0, TRUE);
 
114
          }
 
115
        if (priv->video->direction & TP_MEDIA_STREAM_DIRECTION_SEND)
 
116
          {
 
117
            empathy_debug (DEBUG_DOMAIN, "SENDING");
 
118
            g_signal_emit (call, signals[SENDING_VIDEO_SIGNAL], 0, TRUE);
 
119
          }
 
120
      }
 
121
    }
 
122
 
 
123
  g_signal_emit (call, signals[STATUS_CHANGED_SIGNAL], 0);
 
124
}
 
125
 
 
126
static void
 
127
tp_call_identify_streams (EmpathyTpCall *call)
 
128
{
 
129
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
130
  GPtrArray *stream_infos;
 
131
  DBusGProxy *streamed_iface;
 
132
  GError *error = NULL;
 
133
  guint i;
 
134
 
 
135
  empathy_debug (DEBUG_DOMAIN, "Identifying audio/video streams");
 
136
 
 
137
  streamed_iface = tp_chan_get_interface (priv->channel,
 
138
      TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
 
139
 
 
140
  if (!tp_chan_type_streamed_media_list_streams (streamed_iface, &stream_infos,
 
141
        &error))
 
142
    {
 
143
      empathy_debug (DEBUG_DOMAIN, "Couldn't list audio/video streams: %s",
 
144
          error->message);
 
145
      g_clear_error (&error);
 
146
      return;
 
147
    }
 
148
 
 
149
  for (i = 0; i < stream_infos->len; i++)
 
150
    {
 
151
      GValueArray *values;
 
152
      guint stream_id;
 
153
      guint stream_handle;
 
154
      guint stream_type;
 
155
      guint stream_state;
 
156
      guint stream_direction;
 
157
 
 
158
      values = g_ptr_array_index (stream_infos, i);
 
159
      stream_id = g_value_get_uint (g_value_array_get_nth (values, 0));
 
160
      stream_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
 
161
      stream_type = g_value_get_uint (g_value_array_get_nth (values, 2));
 
162
      stream_state = g_value_get_uint (g_value_array_get_nth (values, 3));
 
163
      stream_direction = g_value_get_uint (g_value_array_get_nth (values, 4));
 
164
 
 
165
      switch (stream_type)
 
166
        {
 
167
        case TP_MEDIA_STREAM_TYPE_AUDIO:
 
168
          empathy_debug (DEBUG_DOMAIN,
 
169
              "Audio stream - id: %d, state: %d, direction: %d",
 
170
              stream_id, stream_state, stream_direction);
 
171
          priv->audio->exists = TRUE;
 
172
          priv->audio->id = stream_id;
 
173
          priv->audio->state = stream_state;
 
174
          priv->audio->direction = stream_direction;
 
175
          break;
 
176
        case TP_MEDIA_STREAM_TYPE_VIDEO:
 
177
          empathy_debug (DEBUG_DOMAIN,
 
178
              "Video stream - id: %d, state: %d, direction: %d",
 
179
              stream_id, stream_state, stream_direction);
 
180
          priv->video->exists = TRUE;
 
181
          priv->video->id = stream_id;
 
182
          priv->video->state = stream_state;
 
183
          priv->video->direction = stream_direction;
 
184
          break;
 
185
        default:
 
186
          empathy_debug (DEBUG_DOMAIN, "Unknown stream type: %d",
 
187
              stream_type);
 
188
        }
 
189
 
 
190
      g_value_array_free (values);
 
191
    }
 
192
}
 
193
 
 
194
static void
 
195
tp_call_stream_added_cb (DBusGProxy *channel,
 
196
                         guint stream_id,
 
197
                         guint contact_handle,
 
198
                         guint stream_type,
 
199
                         EmpathyTpCall *call)
 
200
{
 
201
  empathy_debug (DEBUG_DOMAIN,
 
202
      "Stream added - stream id: %d, contact handle: %d, stream type: %d",
 
203
      stream_id, contact_handle, stream_type);
 
204
 
 
205
  tp_call_identify_streams (call);
 
206
}
 
207
 
 
208
 
 
209
static void
 
210
tp_call_stream_removed_cb (DBusGProxy *channel,
 
211
                           guint stream_id,
 
212
                           EmpathyTpCall *call)
 
213
{
 
214
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
215
 
 
216
  empathy_debug (DEBUG_DOMAIN, "Stream removed - stream id: %d", stream_id);
 
217
 
 
218
  if (stream_id == priv->audio->id)
 
219
    {
 
220
      priv->audio->exists = FALSE;
 
221
    }
 
222
  else if (stream_id == priv->video->id)
 
223
    {
 
224
      priv->video->exists = FALSE;
 
225
    }
 
226
}
 
227
 
 
228
static void
 
229
tp_call_channel_closed_cb (TpChan *channel,
 
230
                           EmpathyTpCall *call)
 
231
{
 
232
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
233
  DBusGProxy *streamed_iface;
 
234
  DBusGProxy *group_iface;
 
235
 
 
236
  empathy_debug (DEBUG_DOMAIN, "Channel closed");
 
237
 
 
238
  priv->status = EMPATHY_TP_CALL_STATUS_CLOSED;
 
239
  g_signal_emit (call, signals[STATUS_CHANGED_SIGNAL], 0);
 
240
 
 
241
  streamed_iface = tp_chan_get_interface (priv->channel,
 
242
      TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
 
243
  group_iface = tp_chan_get_interface (priv->channel,
 
244
      TELEPATHY_CHAN_IFACE_GROUP_QUARK);
 
245
 
 
246
  dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->channel), "Closed",
 
247
      G_CALLBACK (tp_call_channel_closed_cb), (gpointer) call);
 
248
  dbus_g_proxy_disconnect_signal (streamed_iface, "StreamStateChanged",
 
249
      G_CALLBACK (tp_call_stream_state_changed_cb), (gpointer) call);
 
250
  dbus_g_proxy_disconnect_signal (streamed_iface, "StreamAdded",
 
251
      G_CALLBACK (tp_call_stream_added_cb), (gpointer) call);
 
252
  dbus_g_proxy_disconnect_signal (streamed_iface, "StreamRemoved",
 
253
      G_CALLBACK (tp_call_stream_removed_cb), (gpointer) call);
 
254
}
 
255
 
 
256
static void
 
257
tp_call_stream_direction_changed_cb (DBusGProxy *channel,
 
258
                                     guint stream_id,
 
259
                                     guint stream_direction,
 
260
                                     guint flags,
 
261
                                     EmpathyTpCall *call)
 
262
{
 
263
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
264
 
 
265
  empathy_debug (DEBUG_DOMAIN,
 
266
      "Stream direction changed - stream: %d, direction: %d",
 
267
      stream_id, stream_direction);
 
268
 
 
269
  if (stream_id == priv->audio->id)
 
270
    {
 
271
      priv->audio->direction = stream_direction;
 
272
    }
 
273
  else if (stream_id == priv->video->id)
 
274
    {
 
275
      priv->video->direction = stream_direction;
 
276
 
 
277
      if (stream_direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE)
 
278
        {
 
279
          empathy_debug (DEBUG_DOMAIN, "RECEIVING");
 
280
          g_signal_emit (call, signals[RECEIVING_VIDEO_SIGNAL], 0, TRUE);
 
281
        }
 
282
      else
 
283
        {
 
284
          empathy_debug (DEBUG_DOMAIN, "NOT RECEIVING");
 
285
          g_signal_emit (call, signals[RECEIVING_VIDEO_SIGNAL], 0, FALSE);
 
286
        }
 
287
 
 
288
      if (stream_direction & TP_MEDIA_STREAM_DIRECTION_SEND)
 
289
        {
 
290
          empathy_debug (DEBUG_DOMAIN, "SENDING");
 
291
          g_signal_emit (call, signals[SENDING_VIDEO_SIGNAL], 0, TRUE);
 
292
        }
 
293
      else
 
294
        {
 
295
          empathy_debug (DEBUG_DOMAIN, "NOT SENDING");
 
296
          g_signal_emit (call, signals[SENDING_VIDEO_SIGNAL], 0, FALSE);
 
297
        }
 
298
    }
 
299
}
 
300
 
 
301
static void
 
302
tp_call_request_streams_for_capabilities (EmpathyTpCall *call,
 
303
                                          EmpathyCapabilities capabilities)
 
304
{
 
305
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
306
  DBusGProxy *streamed_iface;
 
307
  GArray *stream_types;
 
308
  guint handle;
 
309
  guint stream_type;
 
310
  GError *error = NULL;
 
311
 
 
312
  empathy_debug (DEBUG_DOMAIN, "Requesting new stream for capabilities %d",
 
313
      capabilities);
 
314
 
 
315
  streamed_iface = tp_chan_get_interface (priv->channel,
 
316
      TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
 
317
  stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
 
318
  handle = empathy_contact_get_handle (priv->contact);
 
319
 
 
320
  if (capabilities & EMPATHY_CAPABILITIES_AUDIO)
 
321
    {
 
322
      stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
 
323
      g_array_append_val (stream_types, stream_type);
 
324
    }
 
325
  if (capabilities & EMPATHY_CAPABILITIES_VIDEO)
 
326
    {
 
327
      stream_type = TP_MEDIA_STREAM_TYPE_VIDEO;
 
328
      g_array_append_val (stream_types, stream_type);
 
329
    }
 
330
 
 
331
  if (!tp_chan_type_streamed_media_request_streams (streamed_iface, handle,
 
332
        stream_types, NULL, &error))
 
333
    {
 
334
      empathy_debug (DEBUG_DOMAIN, "Couldn't request new stream: %s",
 
335
          error->message);
 
336
      g_clear_error (&error);
 
337
    }
 
338
 
 
339
  g_array_free (stream_types, TRUE);
 
340
}
 
341
 
 
342
static void
 
343
tp_call_request_streams_capabilities_cb (EmpathyContact *contact,
 
344
                                         GParamSpec *property,
 
345
                                         gpointer user_data)
 
346
{
 
347
  EmpathyTpCall *call = EMPATHY_TP_CALL (user_data);
 
348
 
 
349
  g_signal_handlers_disconnect_by_func (contact,
 
350
      tp_call_request_streams_capabilities_cb,
 
351
      user_data);
 
352
 
 
353
  tp_call_request_streams_for_capabilities (call,
 
354
     empathy_contact_get_capabilities (contact));
 
355
}
 
356
 
 
357
static void
 
358
tp_call_request_streams (EmpathyTpCall *call)
 
359
{
 
360
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
361
  EmpathyCapabilities capabilities;
 
362
  DBusGProxy *capabilities_iface;
 
363
 
 
364
  empathy_debug (DEBUG_DOMAIN,
 
365
      "Requesting appropriate audio/video streams from contact");
 
366
 
 
367
 
 
368
  /* FIXME: SIP don't have capabilities interface but we know it supports
 
369
   *        only audio and not video. */
 
370
  capabilities_iface = tp_conn_get_interface (priv->connection,
 
371
      TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES);
 
372
  if (!capabilities_iface)
 
373
    {
 
374
      capabilities = EMPATHY_CAPABILITIES_AUDIO;
 
375
    }
 
376
  else
 
377
    {
 
378
      capabilities = empathy_contact_get_capabilities (priv->contact);
 
379
      if (capabilities == EMPATHY_CAPABILITIES_UNKNOWN)
 
380
        {
 
381
          g_signal_connect (priv->contact, "notify::capabilities",
 
382
              G_CALLBACK (tp_call_request_streams_capabilities_cb), call);
 
383
          return;
 
384
        }
 
385
    }
 
386
 
 
387
  tp_call_request_streams_for_capabilities (call, capabilities);
 
388
}
 
389
 
 
390
static void
 
391
tp_call_is_ready (EmpathyTpCall *call)
 
392
{
 
393
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
394
  EmpathyContact *self_contact;
 
395
  GList *members;
 
396
  GList *local_pendings;
 
397
  GList *remote_pendings;
 
398
 
 
399
  if (priv->status > EMPATHY_TP_CALL_STATUS_READYING)
 
400
    return;
 
401
 
 
402
  members = empathy_tp_group_get_members (priv->group);
 
403
  if (!members)
 
404
    return;
 
405
 
 
406
  self_contact = empathy_tp_group_get_self_contact (priv->group);
 
407
  local_pendings = empathy_tp_group_get_local_pendings (priv->group);
 
408
  remote_pendings = empathy_tp_group_get_remote_pendings (priv->group);
 
409
 
 
410
  if (local_pendings &&
 
411
      empathy_contact_equal (EMPATHY_CONTACT (((EmpathyPendingInfo *)
 
412
            local_pendings->data)->member), self_contact))
 
413
    {
 
414
      empathy_debug (DEBUG_DOMAIN,
 
415
          "Incoming call is ready - %p",
 
416
          ((EmpathyPendingInfo *) local_pendings->data)->member);
 
417
      priv->is_incoming = TRUE;
 
418
      priv->contact = g_object_ref (members->data);
 
419
    }
 
420
  else if (remote_pendings &&
 
421
      empathy_contact_equal (EMPATHY_CONTACT (members->data), self_contact))
 
422
    {
 
423
      empathy_debug (DEBUG_DOMAIN,
 
424
          "Outgoing call is ready - %p", remote_pendings->data);
 
425
      priv->is_incoming = FALSE;
 
426
      priv->contact = g_object_ref (remote_pendings->data);
 
427
      tp_call_request_streams (call);
 
428
    }
 
429
 
 
430
  g_object_unref (self_contact);
 
431
  g_list_foreach (members, (GFunc) g_object_unref, NULL);
 
432
  g_list_free (members);
 
433
  g_list_foreach (local_pendings, (GFunc) empathy_pending_info_free, NULL);
 
434
  g_list_free (local_pendings);
 
435
  g_list_foreach (remote_pendings, (GFunc) g_object_unref, NULL);
 
436
  g_list_free (remote_pendings);
 
437
 
 
438
  if (priv->contact)
 
439
    {
 
440
      priv->status = EMPATHY_TP_CALL_STATUS_PENDING;
 
441
      g_signal_emit (call, signals[STATUS_CHANGED_SIGNAL], 0);
 
442
    }
243
443
}
244
444
 
245
445
static void
246
446
tp_call_member_added_cb (EmpathyTpGroup *group,
247
 
                         EmpathyContact *contact,
248
 
                         EmpathyContact *actor,
249
 
                         guint           reason,
250
 
                         const gchar    *message,
251
 
                         EmpathyTpCall  *call)
252
 
{
253
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
254
 
 
255
 
        empathy_debug (DEBUG_DOMAIN, "Members added %s (%d)",
256
 
                       empathy_contact_get_id (contact),
257
 
                       empathy_contact_get_handle (contact));
258
 
 
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);
264
 
                }
265
 
                return;
266
 
        }
267
 
 
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);
 
447
                         EmpathyContact *contact,
 
448
                         EmpathyContact *actor,
 
449
                         guint reason,
 
450
                         const gchar *message,
 
451
                         EmpathyTpCall *call)
 
452
{
 
453
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
454
 
 
455
  empathy_debug (DEBUG_DOMAIN, "New member added callback %p", contact);
 
456
  tp_call_is_ready (call);
 
457
 
 
458
  if (priv->status == EMPATHY_TP_CALL_STATUS_PENDING)
 
459
    {
 
460
      if ((priv->is_incoming &&
 
461
            !empathy_contact_equal (contact, priv->contact))
 
462
          || (!priv->is_incoming &&
 
463
            empathy_contact_equal (contact, priv->contact)))
 
464
        {
 
465
          priv->status = EMPATHY_TP_CALL_STATUS_ACCEPTED;
 
466
          g_signal_emit (call, signals[STATUS_CHANGED_SIGNAL], 0);
 
467
        }
 
468
    }
 
469
}
 
470
 
 
471
static void
 
472
tp_call_local_pending_cb (EmpathyTpGroup *group,
 
473
                          EmpathyContact *contact,
 
474
                          EmpathyContact *actor,
 
475
                          guint reason,
 
476
                          const gchar *message,
 
477
                          EmpathyTpCall *call)
 
478
{
 
479
  empathy_debug (DEBUG_DOMAIN, "New local pending added callback %p", contact);
 
480
  tp_call_is_ready (call);
271
481
}
272
482
 
273
483
static void
274
484
tp_call_remote_pending_cb (EmpathyTpGroup *group,
275
 
                           EmpathyContact *contact,
276
 
                           EmpathyContact *actor,
277
 
                           guint           reason,
278
 
                           const gchar    *message,
279
 
                           EmpathyTpCall  *call)
280
 
{
281
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
282
 
 
283
 
        empathy_debug (DEBUG_DOMAIN, "Remote pending: %s (%d)",
284
 
                       empathy_contact_get_id (contact),
285
 
                       empathy_contact_get_handle (contact));
286
 
 
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);
291
 
        }
292
 
}
293
 
 
294
 
static void
295
 
tp_call_async_cb (DBusGProxy *proxy,
296
 
                  GError     *error,
297
 
                  gpointer    user_data)
298
 
{
299
 
        if (error) {
300
 
                empathy_debug (DEBUG_DOMAIN, "Failed to %s: %s",
301
 
                               user_data,
302
 
                               error->message);
303
 
        }
 
485
                           EmpathyContact *contact,
 
486
                           EmpathyContact *actor,
 
487
                           guint reason,
 
488
                           const gchar *message,
 
489
                           EmpathyTpCall *call)
 
490
{
 
491
  empathy_debug (DEBUG_DOMAIN, "New remote pending added callback %p", contact);
 
492
  tp_call_is_ready (call);
 
493
}
 
494
 
 
495
static void
 
496
tp_call_async_cb (TpProxy *proxy,
 
497
                  const GError *error,
 
498
                  gpointer user_data,
 
499
                  GObject *call)
 
500
{
 
501
  if (error)
 
502
    {
 
503
      empathy_debug (DEBUG_DOMAIN, "Error %s: %s",
 
504
          user_data, error->message);
 
505
    }
 
506
}
 
507
 
 
508
static void
 
509
tp_call_invalidated_cb (TpProxy       *stream_engine,
 
510
                        GQuark         domain,
 
511
                        gint           code,
 
512
                        gchar         *message,
 
513
                        EmpathyTpCall *call)
 
514
{
 
515
  empathy_debug (DEBUG_DOMAIN, "Stream engine proxy invalidated: %s",
 
516
      message);
 
517
  empathy_tp_call_close_channel (call);
 
518
}
 
519
 
 
520
static void
 
521
tp_call_watch_name_owner_cb (TpDBusDaemon *daemon,
 
522
                             const gchar *name,
 
523
                             const gchar *new_owner,
 
524
                             gpointer call)
 
525
{
 
526
  if (G_STR_EMPTY (new_owner))
 
527
    {
 
528
      empathy_debug (DEBUG_DOMAIN, "Stream engine falled off the bus");
 
529
      empathy_tp_call_close_channel (call);
 
530
    }
 
531
}
 
532
 
 
533
static void
 
534
tp_call_start_stream_engine (EmpathyTpCall *call)
 
535
{
 
536
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
537
 
 
538
  empathy_debug (DEBUG_DOMAIN, "Revving up the stream engine");
 
539
 
 
540
  priv->stream_engine = g_object_new (TP_TYPE_PROXY,
 
541
      "bus-name", STREAM_ENGINE_BUS_NAME,
 
542
      "dbus-connection", tp_get_bus (),
 
543
      "object-path", STREAM_ENGINE_OBJECT_PATH,
 
544
       NULL);
 
545
  tp_proxy_add_interface_by_id (priv->stream_engine,
 
546
      EMP_IFACE_QUARK_STREAM_ENGINE);
 
547
  tp_proxy_add_interface_by_id (priv->stream_engine,
 
548
      EMP_IFACE_QUARK_CHANNEL_HANDLER);
 
549
 
 
550
  g_signal_connect (priv->stream_engine, "invalidated",
 
551
      G_CALLBACK (tp_call_invalidated_cb),
 
552
      call);
 
553
  
 
554
  /* FIXME: dbus daemon should be unique */
 
555
  priv->dbus_daemon = tp_dbus_daemon_new (tp_get_bus ());
 
556
  tp_dbus_daemon_watch_name_owner (priv->dbus_daemon, STREAM_ENGINE_BUS_NAME,
 
557
      tp_call_watch_name_owner_cb,
 
558
      call, NULL);
 
559
 
 
560
  emp_cli_channel_handler_call_handle_channel (priv->stream_engine, -1,
 
561
        dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->connection)),
 
562
        dbus_g_proxy_get_path (DBUS_G_PROXY (priv->connection)),
 
563
        priv->channel->type,
 
564
        dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
 
565
        priv->channel->handle_type, priv->channel->handle,
 
566
        tp_call_async_cb,
 
567
        "calling handle channel", NULL,
 
568
        G_OBJECT (call));
304
569
}
305
570
 
306
571
static GObject *
307
 
tp_call_constructor (GType                  type,
308
 
                     guint                  n_props,
309
 
                     GObjectConstructParam *props)
 
572
tp_call_constructor (GType type,
 
573
                     guint n_construct_params,
 
574
                     GObjectConstructParam *construct_params)
310
575
{
311
 
        GObject           *call;
312
 
        EmpathyTpCallPriv *priv;
313
 
        TpConn            *tp_conn;
314
 
        MissionControl    *mc;
315
 
 
316
 
        call = G_OBJECT_CLASS (empathy_tp_call_parent_class)->constructor (type, n_props, props);
317
 
        priv = GET_PRIV (call);
318
 
 
319
 
        priv->group = empathy_tp_group_new (priv->account, priv->tp_chan);
320
 
        priv->streamed_iface = tp_chan_get_interface (priv->tp_chan,
321
 
                                                      TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA);
322
 
 
323
 
        /* Connect signals */
324
 
        dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamAdded",
325
 
                                     G_CALLBACK (tp_call_stream_added_cb),
326
 
                                     call, NULL);
327
 
        dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamRemoved",
328
 
                                     G_CALLBACK (tp_call_stream_removed_cb), 
329
 
                                     call, NULL);
330
 
        dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed",
331
 
                                     G_CALLBACK (tp_call_closed_cb),
332
 
                                     call, NULL);
333
 
        g_signal_connect (priv->tp_chan, "destroy",
334
 
                          G_CALLBACK (tp_call_destroy_cb),
335
 
                          call);
336
 
        g_signal_connect (priv->group, "member-added",
337
 
                          G_CALLBACK (tp_call_member_added_cb),
338
 
                          call);
339
 
        g_signal_connect (priv->group, "remote-pending",
340
 
                          G_CALLBACK (tp_call_remote_pending_cb),
341
 
                          call);
342
 
 
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)),
357
 
                priv->tp_chan->type,
358
 
                dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
359
 
                priv->tp_chan->handle_type,
360
 
                priv->tp_chan->handle,
361
 
                tp_call_async_cb,
362
 
                "handle channel");
363
 
        g_object_unref (tp_conn);
364
 
        g_object_unref (mc);
365
 
 
366
 
        /* Get streams */
367
 
        tp_chan_type_streamed_media_list_streams_async (priv->streamed_iface,
368
 
                                                        tp_call_list_streams_cb,
369
 
                                                        call);
370
 
 
371
 
        return call;
 
576
  GObject *object;
 
577
  EmpathyTpCall *call;
 
578
  EmpathyTpCallPriv *priv;
 
579
  DBusGProxy *streamed_iface;
 
580
  MissionControl *mc;
 
581
  McAccount *account;
 
582
 
 
583
  object = G_OBJECT_CLASS (empathy_tp_call_parent_class)->constructor (type,
 
584
      n_construct_params, construct_params);
 
585
 
 
586
  call = EMPATHY_TP_CALL (object);
 
587
  priv = GET_PRIV (call);
 
588
 
 
589
  dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->channel), "Closed",
 
590
     G_CALLBACK (tp_call_channel_closed_cb), (gpointer) call, NULL);
 
591
 
 
592
  streamed_iface = tp_chan_get_interface (priv->channel,
 
593
      TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
 
594
  dbus_g_proxy_connect_signal (streamed_iface, "StreamStateChanged",
 
595
      G_CALLBACK (tp_call_stream_state_changed_cb),
 
596
      (gpointer) call, NULL);
 
597
  dbus_g_proxy_connect_signal (streamed_iface, "StreamDirectionChanged",
 
598
      G_CALLBACK (tp_call_stream_direction_changed_cb),
 
599
      (gpointer) call, NULL);
 
600
  dbus_g_proxy_connect_signal (streamed_iface, "StreamAdded",
 
601
      G_CALLBACK (tp_call_stream_added_cb), (gpointer) call, NULL);
 
602
  dbus_g_proxy_connect_signal (streamed_iface, "StreamRemoved",
 
603
      G_CALLBACK (tp_call_stream_removed_cb), (gpointer) call, NULL);
 
604
 
 
605
  mc = empathy_mission_control_new ();
 
606
  account = mission_control_get_account_for_connection (mc, priv->connection,
 
607
      NULL);
 
608
  priv->group = empathy_tp_group_new (account, priv->channel);
 
609
  g_object_unref (mc);
 
610
 
 
611
  g_signal_connect (G_OBJECT (priv->group), "member-added",
 
612
      G_CALLBACK (tp_call_member_added_cb), (gpointer) call);
 
613
  g_signal_connect (G_OBJECT (priv->group), "local-pending",
 
614
      G_CALLBACK (tp_call_local_pending_cb), (gpointer) call);
 
615
  g_signal_connect (G_OBJECT (priv->group), "remote-pending",
 
616
      G_CALLBACK (tp_call_remote_pending_cb), (gpointer) call);
 
617
 
 
618
  tp_call_start_stream_engine (call);
 
619
  /* FIXME: unnecessary for outgoing? */
 
620
  tp_call_identify_streams (call);
 
621
 
 
622
  return object;
372
623
}
373
624
 
374
 
static void
 
625
static void 
375
626
tp_call_finalize (GObject *object)
376
627
{
377
 
        EmpathyTpCallPriv *priv = GET_PRIV (object);
378
 
 
379
 
        empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
380
 
 
381
 
        if (priv->tp_chan) {
382
 
                GError *error = NULL;
383
 
 
384
 
                g_signal_handlers_disconnect_by_func (priv->tp_chan,
385
 
                                                      tp_call_destroy_cb,
386
 
                                                      object);
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);
393
 
                }
394
 
                g_object_unref (priv->tp_chan);
395
 
        }
396
 
 
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);
402
 
 
403
 
        G_OBJECT_CLASS (empathy_tp_call_parent_class)->finalize (object);
 
628
  EmpathyTpCallPriv *priv = GET_PRIV (object);
 
629
 
 
630
  empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
 
631
 
 
632
  g_slice_free (EmpathyTpCallStream, priv->audio);
 
633
  g_slice_free (EmpathyTpCallStream, priv->video);
 
634
  g_object_unref (priv->group);
 
635
 
 
636
  if (priv->connection != NULL)
 
637
    g_object_unref (priv->connection);
 
638
 
 
639
  if (priv->channel != NULL)
 
640
    g_object_unref (priv->channel);
 
641
 
 
642
  if (priv->stream_engine != NULL)
 
643
    g_object_unref (priv->stream_engine);
 
644
 
 
645
  if (priv->contact != NULL)
 
646
      g_object_unref (priv->contact);
 
647
 
 
648
  if (priv->dbus_daemon != NULL)
 
649
    {
 
650
      tp_dbus_daemon_cancel_name_owner_watch (priv->dbus_daemon,
 
651
          STREAM_ENGINE_BUS_NAME,
 
652
          tp_call_watch_name_owner_cb,
 
653
          object);
 
654
      g_object_unref (priv->dbus_daemon);
 
655
    }
 
656
 
 
657
  (G_OBJECT_CLASS (empathy_tp_call_parent_class)->finalize) (object);
 
658
}
 
659
 
 
660
static void 
 
661
tp_call_set_property (GObject *object,
 
662
                      guint prop_id,
 
663
                      const GValue *value,
 
664
                      GParamSpec *pspec)
 
665
{
 
666
  EmpathyTpCallPriv *priv = GET_PRIV (object);
 
667
 
 
668
  switch (prop_id)
 
669
    {
 
670
    case PROP_CONNECTION:
 
671
      priv->connection = g_value_dup_object (value);
 
672
      break;
 
673
    case PROP_CHANNEL:
 
674
      priv->channel = g_value_dup_object (value);
 
675
      break;
 
676
    case PROP_CONTACT:
 
677
      /* FIXME should this one be writable in the first place ? */
 
678
      g_assert (priv->contact == NULL);
 
679
      priv->contact = g_value_dup_object (value);
 
680
      break;
 
681
    case PROP_IS_INCOMING:
 
682
      priv->is_incoming = g_value_get_boolean (value);
 
683
      break;
 
684
    case PROP_STATUS:
 
685
      priv->status = g_value_get_uint (value);
 
686
      break;
 
687
    case PROP_AUDIO_STREAM:
 
688
      priv->audio = g_value_get_pointer (value);
 
689
      break;
 
690
    case PROP_VIDEO_STREAM:
 
691
      priv->video = g_value_get_pointer (value);
 
692
      break;
 
693
    default:
 
694
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
695
      break;
 
696
  }
 
697
}
 
698
 
 
699
 
 
700
static void
 
701
tp_call_get_property (GObject *object,
 
702
                      guint prop_id,
 
703
                      GValue *value,
 
704
                      GParamSpec *pspec)
 
705
{
 
706
  EmpathyTpCallPriv *priv = GET_PRIV (object);
 
707
 
 
708
  switch (prop_id)
 
709
    {
 
710
    case PROP_CONNECTION:
 
711
      g_value_set_object (value, priv->connection);
 
712
      break;
 
713
    case PROP_CHANNEL:
 
714
      g_value_set_object (value, priv->channel);
 
715
      break;
 
716
    case PROP_CONTACT:
 
717
      g_value_set_object (value, priv->contact);
 
718
      break;
 
719
    case PROP_IS_INCOMING:
 
720
      g_value_set_boolean (value, priv->is_incoming);
 
721
      break;
 
722
    case PROP_STATUS:
 
723
      g_value_set_uint (value, priv->status);
 
724
      break;
 
725
    case PROP_AUDIO_STREAM:
 
726
      g_value_set_pointer (value, priv->audio);
 
727
      break;
 
728
    case PROP_VIDEO_STREAM:
 
729
      g_value_set_pointer (value, priv->video);
 
730
      break;
 
731
    default:
 
732
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
733
      break;
 
734
  }
404
735
}
405
736
 
406
737
static void
407
738
empathy_tp_call_class_init (EmpathyTpCallClass *klass)
408
739
{
409
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
410
 
 
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;
415
 
 
416
 
        /* Construct-only properties */
417
 
        g_object_class_install_property (object_class,
418
 
                                         PROP_ACCOUNT,
419
 
                                         g_param_spec_object ("account",
420
 
                                                              "channel Account",
421
 
                                                              "The account associated with the channel",
422
 
                                                              MC_TYPE_ACCOUNT,
423
 
                                                              G_PARAM_READWRITE |
424
 
                                                              G_PARAM_CONSTRUCT_ONLY));
425
 
        g_object_class_install_property (object_class,
426
 
                                         PROP_TP_CHAN,
427
 
                                         g_param_spec_object ("tp-chan",
428
 
                                                              "telepathy channel",
429
 
                                                              "The media channel for the call",
430
 
                                                              TELEPATHY_CHAN_TYPE,
431
 
                                                              G_PARAM_READWRITE |
432
 
                                                              G_PARAM_CONSTRUCT_ONLY));
433
 
 
434
 
        /* Normal properties */
435
 
        g_object_class_install_property (object_class,
436
 
                                         PROP_STATUS,
437
 
                                         g_param_spec_enum ("status",
438
 
                                                            "call status",
439
 
                                                            "The status of the call",
440
 
                                                            EMPATHY_TYPE_TP_CALL_STATUS,
441
 
                                                            EMPATHY_TP_CALL_STATUS_PREPARING,
442
 
                                                            G_PARAM_READABLE));
443
 
 
444
 
        /* Signals */
445
 
        signals[DESTROY] =
446
 
                g_signal_new ("destroy",
447
 
                              G_TYPE_FROM_CLASS (klass),
448
 
                              G_SIGNAL_RUN_LAST,
449
 
                              0,
450
 
                              NULL, NULL,
451
 
                              g_cclosure_marshal_VOID__VOID,
452
 
                              G_TYPE_NONE,
453
 
                              0);
454
 
 
455
 
 
456
 
        g_type_class_add_private (klass, sizeof (EmpathyTpCallPriv));
 
740
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
741
 
 
742
  emp_cli_init ();
 
743
 
 
744
  object_class->constructor = tp_call_constructor;
 
745
  object_class->finalize = tp_call_finalize;
 
746
  object_class->set_property = tp_call_set_property;
 
747
  object_class->get_property = tp_call_get_property;
 
748
 
 
749
  g_type_class_add_private (klass, sizeof (EmpathyTpCallPriv));
 
750
 
 
751
  signals[STATUS_CHANGED_SIGNAL] =
 
752
      g_signal_new ("status-changed", G_TYPE_FROM_CLASS (klass),
 
753
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
 
754
      G_TYPE_NONE, 0);
 
755
  signals[RECEIVING_VIDEO_SIGNAL] =
 
756
      g_signal_new ("receiving-video", G_TYPE_FROM_CLASS (klass),
 
757
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
 
758
      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
759
  signals[SENDING_VIDEO_SIGNAL] =
 
760
      g_signal_new ("sending-video", G_TYPE_FROM_CLASS (klass),
 
761
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
 
762
      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
763
 
 
764
  g_object_class_install_property (object_class, PROP_CONNECTION,
 
765
      g_param_spec_object ("connection", "connection", "connection",
 
766
      TELEPATHY_CONN_TYPE,
 
767
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
 
768
      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
769
  g_object_class_install_property (object_class, PROP_CHANNEL,
 
770
      g_param_spec_object ("channel", "channel", "channel",
 
771
      TELEPATHY_CHAN_TYPE,
 
772
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
 
773
      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
774
  g_object_class_install_property (object_class, PROP_CONTACT,
 
775
      g_param_spec_object ("contact", "Call contact", "Call contact",
 
776
      EMPATHY_TYPE_CONTACT,
 
777
      G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
778
  g_object_class_install_property (object_class, PROP_IS_INCOMING,
 
779
      g_param_spec_boolean ("is-incoming", "Is media stream incoming",
 
780
      "Is media stream incoming", FALSE, G_PARAM_READABLE |
 
781
      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
782
  g_object_class_install_property (object_class, PROP_STATUS,
 
783
      g_param_spec_uint ("status", "Call status",
 
784
      "Call status", 0, 255, 0, G_PARAM_READABLE | G_PARAM_STATIC_NICK |
 
785
      G_PARAM_STATIC_BLURB));
 
786
  g_object_class_install_property (object_class, PROP_AUDIO_STREAM,
 
787
      g_param_spec_pointer ("audio-stream", "Audio stream data",
 
788
      "Audio stream data",
 
789
      G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
790
  g_object_class_install_property (object_class, PROP_VIDEO_STREAM,
 
791
      g_param_spec_pointer ("video-stream", "Video stream data",
 
792
      "Video stream data",
 
793
      G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
457
794
}
458
795
 
459
796
static void
460
797
empathy_tp_call_init (EmpathyTpCall *call)
461
798
{
 
799
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
800
 
 
801
  priv->status = EMPATHY_TP_CALL_STATUS_READYING;
 
802
  priv->contact = NULL;
 
803
  priv->audio = g_slice_new0 (EmpathyTpCallStream);
 
804
  priv->video = g_slice_new0 (EmpathyTpCallStream);
 
805
  priv->audio->exists = FALSE;
 
806
  priv->video->exists = FALSE;
462
807
}
463
808
 
464
809
EmpathyTpCall *
465
 
empathy_tp_call_new (McAccount *account, TpChan *channel)
466
 
{
467
 
        return g_object_new (EMPATHY_TYPE_TP_CALL,
468
 
                             "account", account,
469
 
                             "tp_chan", channel,
470
 
                             NULL);
471
 
}
472
 
 
473
 
gboolean
474
 
empathy_tp_call_is_incoming (EmpathyTpCall *call)
475
 
{
476
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
477
 
 
478
 
        return priv->is_incoming;
479
 
}
480
 
 
481
 
EmpathyTpCallStatus
482
 
empathy_tp_call_get_status (EmpathyTpCall *call)
483
 
{
484
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
485
 
 
486
 
        return priv->status;
487
 
}
488
 
 
489
 
EmpathyContact *
490
 
empathy_tp_call_get_contact (EmpathyTpCall *call)
491
 
{
492
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
493
 
 
494
 
        return priv->contact;
495
 
}
496
 
 
497
 
void
498
 
empathy_tp_call_accept (EmpathyTpCall *call)
499
 
{
500
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
501
 
        EmpathyContact    *contact;
502
 
 
503
 
        contact = empathy_tp_group_get_self_contact (priv->group);
504
 
        empathy_tp_group_add_member (priv->group, contact, "");
505
 
}
506
 
 
507
 
void
508
 
empathy_tp_call_invite (EmpathyTpCall  *call,
509
 
                        EmpathyContact *contact)
510
 
{
511
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
512
 
 
513
 
        empathy_tp_group_add_member (priv->group, contact, "you're welcome");
514
 
}
515
 
 
516
 
void
517
 
empathy_tp_call_request_streams (EmpathyTpCall *call,
518
 
                                 gboolean       audio,
519
 
                                 gboolean       video)
520
 
{
521
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
522
 
        GArray            *stream_types;
523
 
        guint              handle;
524
 
        guint              type;
525
 
 
526
 
        empathy_debug (DEBUG_DOMAIN, "Requesting streams for audio=%s video=%s",
527
 
                       audio ? "Yes" : "No",
528
 
                       video ? "Yes" : "No");
529
 
 
530
 
        stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
531
 
        if (audio) {
532
 
                type = TP_MEDIA_STREAM_TYPE_AUDIO;
533
 
                g_array_append_val (stream_types, type);
534
 
        }
535
 
        if (video) {
536
 
                type = TP_MEDIA_STREAM_TYPE_VIDEO;
537
 
                g_array_append_val (stream_types, type);
538
 
        }
539
 
 
540
 
        handle = empathy_contact_get_handle (priv->contact);
541
 
        tp_chan_type_streamed_media_request_streams_async (priv->streamed_iface,
542
 
                                                           handle,
543
 
                                                           stream_types,
544
 
                                                           tp_call_list_streams_cb,
545
 
                                                           call);
546
 
 
547
 
        g_array_free (stream_types, TRUE);
548
 
}
549
 
 
550
 
void
551
 
empathy_tp_call_send_video (EmpathyTpCall *call,
552
 
                            gboolean       send)
553
 
{
554
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
555
 
        guint              new_direction;
556
 
 
557
 
        if (!priv->video_stream) {
558
 
                return;
559
 
        }
560
 
 
561
 
        if (send) {
562
 
                new_direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
563
 
        } else {
564
 
                new_direction = TP_MEDIA_STREAM_DIRECTION_RECEIVE;
565
 
        }
566
 
 
567
 
        tp_chan_type_streamed_media_request_stream_direction_async (priv->streamed_iface,
568
 
                                                                    priv->video_stream,
569
 
                                                                    new_direction,
570
 
                                                                    tp_call_async_cb,
571
 
                                                                    "request stream direction");
572
 
}
573
 
 
574
 
void
575
 
empathy_tp_call_add_preview_window (EmpathyTpCall *call,
576
 
                                    guint          socket_id)
577
 
{
578
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
579
 
 
580
 
        org_freedesktop_Telepathy_StreamEngine_add_preview_window_async (priv->se_proxy,
581
 
                                                                         socket_id,
582
 
                                                                         tp_call_async_cb,
583
 
                                                                         "add preview window");
584
 
}
585
 
 
586
 
void
587
 
empathy_tp_call_remove_preview_window (EmpathyTpCall *call,
588
 
                                       guint          socket_id)
589
 
{
590
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
591
 
 
592
 
        org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async (priv->se_proxy,
593
 
                                                                            socket_id,
594
 
                                                                            tp_call_async_cb,
595
 
                                                                            "remove preview window");
596
 
}
597
 
 
598
 
void
599
 
empathy_tp_call_set_output_window (EmpathyTpCall *call,
600
 
                                   guint          socket_id)
601
 
{
602
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
603
 
 
604
 
        org_freedesktop_Telepathy_StreamEngine_set_output_window_async (priv->se_proxy,
605
 
                                                                        dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
606
 
                                                                        priv->video_stream,
607
 
                                                                        socket_id,
608
 
                                                                        tp_call_async_cb,
609
 
                                                                        "set output window");
 
810
empathy_tp_call_new (TpConn *connection, TpChan *channel)
 
811
{
 
812
  return g_object_new (EMPATHY_TYPE_TP_CALL,
 
813
      "connection", connection,
 
814
      "channel", channel,
 
815
      NULL);
 
816
}
 
817
 
 
818
void
 
819
empathy_tp_call_accept_incoming_call (EmpathyTpCall *call)
 
820
{
 
821
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
822
  GList *local_pendings;
 
823
 
 
824
  empathy_debug (DEBUG_DOMAIN, "Accepting incoming call");
 
825
 
 
826
  local_pendings = empathy_tp_group_get_local_pendings (priv->group);
 
827
 
 
828
  empathy_tp_group_add_member (priv->group, EMPATHY_CONTACT
 
829
      (((EmpathyPendingInfo *) local_pendings->data)->member), NULL);
 
830
 
 
831
  g_list_foreach (local_pendings, (GFunc) empathy_pending_info_free, NULL);
 
832
  g_list_free (local_pendings);
 
833
}
 
834
 
 
835
void
 
836
empathy_tp_call_request_video_stream_direction (EmpathyTpCall *call,
 
837
                                                gboolean is_sending)
 
838
{
 
839
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
840
  DBusGProxy *streamed_iface;
 
841
  guint new_direction;
 
842
  GError *error = NULL;
 
843
 
 
844
  empathy_debug (DEBUG_DOMAIN,
 
845
      "Requesting video stream direction - is_sending: %d", is_sending);
 
846
 
 
847
  if (!priv->video->exists)
 
848
    {
 
849
      tp_call_request_streams_for_capabilities (call, EMPATHY_CAPABILITIES_VIDEO);
 
850
      return;
 
851
    }
 
852
 
 
853
  streamed_iface = tp_chan_get_interface (priv->channel,
 
854
      TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
 
855
 
 
856
  if (is_sending)
 
857
    {
 
858
      new_direction = priv->video->direction | TP_MEDIA_STREAM_DIRECTION_SEND;
 
859
    }
 
860
  else
 
861
    {
 
862
      new_direction = priv->video->direction & ~TP_MEDIA_STREAM_DIRECTION_SEND;
 
863
    }
 
864
 
 
865
  if (!tp_chan_type_streamed_media_request_stream_direction (streamed_iface,
 
866
        priv->video->id, new_direction, &error))
 
867
    {
 
868
      empathy_debug (DEBUG_DOMAIN,
 
869
          "Couldn't request video stream direction: %s", error->message);
 
870
      g_clear_error (&error);
 
871
    }
 
872
}
 
873
 
 
874
void
 
875
empathy_tp_call_close_channel (EmpathyTpCall *call)
 
876
{
 
877
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
878
  GError *error = NULL;
 
879
 
 
880
  if (priv->status == EMPATHY_TP_CALL_STATUS_CLOSED)
 
881
      return;
 
882
 
 
883
  empathy_debug (DEBUG_DOMAIN, "Closing channel");
 
884
 
 
885
  if (!tp_chan_close (DBUS_G_PROXY (priv->channel), &error))
 
886
    {
 
887
      empathy_debug (DEBUG_DOMAIN, "Error closing channel: %s",
 
888
          error ? error->message : "No error given");
 
889
      g_clear_error (&error);
 
890
    }
 
891
  else
 
892
        priv->status = EMPATHY_TP_CALL_STATUS_CLOSED;
 
893
}
 
894
 
 
895
void
 
896
empathy_tp_call_add_preview_video (EmpathyTpCall *call,
 
897
                                   guint preview_video_socket_id)
 
898
{
 
899
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
900
 
 
901
  empathy_debug (DEBUG_DOMAIN, "Adding preview video");
 
902
 
 
903
  emp_cli_stream_engine_call_add_preview_window (priv->stream_engine, -1,
 
904
      preview_video_socket_id,
 
905
      tp_call_async_cb,
 
906
      "adding preview window", NULL,
 
907
      G_OBJECT (call));
 
908
}
 
909
 
 
910
void
 
911
empathy_tp_call_remove_preview_video (EmpathyTpCall *call,
 
912
                                      guint preview_video_socket_id)
 
913
{
 
914
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
915
 
 
916
  empathy_debug (DEBUG_DOMAIN, "Removing preview video");
 
917
 
 
918
  emp_cli_stream_engine_call_remove_preview_window (priv->stream_engine, -1,
 
919
      preview_video_socket_id,
 
920
      tp_call_async_cb,
 
921
      "removing preview window", NULL,
 
922
      G_OBJECT (call));
 
923
}
 
924
 
 
925
void
 
926
empathy_tp_call_add_output_video (EmpathyTpCall *call,
 
927
                                  guint output_video_socket_id)
 
928
{
 
929
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
930
 
 
931
  empathy_debug (DEBUG_DOMAIN, "Adding output video - socket: %d",
 
932
      output_video_socket_id);
 
933
 
 
934
  emp_cli_stream_engine_call_set_output_window (priv->stream_engine, -1,
 
935
      dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
 
936
      priv->video->id, output_video_socket_id,
 
937
      tp_call_async_cb,
 
938
      "setting output window", NULL,
 
939
      G_OBJECT (call));
610
940
}
611
941
 
612
942
void
613
943
empathy_tp_call_set_output_volume (EmpathyTpCall *call,
614
 
                                   guint          volume)
 
944
                                   guint volume)
615
945
{
616
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
617
 
 
618
 
        org_freedesktop_Telepathy_StreamEngine_set_output_volume_async (priv->se_proxy,
619
 
                                                                        dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
620
 
                                                                        priv->audio_stream,
621
 
                                                                        volume,
622
 
                                                                        tp_call_async_cb,
623
 
                                                                        "set output volume");
 
946
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
947
 
 
948
  if (priv->status == EMPATHY_TP_CALL_STATUS_CLOSED)
 
949
    return;
 
950
 
 
951
  empathy_debug (DEBUG_DOMAIN, "Setting output volume: %d", volume);
 
952
 
 
953
  emp_cli_stream_engine_call_set_output_volume (priv->stream_engine, -1,
 
954
      dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
 
955
      priv->audio->id, volume,
 
956
      tp_call_async_cb,
 
957
      "setting output volume", NULL,
 
958
      G_OBJECT (call));
624
959
}
625
960
 
626
 
 
627
961
void
628
962
empathy_tp_call_mute_output (EmpathyTpCall *call,
629
 
                             gboolean       is_muted)
 
963
                             gboolean is_muted)
630
964
{
631
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
632
 
 
633
 
        org_freedesktop_Telepathy_StreamEngine_mute_output_async (priv->se_proxy,
634
 
                                                                  dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
635
 
                                                                  priv->audio_stream,
636
 
                                                                  is_muted,
637
 
                                                                  tp_call_async_cb,
638
 
                                                                  "mute output");
 
965
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
966
 
 
967
  if (priv->status == EMPATHY_TP_CALL_STATUS_CLOSED)
 
968
    return;
 
969
 
 
970
  empathy_debug (DEBUG_DOMAIN, "Setting output mute: %d", is_muted);
 
971
 
 
972
  emp_cli_stream_engine_call_mute_output (priv->stream_engine, -1,
 
973
      dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
 
974
      priv->audio->id, is_muted,
 
975
      tp_call_async_cb,
 
976
      "muting output", NULL,
 
977
      G_OBJECT (call));
639
978
}
640
979
 
641
 
 
642
980
void
643
981
empathy_tp_call_mute_input (EmpathyTpCall *call,
644
 
                            gboolean       is_muted)
 
982
                            gboolean is_muted)
645
983
{
646
 
        EmpathyTpCallPriv *priv = GET_PRIV (call);
647
 
 
648
 
        org_freedesktop_Telepathy_StreamEngine_mute_input_async (priv->se_proxy,
649
 
                                                                 dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
650
 
                                                                 priv->audio_stream,
651
 
                                                                 is_muted,
652
 
                                                                 tp_call_async_cb,
653
 
                                                                 "mute output");
 
984
  EmpathyTpCallPriv *priv = GET_PRIV (call);
 
985
 
 
986
  if (priv->status == EMPATHY_TP_CALL_STATUS_CLOSED)
 
987
    return;
 
988
 
 
989
  empathy_debug (DEBUG_DOMAIN, "Setting input mute: %d", is_muted);
 
990
 
 
991
  emp_cli_stream_engine_call_mute_input (priv->stream_engine, -1,
 
992
      dbus_g_proxy_get_path (DBUS_G_PROXY (priv->channel)),
 
993
      priv->audio->id, is_muted,
 
994
      tp_call_async_cb,
 
995
      "muting input", NULL,
 
996
      G_OBJECT (call));
654
997
}
655
998