2
* call-stream.h - high level API for Call streams
4
* Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
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.
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.
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
23
* @title: TpCallStream
24
* @short_description: proxy object for a call stream
26
* #TpCallStream is a sub-class of #TpProxy providing convenient API
27
* to represent #TpCallChannel's stream.
33
* Data structure representing a #TpCallStream.
41
* The class of a #TpCallStream.
48
#include "telepathy-glib/call-stream.h"
50
#include <telepathy-glib/call-misc.h>
51
#include <telepathy-glib/dbus.h>
52
#include <telepathy-glib/enums.h>
53
#include <telepathy-glib/errors.h>
54
#include <telepathy-glib/gtypes.h>
55
#include <telepathy-glib/interfaces.h>
56
#include <telepathy-glib/proxy-subclass.h>
57
#include <telepathy-glib/util.h>
59
#define DEBUG_FLAG TP_DEBUG_CALL
60
#include "telepathy-glib/debug-internal.h"
61
#include "telepathy-glib/call-internal.h"
62
#include "telepathy-glib/proxy-internal.h"
63
#include "telepathy-glib/util-internal.h"
64
#include "telepathy-glib/_gen/signals-marshal.h"
66
#include "_gen/tp-cli-call-stream-body.h"
68
G_DEFINE_TYPE (TpCallStream, tp_call_stream, TP_TYPE_PROXY)
70
struct _TpCallStreamPrivate
72
TpConnection *connection;
74
/* TpContact -> TpSendingState */
75
GHashTable *remote_members;
76
TpSendingState local_sending_state;
77
gboolean can_request_receiving;
79
gboolean properties_retrieved;
85
PROP_LOCAL_SENDING_STATE,
86
PROP_CAN_REQUEST_RECEIVING,
91
LOCAL_SENDING_STATE_CHANGED,
92
REMOTE_MEMBERS_CHANGED,
96
static guint _signals[LAST_SIGNAL] = { 0, };
99
update_remote_members (TpCallStream *self,
105
tp_g_hash_table_update (self->priv->remote_members, updates,
113
for (i = 0; i < removed->len; i++)
115
g_hash_table_remove (self->priv->remote_members,
116
g_ptr_array_index (removed, i));
122
remote_members_changed_cb (TpCallStream *self,
124
GHashTable *identifiers,
125
const GArray *removed,
126
const GValueArray *reason,
128
GObject *weak_object)
130
GHashTable *updates_contacts;
131
GPtrArray *removed_contacts;
132
TpCallStateReason *r;
134
if (!self->priv->properties_retrieved)
137
DEBUG ("Remote members: %d updated, %d removed",
138
g_hash_table_size (updates), removed->len);
140
updates_contacts = _tp_call_members_convert_table (self->priv->connection,
141
updates, identifiers);
142
removed_contacts = _tp_call_members_convert_array (self->priv->connection,
144
r = _tp_call_state_reason_new (reason);
146
update_remote_members (self, updates_contacts, removed_contacts);
148
g_signal_emit (self, _signals[REMOTE_MEMBERS_CHANGED], 0,
149
updates_contacts, removed_contacts, r);
151
g_hash_table_unref (updates_contacts);
152
g_ptr_array_unref (removed_contacts);
153
_tp_call_state_reason_unref (r);
157
local_sending_state_changed_cb (TpCallStream *self,
159
const GValueArray *reason,
161
GObject *weak_object)
163
TpCallStateReason *r;
165
if (!self->priv->properties_retrieved)
168
self->priv->local_sending_state = state;
169
g_object_notify (G_OBJECT (self), "local-sending-state");
171
r = _tp_call_state_reason_new (reason);
172
g_signal_emit (self, _signals[LOCAL_SENDING_STATE_CHANGED], 0,
173
self->priv->local_sending_state, r);
174
_tp_call_state_reason_unref (r);
178
got_all_properties_cb (TpProxy *proxy,
179
GHashTable *properties,
182
GObject *weak_object)
184
TpCallStream *self = (TpCallStream *) proxy;
185
const gchar * const *interfaces;
186
GHashTable *remote_members;
187
GHashTable *identifiers;
188
GHashTable *contacts;
192
DEBUG ("Could not get the call stream properties: %s", error->message);
193
_tp_proxy_set_feature_prepared (proxy,
194
TP_CALL_STREAM_FEATURE_CORE, FALSE);
198
self->priv->properties_retrieved = TRUE;
200
interfaces = tp_asv_get_boxed (properties,
201
"Interfaces", G_TYPE_STRV);
202
remote_members = tp_asv_get_boxed (properties,
203
"RemoteMembers", TP_HASH_TYPE_CONTACT_SENDING_STATE_MAP),
204
identifiers = tp_asv_get_boxed (properties,
205
"RemoteMemberIdentifiers", TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP);
206
self->priv->local_sending_state = tp_asv_get_uint32 (properties,
207
"LocalSendingState", NULL);
208
self->priv->can_request_receiving = tp_asv_get_boolean (properties,
209
"CanRequestReceiving", NULL);
211
tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
213
contacts = _tp_call_members_convert_table (self->priv->connection,
214
remote_members, identifiers);
215
update_remote_members (self, contacts, NULL);
216
g_hash_table_unref (contacts);
218
_tp_proxy_set_feature_prepared (proxy, TP_CALL_STREAM_FEATURE_CORE, TRUE);
222
tp_call_stream_constructed (GObject *obj)
224
TpCallStream *self = (TpCallStream *) obj;
226
((GObjectClass *) tp_call_stream_parent_class)->constructed (obj);
228
/* Connect signals for mutable properties */
229
tp_cli_call_stream_connect_to_remote_members_changed (self,
230
remote_members_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
231
tp_cli_call_stream_connect_to_local_sending_state_changed (self,
232
local_sending_state_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
234
tp_cli_dbus_properties_call_get_all (self, -1,
235
TP_IFACE_CALL_STREAM,
236
got_all_properties_cb, NULL, NULL, G_OBJECT (self));
240
tp_call_stream_dispose (GObject *object)
242
TpCallStream *self = (TpCallStream *) object;
244
g_clear_object (&self->priv->connection);
245
tp_clear_pointer (&self->priv->remote_members, g_hash_table_unref);
247
G_OBJECT_CLASS (tp_call_stream_parent_class)->dispose (object);
251
tp_call_stream_get_property (GObject *object,
256
TpCallStream *self = (TpCallStream *) object;
257
TpCallStreamPrivate *priv = self->priv;
261
case PROP_CONNECTION:
262
g_value_set_object (value, self->priv->connection);
264
case PROP_LOCAL_SENDING_STATE:
265
g_value_set_uint (value, priv->local_sending_state);
267
case PROP_CAN_REQUEST_RECEIVING:
268
g_value_set_boolean (value, priv->can_request_receiving);
271
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
277
tp_call_stream_set_property (GObject *object,
282
TpCallStream *self = (TpCallStream *) object;
286
case PROP_CONNECTION:
287
g_assert (self->priv->connection == NULL); /* construct-only */
288
self->priv->connection = g_value_dup_object (value);
291
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
301
static const TpProxyFeature *
302
tp_call_stream_list_features (TpProxyClass *cls G_GNUC_UNUSED)
304
static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
306
if (G_LIKELY (features[0].name != 0))
309
/* started from constructed */
310
features[FEAT_CORE].name = TP_CALL_STREAM_FEATURE_CORE;
311
features[FEAT_CORE].core = TRUE;
313
/* assert that the terminator at the end is there */
314
g_assert (features[N_FEAT].name == 0);
320
tp_call_stream_class_init (TpCallStreamClass *klass)
322
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
323
TpProxyClass *proxy_class = (TpProxyClass *) klass;
324
GParamSpec *param_spec;
326
gobject_class->constructed = tp_call_stream_constructed;
327
gobject_class->get_property = tp_call_stream_get_property;
328
gobject_class->set_property = tp_call_stream_set_property;
329
gobject_class->dispose = tp_call_stream_dispose;
331
proxy_class->list_features = tp_call_stream_list_features;
332
proxy_class->interface = TP_IFACE_QUARK_CALL_STREAM;
334
g_type_class_add_private (gobject_class, sizeof (TpCallStreamPrivate));
335
tp_call_stream_init_known_interfaces ();
338
* TpCallStream:connection:
340
* The #TpConnection of the call.
344
param_spec = g_param_spec_object ("connection", "Connection",
345
"The connection of this content",
347
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
348
g_object_class_install_property (gobject_class, PROP_CONNECTION,
352
* TpCallStream:local-sending-state:
354
* The local user's sending state, from #TpSendingState.
358
param_spec = g_param_spec_uint ("local-sending-state", "LocalSendingState",
359
"Local sending state",
361
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
362
g_object_class_install_property (gobject_class, PROP_LOCAL_SENDING_STATE,
366
* TpCallStream:can-request-receiving:
368
* If %TRUE, the user can request that a remote contact starts sending on this
373
param_spec = g_param_spec_boolean ("can-request-receiving",
374
"CanRequestReceiving",
375
"If true, the user can request that a remote contact starts sending on"
378
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
379
g_object_class_install_property (gobject_class, PROP_CAN_REQUEST_RECEIVING,
383
* TpCallStream::local-sending-state-changed
384
* @self: the #TpCallStream
385
* @state: the new #TpSendingState
386
* @reason: the #TpCallStateReason for the change
388
* The ::local-sending-state-changed signal is emitted whenever the
389
* stream sending state changes.
393
_signals[LOCAL_SENDING_STATE_CHANGED] = g_signal_new ("local-sending-state-changed",
394
G_OBJECT_CLASS_TYPE (klass),
397
_tp_marshal_VOID__UINT_BOXED,
399
4, G_TYPE_UINT, G_TYPE_UINT, TP_TYPE_CALL_STATE_REASON,
403
* TpCallStream::remote-members-changed
404
* @self: the #TpCallStream
405
* @updates: (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
406
* #GHashTable mapping #TpContact to its new #TpSendingState
407
* @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
408
* #GPtrArray of #TpContact removed from remote contacts
409
* @reason: the #TpCallStateReason for the change
411
* The ::remote-members-changed signal is emitted whenever the
412
* stream's remote members changes.
414
* It is NOT guaranteed that #TpContact objects have any feature prepared.
418
_signals[REMOTE_MEMBERS_CHANGED] = g_signal_new ("remote-members-changed",
419
G_OBJECT_CLASS_TYPE (klass),
422
_tp_marshal_VOID__BOXED_BOXED_BOXED,
424
3, G_TYPE_HASH_TABLE, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON);
428
tp_call_stream_init (TpCallStream *self)
430
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_STREAM,
431
TpCallStreamPrivate);
433
self->priv->remote_members = g_hash_table_new_full (NULL, NULL,
434
g_object_unref, NULL);
438
* tp_call_stream_init_known_interfaces:
440
* Ensure that the known interfaces for #TpCallStream have been set up.
441
* This is done automatically when necessary, but for correct
442
* overriding of library interfaces by local extensions, you should
443
* call this function before calling
444
* tp_proxy_or_subclass_hook_on_interface_add() with first argument
445
* %TP_TYPE_CALL_STREAM.
450
tp_call_stream_init_known_interfaces (void)
452
static gsize once = 0;
454
if (g_once_init_enter (&once))
456
GType tp_type = TP_TYPE_CALL_STREAM;
458
tp_proxy_init_known_interfaces ();
459
tp_proxy_or_subclass_hook_on_interface_add (tp_type,
460
tp_cli_call_stream_add_signals);
461
tp_proxy_subclass_add_error_mapping (tp_type,
462
TP_ERROR_PREFIX, TP_ERRORS, TP_TYPE_ERROR);
464
g_once_init_leave (&once, 1);
469
* TP_CALL_STREAM_FEATURE_CORE:
471
* Expands to a call to a function that returns a quark for the "core"
472
* feature on a #TpCallStream.
474
* One can ask for a feature to be prepared using the tp_proxy_prepare_async()
475
* function, and waiting for it to trigger the callback.
478
tp_call_stream_get_feature_quark_core (void)
480
return g_quark_from_static_string ("tp-call-stream-feature-core");
484
* tp_call_stream_get_local_sending_state:
485
* @self: a #TpCallStream
489
* Returns: the value of #TpCallStream:local-seding-state
493
tp_call_stream_get_local_sending_state (TpCallStream *self)
495
g_return_val_if_fail (TP_IS_CALL_STREAM (self), TP_SENDING_STATE_NONE);
497
return self->priv->local_sending_state;
501
* tp_call_stream_can_request_receiving:
502
* @self: a #TpCallStream
506
* Returns: the value of #TpCallStream:can-request-receiving
510
tp_call_stream_can_request_receiving (TpCallStream *self)
512
g_return_val_if_fail (TP_IS_CALL_STREAM (self), FALSE);
514
return self->priv->can_request_receiving;
518
* tp_call_stream_get_remote_members:
519
* @self: a #TpCallStream
521
* Get the remote contacts to who this stream is connected, mapped to their
524
* It is NOT guaranteed that #TpContact objects have any feature prepared.
526
* Returns: (transfer none) (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
527
* #GHashTable mapping #TpContact to its new #TpSendingState
531
tp_call_stream_get_remote_members (TpCallStream *self)
533
g_return_val_if_fail (TP_IS_CALL_STREAM (self), NULL);
535
return self->priv->remote_members;
539
generic_async_cb (TpCallStream *self,
542
GObject *weak_object)
544
GSimpleAsyncResult *result = user_data;
548
DEBUG ("Error: %s", error->message);
549
g_simple_async_result_set_from_error (result, error);
552
g_simple_async_result_complete (result);
556
* tp_call_stream_set_sending_async:
557
* @self: a #TpCallStream
558
* @send: the requested sending state
559
* @callback: a callback to call when the operation finishes
560
* @user_data: data to pass to @callback
562
* Set the stream to start or stop sending media from the local user to other
565
* If @send is %TRUE, #TpCallStream:local-sending-state should change to
566
* %TP_SENDING_STATE_SENDING, if it isn't already.
567
* If @send is %FALSE, #TpCallStream:local-sending-state should change to
568
* %TP_SENDING_STATE_NONE, if it isn't already.
573
tp_call_stream_set_sending_async (TpCallStream *self,
575
GAsyncReadyCallback callback,
578
GSimpleAsyncResult *result;
580
g_return_if_fail (TP_IS_CALL_STREAM (self));
582
result = g_simple_async_result_new (G_OBJECT (self), callback,
583
user_data, tp_call_stream_set_sending_async);
585
tp_cli_call_stream_call_set_sending (self, -1, send,
586
generic_async_cb, result, g_object_unref, G_OBJECT (self));
590
* tp_call_stream_set_sending_finish:
591
* @self: a #TpCallStream
592
* @result: a #GAsyncResult
593
* @error: a #GError to fill
595
* Finishes tp_call_stream_set_sending_async().
600
tp_call_stream_set_sending_finish (TpCallStream *self,
601
GAsyncResult *result,
604
_tp_implement_finish_void (self, tp_call_stream_set_sending_async);
608
* tp_call_stream_request_receiving_async:
609
* @self: a #TpCallStream
610
* @contact: contact from which sending is requested
611
* @receive: the requested receiving state
612
* @callback: a callback to call when the operation finishes
613
* @user_data: data to pass to @callback
615
* Request that a remote contact stops or starts sending on this stream.
617
* The #TpCallStream:can-request-receiving property defines whether the protocol
618
* allows the local user to request the other side start sending on this stream.
620
* If @receive is %TRUE, request that the given contact starts to send media.
621
* If @receive is %FALSE, request that the given contact stops sending media.
626
tp_call_stream_request_receiving_async (TpCallStream *self,
629
GAsyncReadyCallback callback,
632
GSimpleAsyncResult *result;
634
g_return_if_fail (TP_IS_CALL_STREAM (self));
635
g_return_if_fail (TP_IS_CONTACT (contact));
636
g_return_if_fail (tp_contact_get_connection (contact) ==
637
self->priv->connection);
639
result = g_simple_async_result_new (G_OBJECT (self), callback,
640
user_data, tp_call_stream_set_sending_async);
642
tp_cli_call_stream_call_request_receiving (self, -1,
643
tp_contact_get_handle (contact), receive,
644
generic_async_cb, result, g_object_unref, G_OBJECT (self));
648
* tp_call_stream_request_receiving_finish:
649
* @self: a #TpCallStream
650
* @result: a #GAsyncResult
651
* @error: a #GError to fill
653
* Finishes tp_call_stream_request_receiving_async().
658
tp_call_stream_request_receiving_finish (TpCallStream *self,
659
GAsyncResult *result,
662
_tp_implement_finish_void (self, tp_call_stream_request_receiving_async);