~ubuntu-branches/ubuntu/precise/empathy/precise-proposed-201205180810

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Brian Curtis, Brian Curtis, Ken VanDine
  • Date: 2011-06-01 10:35:24 UTC
  • mfrom: (1.1.70 upstream) (6.3.44 experimental)
  • Revision ID: james.westby@ubuntu.com-20110601103524-wx3wgp71394730jt
Tags: 3.1.1-1ubuntu1
[ Brian Curtis ]
* Merge with Debian experimental, remaining Ubuntu changes:
* debian/control:
  - Drop geoclue/mapping build-depends (they are in Universe)
  - Add Vcz-Bzr link
  - Add Suggests on telepathy-idle
  - Bump telepathy-butterfly, telepathy-haze to recommends
  - Don't recommend the freedesktop sound theme we have an ubuntu one
  - Add build depend for libunity-dev
* debian/rules:
  - Use autoreconf.mk
  - Disable map and location
* debian/empathy.install:
  - Install message indicator configuration
* debian/indicators/empathy:
  - Message indicator configuration
* debian/patches/01_lpi.patch:
  - Add Launchpad integration
* debian/patches/10_use_notify_osd_icons.patch:
  - Use the notify-osd image for new messages
* debian/patches/34_start_raised_execpt_in_session.patch
  - If not started with the session, we should always raise
* debian/patches/36_chat_window_default_size.patch:
  - Make the default chat window size larger
* debian/patches/37_facebook_default.patch:
  - Make facebook the default chat account type
* debian/patches/38_lp_569289.patch
  - Set freenode as default IRC network for new IRC accounts 
* debian/patches/41_unity_launcher_progress.patch
  - Display file transfer progress in the unity launcher

[ Ken VanDine ]
* debian/control
  - build depend on libgcr-3-dev instead of libgcr-dev
  - dropped build depends for libindicate, we will use telepathy-indicator
  - Depend on dconf-gsettings-backend | gsettings-backend
  - Added a Recommends for telepathy-indicator
* +debian/empathy.gsettings-override
  - Added an override for notifications-focus
* debian/patches/series
  - commented out 23_idomessagedialog_for_voip_and_ft.patch, until ido has 
    been ported to gtk3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * call-content.c - Source for TpyCallContent
3
 
 * Copyright © 2009–2011 Collabora Ltd.
4
 
 * @author Emilio Pozuelo Monfort <emilio.pozuelo@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
 
#include <telepathy-glib/proxy-subclass.h>
22
 
 
23
 
#include "call-content.h"
24
 
 
25
 
#define DEBUG_FLAG TPY_DEBUG_CALL
26
 
#include "debug.h"
27
 
 
28
 
#include <telepathy-yell/call-stream.h>
29
 
#include <telepathy-yell/interfaces.h>
30
 
#include <telepathy-yell/gtypes.h>
31
 
#include <telepathy-yell/enums.h>
32
 
#include <telepathy-yell/cli-call.h>
33
 
 
34
 
G_DEFINE_TYPE (TpyCallContent, tpy_call_content, TP_TYPE_PROXY)
35
 
 
36
 
struct _TpyCallContentPrivate
37
 
{
38
 
  const gchar * const *extra_interfaces;
39
 
  gchar *name;
40
 
  TpMediaStreamType media_type;
41
 
  TpyCallContentDisposition disposition;
42
 
  GList *streams;
43
 
  gboolean ready;
44
 
  gboolean properties_retrieved;
45
 
 
46
 
  GSimpleAsyncResult *result;
47
 
};
48
 
 
49
 
enum
50
 
{
51
 
  PROP_NAME = 1,
52
 
  PROP_MEDIA_TYPE,
53
 
  PROP_DISPOSITION,
54
 
  PROP_STREAMS,
55
 
  PROP_READY
56
 
};
57
 
 
58
 
enum
59
 
{
60
 
  REMOVED,
61
 
  STREAMS_ADDED,
62
 
  STREAMS_REMOVED,
63
 
  LAST_SIGNAL
64
 
};
65
 
 
66
 
static guint _signals[LAST_SIGNAL] = { 0, };
67
 
 
68
 
static void on_call_content_get_all_properties_cb (TpProxy *proxy,
69
 
    GHashTable *properties,
70
 
    const GError *error,
71
 
    gpointer user_data,
72
 
    GObject *weak_object);
73
 
 
74
 
static gint
75
 
find_stream_for_object_path (gconstpointer a,
76
 
    gconstpointer b)
77
 
{
78
 
  return g_strcmp0 (tp_proxy_get_object_path ((gpointer) a), b);
79
 
}
80
 
 
81
 
static void
82
 
on_content_removed_cb (TpProxy *proxy,
83
 
    gpointer user_data,
84
 
    GObject *weak_object)
85
 
{
86
 
  TpyCallContent *self = TPY_CALL_CONTENT (proxy);
87
 
 
88
 
  g_signal_emit (self, _signals[REMOVED], 0);
89
 
}
90
 
 
91
 
static void
92
 
maybe_go_to_ready (TpyCallContent *self)
93
 
{
94
 
  TpyCallContentPrivate *priv = self->priv;
95
 
  GList *l;
96
 
 
97
 
  if (priv->ready)
98
 
    return;
99
 
 
100
 
  if (!priv->properties_retrieved)
101
 
    return;
102
 
 
103
 
 
104
 
  for (l = priv->streams; l != NULL ; l = g_list_next (l))
105
 
    {
106
 
      TpyCallStream *s = l->data;
107
 
      gboolean ready;
108
 
 
109
 
      g_object_get (s, "ready", &ready, NULL);
110
 
 
111
 
      if (!ready)
112
 
        return;
113
 
    }
114
 
 
115
 
  priv->ready = TRUE;
116
 
  g_object_notify (G_OBJECT (self), "ready");
117
 
}
118
 
 
119
 
static void
120
 
on_stream_ready_cb (TpyCallStream *stream,
121
 
  GParamSpec *spec,
122
 
  TpyCallContent *self)
123
 
{
124
 
  maybe_go_to_ready (self);
125
 
}
126
 
 
127
 
static void
128
 
add_streams (TpyCallContent *self, const GPtrArray *streams)
129
 
{
130
 
  GPtrArray *object_streams;
131
 
  guint i;
132
 
 
133
 
  object_streams = g_ptr_array_sized_new (streams->len);
134
 
 
135
 
  for (i = 0; i < streams->len; i++)
136
 
    {
137
 
      TpyCallStream *stream;
138
 
      const gchar *object_path;
139
 
 
140
 
      object_path = g_ptr_array_index (streams, i);
141
 
 
142
 
      stream = g_object_new (TPY_TYPE_CALL_STREAM,
143
 
          "bus-name", tp_proxy_get_bus_name (self),
144
 
          "dbus-daemon", tp_proxy_get_dbus_daemon (self),
145
 
          "dbus-connection", tp_proxy_get_dbus_connection (self),
146
 
          "object-path", object_path,
147
 
          NULL);
148
 
 
149
 
      if (stream == NULL)
150
 
        {
151
 
          g_warning ("Could not create a CallStream for path %s", object_path);
152
 
          continue;
153
 
        }
154
 
 
155
 
      tp_g_signal_connect_object (stream, "notify::ready",
156
 
        G_CALLBACK (on_stream_ready_cb), self, 0);
157
 
 
158
 
      self->priv->streams = g_list_prepend (self->priv->streams, stream);
159
 
      g_ptr_array_add (object_streams, stream);
160
 
    }
161
 
 
162
 
  g_signal_emit (self, _signals[STREAMS_ADDED], 0, object_streams);
163
 
  g_ptr_array_unref (object_streams);
164
 
}
165
 
 
166
 
static void
167
 
on_streams_added_cb (TpProxy *proxy,
168
 
    const GPtrArray *streams,
169
 
    gpointer user_data,
170
 
    GObject *weak_object)
171
 
{
172
 
  TpyCallContent *self = TPY_CALL_CONTENT (proxy);
173
 
 
174
 
  add_streams (self, streams);
175
 
}
176
 
 
177
 
static void
178
 
on_streams_removed_cb (TpProxy *proxy,
179
 
    const GPtrArray *streams,
180
 
    gpointer user_data,
181
 
    GObject *weak_object)
182
 
{
183
 
  TpyCallContent *self = TPY_CALL_CONTENT (proxy);
184
 
  GPtrArray *object_streams;
185
 
  guint i;
186
 
 
187
 
  object_streams = g_ptr_array_sized_new (streams->len);
188
 
  g_ptr_array_set_free_func (object_streams, g_object_unref);
189
 
 
190
 
  for (i = 0; i < streams->len; i++)
191
 
    {
192
 
      GList *s;
193
 
      const gchar *object_path;
194
 
 
195
 
      object_path = g_ptr_array_index (streams, i);
196
 
 
197
 
      s = g_list_find_custom (self->priv->streams,
198
 
          object_path,
199
 
          find_stream_for_object_path);
200
 
 
201
 
      if (s == NULL)
202
 
        {
203
 
          g_warning ("Could not find a CallStream for path %s", object_path);
204
 
          continue;
205
 
        }
206
 
 
207
 
      self->priv->streams = g_list_remove_link (self->priv->streams, s);
208
 
      g_ptr_array_add (object_streams, s->data);
209
 
    }
210
 
 
211
 
  g_signal_emit (self, _signals[STREAMS_REMOVED], 0, object_streams);
212
 
 
213
 
  g_ptr_array_unref (object_streams);
214
 
}
215
 
 
216
 
static void
217
 
tpy_call_content_constructed (GObject *obj)
218
 
{
219
 
  TpyCallContent *self = (TpyCallContent *) obj;
220
 
  GError *error = NULL;
221
 
 
222
 
  ((GObjectClass *) tpy_call_content_parent_class)->constructed (obj);
223
 
 
224
 
  tpy_cli_call_content_connect_to_removed (TP_PROXY (self),
225
 
      on_content_removed_cb, NULL, NULL, G_OBJECT (self), &error);
226
 
 
227
 
  if (error != NULL)
228
 
    {
229
 
      g_critical ("Failed to connect to Removed signal: %s",
230
 
          error->message);
231
 
 
232
 
      g_error_free (error);
233
 
      return;
234
 
    }
235
 
 
236
 
  tpy_cli_call_content_connect_to_streams_added (TP_PROXY (self),
237
 
      on_streams_added_cb, NULL, NULL, G_OBJECT (self), &error);
238
 
 
239
 
  if (error != NULL)
240
 
    {
241
 
      g_critical ("Failed to connect to StreamsAdded signal: %s",
242
 
          error->message);
243
 
 
244
 
      g_error_free (error);
245
 
      return;
246
 
    }
247
 
 
248
 
  tpy_cli_call_content_connect_to_streams_removed (TP_PROXY (self),
249
 
      on_streams_removed_cb, NULL, NULL, G_OBJECT (self), &error);
250
 
 
251
 
  if (error != NULL)
252
 
    {
253
 
      g_critical ("Failed to connect to StreamsRemoved signal: %s",
254
 
          error->message);
255
 
 
256
 
      g_error_free (error);
257
 
      return;
258
 
    }
259
 
 
260
 
  tp_cli_dbus_properties_call_get_all (self, -1,
261
 
      TPY_IFACE_CALL_CONTENT,
262
 
      on_call_content_get_all_properties_cb, NULL, NULL, G_OBJECT (self));
263
 
}
264
 
 
265
 
static void
266
 
tpy_call_content_init (TpyCallContent *self)
267
 
{
268
 
  TpyCallContentPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
269
 
      TPY_TYPE_CALL_CONTENT, TpyCallContentPrivate);
270
 
 
271
 
  self->priv = priv;
272
 
}
273
 
 
274
 
static void
275
 
tpy_call_content_dispose (GObject *object)
276
 
{
277
 
  TpyCallContent *self = TPY_CALL_CONTENT (object);
278
 
 
279
 
  tp_clear_pointer (&self->priv->name, g_free);
280
 
  tp_clear_object (&self->priv->result);
281
 
 
282
 
  g_list_free_full (self->priv->streams, g_object_unref);
283
 
  self->priv->streams = NULL;
284
 
 
285
 
  G_OBJECT_CLASS (tpy_call_content_parent_class)->dispose (object);
286
 
}
287
 
 
288
 
static void
289
 
tpy_call_content_get_property (
290
 
    GObject *object,
291
 
    guint property_id,
292
 
    GValue *value,
293
 
    GParamSpec *pspec)
294
 
{
295
 
  TpyCallContent *self = TPY_CALL_CONTENT (object);
296
 
 
297
 
  switch (property_id)
298
 
    {
299
 
      case PROP_NAME:
300
 
        g_value_set_string (value, self->priv->name);
301
 
        break;
302
 
      case PROP_MEDIA_TYPE:
303
 
        g_value_set_uint (value, self->priv->media_type);
304
 
        break;
305
 
      case PROP_DISPOSITION:
306
 
        g_value_set_uint (value, self->priv->disposition);
307
 
        break;
308
 
      case PROP_STREAMS:
309
 
        g_value_set_boxed (value, self->priv->streams);
310
 
        break;
311
 
      case PROP_READY:
312
 
        g_value_set_boolean (value, self->priv->ready);
313
 
        break;
314
 
      default:
315
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
316
 
        break;
317
 
    }
318
 
}
319
 
 
320
 
static void
321
 
tpy_call_content_set_property (
322
 
    GObject *object,
323
 
    guint property_id,
324
 
    const GValue *value,
325
 
    GParamSpec *pspec)
326
 
{
327
 
  TpyCallContent *self = TPY_CALL_CONTENT (object);
328
 
 
329
 
  switch (property_id)
330
 
    {
331
 
      case PROP_NAME:
332
 
        g_assert (self->priv->name == NULL);
333
 
        self->priv->name = g_value_dup_string (value);
334
 
        break;
335
 
      case PROP_MEDIA_TYPE:
336
 
        self->priv->media_type = g_value_get_uint (value);
337
 
        break;
338
 
      case PROP_DISPOSITION:
339
 
        self->priv->disposition = g_value_get_uint (value);
340
 
        break;
341
 
      default:
342
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
343
 
        break;
344
 
    }
345
 
}
346
 
 
347
 
static void
348
 
tpy_call_content_class_init (
349
 
    TpyCallContentClass *klass)
350
 
{
351
 
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
352
 
  TpProxyClass *proxy_class = TP_PROXY_CLASS (klass);
353
 
  GParamSpec *param_spec;
354
 
 
355
 
  g_type_class_add_private (klass, sizeof (TpyCallContentPrivate));
356
 
 
357
 
  object_class->constructed = tpy_call_content_constructed;
358
 
  object_class->dispose = tpy_call_content_dispose;
359
 
  object_class->get_property = tpy_call_content_get_property;
360
 
  object_class->set_property = tpy_call_content_set_property;
361
 
 
362
 
  proxy_class->interface = TPY_IFACE_QUARK_CALL_CONTENT;
363
 
 
364
 
  param_spec = g_param_spec_string ("name", "Name",
365
 
      "The name of this content, if any",
366
 
      "",
367
 
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
368
 
  g_object_class_install_property (object_class, PROP_NAME, param_spec);
369
 
 
370
 
  param_spec = g_param_spec_uint ("media-type", "Media Type",
371
 
      "The media type of this content",
372
 
      0, G_MAXUINT, 0,
373
 
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
374
 
  g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec);
375
 
 
376
 
  param_spec = g_param_spec_uint ("disposition", "Disposition",
377
 
      "The disposition of this content",
378
 
      0, G_MAXUINT, 0,
379
 
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
380
 
  g_object_class_install_property (object_class, PROP_DISPOSITION, param_spec);
381
 
 
382
 
  param_spec = g_param_spec_boxed ("streams", "Stream",
383
 
      "The streams of this content",
384
 
      TP_ARRAY_TYPE_OBJECT_PATH_LIST,
385
 
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
386
 
  g_object_class_install_property (object_class, PROP_STREAMS,
387
 
      param_spec);
388
 
 
389
 
  param_spec = g_param_spec_boolean ("ready",
390
 
      "Ready",
391
 
      "If true the content and all its streams have retrieved all"
392
 
      "all async information from the CM",
393
 
      FALSE,
394
 
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
395
 
  g_object_class_install_property (object_class, PROP_READY,
396
 
      param_spec);
397
 
 
398
 
  /**
399
 
   * TpyCallContent::removed
400
 
   * @self: the #TpyCallContent
401
 
   *
402
 
   * The ::removed signal is emitted when @self is removed from
403
 
   * a #TpyCallChannel.
404
 
   */
405
 
  _signals[REMOVED] = g_signal_new ("removed",
406
 
      G_OBJECT_CLASS_TYPE (klass),
407
 
      G_SIGNAL_RUN_LAST,
408
 
      0, NULL, NULL,
409
 
      g_cclosure_marshal_VOID__VOID,
410
 
      G_TYPE_NONE,
411
 
      0);
412
 
 
413
 
  /**
414
 
   * TpyCallContent::streams-added
415
 
   * @self: the #TpyCallContent
416
 
   * @stream: the newly added streams
417
 
   *
418
 
   * The ::content-removed signal is emitted whenever
419
 
   * #TpyCallContent are added from @self.
420
 
   */
421
 
  _signals[STREAMS_ADDED] = g_signal_new ("streams-added",
422
 
      G_OBJECT_CLASS_TYPE (klass),
423
 
      G_SIGNAL_RUN_LAST,
424
 
      0, NULL, NULL,
425
 
      g_cclosure_marshal_VOID__BOXED,
426
 
      G_TYPE_NONE,
427
 
      1, G_TYPE_PTR_ARRAY);
428
 
 
429
 
  /**
430
 
   * TpyCallContent::streams-removed
431
 
   * @self: the #TpyCallContent
432
 
   * @stream: the newly removed streams
433
 
   *
434
 
   * The ::content-removed signal is emitted whenever
435
 
   * #TpyCallContent are removed from @self.
436
 
   */
437
 
  _signals[STREAMS_REMOVED] = g_signal_new ("streams-removed",
438
 
      G_OBJECT_CLASS_TYPE (klass),
439
 
      G_SIGNAL_RUN_LAST,
440
 
      0, NULL, NULL,
441
 
      g_cclosure_marshal_VOID__BOXED,
442
 
      G_TYPE_NONE,
443
 
      1, G_TYPE_PTR_ARRAY);
444
 
}
445
 
 
446
 
const gchar *
447
 
tpy_call_content_get_name (TpyCallContent *self)
448
 
{
449
 
  g_return_val_if_fail (TPY_IS_CALL_CONTENT (self), NULL);
450
 
 
451
 
  return self->priv->name;
452
 
}
453
 
 
454
 
TpMediaStreamType
455
 
tpy_call_content_get_media_type (TpyCallContent *self)
456
 
{
457
 
  g_return_val_if_fail (TPY_IS_CALL_CONTENT (self),
458
 
      TP_MEDIA_STREAM_TYPE_AUDIO);
459
 
 
460
 
  return self->priv->media_type;
461
 
}
462
 
 
463
 
TpyCallContentDisposition
464
 
tpy_call_content_get_disposition (TpyCallContent *self)
465
 
{
466
 
  g_return_val_if_fail (TPY_IS_CALL_CONTENT (self),
467
 
      TPY_CALL_CONTENT_DISPOSITION_NONE);
468
 
 
469
 
  return self->priv->disposition;
470
 
}
471
 
 
472
 
static void
473
 
on_call_content_get_all_properties_cb (TpProxy *proxy,
474
 
    GHashTable *properties,
475
 
    const GError *error,
476
 
    gpointer user_data,
477
 
    GObject *weak_object)
478
 
{
479
 
  TpyCallContent *self = TPY_CALL_CONTENT (proxy);
480
 
  GPtrArray *streams;
481
 
 
482
 
  if (error != NULL)
483
 
    {
484
 
      g_warning ("Could not get the content properties: %s", error->message);
485
 
      return;
486
 
    }
487
 
 
488
 
  self->priv->media_type = tp_asv_get_uint32 (properties, "Type", NULL);
489
 
  g_free (self->priv->name);
490
 
  self->priv->name = g_strdup (tp_asv_get_string (properties, "Name"));
491
 
  self->priv->disposition = tp_asv_get_uint32 (properties, "Disposition",
492
 
    NULL);
493
 
 
494
 
  streams = tp_asv_get_boxed (properties, "Streams",
495
 
    TP_ARRAY_TYPE_OBJECT_PATH_LIST);
496
 
 
497
 
  if (streams != NULL)
498
 
    add_streams (self, streams);
499
 
 
500
 
  self->priv->properties_retrieved = TRUE;
501
 
  maybe_go_to_ready (self);
502
 
}
503
 
 
504
 
static void
505
 
on_content_remove_cb (TpProxy *proxy,
506
 
    const GError *error,
507
 
    gpointer user_data,
508
 
    GObject *weak_object)
509
 
{
510
 
  TpyCallContent *self = TPY_CALL_CONTENT (proxy);
511
 
 
512
 
  if (error != NULL)
513
 
    {
514
 
      DEBUG ("Failed to remove content: %s", error->message);
515
 
 
516
 
      g_simple_async_result_set_from_error (self->priv->result, error);
517
 
    }
518
 
 
519
 
  g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
520
 
  g_simple_async_result_complete (self->priv->result);
521
 
  tp_clear_object (&self->priv->result);
522
 
}
523
 
 
524
 
void
525
 
tpy_call_content_remove_async (TpyCallContent *self,
526
 
    TpyContentRemovalReason reason,
527
 
    const gchar *detailed_removal_reason,
528
 
    const gchar *message,
529
 
    GAsyncReadyCallback callback,
530
 
    gpointer user_data)
531
 
{
532
 
  DEBUG ("removing content for reason %u, detailed reason: %s, message: %s",
533
 
      reason, detailed_removal_reason, message);
534
 
 
535
 
  tpy_cli_call_content_call_remove (TP_PROXY (self), -1,
536
 
      reason, detailed_removal_reason, message,
537
 
      on_content_remove_cb, NULL, NULL, G_OBJECT (self));
538
 
}
539
 
 
540
 
gboolean
541
 
tpy_call_content_remove_finish (TpyCallContent *self,
542
 
    GAsyncResult *result,
543
 
    GError **error)
544
 
{
545
 
  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
546
 
      error))
547
 
    return FALSE;
548
 
 
549
 
  g_return_val_if_fail (g_simple_async_result_is_valid (result,
550
 
    G_OBJECT (self), tpy_call_content_remove_async),
551
 
    FALSE);
552
 
 
553
 
  return g_simple_async_result_get_op_res_gboolean (
554
 
    G_SIMPLE_ASYNC_RESULT (result));
555
 
}
556
 
 
557
 
GList *
558
 
tpy_call_content_get_streams (TpyCallContent *self)
559
 
{
560
 
  return self->priv->streams;
561
 
}