1 /* |
|
2 * gabble-media-channel.c - Source for GabbleMediaChannel |
|
3 * Copyright (C) 2006 Collabora Ltd. |
|
4 * |
|
5 * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Lesser General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2.1 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Lesser General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Lesser General Public |
|
18 * License along with this library; if not, write to the Free Software |
|
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
20 */ |
|
21 |
|
22 #include <dbus/dbus-glib.h> |
|
23 #include <stdio.h> |
|
24 #include <stdlib.h> |
|
25 |
|
26 |
|
27 #include "ansi.h" |
|
28 #include "debug.h" |
|
29 #include "gabble-connection.h" |
|
30 #include "gabble-media-session.h" |
|
31 #include "gabble-presence.h" |
|
32 #include "gabble-presence-cache.h" |
|
33 |
|
34 #include "telepathy-errors.h" |
|
35 #include "telepathy-helpers.h" |
|
36 #include "telepathy-interfaces.h" |
|
37 #include "tp-channel-iface.h" |
|
38 |
|
39 #include "gabble-media-channel.h" |
|
40 #include "gabble-media-channel-signals-marshal.h" |
|
41 #include "gabble-media-channel-glue.h" |
|
42 |
|
43 #include "gabble-media-session.h" |
|
44 #include "gabble-media-stream.h" |
|
45 |
|
46 #include "media-factory.h" |
|
47 |
|
48 #include "gabble_enums.h" |
|
49 |
|
50 #define DEBUG_FLAG GABBLE_DEBUG_MEDIA |
|
51 |
|
52 #define TP_SESSION_HANDLER_SET_TYPE (dbus_g_type_get_struct ("GValueArray", \ |
|
53 DBUS_TYPE_G_OBJECT_PATH, \ |
|
54 G_TYPE_STRING, \ |
|
55 G_TYPE_INVALID)) |
|
56 |
|
57 #define TP_CHANNEL_STREAM_TYPE (dbus_g_type_get_struct ("GValueArray", \ |
|
58 G_TYPE_UINT, \ |
|
59 G_TYPE_UINT, \ |
|
60 G_TYPE_UINT, \ |
|
61 G_TYPE_UINT, \ |
|
62 G_TYPE_UINT, \ |
|
63 G_TYPE_UINT, \ |
|
64 G_TYPE_INVALID)) |
|
65 |
|
66 #ifndef EMULATOR |
|
67 G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel, |
|
68 G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)); |
|
69 #endif |
|
70 |
|
71 /* signal enum */ |
|
72 enum |
|
73 { |
|
74 CLOSED, |
|
75 NEW_SESSION_HANDLER, |
|
76 STREAM_ADDED, |
|
77 STREAM_DIRECTION_CHANGED, |
|
78 STREAM_ERROR, |
|
79 STREAM_REMOVED, |
|
80 STREAM_STATE_CHANGED, |
|
81 LAST_SIGNAL |
|
82 #ifdef EMULATOR |
|
83 = LAST_SIGNAL_MED_CHANNEL |
|
84 #endif |
|
85 |
|
86 }; |
|
87 |
|
88 |
|
89 #ifdef EMULATOR |
|
90 #include "libgabble_wsd_solution.h" |
|
91 |
|
92 GET_STATIC_ARRAY_FROM_TLS(signals,gabble_med_chnl,guint) |
|
93 #define signals (GET_WSD_VAR_NAME(signals,gabble_med_chnl, s)()) |
|
94 |
|
95 GET_STATIC_VAR_FROM_TLS(gabble_media_channel_parent_class,gabble_med_chnl,gpointer) |
|
96 #define gabble_media_channel_parent_class (*GET_WSD_VAR_NAME(gabble_media_channel_parent_class,gabble_med_chnl,s)()) |
|
97 |
|
98 GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_med_chnl,GType) |
|
99 #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_med_chnl,s)()) |
|
100 |
|
101 static void gabble_media_channel_init (GabbleMediaChannel *self); |
|
102 static void gabble_media_channel_class_init (GabbleMediaChannelClass *klass); |
|
103 static void gabble_media_channel_class_intern_init (gpointer klass) |
|
104 { |
|
105 gabble_media_channel_parent_class = g_type_class_peek_parent (klass); |
|
106 gabble_media_channel_class_init ((GabbleMediaChannelClass*) klass); |
|
107 } |
|
108 EXPORT_C GType gabble_media_channel_get_type (void) |
|
109 { |
|
110 if ((g_define_type_id == 0)) |
|
111 { static const GTypeInfo g_define_type_info = { sizeof (GabbleMediaChannelClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_media_channel_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMediaChannel), 0, (GInstanceInitFunc) gabble_media_channel_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMediaChannel"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) ((void *)0) }; g_type_add_interface_static (g_define_type_id, tp_channel_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; |
|
112 }; |
|
113 |
|
114 #else |
|
115 |
|
116 static guint signals[LAST_SIGNAL] = {0}; |
|
117 |
|
118 #endif |
|
119 |
|
120 |
|
121 /* properties */ |
|
122 enum |
|
123 { |
|
124 PROP_OBJECT_PATH = 1, |
|
125 PROP_CHANNEL_TYPE, |
|
126 PROP_HANDLE_TYPE, |
|
127 PROP_HANDLE, |
|
128 PROP_CONNECTION, |
|
129 PROP_CREATOR, |
|
130 PROP_FACTORY, |
|
131 LAST_PROPERTY |
|
132 }; |
|
133 |
|
134 /* private structure */ |
|
135 typedef struct _GabbleMediaChannelPrivate GabbleMediaChannelPrivate; |
|
136 |
|
137 struct _GabbleMediaChannelPrivate |
|
138 { |
|
139 GabbleConnection *conn; |
|
140 gchar *object_path; |
|
141 GabbleHandle creator; |
|
142 |
|
143 GabbleMediaFactory *factory; |
|
144 GabbleMediaSession *session; |
|
145 GPtrArray *streams; |
|
146 |
|
147 guint next_stream_id; |
|
148 |
|
149 gboolean closed; |
|
150 gboolean dispose_has_run; |
|
151 }; |
|
152 |
|
153 #define GABBLE_MEDIA_CHANNEL_GET_PRIVATE(obj) \ |
|
154 ((GabbleMediaChannelPrivate *)obj->priv) |
|
155 |
|
156 static void |
|
157 gabble_media_channel_init (GabbleMediaChannel *self) |
|
158 { |
|
159 GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, |
|
160 GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate); |
|
161 |
|
162 self->priv = priv; |
|
163 |
|
164 priv->next_stream_id = 1; |
|
165 } |
|
166 |
|
167 static GObject * |
|
168 gabble_media_channel_constructor (GType type, guint n_props, |
|
169 GObjectConstructParam *props) |
|
170 { |
|
171 GObject *obj; |
|
172 GabbleMediaChannelPrivate *priv; |
|
173 DBusGConnection *bus; |
|
174 GIntSet *set; |
|
175 |
|
176 obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)-> |
|
177 constructor (type, n_props, props); |
|
178 |
|
179 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (GABBLE_MEDIA_CHANNEL (obj)); |
|
180 |
|
181 /* register object on the bus */ |
|
182 bus = tp_get_bus (); |
|
183 dbus_g_connection_register_g_object (bus, priv->object_path, obj); |
|
184 |
|
185 gabble_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group), |
|
186 priv->conn->handles, priv->conn->self_handle); |
|
187 |
|
188 /* automatically add creator to channel */ |
|
189 set = g_intset_new (); |
|
190 g_intset_add (set, priv->creator); |
|
191 |
|
192 gabble_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0); |
|
193 |
|
194 g_intset_destroy (set); |
|
195 |
|
196 /* allow member adding */ |
|
197 gabble_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0); |
|
198 |
|
199 return obj; |
|
200 } |
|
201 |
|
202 static void session_state_changed_cb (GabbleMediaSession *session, GParamSpec *arg1, GabbleMediaChannel *channel); |
|
203 static void session_stream_added_cb (GabbleMediaSession *session, GabbleMediaStream *stream, GabbleMediaChannel *chan); |
|
204 static void session_terminated_cb (GabbleMediaSession *session, guint terminator, guint reason, gpointer user_data); |
|
205 |
|
206 /** |
|
207 * create_session |
|
208 * |
|
209 * Creates a GabbleMediaSession object for given peer. |
|
210 * |
|
211 * If sid is set to NULL a unique sid is generated and |
|
212 * the "initiator" property of the newly created |
|
213 * GabbleMediaSession is set to our own handle. |
|
214 */ |
|
215 static GabbleMediaSession* |
|
216 create_session (GabbleMediaChannel *channel, |
|
217 GabbleHandle peer, |
|
218 const gchar *peer_resource, |
|
219 const gchar *sid) |
|
220 { |
|
221 GabbleMediaChannelPrivate *priv; |
|
222 GabbleMediaSession *session; |
|
223 gchar *object_path; |
|
224 JingleInitiator initiator; |
|
225 |
|
226 g_assert (GABBLE_IS_MEDIA_CHANNEL (channel)); |
|
227 |
|
228 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); |
|
229 |
|
230 g_assert (priv->session == NULL); |
|
231 |
|
232 object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer); |
|
233 |
|
234 if (sid == NULL) |
|
235 { |
|
236 initiator = INITIATOR_LOCAL; |
|
237 sid = _gabble_media_factory_allocate_sid (priv->factory, channel); |
|
238 } |
|
239 else |
|
240 { |
|
241 initiator = INITIATOR_REMOTE; |
|
242 _gabble_media_factory_register_sid (priv->factory, sid, channel); |
|
243 } |
|
244 |
|
245 session = g_object_new (GABBLE_TYPE_MEDIA_SESSION, |
|
246 "connection", priv->conn, |
|
247 "media-channel", channel, |
|
248 "object-path", object_path, |
|
249 "session-id", sid, |
|
250 "initiator", initiator, |
|
251 "peer", peer, |
|
252 "peer-resource", peer_resource, |
|
253 NULL); |
|
254 |
|
255 g_signal_connect (session, "notify::state", |
|
256 (GCallback) session_state_changed_cb, channel); |
|
257 g_signal_connect (session, "stream-added", |
|
258 (GCallback) session_stream_added_cb, channel); |
|
259 g_signal_connect (session, "terminated", |
|
260 (GCallback) session_terminated_cb, channel); |
|
261 |
|
262 priv->session = session; |
|
263 |
|
264 priv->streams = g_ptr_array_sized_new (1); |
|
265 |
|
266 g_signal_emit (channel, signals[NEW_SESSION_HANDLER], 0, |
|
267 object_path, "rtp"); |
|
268 |
|
269 g_free (object_path); |
|
270 |
|
271 return session; |
|
272 } |
|
273 |
|
274 gboolean |
|
275 _gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan, |
|
276 GabbleHandle peer, |
|
277 const gchar *peer_resource, |
|
278 const gchar *sid, |
|
279 LmMessage *message, |
|
280 LmMessageNode *session_node, |
|
281 const gchar *action, |
|
282 GError **error) |
|
283 { |
|
284 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
285 GabbleMediaSession *session = priv->session; |
|
286 gboolean session_is_new = FALSE; |
|
287 |
|
288 if (session == NULL) |
|
289 { |
|
290 GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (chan); |
|
291 GIntSet *set; |
|
292 |
|
293 session = create_session (chan, peer, peer_resource, sid); |
|
294 session_is_new = TRUE; |
|
295 |
|
296 /* make us local pending */ |
|
297 set = g_intset_new (); |
|
298 g_intset_add (set, mixin->self_handle); |
|
299 |
|
300 gabble_group_mixin_change_members (G_OBJECT (chan), "", NULL, NULL, set, |
|
301 NULL, 0, 0); |
|
302 |
|
303 g_intset_destroy (set); |
|
304 |
|
305 /* and update flags accordingly */ |
|
306 gabble_group_mixin_change_flags (G_OBJECT (chan), |
|
307 TP_CHANNEL_GROUP_FLAG_CAN_ADD | |
|
308 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, |
|
309 0); |
|
310 } |
|
311 |
|
312 g_object_ref (session); |
|
313 |
|
314 if (_gabble_media_session_handle_action (session, message, session_node, |
|
315 action, error)) |
|
316 { |
|
317 g_object_unref (session); |
|
318 return TRUE; |
|
319 } |
|
320 else |
|
321 { |
|
322 if (session_is_new) |
|
323 _gabble_media_session_terminate (session, INITIATOR_LOCAL, |
|
324 TP_CHANNEL_GROUP_CHANGE_REASON_ERROR); |
|
325 |
|
326 g_object_unref (session); |
|
327 return FALSE; |
|
328 } |
|
329 } |
|
330 |
|
331 static void |
|
332 gabble_media_channel_get_property (GObject *object, |
|
333 guint property_id, |
|
334 GValue *value, |
|
335 GParamSpec *pspec) |
|
336 { |
|
337 GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); |
|
338 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
339 |
|
340 switch (property_id) { |
|
341 case PROP_OBJECT_PATH: |
|
342 g_value_set_string (value, priv->object_path); |
|
343 break; |
|
344 case PROP_CHANNEL_TYPE: |
|
345 g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); |
|
346 break; |
|
347 case PROP_HANDLE_TYPE: |
|
348 g_value_set_uint (value, TP_HANDLE_TYPE_NONE); |
|
349 break; |
|
350 case PROP_HANDLE: |
|
351 g_value_set_uint (value, 0); |
|
352 break; |
|
353 case PROP_CONNECTION: |
|
354 g_value_set_object (value, priv->conn); |
|
355 break; |
|
356 case PROP_CREATOR: |
|
357 g_value_set_uint (value, priv->creator); |
|
358 break; |
|
359 case PROP_FACTORY: |
|
360 g_value_set_object (value, priv->factory); |
|
361 break; |
|
362 default: |
|
363 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
364 break; |
|
365 } |
|
366 } |
|
367 |
|
368 static void |
|
369 gabble_media_channel_set_property (GObject *object, |
|
370 guint property_id, |
|
371 const GValue *value, |
|
372 GParamSpec *pspec) |
|
373 { |
|
374 GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); |
|
375 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
376 |
|
377 switch (property_id) { |
|
378 case PROP_OBJECT_PATH: |
|
379 g_free (priv->object_path); |
|
380 priv->object_path = g_value_dup_string (value); |
|
381 break; |
|
382 case PROP_HANDLE: |
|
383 /* this property is writable in the interface, but not actually |
|
384 * meaningfully changable on this channel, so we do nothing */ |
|
385 break; |
|
386 case PROP_CONNECTION: |
|
387 priv->conn = g_value_get_object (value); |
|
388 break; |
|
389 case PROP_CREATOR: |
|
390 priv->creator = g_value_get_uint (value); |
|
391 break; |
|
392 case PROP_FACTORY: |
|
393 priv->factory = g_value_get_object (value); |
|
394 break; |
|
395 default: |
|
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
397 break; |
|
398 } |
|
399 } |
|
400 |
|
401 static void gabble_media_channel_dispose (GObject *object); |
|
402 static void gabble_media_channel_finalize (GObject *object); |
|
403 static gboolean gabble_media_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error); |
|
404 |
|
405 static void |
|
406 gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class) |
|
407 { |
|
408 GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class); |
|
409 GParamSpec *param_spec; |
|
410 |
|
411 g_type_class_add_private (gabble_media_channel_class, sizeof (GabbleMediaChannelPrivate)); |
|
412 |
|
413 object_class->constructor = gabble_media_channel_constructor; |
|
414 |
|
415 object_class->get_property = gabble_media_channel_get_property; |
|
416 object_class->set_property = gabble_media_channel_set_property; |
|
417 |
|
418 object_class->dispose = gabble_media_channel_dispose; |
|
419 object_class->finalize = gabble_media_channel_finalize; |
|
420 |
|
421 gabble_group_mixin_class_init (object_class, |
|
422 G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class), |
|
423 _gabble_media_channel_add_member, |
|
424 gabble_media_channel_remove_member); |
|
425 |
|
426 g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path"); |
|
427 g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type"); |
|
428 g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type"); |
|
429 g_object_class_override_property (object_class, PROP_HANDLE, "handle"); |
|
430 |
|
431 param_spec = g_param_spec_object ("connection", "GabbleConnection object", |
|
432 "Gabble connection object that owns this " |
|
433 "media channel object.", |
|
434 GABBLE_TYPE_CONNECTION, |
|
435 G_PARAM_CONSTRUCT_ONLY | |
|
436 G_PARAM_READWRITE | |
|
437 G_PARAM_STATIC_NICK | |
|
438 G_PARAM_STATIC_BLURB); |
|
439 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); |
|
440 |
|
441 param_spec = g_param_spec_uint ("creator", "Channel creator", |
|
442 "The GabbleHandle representing the contact " |
|
443 "who created the channel.", |
|
444 0, G_MAXUINT32, 0, |
|
445 G_PARAM_CONSTRUCT_ONLY | |
|
446 G_PARAM_READWRITE | |
|
447 G_PARAM_STATIC_NAME | |
|
448 G_PARAM_STATIC_BLURB); |
|
449 g_object_class_install_property (object_class, PROP_CREATOR, param_spec); |
|
450 |
|
451 param_spec = g_param_spec_object ("factory", "GabbleMediaFactory object", |
|
452 "The factory that created this object.", |
|
453 GABBLE_TYPE_MEDIA_FACTORY, |
|
454 G_PARAM_CONSTRUCT_ONLY | |
|
455 G_PARAM_READWRITE | |
|
456 G_PARAM_STATIC_NICK | |
|
457 G_PARAM_STATIC_BLURB); |
|
458 g_object_class_install_property (object_class, PROP_FACTORY, param_spec); |
|
459 |
|
460 signals[CLOSED] = |
|
461 g_signal_new ("closed", |
|
462 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
463 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
464 0, |
|
465 NULL, NULL, |
|
466 g_cclosure_marshal_VOID__VOID, |
|
467 G_TYPE_NONE, 0); |
|
468 |
|
469 signals[NEW_SESSION_HANDLER] = |
|
470 g_signal_new ("new-session-handler", |
|
471 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
472 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
473 0, |
|
474 NULL, NULL, |
|
475 gabble_media_channel_marshal_VOID__STRING_STRING, |
|
476 G_TYPE_NONE, 2, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING); |
|
477 |
|
478 signals[STREAM_ADDED] = |
|
479 g_signal_new ("stream-added", |
|
480 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
481 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
482 0, |
|
483 NULL, NULL, |
|
484 gabble_media_channel_marshal_VOID__UINT_UINT_UINT, |
|
485 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); |
|
486 |
|
487 signals[STREAM_DIRECTION_CHANGED] = |
|
488 g_signal_new ("stream-direction-changed", |
|
489 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
490 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
491 0, |
|
492 NULL, NULL, |
|
493 gabble_media_channel_marshal_VOID__UINT_UINT_UINT, |
|
494 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); |
|
495 |
|
496 signals[STREAM_ERROR] = |
|
497 g_signal_new ("stream-error", |
|
498 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
499 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
500 0, |
|
501 NULL, NULL, |
|
502 gabble_media_channel_marshal_VOID__UINT_UINT_STRING, |
|
503 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); |
|
504 |
|
505 signals[STREAM_REMOVED] = |
|
506 g_signal_new ("stream-removed", |
|
507 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
508 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
509 0, |
|
510 NULL, NULL, |
|
511 g_cclosure_marshal_VOID__UINT, |
|
512 G_TYPE_NONE, 1, G_TYPE_UINT); |
|
513 |
|
514 signals[STREAM_STATE_CHANGED] = |
|
515 g_signal_new ("stream-state-changed", |
|
516 G_OBJECT_CLASS_TYPE (gabble_media_channel_class), |
|
517 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
518 0, |
|
519 NULL, NULL, |
|
520 gabble_media_channel_marshal_VOID__UINT_UINT, |
|
521 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); |
|
522 |
|
523 dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_media_channel_class), &dbus_glib_gabble_media_channel_object_info); |
|
524 } |
|
525 |
|
526 void |
|
527 gabble_media_channel_dispose (GObject *object) |
|
528 { |
|
529 GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); |
|
530 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
531 |
|
532 if (priv->dispose_has_run) |
|
533 return; |
|
534 |
|
535 priv->dispose_has_run = TRUE; |
|
536 |
|
537 /** In this we set the state to ENDED, then the callback unrefs |
|
538 * the session |
|
539 */ |
|
540 |
|
541 if (!priv->closed) |
|
542 gabble_media_channel_close (self, NULL); |
|
543 |
|
544 g_assert (priv->closed); |
|
545 g_assert (priv->session == NULL); |
|
546 g_assert (priv->streams == NULL); |
|
547 |
|
548 if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose) |
|
549 G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object); |
|
550 } |
|
551 |
|
552 void |
|
553 gabble_media_channel_finalize (GObject *object) |
|
554 { |
|
555 GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); |
|
556 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
557 |
|
558 g_free (priv->object_path); |
|
559 |
|
560 gabble_group_mixin_finalize (object); |
|
561 |
|
562 G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object); |
|
563 } |
|
564 |
|
565 |
|
566 |
|
567 /** |
|
568 * gabble_media_channel_add_members |
|
569 * |
|
570 * Implements D-Bus method AddMembers |
|
571 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
572 * |
|
573 * @error: Used to return a pointer to a GError detailing any error |
|
574 * that occurred, D-Bus will throw the error only if this |
|
575 * function returns FALSE. |
|
576 * |
|
577 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
578 */ |
|
579 gboolean |
|
580 gabble_media_channel_add_members (GabbleMediaChannel *self, |
|
581 const GArray *contacts, |
|
582 const gchar *message, |
|
583 GError **error) |
|
584 { |
|
585 return gabble_group_mixin_add_members (G_OBJECT (self), contacts, message, |
|
586 error); |
|
587 } |
|
588 |
|
589 |
|
590 /** |
|
591 * gabble_media_channel_close |
|
592 * |
|
593 * Implements D-Bus method Close |
|
594 * on interface org.freedesktop.Telepathy.Channel |
|
595 * |
|
596 * @error: Used to return a pointer to a GError detailing any error |
|
597 * that occurred, D-Bus will throw the error only if this |
|
598 * function returns FALSE. |
|
599 * |
|
600 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
601 */ |
|
602 gboolean |
|
603 gabble_media_channel_close (GabbleMediaChannel *self, |
|
604 GError **error) |
|
605 { |
|
606 GabbleMediaChannelPrivate *priv; |
|
607 |
|
608 gabble_debug (DEBUG_FLAG, "called on %p", self); |
|
609 |
|
610 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
611 |
|
612 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
613 |
|
614 if (priv->closed) |
|
615 return TRUE; |
|
616 |
|
617 priv->closed = TRUE; |
|
618 |
|
619 if (priv->session) |
|
620 { |
|
621 _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); |
|
622 } |
|
623 |
|
624 g_signal_emit (self, signals[CLOSED], 0); |
|
625 |
|
626 return TRUE; |
|
627 } |
|
628 |
|
629 |
|
630 /** |
|
631 * gabble_media_channel_get_all_members |
|
632 * |
|
633 * Implements D-Bus method GetAllMembers |
|
634 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
635 * |
|
636 * @error: Used to return a pointer to a GError detailing any error |
|
637 * that occurred, D-Bus will throw the error only if this |
|
638 * function returns FALSE. |
|
639 * |
|
640 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
641 */ |
|
642 gboolean |
|
643 gabble_media_channel_get_all_members (GabbleMediaChannel *self, |
|
644 GArray **ret, |
|
645 GArray **ret1, |
|
646 GArray **ret2, |
|
647 GError **error) |
|
648 { |
|
649 return gabble_group_mixin_get_all_members (G_OBJECT (self), ret, ret1, ret2, |
|
650 error); |
|
651 } |
|
652 |
|
653 |
|
654 /** |
|
655 * gabble_media_channel_get_channel_type |
|
656 * |
|
657 * Implements D-Bus method GetChannelType |
|
658 * on interface org.freedesktop.Telepathy.Channel |
|
659 * |
|
660 * @error: Used to return a pointer to a GError detailing any error |
|
661 * that occurred, D-Bus will throw the error only if this |
|
662 * function returns FALSE. |
|
663 * |
|
664 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
665 */ |
|
666 gboolean |
|
667 gabble_media_channel_get_channel_type (GabbleMediaChannel *self, |
|
668 gchar **ret, |
|
669 GError **error) |
|
670 { |
|
671 *ret = g_strdup (TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); |
|
672 |
|
673 return TRUE; |
|
674 } |
|
675 |
|
676 |
|
677 /** |
|
678 * gabble_media_channel_get_group_flags |
|
679 * |
|
680 * Implements D-Bus method GetGroupFlags |
|
681 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
682 * |
|
683 * @error: Used to return a pointer to a GError detailing any error |
|
684 * that occurred, D-Bus will throw the error only if this |
|
685 * function returns FALSE. |
|
686 * |
|
687 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
688 */ |
|
689 gboolean |
|
690 gabble_media_channel_get_group_flags (GabbleMediaChannel *self, |
|
691 guint *ret, |
|
692 GError **error) |
|
693 { |
|
694 return gabble_group_mixin_get_group_flags (G_OBJECT (self), ret, error); |
|
695 } |
|
696 |
|
697 |
|
698 /** |
|
699 * gabble_media_channel_get_handle |
|
700 * |
|
701 * Implements D-Bus method GetHandle |
|
702 * on interface org.freedesktop.Telepathy.Channel |
|
703 * |
|
704 * @error: Used to return a pointer to a GError detailing any error |
|
705 * that occurred, D-Bus will throw the error only if this |
|
706 * function returns FALSE. |
|
707 * |
|
708 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
709 */ |
|
710 gboolean |
|
711 gabble_media_channel_get_handle (GabbleMediaChannel *self, |
|
712 guint *ret, |
|
713 guint *ret1, |
|
714 GError **error) |
|
715 { |
|
716 GabbleMediaChannelPrivate *priv; |
|
717 |
|
718 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
719 |
|
720 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
721 |
|
722 *ret = 0; |
|
723 *ret1 = 0; |
|
724 |
|
725 return TRUE; |
|
726 } |
|
727 |
|
728 |
|
729 /** |
|
730 * gabble_media_channel_get_handle_owners |
|
731 * |
|
732 * Implements D-Bus method GetHandleOwners |
|
733 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
734 * |
|
735 * @error: Used to return a pointer to a GError detailing any error |
|
736 * that occurred, D-Bus will throw the error only if this |
|
737 * function returns FALSE. |
|
738 * |
|
739 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
740 */ |
|
741 gboolean |
|
742 gabble_media_channel_get_handle_owners (GabbleMediaChannel *self, |
|
743 const GArray *handles, |
|
744 GArray **ret, |
|
745 GError **error) |
|
746 { |
|
747 return gabble_group_mixin_get_handle_owners (G_OBJECT (self), handles, ret, |
|
748 error); |
|
749 } |
|
750 |
|
751 |
|
752 /** |
|
753 * gabble_media_channel_get_interfaces |
|
754 * |
|
755 * Implements D-Bus method GetInterfaces |
|
756 * on interface org.freedesktop.Telepathy.Channel |
|
757 * |
|
758 * @error: Used to return a pointer to a GError detailing any error |
|
759 * that occurred, D-Bus will throw the error only if this |
|
760 * function returns FALSE. |
|
761 * |
|
762 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
763 */ |
|
764 gboolean |
|
765 gabble_media_channel_get_interfaces (GabbleMediaChannel *self, |
|
766 gchar ***ret, |
|
767 GError **error) |
|
768 { |
|
769 const gchar *interfaces[] = { |
|
770 TP_IFACE_CHANNEL_INTERFACE_GROUP, |
|
771 TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING, |
|
772 NULL |
|
773 }; |
|
774 |
|
775 *ret = g_strdupv ((gchar **) interfaces); |
|
776 |
|
777 return TRUE; |
|
778 } |
|
779 |
|
780 |
|
781 /** |
|
782 * gabble_media_channel_get_local_pending_members |
|
783 * |
|
784 * Implements D-Bus method GetLocalPendingMembers |
|
785 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
786 * |
|
787 * @error: Used to return a pointer to a GError detailing any error |
|
788 * that occurred, D-Bus will throw the error only if this |
|
789 * function returns FALSE. |
|
790 * |
|
791 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
792 */ |
|
793 gboolean |
|
794 gabble_media_channel_get_local_pending_members (GabbleMediaChannel *self, |
|
795 GArray **ret, |
|
796 GError **error) |
|
797 { |
|
798 return gabble_group_mixin_get_local_pending_members (G_OBJECT (self), ret, |
|
799 error); |
|
800 } |
|
801 |
|
802 |
|
803 /** |
|
804 * gabble_media_channel_get_members |
|
805 * |
|
806 * Implements D-Bus method GetMembers |
|
807 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
808 * |
|
809 * @error: Used to return a pointer to a GError detailing any error |
|
810 * that occurred, D-Bus will throw the error only if this |
|
811 * function returns FALSE. |
|
812 * |
|
813 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
814 */ |
|
815 gboolean |
|
816 gabble_media_channel_get_members (GabbleMediaChannel *self, |
|
817 GArray **ret, |
|
818 GError **error) |
|
819 { |
|
820 return gabble_group_mixin_get_members (G_OBJECT (self), ret, error); |
|
821 } |
|
822 |
|
823 |
|
824 /** |
|
825 * gabble_media_channel_get_remote_pending_members |
|
826 * |
|
827 * Implements D-Bus method GetRemotePendingMembers |
|
828 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
829 * |
|
830 * @error: Used to return a pointer to a GError detailing any error |
|
831 * that occurred, D-Bus will throw the error only if this |
|
832 * function returns FALSE. |
|
833 * |
|
834 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
835 */ |
|
836 gboolean |
|
837 gabble_media_channel_get_remote_pending_members (GabbleMediaChannel *self, |
|
838 GArray **ret, |
|
839 GError **error) |
|
840 { |
|
841 return gabble_group_mixin_get_remote_pending_members (G_OBJECT (self), ret, |
|
842 error); |
|
843 } |
|
844 |
|
845 |
|
846 /** |
|
847 * gabble_media_channel_get_self_handle |
|
848 * |
|
849 * Implements D-Bus method GetSelfHandle |
|
850 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
851 * |
|
852 * @error: Used to return a pointer to a GError detailing any error |
|
853 * that occurred, D-Bus will throw the error only if this |
|
854 * function returns FALSE. |
|
855 * |
|
856 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
857 */ |
|
858 gboolean |
|
859 gabble_media_channel_get_self_handle (GabbleMediaChannel *self, |
|
860 guint *ret, |
|
861 GError **error) |
|
862 { |
|
863 return gabble_group_mixin_get_self_handle (G_OBJECT (self), ret, error); |
|
864 } |
|
865 |
|
866 |
|
867 /** |
|
868 * gabble_media_channel_get_session_handlers |
|
869 * |
|
870 * Implements D-Bus method GetSessionHandlers |
|
871 * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling |
|
872 * |
|
873 * @error: Used to return a pointer to a GError detailing any error |
|
874 * that occurred, D-Bus will throw the error only if this |
|
875 * function returns FALSE. |
|
876 * |
|
877 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
878 */ |
|
879 gboolean |
|
880 gabble_media_channel_get_session_handlers (GabbleMediaChannel *self, |
|
881 GPtrArray **ret, |
|
882 GError **error) |
|
883 { |
|
884 GabbleMediaChannelPrivate *priv; |
|
885 |
|
886 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
887 |
|
888 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
889 |
|
890 if (priv->session) |
|
891 { |
|
892 GValue handler = { 0, }; |
|
893 GabbleHandle member; |
|
894 gchar *path; |
|
895 |
|
896 g_value_init (&handler, TP_SESSION_HANDLER_SET_TYPE); |
|
897 g_value_take_boxed (&handler, |
|
898 dbus_g_type_specialized_construct (TP_SESSION_HANDLER_SET_TYPE)); |
|
899 |
|
900 g_object_get (priv->session, |
|
901 "peer", &member, |
|
902 "object-path", &path, |
|
903 NULL); |
|
904 |
|
905 dbus_g_type_struct_set (&handler, |
|
906 0, path, |
|
907 1, "rtp", |
|
908 G_MAXUINT); |
|
909 |
|
910 g_free (path); |
|
911 |
|
912 *ret = g_ptr_array_sized_new (1); |
|
913 g_ptr_array_add (*ret, g_value_get_boxed (&handler)); |
|
914 } |
|
915 else |
|
916 { |
|
917 *ret = g_ptr_array_sized_new (0); |
|
918 } |
|
919 |
|
920 return TRUE; |
|
921 } |
|
922 |
|
923 |
|
924 static GPtrArray * |
|
925 make_stream_list (GabbleMediaChannel *self, |
|
926 GPtrArray *streams) |
|
927 { |
|
928 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
929 GPtrArray *ret; |
|
930 guint i; |
|
931 |
|
932 ret = g_ptr_array_sized_new (streams->len); |
|
933 |
|
934 for (i = 0; i < streams->len; i++) |
|
935 { |
|
936 GabbleMediaStream *stream = g_ptr_array_index (streams, i); |
|
937 GValue entry = { 0, }; |
|
938 guint id; |
|
939 GabbleHandle peer; |
|
940 TpCodecMediaType type; |
|
941 TpMediaStreamState connection_state; |
|
942 CombinedStreamDirection combined_direction; |
|
943 |
|
944 g_object_get (stream, |
|
945 "id", &id, |
|
946 "media-type", &type, |
|
947 "connection-state", &connection_state, |
|
948 "combined-direction", &combined_direction, |
|
949 NULL); |
|
950 |
|
951 g_object_get (priv->session, "peer", &peer, NULL); |
|
952 |
|
953 g_value_init (&entry, TP_CHANNEL_STREAM_TYPE); |
|
954 g_value_take_boxed (&entry, |
|
955 dbus_g_type_specialized_construct (TP_CHANNEL_STREAM_TYPE)); |
|
956 |
|
957 dbus_g_type_struct_set (&entry, |
|
958 0, id, |
|
959 1, peer, |
|
960 2, type, |
|
961 3, connection_state, |
|
962 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction), |
|
963 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction), |
|
964 G_MAXUINT); |
|
965 |
|
966 g_ptr_array_add (ret, g_value_get_boxed (&entry)); |
|
967 } |
|
968 |
|
969 return ret; |
|
970 } |
|
971 |
|
972 /** |
|
973 * gabble_media_channel_list_streams |
|
974 * |
|
975 * Implements D-Bus method ListStreams |
|
976 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia |
|
977 * |
|
978 * @error: Used to return a pointer to a GError detailing any error |
|
979 * that occurred, D-Bus will throw the error only if this |
|
980 * function returns FALSE. |
|
981 * |
|
982 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
983 */ |
|
984 gboolean |
|
985 gabble_media_channel_list_streams (GabbleMediaChannel *self, |
|
986 GPtrArray **ret, |
|
987 GError **error) |
|
988 { |
|
989 GabbleMediaChannelPrivate *priv; |
|
990 |
|
991 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
992 |
|
993 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
994 |
|
995 /* no session yet? return an empty array */ |
|
996 if (priv->session == NULL) |
|
997 { |
|
998 *ret = g_ptr_array_new (); |
|
999 |
|
1000 return TRUE; |
|
1001 } |
|
1002 |
|
1003 *ret = make_stream_list (self, priv->streams); |
|
1004 |
|
1005 return TRUE; |
|
1006 } |
|
1007 |
|
1008 |
|
1009 /** |
|
1010 * gabble_media_channel_remove_members |
|
1011 * |
|
1012 * Implements D-Bus method RemoveMembers |
|
1013 * on interface org.freedesktop.Telepathy.Channel.Interface.Group |
|
1014 * |
|
1015 * @error: Used to return a pointer to a GError detailing any error |
|
1016 * that occurred, D-Bus will throw the error only if this |
|
1017 * function returns FALSE. |
|
1018 * |
|
1019 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
1020 */ |
|
1021 gboolean |
|
1022 gabble_media_channel_remove_members (GabbleMediaChannel *self, |
|
1023 const GArray *contacts, |
|
1024 const gchar *message, |
|
1025 GError **error) |
|
1026 { |
|
1027 return gabble_group_mixin_remove_members (G_OBJECT (self), contacts, message, |
|
1028 error); |
|
1029 } |
|
1030 |
|
1031 |
|
1032 static GabbleMediaStream * |
|
1033 _find_stream_by_id (GabbleMediaChannel *chan, guint stream_id) |
|
1034 { |
|
1035 GabbleMediaChannelPrivate *priv; |
|
1036 guint i; |
|
1037 |
|
1038 g_assert (GABBLE_IS_MEDIA_CHANNEL (chan)); |
|
1039 |
|
1040 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1041 |
|
1042 for (i = 0; i < priv->streams->len; i++) |
|
1043 { |
|
1044 GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); |
|
1045 guint id; |
|
1046 |
|
1047 g_object_get (stream, "id", &id, NULL); |
|
1048 if (id == stream_id) |
|
1049 return stream; |
|
1050 } |
|
1051 |
|
1052 return NULL; |
|
1053 } |
|
1054 |
|
1055 /** |
|
1056 * gabble_media_channel_remove_streams |
|
1057 * |
|
1058 * Implements DBus method RemoveStreams |
|
1059 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia |
|
1060 * |
|
1061 * @error: Used to return a pointer to a GError detailing any error |
|
1062 * that occured, DBus will throw the error only if this |
|
1063 * function returns false. |
|
1064 * |
|
1065 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
1066 */ |
|
1067 gboolean gabble_media_channel_remove_streams (GabbleMediaChannel *obj, const GArray * streams, GError **error) |
|
1068 { |
|
1069 GabbleMediaChannelPrivate *priv; |
|
1070 GPtrArray *stream_objs; |
|
1071 guint i; |
|
1072 |
|
1073 g_assert (GABBLE_IS_MEDIA_CHANNEL (obj)); |
|
1074 |
|
1075 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (obj); |
|
1076 |
|
1077 *error = NULL; |
|
1078 |
|
1079 stream_objs = g_ptr_array_sized_new (streams->len); |
|
1080 |
|
1081 /* check that all stream ids are valid and at the same time build an array |
|
1082 * of stream objects so we don't have to look them up again after verifying |
|
1083 * all stream identifiers. */ |
|
1084 for (i = 0; i < streams->len; i++) |
|
1085 { |
|
1086 guint id = g_array_index (streams, guint, i); |
|
1087 GabbleMediaStream *stream; |
|
1088 guint j; |
|
1089 |
|
1090 stream = _find_stream_by_id (obj, id); |
|
1091 if (stream == NULL) |
|
1092 { |
|
1093 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
1094 "given stream id %u does not exist", id); |
|
1095 goto OUT; |
|
1096 } |
|
1097 |
|
1098 /* make sure we don't allow the client to repeatedly remove the same stream */ |
|
1099 for (j = 0; j < stream_objs->len; j++) |
|
1100 { |
|
1101 GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j); |
|
1102 |
|
1103 if (tmp == stream) |
|
1104 { |
|
1105 stream = NULL; |
|
1106 break; |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 if (stream != NULL) |
|
1111 g_ptr_array_add (stream_objs, stream); |
|
1112 } |
|
1113 |
|
1114 /* groovy, it's all good dude, let's remove them */ |
|
1115 if (stream_objs->len > 0) |
|
1116 _gabble_media_session_remove_streams (priv->session, (GabbleMediaStream **) |
|
1117 stream_objs->pdata, stream_objs->len); |
|
1118 |
|
1119 OUT: |
|
1120 g_ptr_array_free (stream_objs, TRUE); |
|
1121 |
|
1122 return (*error == NULL); |
|
1123 } |
|
1124 |
|
1125 |
|
1126 /** |
|
1127 * gabble_media_channel_request_stream_direction |
|
1128 * |
|
1129 * Implements D-Bus method RequestStreamDirection |
|
1130 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia |
|
1131 * |
|
1132 * @error: Used to return a pointer to a GError detailing any error |
|
1133 * that occurred, D-Bus will throw the error only if this |
|
1134 * function returns FALSE. |
|
1135 * |
|
1136 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
1137 */ |
|
1138 gboolean |
|
1139 gabble_media_channel_request_stream_direction (GabbleMediaChannel *self, |
|
1140 guint stream_id, |
|
1141 guint stream_direction, |
|
1142 GError **error) |
|
1143 { |
|
1144 GabbleMediaChannelPrivate *priv; |
|
1145 GabbleMediaStream *stream; |
|
1146 |
|
1147 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
1148 |
|
1149 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
1150 |
|
1151 if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) |
|
1152 { |
|
1153 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
1154 "given stream direction %u is not valid", stream_direction); |
|
1155 return FALSE; |
|
1156 } |
|
1157 |
|
1158 stream = _find_stream_by_id (self, stream_id); |
|
1159 if (stream == NULL) |
|
1160 { |
|
1161 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
1162 "given stream id %u does not exist", stream_id); |
|
1163 return FALSE; |
|
1164 } |
|
1165 |
|
1166 /* streams with no session? I think not... */ |
|
1167 g_assert (priv->session != NULL); |
|
1168 |
|
1169 return _gabble_media_session_request_stream_direction (priv->session, stream, |
|
1170 stream_direction, error); |
|
1171 } |
|
1172 |
|
1173 |
|
1174 /** |
|
1175 * gabble_media_channel_request_streams |
|
1176 * |
|
1177 * Implements D-Bus method RequestStreams |
|
1178 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia |
|
1179 * |
|
1180 * @error: Used to return a pointer to a GError detailing any error |
|
1181 * that occurred, D-Bus will throw the error only if this |
|
1182 * function returns FALSE. |
|
1183 * |
|
1184 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
1185 */ |
|
1186 gboolean |
|
1187 gabble_media_channel_request_streams (GabbleMediaChannel *self, |
|
1188 guint contact_handle, |
|
1189 const GArray *types, |
|
1190 GPtrArray **ret, |
|
1191 GError **error) |
|
1192 { |
|
1193 GabbleMediaChannelPrivate *priv; |
|
1194 GPtrArray *streams; |
|
1195 |
|
1196 g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); |
|
1197 |
|
1198 priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); |
|
1199 |
|
1200 if (!gabble_handle_is_valid (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, |
|
1201 contact_handle, error)) |
|
1202 return FALSE; |
|
1203 |
|
1204 if (!handle_set_is_member (self->group.members, contact_handle) && |
|
1205 !handle_set_is_member (self->group.remote_pending, contact_handle)) |
|
1206 { |
|
1207 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
1208 "given handle %u is not a member of the channel", contact_handle); |
|
1209 return FALSE; |
|
1210 } |
|
1211 |
|
1212 /* if the person is a channel member, we should have a session */ |
|
1213 g_assert (priv->session != NULL); |
|
1214 |
|
1215 if (!_gabble_media_session_request_streams (priv->session, types, &streams, |
|
1216 error)) |
|
1217 return FALSE; |
|
1218 |
|
1219 *ret = make_stream_list (self, streams); |
|
1220 |
|
1221 g_ptr_array_free (streams, TRUE); |
|
1222 |
|
1223 return TRUE; |
|
1224 } |
|
1225 |
|
1226 |
|
1227 gboolean |
|
1228 _gabble_media_channel_add_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error) |
|
1229 { |
|
1230 GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); |
|
1231 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1232 GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); |
|
1233 |
|
1234 /* did we create this channel? */ |
|
1235 if (priv->creator == mixin->self_handle) |
|
1236 { |
|
1237 GabblePresence *presence; |
|
1238 GIntSet *set; |
|
1239 |
|
1240 /* yes: check the peer's capabilities */ |
|
1241 |
|
1242 presence = gabble_presence_cache_get (priv->conn->presence_cache, handle); |
|
1243 |
|
1244 if (presence == NULL || |
|
1245 !(presence->caps & PRESENCE_CAP_GOOGLE_VOICE || |
|
1246 presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO || |
|
1247 presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO)) |
|
1248 { |
|
1249 if (presence == NULL) |
|
1250 gabble_debug (DEBUG_FLAG, "failed to add contact %d (%s) to media channel: " |
|
1251 "no presence available", handle, |
|
1252 gabble_handle_inspect (priv->conn->handles, |
|
1253 TP_HANDLE_TYPE_CONTACT, handle)); |
|
1254 else |
|
1255 gabble_debug (DEBUG_FLAG, "failed to add contact %d (%s) to media channel: " |
|
1256 "caps %x aren't sufficient", handle, |
|
1257 gabble_handle_inspect (priv->conn->handles, |
|
1258 TP_HANDLE_TYPE_CONTACT, handle), |
|
1259 presence->caps); |
|
1260 |
|
1261 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
1262 "handle %u has no media capabilities", handle); |
|
1263 return FALSE; |
|
1264 } |
|
1265 |
|
1266 /* yes: invite the peer */ |
|
1267 |
|
1268 |
|
1269 /* create a new session */ |
|
1270 create_session (chan, handle, NULL, NULL); |
|
1271 |
|
1272 /* make the peer remote pending */ |
|
1273 set = g_intset_new (); |
|
1274 g_intset_add (set, handle); |
|
1275 |
|
1276 gabble_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, 0, 0); |
|
1277 |
|
1278 g_intset_destroy (set); |
|
1279 |
|
1280 /* and update flags accordingly */ |
|
1281 gabble_group_mixin_change_flags (obj, |
|
1282 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | |
|
1283 TP_CHANNEL_GROUP_FLAG_CAN_RESCIND, |
|
1284 TP_CHANNEL_GROUP_FLAG_CAN_ADD); |
|
1285 |
|
1286 return TRUE; |
|
1287 } |
|
1288 else |
|
1289 { |
|
1290 /* no: has a session been created, is the handle being added ours, |
|
1291 * and are we in local pending? */ |
|
1292 |
|
1293 if (priv->session && |
|
1294 handle == mixin->self_handle && |
|
1295 handle_set_is_member (mixin->local_pending, handle)) |
|
1296 { |
|
1297 /* yes: accept the request */ |
|
1298 |
|
1299 GIntSet *set; |
|
1300 |
|
1301 /* make us a member */ |
|
1302 set = g_intset_new (); |
|
1303 g_intset_add (set, handle); |
|
1304 |
|
1305 gabble_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0); |
|
1306 |
|
1307 g_intset_destroy (set); |
|
1308 |
|
1309 /* update flags */ |
|
1310 gabble_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); |
|
1311 |
|
1312 /* signal acceptance */ |
|
1313 _gabble_media_session_accept (priv->session); |
|
1314 |
|
1315 return TRUE; |
|
1316 } |
|
1317 } |
|
1318 |
|
1319 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
1320 "handle %u cannot be added in the current state", handle); |
|
1321 return FALSE; |
|
1322 } |
|
1323 |
|
1324 static gboolean |
|
1325 gabble_media_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error) |
|
1326 { |
|
1327 GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); |
|
1328 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1329 GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); |
|
1330 GIntSet *set; |
|
1331 |
|
1332 if (priv->session == NULL) |
|
1333 { |
|
1334 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
1335 "handle %u cannot be removed in the current state", handle); |
|
1336 |
|
1337 return FALSE; |
|
1338 } |
|
1339 |
|
1340 if (priv->creator != mixin->self_handle && |
|
1341 handle != mixin->self_handle) |
|
1342 { |
|
1343 g_set_error (error, TELEPATHY_ERRORS, PermissionDenied, |
|
1344 "handle %u cannot be removed because you are not the creator of the" |
|
1345 " channel", handle); |
|
1346 |
|
1347 return FALSE; |
|
1348 } |
|
1349 |
|
1350 _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); |
|
1351 |
|
1352 /* remove the member */ |
|
1353 set = g_intset_new (); |
|
1354 g_intset_add (set, handle); |
|
1355 |
|
1356 gabble_group_mixin_change_members (obj, "", NULL, set, NULL, NULL, 0, 0); |
|
1357 |
|
1358 g_intset_destroy (set); |
|
1359 |
|
1360 /* and update flags accordingly */ |
|
1361 gabble_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, |
|
1362 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | |
|
1363 TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); |
|
1364 |
|
1365 return TRUE; |
|
1366 } |
|
1367 |
|
1368 static void |
|
1369 session_terminated_cb (GabbleMediaSession *session, |
|
1370 guint terminator, |
|
1371 guint reason, |
|
1372 gpointer user_data) |
|
1373 { |
|
1374 GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data; |
|
1375 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); |
|
1376 GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (channel); |
|
1377 GError *error = NULL; |
|
1378 gchar *sid; |
|
1379 JingleSessionState state; |
|
1380 GabbleHandle peer; |
|
1381 GIntSet *set; |
|
1382 |
|
1383 g_object_get (session, |
|
1384 "state", &state, |
|
1385 "peer", &peer, |
|
1386 NULL); |
|
1387 |
|
1388 set = g_intset_new (); |
|
1389 |
|
1390 /* remove us and the peer from the member list */ |
|
1391 g_intset_add (set, mixin->self_handle); |
|
1392 g_intset_add (set, peer); |
|
1393 |
|
1394 gabble_group_mixin_change_members (G_OBJECT (channel), "", NULL, set, NULL, NULL, terminator, reason); |
|
1395 |
|
1396 /* update flags accordingly -- allow adding, deny removal */ |
|
1397 gabble_group_mixin_change_flags (G_OBJECT (channel), TP_CHANNEL_GROUP_FLAG_CAN_ADD, |
|
1398 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE); |
|
1399 |
|
1400 /* free the session ID */ |
|
1401 g_object_get (priv->session, "session-id", &sid, NULL); |
|
1402 _gabble_media_factory_free_sid (priv->factory, sid); |
|
1403 g_free (sid); |
|
1404 |
|
1405 /* unref streams */ |
|
1406 if (priv->streams != NULL) |
|
1407 { |
|
1408 GPtrArray *tmp = priv->streams; |
|
1409 |
|
1410 /* move priv->streams aside so that the stream_close_cb |
|
1411 * doesn't double unref */ |
|
1412 priv->streams = NULL; |
|
1413 g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL); |
|
1414 g_ptr_array_free (tmp, TRUE); |
|
1415 } |
|
1416 |
|
1417 /* remove the session */ |
|
1418 g_object_unref (priv->session); |
|
1419 priv->session = NULL; |
|
1420 |
|
1421 /* close the channel */ |
|
1422 if (!gabble_media_channel_close (channel, &error)) |
|
1423 { |
|
1424 g_warning ("%s: failed to close media channel: %s", G_STRFUNC, |
|
1425 error->message); |
|
1426 } |
|
1427 } |
|
1428 |
|
1429 |
|
1430 static void |
|
1431 session_state_changed_cb (GabbleMediaSession *session, |
|
1432 GParamSpec *arg1, |
|
1433 GabbleMediaChannel *channel) |
|
1434 { |
|
1435 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); |
|
1436 GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (channel); |
|
1437 JingleSessionState state; |
|
1438 GabbleHandle peer; |
|
1439 GIntSet *set; |
|
1440 |
|
1441 g_object_get (session, |
|
1442 "state", &state, |
|
1443 "peer", &peer, |
|
1444 NULL); |
|
1445 |
|
1446 if (state != JS_STATE_ACTIVE) |
|
1447 return; |
|
1448 |
|
1449 if (priv->creator != mixin->self_handle) |
|
1450 return; |
|
1451 |
|
1452 set = g_intset_new (); |
|
1453 |
|
1454 /* add the peer to the member list */ |
|
1455 g_intset_add (set, peer); |
|
1456 |
|
1457 gabble_group_mixin_change_members (G_OBJECT (channel), "", set, NULL, NULL, NULL, 0, 0); |
|
1458 |
|
1459 /* update flags accordingly -- allow removal, deny adding and rescinding */ |
|
1460 gabble_group_mixin_change_flags (G_OBJECT (channel), |
|
1461 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, |
|
1462 TP_CHANNEL_GROUP_FLAG_CAN_ADD | |
|
1463 TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); |
|
1464 |
|
1465 g_intset_destroy (set); |
|
1466 } |
|
1467 |
|
1468 static void |
|
1469 stream_close_cb (GabbleMediaStream *stream, |
|
1470 GabbleMediaChannel *chan) |
|
1471 { |
|
1472 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1473 guint id; |
|
1474 |
|
1475 g_object_get (stream, "id", &id, NULL); |
|
1476 |
|
1477 g_signal_emit (chan, signals[STREAM_REMOVED], 0, id); |
|
1478 |
|
1479 if (priv->streams != NULL) |
|
1480 { |
|
1481 g_ptr_array_remove (priv->streams, stream); |
|
1482 g_object_unref (stream); |
|
1483 } |
|
1484 } |
|
1485 |
|
1486 static void |
|
1487 stream_error_cb (GabbleMediaStream *stream, |
|
1488 TpMediaStreamError errno, |
|
1489 const gchar *message, |
|
1490 GabbleMediaChannel *chan) |
|
1491 { |
|
1492 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1493 guint id; |
|
1494 |
|
1495 /* emit signal */ |
|
1496 g_object_get (stream, "id", &id, NULL); |
|
1497 g_signal_emit (chan, signals[STREAM_ERROR], 0, id, errno, message); |
|
1498 |
|
1499 /* remove stream from session */ |
|
1500 _gabble_media_session_remove_streams (priv->session, &stream, 1); |
|
1501 } |
|
1502 |
|
1503 static void |
|
1504 stream_state_changed_cb (GabbleMediaStream *stream, |
|
1505 GParamSpec *pspec, |
|
1506 GabbleMediaChannel *chan) |
|
1507 { |
|
1508 guint id; |
|
1509 TpMediaStreamState connection_state; |
|
1510 |
|
1511 g_object_get (stream, "id", &id, "connection-state", &connection_state, NULL); |
|
1512 |
|
1513 g_signal_emit (chan, signals[STREAM_STATE_CHANGED], 0, id, connection_state); |
|
1514 } |
|
1515 |
|
1516 static void |
|
1517 stream_direction_changed_cb (GabbleMediaStream *stream, |
|
1518 GParamSpec *pspec, |
|
1519 GabbleMediaChannel *chan) |
|
1520 { |
|
1521 guint id; |
|
1522 CombinedStreamDirection combined; |
|
1523 TpMediaStreamDirection direction; |
|
1524 TpMediaStreamPendingSend pending_send; |
|
1525 |
|
1526 g_object_get (stream, |
|
1527 "id", &id, |
|
1528 "combined-direction", &combined, |
|
1529 NULL); |
|
1530 |
|
1531 direction = COMBINED_DIRECTION_GET_DIRECTION (combined); |
|
1532 pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined); |
|
1533 |
|
1534 g_signal_emit (chan, signals[STREAM_DIRECTION_CHANGED], 0, id, direction, |
|
1535 pending_send); |
|
1536 } |
|
1537 |
|
1538 static void |
|
1539 session_stream_added_cb (GabbleMediaSession *session, |
|
1540 GabbleMediaStream *stream, |
|
1541 GabbleMediaChannel *chan) |
|
1542 { |
|
1543 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1544 |
|
1545 guint id, handle, type; |
|
1546 |
|
1547 /* keep track of the stream */ |
|
1548 g_object_ref (stream); |
|
1549 g_ptr_array_add (priv->streams, stream); |
|
1550 |
|
1551 g_signal_connect (stream, "close", |
|
1552 (GCallback) stream_close_cb, chan); |
|
1553 g_signal_connect (stream, "error", |
|
1554 (GCallback) stream_error_cb, chan); |
|
1555 g_signal_connect (stream, "notify::connection-state", |
|
1556 (GCallback) stream_state_changed_cb, chan); |
|
1557 g_signal_connect (stream, "notify::combined-direction", |
|
1558 (GCallback) stream_direction_changed_cb, chan); |
|
1559 |
|
1560 /* emit StreamAdded */ |
|
1561 g_object_get (session, "peer", &handle, NULL); |
|
1562 g_object_get (stream, "id", &id, "media-type", &type, NULL); |
|
1563 |
|
1564 g_signal_emit (chan, signals[STREAM_ADDED], 0, id, handle, type); |
|
1565 } |
|
1566 |
|
1567 guint |
|
1568 _gabble_media_channel_get_stream_id (GabbleMediaChannel *chan) |
|
1569 { |
|
1570 GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); |
|
1571 |
|
1572 return priv->next_stream_id++; |
|
1573 } |
|
1574 |
|
1575 #define AUDIO_CAPS \ |
|
1576 ( PRESENCE_CAP_GOOGLE_VOICE | PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO ) |
|
1577 |
|
1578 #define VIDEO_CAPS \ |
|
1579 ( PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO ) |
|
1580 |
|
1581 GabblePresenceCapabilities |
|
1582 _gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags) |
|
1583 { |
|
1584 GabblePresenceCapabilities caps = 0; |
|
1585 |
|
1586 if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) |
|
1587 caps |= AUDIO_CAPS; |
|
1588 |
|
1589 if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) |
|
1590 caps |= VIDEO_CAPS; |
|
1591 |
|
1592 return caps; |
|
1593 } |
|
1594 |
|
1595 TpChannelMediaCapabilities |
|
1596 _gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps) |
|
1597 { |
|
1598 TpChannelMediaCapabilities typeflags = 0; |
|
1599 |
|
1600 if (caps & AUDIO_CAPS) |
|
1601 typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO; |
|
1602 |
|
1603 if (caps & VIDEO_CAPS) |
|
1604 typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO; |
|
1605 |
|
1606 return typeflags; |
|
1607 } |
|
1608 |
|
1609 //Added to avoid type casting error in winscw |
|
1610 GabblePresenceCapabilities |
|
1611 _gabble_media_channel_typeflags_to_caps_tmp (guint flags) |
|
1612 { |
|
1613 return _gabble_media_channel_typeflags_to_caps( (TpChannelMediaCapabilities) flags); |
|
1614 } |
|
1615 |
|
1616 //Added to avoid type casting error in winscw |
|
1617 guint |
|
1618 _gabble_media_channel_caps_to_typeflags_tmp (GabblePresenceCapabilities caps) |
|
1619 { |
|
1620 return (guint) _gabble_media_channel_caps_to_typeflags(caps); |
|
1621 } |
|
1622 |
|