~ubuntu-branches/ubuntu/precise/telepathy-glib/precise-201202222208

« back to all changes in this revision

Viewing changes to telepathy-glib/call-content.c

  • Committer: Ken VanDine
  • Date: 2012-02-22 18:08:37 UTC
  • mfrom: (1.6.39)
  • Revision ID: ken.vandine@canonical.com-20120222180837-02um6fex0eg073lf
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * call-content.h - high level API for Call contents
 
3
 *
 
4
 * Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
 
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
 
 
21
/**
 
22
 * SECTION:call-content
 
23
 * @title: TpCallContent
 
24
 * @short_description: proxy object for a call content
 
25
 *
 
26
 * #TpCallContent is a sub-class of #TpProxy providing convenient API
 
27
 * to represent #TpCallChannel's content.
 
28
 */
 
29
 
 
30
/**
 
31
 * TpCallContent:
 
32
 *
 
33
 * Data structure representing a #TpCallContent.
 
34
 *
 
35
 * Since: 0.17.5
 
36
 */
 
37
 
 
38
/**
 
39
 * TpCallContentClass:
 
40
 *
 
41
 * The class of a #TpCallContent.
 
42
 *
 
43
 * Since: 0.17.5
 
44
 */
 
45
 
 
46
#include "config.h"
 
47
 
 
48
#include "telepathy-glib/call-content.h"
 
49
 
 
50
#include <telepathy-glib/call-misc.h>
 
51
#include <telepathy-glib/call-stream.h>
 
52
#include <telepathy-glib/dbus.h>
 
53
#include <telepathy-glib/dtmf.h>
 
54
#include <telepathy-glib/enums.h>
 
55
#include <telepathy-glib/errors.h>
 
56
#include <telepathy-glib/gtypes.h>
 
57
#include <telepathy-glib/interfaces.h>
 
58
#include <telepathy-glib/proxy-subclass.h>
 
59
#include <telepathy-glib/util.h>
 
60
 
 
61
#define DEBUG_FLAG TP_DEBUG_CALL
 
62
#include "telepathy-glib/debug-internal.h"
 
63
#include "telepathy-glib/call-internal.h"
 
64
#include "telepathy-glib/proxy-internal.h"
 
65
#include "telepathy-glib/util-internal.h"
 
66
#include "telepathy-glib/_gen/signals-marshal.h"
 
67
 
 
68
#include "_gen/tp-cli-call-content-body.h"
 
69
 
 
70
G_DEFINE_TYPE (TpCallContent, tp_call_content, TP_TYPE_PROXY)
 
71
 
 
72
typedef struct _SendTonesData SendTonesData;
 
73
 
 
74
struct _TpCallContentPrivate
 
75
{
 
76
  TpConnection *connection;
 
77
 
 
78
  gchar *name;
 
79
  TpMediaStreamType media_type;
 
80
  TpCallContentDisposition disposition;
 
81
  GPtrArray *streams;
 
82
 
 
83
  gboolean properties_retrieved;
 
84
 
 
85
  GQueue *tones_queue;
 
86
  SendTonesData *current_tones;
 
87
};
 
88
 
 
89
enum
 
90
{
 
91
  PROP_CONNECTION = 1,
 
92
  PROP_NAME,
 
93
  PROP_MEDIA_TYPE,
 
94
  PROP_DISPOSITION,
 
95
  PROP_STREAMS
 
96
};
 
97
 
 
98
enum
 
99
{
 
100
  REMOVED,
 
101
  STREAMS_ADDED,
 
102
  STREAMS_REMOVED,
 
103
  LAST_SIGNAL
 
104
};
 
105
 
 
106
static guint _signals[LAST_SIGNAL] = { 0, };
 
107
 
 
108
static TpCallStream *
 
109
_tp_call_stream_new (TpCallContent *self,
 
110
    const gchar *object_path)
 
111
{
 
112
  return g_object_new (TP_TYPE_CALL_STREAM,
 
113
      "bus-name", tp_proxy_get_bus_name (self),
 
114
      "dbus-daemon", tp_proxy_get_dbus_daemon (self),
 
115
      "dbus-connection", tp_proxy_get_dbus_connection (self),
 
116
      "object-path", object_path,
 
117
      "connection", self->priv->connection,
 
118
      NULL);
 
119
}
 
120
 
 
121
static void
 
122
streams_added_cb (TpCallContent *self,
 
123
    const GPtrArray *streams,
 
124
    gpointer user_data,
 
125
    GObject *weak_object)
 
126
{
 
127
  guint i;
 
128
  GPtrArray *added_streams;
 
129
 
 
130
  if (!self->priv->properties_retrieved)
 
131
    return;
 
132
 
 
133
  added_streams = g_ptr_array_sized_new (streams->len);
 
134
 
 
135
  for (i = 0; i < streams->len; i++)
 
136
    {
 
137
      const gchar *object_path = g_ptr_array_index (streams, i);
 
138
      TpCallStream *stream ;
 
139
 
 
140
      DEBUG ("Stream added: %s", object_path);
 
141
 
 
142
      stream = _tp_call_stream_new (self, object_path);
 
143
      g_ptr_array_add (self->priv->streams, stream);
 
144
      g_ptr_array_add (added_streams, stream);
 
145
    }
 
146
 
 
147
  g_signal_emit (self, _signals[STREAMS_ADDED], 0, added_streams);
 
148
  g_ptr_array_unref (added_streams);
 
149
}
 
150
 
 
151
static void
 
152
streams_removed_cb (TpCallContent *self,
 
153
    const GPtrArray *streams,
 
154
    const GValueArray *reason,
 
155
    gpointer user_data,
 
156
    GObject *weak_object)
 
157
{
 
158
  GPtrArray *removed_streams;
 
159
  guint i;
 
160
 
 
161
  if (!self->priv->properties_retrieved)
 
162
    return;
 
163
 
 
164
  removed_streams = _tp_g_ptr_array_new_full (streams->len, g_object_unref);
 
165
 
 
166
  for (i = 0; i < streams->len; i++)
 
167
    {
 
168
      const gchar *object_path = g_ptr_array_index (streams, i);
 
169
      gboolean found = FALSE;
 
170
      guint j;
 
171
 
 
172
      for (j = 0; j < self->priv->streams->len; j++)
 
173
        {
 
174
          TpCallStream *stream = g_ptr_array_index (self->priv->streams, j);
 
175
 
 
176
          if (!tp_strdiff (tp_proxy_get_object_path (stream), object_path))
 
177
            {
 
178
              DEBUG ("Stream removed: %s", object_path);
 
179
 
 
180
              found = TRUE;
 
181
              g_ptr_array_add (removed_streams, g_object_ref (stream));
 
182
              g_ptr_array_remove_index_fast (self->priv->streams, j);
 
183
              break;
 
184
            }
 
185
        }
 
186
 
 
187
      if (!found)
 
188
        DEBUG ("Stream '%s' removed but not found", object_path);
 
189
    }
 
190
 
 
191
  if (removed_streams->len > 0)
 
192
    {
 
193
      TpCallStateReason *r;
 
194
 
 
195
      r = _tp_call_state_reason_new (reason);
 
196
      g_signal_emit (self, _signals[STREAMS_REMOVED], 0, removed_streams, r);
 
197
      _tp_call_state_reason_unref (r);
 
198
    }
 
199
 
 
200
  g_ptr_array_unref (removed_streams);
 
201
}
 
202
 
 
203
static void tones_stopped_cb (TpCallContent *self,
 
204
    gboolean cancelled,
 
205
    gpointer user_data,
 
206
    GObject *weak_object);
 
207
 
 
208
static void
 
209
got_all_properties_cb (TpProxy *proxy,
 
210
    GHashTable *properties,
 
211
    const GError *error,
 
212
    gpointer user_data,
 
213
    GObject *weak_object)
 
214
{
 
215
  TpCallContent *self = (TpCallContent *) proxy;
 
216
  const gchar * const *interfaces;
 
217
  GPtrArray *streams;
 
218
  guint i;
 
219
 
 
220
  if (error != NULL)
 
221
    {
 
222
      DEBUG ("Could not get the call content properties: %s", error->message);
 
223
      _tp_proxy_set_feature_prepared (proxy,
 
224
          TP_CALL_CONTENT_FEATURE_CORE, FALSE);
 
225
      return;
 
226
    }
 
227
 
 
228
  self->priv->properties_retrieved = TRUE;
 
229
 
 
230
  interfaces = tp_asv_get_boxed (properties,
 
231
      "Interfaces", G_TYPE_STRV);
 
232
  self->priv->name = g_strdup (tp_asv_get_string (properties,
 
233
      "Name"));
 
234
  self->priv->media_type = tp_asv_get_uint32 (properties,
 
235
      "Type", NULL);
 
236
  self->priv->disposition = tp_asv_get_uint32 (properties,
 
237
      "Disposition", NULL);
 
238
  streams = tp_asv_get_boxed (properties,
 
239
      "Streams", TP_ARRAY_TYPE_OBJECT_PATH_LIST);
 
240
 
 
241
  tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
 
242
 
 
243
  for (i = 0; i < streams->len; i++)
 
244
    {
 
245
      const gchar *object_path = g_ptr_array_index (streams, i);
 
246
 
 
247
      DEBUG ("Initial stream added: %s", object_path);
 
248
 
 
249
      g_ptr_array_add (self->priv->streams,
 
250
          _tp_call_stream_new (self, object_path));
 
251
    }
 
252
 
 
253
  if (tp_proxy_has_interface_by_id (self,
 
254
          TP_IFACE_QUARK_CALL_CONTENT_INTERFACE_DTMF))
 
255
    {
 
256
      tp_cli_call_content_interface_dtmf_connect_to_stopped_tones (self,
 
257
          tones_stopped_cb, NULL, NULL, NULL, NULL);
 
258
    }
 
259
 
 
260
  _tp_proxy_set_feature_prepared (proxy, TP_CALL_CONTENT_FEATURE_CORE, TRUE);
 
261
}
 
262
 
 
263
struct _SendTonesData
 
264
{
 
265
  TpCallContent *content;
 
266
  gchar *tones;
 
267
  GSimpleAsyncResult *result;
 
268
  GCancellable *cancellable;
 
269
  guint cancel_id;
 
270
};
 
271
 
 
272
static void maybe_send_tones (TpCallContent *self);
 
273
static void send_tones_cancelled_cb (GCancellable *cancellable,
 
274
    SendTonesData *data);
 
275
 
 
276
static SendTonesData *
 
277
send_tones_data_new (TpCallContent *self,
 
278
    const gchar *tones,
 
279
    GSimpleAsyncResult *result,
 
280
    GCancellable *cancellable)
 
281
{
 
282
  SendTonesData *data;
 
283
 
 
284
  data = g_slice_new0 (SendTonesData);
 
285
  data->content = g_object_ref (self);
 
286
  data->tones = g_strdup (tones);
 
287
  data->result = g_object_ref (result);
 
288
 
 
289
  if (cancellable != NULL)
 
290
    {
 
291
      data->cancellable = g_object_ref (cancellable);
 
292
      data->cancel_id = g_cancellable_connect (cancellable,
 
293
          G_CALLBACK (send_tones_cancelled_cb), data, NULL);
 
294
    }
 
295
 
 
296
  return data;
 
297
}
 
298
 
 
299
static void
 
300
send_tones_data_free (SendTonesData *data)
 
301
{
 
302
  g_free (data->tones);
 
303
  g_object_unref (data->result);
 
304
  g_object_unref (data->content);
 
305
 
 
306
  if (data->cancellable != NULL)
 
307
    {
 
308
      if (data->cancel_id != 0)
 
309
        g_cancellable_disconnect (data->cancellable, data->cancel_id);
 
310
 
 
311
      g_object_unref (data->cancellable);
 
312
    }
 
313
 
 
314
  g_slice_free (SendTonesData, data);
 
315
}
 
316
 
 
317
static gboolean
 
318
send_tones_cancelled_idle_cb (gpointer user_data)
 
319
{
 
320
  SendTonesData *data = user_data;
 
321
  TpCallContent *self = data->content;
 
322
 
 
323
  /* If it is the tone currently being played, stop it. Otherwise wait for its
 
324
   * turn in the queue to preserve order. */
 
325
  if (self->priv->current_tones == data)
 
326
    {
 
327
      tp_cli_call_content_interface_dtmf_call_stop_tone (self, -1,
 
328
          NULL, NULL, NULL, NULL);
 
329
    }
 
330
 
 
331
  return FALSE;
 
332
}
 
333
 
 
334
static void
 
335
send_tones_cancelled_cb (GCancellable *cancellable,
 
336
    SendTonesData *data)
 
337
{
 
338
  /* Cancel in idle for thread-safeness */
 
339
  g_idle_add (send_tones_cancelled_idle_cb, data);
 
340
}
 
341
 
 
342
static void
 
343
complete_sending_tones (TpCallContent *self,
 
344
    const GError *error)
 
345
{
 
346
  if (self->priv->current_tones == NULL)
 
347
    return;
 
348
 
 
349
  if (error != NULL)
 
350
    {
 
351
      g_simple_async_result_set_from_error (self->priv->current_tones->result,
 
352
          error);
 
353
    }
 
354
 
 
355
  g_simple_async_result_complete (self->priv->current_tones->result);
 
356
 
 
357
  send_tones_data_free (self->priv->current_tones);
 
358
  self->priv->current_tones = NULL;
 
359
 
 
360
  maybe_send_tones (self);
 
361
}
 
362
 
 
363
static void
 
364
tones_stopped_cb (TpCallContent *self,
 
365
    gboolean cancelled,
 
366
    gpointer user_data,
 
367
    GObject *weak_object)
 
368
{
 
369
  if (cancelled)
 
370
    {
 
371
      GError e = { TP_ERRORS, TP_ERROR_CANCELLED,
 
372
          "The DTMF tones were actively cancelled via StopTones" };
 
373
      complete_sending_tones (self, &e);
 
374
      return;
 
375
    }
 
376
 
 
377
  complete_sending_tones (self, NULL);
 
378
}
 
379
 
 
380
static void
 
381
multiple_tones_cb (TpCallContent *self,
 
382
    const GError *error,
 
383
    gpointer user_data,
 
384
    GObject *weak_object)
 
385
{
 
386
  if (error != NULL)
 
387
    complete_sending_tones (self, error);
 
388
}
 
389
 
 
390
static void
 
391
maybe_send_tones (TpCallContent *self)
 
392
{
 
393
  if (self->priv->current_tones != NULL)
 
394
    return;
 
395
 
 
396
  if (g_queue_is_empty (self->priv->tones_queue))
 
397
    return;
 
398
 
 
399
  self->priv->current_tones = g_queue_pop_head (self->priv->tones_queue);
 
400
 
 
401
  /* Yes this is safe if cancellable is NULL! */
 
402
  if (g_cancellable_is_cancelled (self->priv->current_tones->cancellable))
 
403
    {
 
404
      GError e = { TP_ERRORS, TP_ERROR_CANCELLED,
 
405
          "The DTMF tones were cancelled before it has started" };
 
406
      complete_sending_tones (self, &e);
 
407
      return;
 
408
    }
 
409
 
 
410
  DEBUG ("Emitting multiple tones: %s", self->priv->current_tones->tones);
 
411
  tp_cli_call_content_interface_dtmf_call_multiple_tones (self, -1,
 
412
      self->priv->current_tones->tones, multiple_tones_cb, NULL, NULL, NULL);
 
413
}
 
414
 
 
415
static void
 
416
tp_call_content_constructed (GObject *obj)
 
417
{
 
418
  TpCallContent *self = (TpCallContent *) obj;
 
419
 
 
420
  ((GObjectClass *) tp_call_content_parent_class)->constructed (obj);
 
421
 
 
422
  /* Connect signals for mutable properties */
 
423
  tp_cli_call_content_connect_to_streams_added (self,
 
424
      streams_added_cb, NULL, NULL, G_OBJECT (self), NULL);
 
425
  tp_cli_call_content_connect_to_streams_removed (self,
 
426
      streams_removed_cb, NULL, NULL, G_OBJECT (self), NULL);
 
427
 
 
428
  tp_cli_dbus_properties_call_get_all (self, -1,
 
429
      TP_IFACE_CALL_CONTENT,
 
430
      got_all_properties_cb, NULL, NULL, G_OBJECT (self));
 
431
}
 
432
 
 
433
static void
 
434
tp_call_content_dispose (GObject *object)
 
435
{
 
436
  TpCallContent *self = (TpCallContent *) object;
 
437
 
 
438
  g_clear_object (&self->priv->connection);
 
439
  tp_clear_pointer (&self->priv->name, g_free);
 
440
  tp_clear_pointer (&self->priv->streams, g_ptr_array_unref);
 
441
 
 
442
  G_OBJECT_CLASS (tp_call_content_parent_class)->dispose (object);
 
443
}
 
444
 
 
445
static void
 
446
tp_call_content_finalize (GObject *object)
 
447
{
 
448
  TpCallContent *self = (TpCallContent *) object;
 
449
 
 
450
  /* Results hold a ref on self, finalize can't happen if queue isn't empty */
 
451
  g_assert (self->priv->current_tones == NULL);
 
452
  g_assert (g_queue_is_empty (self->priv->tones_queue));
 
453
  g_queue_free (self->priv->tones_queue);
 
454
 
 
455
  G_OBJECT_CLASS (tp_call_content_parent_class)->finalize (object);
 
456
}
 
457
 
 
458
static void
 
459
tp_call_content_get_property (GObject *object,
 
460
    guint property_id,
 
461
    GValue *value,
 
462
    GParamSpec *pspec)
 
463
{
 
464
  TpCallContent *self = (TpCallContent *) object;
 
465
 
 
466
  switch (property_id)
 
467
    {
 
468
      case PROP_CONNECTION:
 
469
        g_value_set_object (value, self->priv->connection);
 
470
        break;
 
471
      case PROP_NAME:
 
472
        g_value_set_string (value, self->priv->name);
 
473
        break;
 
474
      case PROP_MEDIA_TYPE:
 
475
        g_value_set_uint (value, self->priv->media_type);
 
476
        break;
 
477
      case PROP_DISPOSITION:
 
478
        g_value_set_uint (value, self->priv->disposition);
 
479
        break;
 
480
      case PROP_STREAMS:
 
481
        g_value_set_boxed (value, self->priv->streams);
 
482
        break;
 
483
      default:
 
484
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
485
        break;
 
486
    }
 
487
}
 
488
 
 
489
static void
 
490
tp_call_content_set_property (GObject *object,
 
491
    guint property_id,
 
492
    const GValue *value,
 
493
    GParamSpec *pspec)
 
494
{
 
495
  TpCallContent *self = (TpCallContent *) object;
 
496
 
 
497
  switch (property_id)
 
498
    {
 
499
      case PROP_CONNECTION:
 
500
        g_assert (self->priv->connection == NULL); /* construct-only */
 
501
        self->priv->connection = g_value_dup_object (value);
 
502
        break;
 
503
      default:
 
504
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
505
        break;
 
506
    }
 
507
}
 
508
 
 
509
enum {
 
510
    FEAT_CORE,
 
511
    N_FEAT
 
512
};
 
513
 
 
514
static const TpProxyFeature *
 
515
tp_call_content_list_features (TpProxyClass *cls G_GNUC_UNUSED)
 
516
{
 
517
  static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
 
518
 
 
519
  if (G_LIKELY (features[0].name != 0))
 
520
    return features;
 
521
 
 
522
  /* started from constructed */
 
523
  features[FEAT_CORE].name = TP_CALL_CONTENT_FEATURE_CORE;
 
524
  features[FEAT_CORE].core = TRUE;
 
525
 
 
526
  /* assert that the terminator at the end is there */
 
527
  g_assert (features[N_FEAT].name == 0);
 
528
 
 
529
  return features;
 
530
}
 
531
 
 
532
static void
 
533
tp_call_content_class_init (TpCallContentClass *klass)
 
534
{
 
535
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
536
  TpProxyClass *proxy_class = (TpProxyClass *) klass;
 
537
  GParamSpec *param_spec;
 
538
 
 
539
  gobject_class->constructed = tp_call_content_constructed;
 
540
  gobject_class->get_property = tp_call_content_get_property;
 
541
  gobject_class->set_property = tp_call_content_set_property;
 
542
  gobject_class->dispose = tp_call_content_dispose;
 
543
  gobject_class->finalize = tp_call_content_finalize;
 
544
 
 
545
  proxy_class->list_features = tp_call_content_list_features;
 
546
  proxy_class->interface = TP_IFACE_QUARK_CALL_CONTENT;
 
547
 
 
548
  g_type_class_add_private (gobject_class, sizeof (TpCallContentPrivate));
 
549
  tp_call_content_init_known_interfaces ();
 
550
 
 
551
  /**
 
552
   * TpCallContent:connection:
 
553
   *
 
554
   * The #TpConnection of the call.
 
555
   *
 
556
   * Since: 0.17.5
 
557
   */
 
558
  param_spec = g_param_spec_object ("connection", "Connection",
 
559
      "The connection of this content",
 
560
      TP_TYPE_CONNECTION,
 
561
      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
562
  g_object_class_install_property (gobject_class, PROP_CONNECTION,
 
563
      param_spec);
 
564
 
 
565
  /**
 
566
   * TpCallContent:name:
 
567
   *
 
568
   * The name of this content.
 
569
   *
 
570
   * Since: 0.17.5
 
571
   */
 
572
  param_spec = g_param_spec_string ("name", "Name",
 
573
      "The name of this content, if any",
 
574
      "",
 
575
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
576
  g_object_class_install_property (gobject_class, PROP_NAME, param_spec);
 
577
 
 
578
  /**
 
579
   * TpCallContent:media-type:
 
580
   *
 
581
   * The media type of this content, from #TpMediaStreamType.
 
582
   *
 
583
   * Since: 0.17.5
 
584
   */
 
585
  param_spec = g_param_spec_uint ("media-type", "Media type",
 
586
      "The media type of this content",
 
587
      0, G_MAXUINT, 0,
 
588
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
589
  g_object_class_install_property (gobject_class, PROP_MEDIA_TYPE, param_spec);
 
590
 
 
591
  /**
 
592
   * TpCallContent:disposition:
 
593
   *
 
594
   * The disposition of this content, from #TpCallContentDisposition.
 
595
   *
 
596
   * Since: 0.17.5
 
597
   */
 
598
  param_spec = g_param_spec_uint ("disposition", "Disposition",
 
599
      "The disposition of this content",
 
600
      0, G_MAXUINT, 0,
 
601
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
602
  g_object_class_install_property (gobject_class, PROP_DISPOSITION, param_spec);
 
603
 
 
604
  /* FIXME: Should be annoted with
 
605
   *
 
606
   * Type: GLib.PtrArray<TelepathyGLib.CallStream>
 
607
   * Transfer: container
 
608
   *
 
609
   * But it does not work (bgo#663846) and makes gtkdoc fail myserably.
 
610
   */
 
611
 
 
612
  /**
 
613
   * TpCallContent:streams:
 
614
   *
 
615
   * #GPtrArray of #TpCallStream objects. The list of stream objects that are
 
616
   * part of this content.
 
617
   *
 
618
   * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
 
619
   * those objects.
 
620
   *
 
621
   * Since: 0.17.5
 
622
   */
 
623
  param_spec = g_param_spec_boxed ("streams", "Stream",
 
624
      "The streams of this content",
 
625
      G_TYPE_PTR_ARRAY,
 
626
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
627
  g_object_class_install_property (gobject_class, PROP_STREAMS,
 
628
      param_spec);
 
629
 
 
630
  /**
 
631
   * TpCallContent::removed
 
632
   * @self: the #TpCallContent
 
633
   *
 
634
   * The ::removed signal is emitted when @self is removed from
 
635
   * a #TpCallChannel.
 
636
   *
 
637
   * Since: 0.17.5
 
638
   */
 
639
  _signals[REMOVED] = g_signal_new ("removed",
 
640
      G_OBJECT_CLASS_TYPE (klass),
 
641
      G_SIGNAL_RUN_LAST,
 
642
      0, NULL, NULL,
 
643
      g_cclosure_marshal_VOID__VOID,
 
644
      G_TYPE_NONE,
 
645
      0);
 
646
 
 
647
  /**
 
648
   * TpCallContent::streams-added
 
649
   * @self: the #TpCallContent
 
650
   * @streams: (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
 
651
   *  a #GPtrArray of newly added #TpCallStream
 
652
   *
 
653
   * The ::streams-added signal is emitted whenever
 
654
   * #TpCallStream are added to @self.
 
655
   *
 
656
   * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
 
657
   * stream objects.
 
658
   *
 
659
   * Since: 0.17.5
 
660
   */
 
661
  _signals[STREAMS_ADDED] = g_signal_new ("streams-added",
 
662
      G_OBJECT_CLASS_TYPE (klass),
 
663
      G_SIGNAL_RUN_LAST,
 
664
      0, NULL, NULL,
 
665
      g_cclosure_marshal_VOID__BOXED,
 
666
      G_TYPE_NONE,
 
667
      1, G_TYPE_PTR_ARRAY);
 
668
 
 
669
  /**
 
670
   * TpCallContent::streams-removed
 
671
   * @self: the #TpCallContent
 
672
   * @streams: (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
 
673
   *  a #GPtrArray of newly removed #TpCallStream
 
674
   * @reason: a #TpCallStateReason
 
675
   *
 
676
   * The ::streams-removed signal is emitted whenever
 
677
   * #TpCallStreams are removed from @self.
 
678
   *
 
679
   * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
 
680
   * stream objects.
 
681
   *
 
682
   * Since: 0.17.5
 
683
   */
 
684
  _signals[STREAMS_REMOVED] = g_signal_new ("streams-removed",
 
685
      G_OBJECT_CLASS_TYPE (klass),
 
686
      G_SIGNAL_RUN_LAST,
 
687
      0, NULL, NULL,
 
688
      _tp_marshal_VOID__BOXED_BOXED,
 
689
      G_TYPE_NONE,
 
690
      2, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON);
 
691
 
 
692
}
 
693
 
 
694
static void
 
695
tp_call_content_init (TpCallContent *self)
 
696
{
 
697
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_CONTENT,
 
698
      TpCallContentPrivate);
 
699
 
 
700
  self->priv->streams = g_ptr_array_new_with_free_func (g_object_unref);
 
701
  self->priv->tones_queue = g_queue_new ();
 
702
}
 
703
 
 
704
/**
 
705
 * tp_call_content_init_known_interfaces:
 
706
 *
 
707
 * Ensure that the known interfaces for #TpCallContent have been set up.
 
708
 * This is done automatically when necessary, but for correct
 
709
 * overriding of library interfaces by local extensions, you should
 
710
 * call this function before calling
 
711
 * tp_proxy_or_subclass_hook_on_interface_add() with first argument
 
712
 * %TP_TYPE_CALL_CONTENT.
 
713
 *
 
714
 * Since: 0.17.5
 
715
 */
 
716
void
 
717
tp_call_content_init_known_interfaces (void)
 
718
{
 
719
  static gsize once = 0;
 
720
 
 
721
  if (g_once_init_enter (&once))
 
722
    {
 
723
      GType tp_type = TP_TYPE_CALL_CONTENT;
 
724
 
 
725
      tp_proxy_init_known_interfaces ();
 
726
      tp_proxy_or_subclass_hook_on_interface_add (tp_type,
 
727
          tp_cli_call_content_add_signals);
 
728
      tp_proxy_subclass_add_error_mapping (tp_type,
 
729
          TP_ERROR_PREFIX, TP_ERRORS, TP_TYPE_ERROR);
 
730
 
 
731
      g_once_init_leave (&once, 1);
 
732
    }
 
733
}
 
734
 
 
735
/**
 
736
 * TP_CALL_CONTENT_FEATURE_CORE:
 
737
 *
 
738
 * Expands to a call to a function that returns a quark for the "core"
 
739
 * feature on a #TpCallContent.
 
740
 *
 
741
 * One can ask for a feature to be prepared using the tp_proxy_prepare_async()
 
742
 * function, and waiting for it to trigger the callback.
 
743
 */
 
744
GQuark
 
745
tp_call_content_get_feature_quark_core (void)
 
746
{
 
747
  return g_quark_from_static_string ("tp-call-content-feature-core");
 
748
}
 
749
 
 
750
/**
 
751
 * tp_call_content_get_name:
 
752
 * @self: a #TpCallContent
 
753
 *
 
754
 * <!-- -->
 
755
 *
 
756
 * Returns: the value of #TpCallContent:name
 
757
 * Since: 0.17.5
 
758
 */
 
759
const gchar *
 
760
tp_call_content_get_name (TpCallContent *self)
 
761
{
 
762
  g_return_val_if_fail (TP_IS_CALL_CONTENT (self), NULL);
 
763
 
 
764
  return self->priv->name;
 
765
}
 
766
 
 
767
/**
 
768
 * tp_call_content_get_media_type:
 
769
 * @self: a #TpCallContent
 
770
 *
 
771
 * <!-- -->
 
772
 *
 
773
 * Returns: the value of #TpCallContent:name
 
774
 * Since: 0.17.5
 
775
 */
 
776
TpMediaStreamType
 
777
tp_call_content_get_media_type (TpCallContent *self)
 
778
{
 
779
  g_return_val_if_fail (TP_IS_CALL_CONTENT (self), 0);
 
780
 
 
781
  return self->priv->media_type;
 
782
}
 
783
 
 
784
/**
 
785
 * tp_call_content_get_disposition:
 
786
 * @self: a #TpCallContent
 
787
 *
 
788
 * <!-- -->
 
789
 *
 
790
 * Returns: the value of #TpCallContent:disposition
 
791
 * Since: 0.17.5
 
792
 */
 
793
TpCallContentDisposition
 
794
tp_call_content_get_disposition (TpCallContent *self)
 
795
{
 
796
  g_return_val_if_fail (TP_IS_CALL_CONTENT (self), 0);
 
797
 
 
798
  return self->priv->disposition;
 
799
}
 
800
 
 
801
/**
 
802
 * tp_call_content_get_streams:
 
803
 * @self: a #TpCallContent
 
804
 *
 
805
 * <!-- -->
 
806
 *
 
807
 * Returns: (transfer none) (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
 
808
 *  the value of #TpCallContent:streams
 
809
 * Since: 0.17.5
 
810
 */
 
811
GPtrArray *
 
812
tp_call_content_get_streams (TpCallContent *self)
 
813
{
 
814
  g_return_val_if_fail (TP_IS_CALL_CONTENT (self), NULL);
 
815
 
 
816
  return self->priv->streams;
 
817
}
 
818
 
 
819
static void
 
820
generic_async_cb (TpCallContent *self,
 
821
    const GError *error,
 
822
    gpointer user_data,
 
823
    GObject *weak_object)
 
824
{
 
825
  GSimpleAsyncResult *result = user_data;
 
826
 
 
827
  if (error != NULL)
 
828
    {
 
829
      DEBUG ("Error: %s", error->message);
 
830
      g_simple_async_result_set_from_error (result, error);
 
831
    }
 
832
 
 
833
  g_simple_async_result_complete (result);
 
834
}
 
835
 
 
836
/**
 
837
 * tp_call_content_remove_async:
 
838
 * @self: a #TpCallContent
 
839
 * @callback: a callback to call when the operation finishes
 
840
 * @user_data: data to pass to @callback
 
841
 *
 
842
 * Remove the content from the call. This will cause #TpCallContent::removed
 
843
 * to be emitted.
 
844
 *
 
845
 * Since: 0.17.5
 
846
 */
 
847
void
 
848
tp_call_content_remove_async (TpCallContent *self,
 
849
    GAsyncReadyCallback callback,
 
850
    gpointer user_data)
 
851
{
 
852
  GSimpleAsyncResult *result;
 
853
 
 
854
  g_return_if_fail (TP_IS_CALL_CONTENT (self));
 
855
 
 
856
  result = g_simple_async_result_new (G_OBJECT (self), callback,
 
857
      user_data, tp_call_content_remove_async);
 
858
 
 
859
  tp_cli_call_content_call_remove (self, -1,
 
860
      generic_async_cb, result, g_object_unref, G_OBJECT (self));
 
861
}
 
862
 
 
863
/**
 
864
 * tp_call_content_remove_finish:
 
865
 * @self: a #TpCallContent
 
866
 * @result: a #GAsyncResult
 
867
 * @error: a #GError to fill
 
868
 *
 
869
 * Finishes tp_call_content_remove_async().
 
870
 *
 
871
 * Since: 0.17.5
 
872
 */
 
873
gboolean
 
874
tp_call_content_remove_finish (TpCallContent *self,
 
875
    GAsyncResult *result,
 
876
    GError **error)
 
877
{
 
878
  _tp_implement_finish_void (self, tp_call_content_remove_async);
 
879
}
 
880
 
 
881
/**
 
882
 * tp_call_content_send_tones_async:
 
883
 * @self: a #TpCallContent
 
884
 * @tones: a string representation of one or more DTMF events.
 
885
 * @cancellable: optional #GCancellable object, %NULL to ignore
 
886
 * @callback: a callback to call when the operation finishes
 
887
 * @user_data: data to pass to @callback
 
888
 *
 
889
 * Send @tones DTMF code on @self content. @self must have the
 
890
 * %TP_IFACE_CALL_CONTENT_INTERFACE_DTMF interface.
 
891
 *
 
892
 * If DTMF tones are already being played, this request is queued.
 
893
 *
 
894
 * Since: 0.17.5
 
895
 */
 
896
void
 
897
tp_call_content_send_tones_async (TpCallContent *self,
 
898
    const gchar *tones,
 
899
    GCancellable *cancellable,
 
900
    GAsyncReadyCallback callback,
 
901
    gpointer user_data)
 
902
{
 
903
  GSimpleAsyncResult *result;
 
904
  SendTonesData *data;
 
905
 
 
906
  g_return_if_fail (TP_IS_CALL_CONTENT (self));
 
907
 
 
908
  if (!tp_proxy_has_interface_by_id (self,
 
909
          TP_IFACE_QUARK_CALL_CONTENT_INTERFACE_DTMF))
 
910
    {
 
911
      g_simple_async_report_error_in_idle (G_OBJECT (self),
 
912
          callback, user_data, TP_ERRORS, TP_ERROR_NOT_CAPABLE,
 
913
          "Content does not support DTMF");
 
914
      return;
 
915
    }
 
916
 
 
917
  result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
 
918
      tp_call_content_send_tones_async);
 
919
 
 
920
  data = send_tones_data_new (self, tones, result, cancellable);
 
921
  g_queue_push_tail (self->priv->tones_queue, data);
 
922
 
 
923
  maybe_send_tones (self);
 
924
 
 
925
  g_object_unref (result);
 
926
}
 
927
 
 
928
/**
 
929
 * tp_call_content_send_tones_finish:
 
930
 * @self: a #TpCallContent
 
931
 * @result: a #GAsyncResult
 
932
 * @error: a #GError to fill
 
933
 *
 
934
 * Finishes tp_call_content_send_tones_async().
 
935
 *
 
936
 * Returns: %TRUE on success, %FALSE otherwise.
 
937
 * Since: 0.17.5
 
938
 */
 
939
gboolean
 
940
tp_call_content_send_tones_finish (TpCallContent *self,
 
941
    GAsyncResult *result,
 
942
    GError **error)
 
943
{
 
944
  _tp_implement_finish_void (self, tp_call_content_send_tones_async);
 
945
}