[Telepathy-commits] [telepathy-gabble/master] rename gabble-media-channel.c to media-channel.c
Dafydd Harries
dafydd.harries at collabora.co.uk
Tue Aug 19 10:53:17 PDT 2008
20080714115834-c9803-d4d622d4f2e0b8b2f97748166a4961b5e1d36518.gz
---
src/Makefile.am | 2 +-
src/gabble-media-channel.c | 1874 --------------------------------------------
src/gabble-media-session.c | 2 +-
src/media-channel.c | 1874 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1876 insertions(+), 1876 deletions(-)
delete mode 100644 src/gabble-media-channel.c
create mode 100644 src/media-channel.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 81836ea..e98ebd0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,7 +58,7 @@ libgabble_convenience_la_our_sources = \
muc-channel.h \
gabble-muc-channel.c \
media-channel.h \
- gabble-media-channel.c \
+ media-channel.c \
media-session.h \
gabble-media-session.c \
media-stream.h \
diff --git a/src/gabble-media-channel.c b/src/gabble-media-channel.c
deleted file mode 100644
index 8bb71d8..0000000
--- a/src/gabble-media-channel.c
+++ /dev/null
@@ -1,1874 +0,0 @@
-/*
- * gabble-media-channel.c - Source for GabbleMediaChannel
- * Copyright (C) 2006 Collabora Ltd.
- * Copyright (C) 2006 Nokia Corporation
- * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas at collabora.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "media-channel.h"
-
-#include <dbus/dbus-glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
-
-#include "debug.h"
-#include "connection.h"
-#include "media-session.h"
-#include "presence.h"
-#include "presence-cache.h"
-
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/channel-iface.h>
-#include <telepathy-glib/svc-channel.h>
-#include <telepathy-glib/svc-properties-interface.h>
-
-#include "media-session.h"
-#include "media-stream.h"
-
-#include "media-factory.h"
-
-#define GABBLE_TP_SESSION_HANDLER_SET_TYPE (dbus_g_type_get_struct ("GValueArray", \
- DBUS_TYPE_G_OBJECT_PATH, \
- G_TYPE_STRING, \
- G_TYPE_INVALID))
-
-#define GABBLE_TP_CHANNEL_STREAM_TYPE (dbus_g_type_get_struct ("GValueArray", \
- G_TYPE_UINT, \
- G_TYPE_UINT, \
- G_TYPE_UINT, \
- G_TYPE_UINT, \
- G_TYPE_UINT, \
- G_TYPE_UINT, \
- G_TYPE_INVALID))
-
-static void call_state_iface_init (gpointer, gpointer);
-static void channel_iface_init (gpointer, gpointer);
-static void hold_iface_init (gpointer, gpointer);
-static void media_signalling_iface_init (gpointer, gpointer);
-static void streamed_media_iface_init (gpointer, gpointer);
-
-G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CALL_STATE,
- call_state_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
- tp_group_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD,
- hold_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
- media_signalling_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA,
- streamed_media_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE,
- tp_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
- tp_dbus_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
-
-static const gchar *gabble_media_channel_interfaces[] = {
- /* FIXME: our implementation of CallState is a stub, so it doesn't
- appear in GetInterfaces' output to avoid confusing clients
- TP_IFACE_CHANNEL_INTERFACE_CALL_STATE,
- */
- TP_IFACE_CHANNEL_INTERFACE_GROUP,
- TP_IFACE_CHANNEL_INTERFACE_HOLD,
- TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
- TP_IFACE_PROPERTIES_INTERFACE,
- NULL
-};
-
-/* properties */
-enum
-{
- PROP_OBJECT_PATH = 1,
- PROP_CHANNEL_TYPE,
- PROP_HANDLE_TYPE,
- PROP_HANDLE,
- PROP_CONNECTION,
- PROP_CREATOR,
- PROP_FACTORY,
- PROP_INTERFACES,
- /* TP properties (see also below) */
- PROP_NAT_TRAVERSAL,
- PROP_STUN_SERVER,
- PROP_STUN_PORT,
- PROP_GTALK_P2P_RELAY_TOKEN,
- LAST_PROPERTY
-};
-
-/* TP properties */
-enum
-{
- CHAN_PROP_NAT_TRAVERSAL = 0,
- CHAN_PROP_STUN_SERVER,
- CHAN_PROP_STUN_PORT,
- CHAN_PROP_GTALK_P2P_RELAY_TOKEN,
- NUM_CHAN_PROPS,
- INVALID_CHAN_PROP
-};
-
-const TpPropertySignature channel_property_signatures[NUM_CHAN_PROPS] = {
- { "nat-traversal", G_TYPE_STRING },
- { "stun-server", G_TYPE_STRING },
- { "stun-port", G_TYPE_UINT },
- { "gtalk-p2p-relay-token", G_TYPE_STRING }
-};
-
-struct _GabbleMediaChannelPrivate
-{
- GabbleConnection *conn;
- gchar *object_path;
- TpHandle creator;
-
- GabbleMediaFactory *factory;
- GabbleMediaSession *session;
- GPtrArray *streams;
-
- guint next_stream_id;
-
- TpLocalHoldState hold_state;
- TpLocalHoldStateReason hold_state_reason;
-
- gboolean closed:1;
- gboolean dispose_has_run:1;
-};
-
-#define GABBLE_MEDIA_CHANNEL_GET_PRIVATE(obj) (obj->priv)
-
-static void
-gabble_media_channel_init (GabbleMediaChannel *self)
-{
- GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate);
-
- self->priv = priv;
-
- priv->next_stream_id = 1;
-
- /* initialize properties mixin */
- tp_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET (
- GabbleMediaChannel, properties));
-}
-
-static GObject *
-gabble_media_channel_constructor (GType type, guint n_props,
- GObjectConstructParam *props)
-{
- GObject *obj;
- GabbleMediaChannelPrivate *priv;
- TpBaseConnection *conn;
- DBusGConnection *bus;
- TpIntSet *set;
- TpHandleRepoIface *contact_handles;
-
- obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)->
- constructor (type, n_props, props);
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (GABBLE_MEDIA_CHANNEL (obj));
- conn = (TpBaseConnection *)priv->conn;
- contact_handles = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
-
- /* register object on the bus */
- bus = tp_get_bus ();
- dbus_g_connection_register_g_object (bus, priv->object_path, obj);
-
- tp_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group),
- contact_handles, conn->self_handle);
-
- /* automatically add creator to channel */
- set = tp_intset_new ();
- tp_intset_add (set, priv->creator);
-
- tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0);
-
- tp_intset_destroy (set);
-
- /* Allow member adding; also, we implement the 0.17.6 properties correctly */
- tp_group_mixin_change_flags (obj,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0);
-
- return obj;
-}
-
-static void session_state_changed_cb (GabbleMediaSession *session,
- GParamSpec *arg1, GabbleMediaChannel *channel);
-static void session_stream_added_cb (GabbleMediaSession *session,
- GabbleMediaStream *stream, GabbleMediaChannel *chan);
-static void session_terminated_cb (GabbleMediaSession *session,
- guint terminator, guint reason, gpointer user_data);
-
-/**
- * create_session
- *
- * Creates a GabbleMediaSession object for given peer.
- *
- * If sid is set to NULL a unique sid is generated and
- * the "initiator" property of the newly created
- * GabbleMediaSession is set to our own handle.
- */
-static GabbleMediaSession *
-create_session (GabbleMediaChannel *channel,
- TpHandle peer,
- const gchar *peer_resource,
- const gchar *sid,
- GError **error)
-{
- GabbleMediaChannelPrivate *priv;
- GabbleMediaSession *session;
- gchar *object_path;
- JingleInitiator initiator;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (channel));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
-
- g_assert (priv->session == NULL);
-
- object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer);
-
- if (sid == NULL)
- {
- /* We are the initiator */
- GabblePresence *presence;
-#ifdef ENABLE_DEBUG
- TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
- conn, TP_HANDLE_TYPE_CONTACT);
-#endif
-
- initiator = INITIATOR_LOCAL;
-
- presence = gabble_presence_cache_get (priv->conn->presence_cache, peer);
-
- if (presence == NULL)
- {
- DEBUG ("failed to add contact %d (%s) to media channel: "
- "no presence available", peer,
- tp_handle_inspect (contact_handles, peer));
- goto NO_CAPS;
- }
-
- if ((_gabble_media_channel_caps_to_typeflags (presence->caps) &
- (TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
- TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)) == 0)
- {
- DEBUG ("failed to add contact %d (%s) to media channel: "
- "caps %x aren't sufficient", peer,
- tp_handle_inspect (contact_handles, peer),
- presence->caps);
- goto NO_CAPS;
- }
-
- sid = _gabble_media_factory_allocate_sid (priv->factory, channel);
- }
- else
- {
- initiator = INITIATOR_REMOTE;
- _gabble_media_factory_register_sid (priv->factory, sid, channel);
- }
-
- session = g_object_new (GABBLE_TYPE_MEDIA_SESSION,
- "connection", priv->conn,
- "media-channel", channel,
- "object-path", object_path,
- "session-id", sid,
- "initiator", initiator,
- "peer", peer,
- "peer-resource", peer_resource,
- NULL);
-
- g_signal_connect (session, "notify::state",
- (GCallback) session_state_changed_cb, channel);
- g_signal_connect (session, "stream-added",
- (GCallback) session_stream_added_cb, channel);
- g_signal_connect (session, "terminated",
- (GCallback) session_terminated_cb, channel);
-
- priv->session = session;
-
- priv->streams = g_ptr_array_sized_new (1);
-
- tp_svc_channel_interface_media_signalling_emit_new_session_handler (
- channel, object_path, "rtp");
-
- g_free (object_path);
-
- return session;
-
-NO_CAPS:
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "handle %u has no media capabilities", peer);
- return NULL;
-}
-
-gboolean
-_gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan,
- TpHandle peer,
- const gchar *peer_resource,
- const gchar *sid,
- LmMessage *message,
- LmMessageNode *session_node,
- const gchar *action,
- GError **error)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- GabbleMediaSession *session = priv->session;
- gboolean session_is_new = FALSE;
-
- /* If this assertion fails, create_session() would think we're the
- * initiator. However, GabbleMediaFactory checks this, so it can't fail */
- g_return_val_if_fail (sid != NULL, FALSE);
-
- if (session == NULL)
- {
- TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
- TpIntSet *set;
-
- session = create_session (chan, peer, peer_resource, sid, NULL);
- g_assert (session != NULL);
- session_is_new = TRUE;
-
- /* make us local pending */
- set = tp_intset_new ();
- tp_intset_add (set, mixin->self_handle);
-
- tp_group_mixin_change_members ((GObject *)chan,
- "", NULL, NULL, set, NULL, peer, 0);
-
- tp_intset_destroy (set);
-
- /* and update flags accordingly */
- tp_group_mixin_change_flags ((GObject *)chan,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
- 0);
- }
-
- g_object_ref (session);
-
- if (_gabble_media_session_handle_action (session, message, session_node,
- action, error))
- {
- g_object_unref (session);
- return TRUE;
- }
- else
- {
- if (session_is_new)
- _gabble_media_session_terminate (session, INITIATOR_LOCAL,
- TP_CHANNEL_GROUP_CHANGE_REASON_ERROR);
-
- g_object_unref (session);
- return FALSE;
- }
-}
-
-static void
-gabble_media_channel_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- const gchar *param_name;
- guint tp_property_id;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_value_set_string (value, priv->object_path);
- break;
- case PROP_CHANNEL_TYPE:
- g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
- break;
- case PROP_HANDLE_TYPE:
- g_value_set_uint (value, TP_HANDLE_TYPE_NONE);
- break;
- case PROP_HANDLE:
- g_value_set_uint (value, 0);
- break;
- case PROP_CONNECTION:
- g_value_set_object (value, priv->conn);
- break;
- case PROP_CREATOR:
- g_value_set_uint (value, priv->creator);
- break;
- case PROP_FACTORY:
- g_value_set_object (value, priv->factory);
- break;
- case PROP_INTERFACES:
- g_value_set_boxed (value, gabble_media_channel_interfaces);
- break;
- default:
- param_name = g_param_spec_get_name (pspec);
-
- if (tp_properties_mixin_has_property (object, param_name,
- &tp_property_id))
- {
- GValue *tp_property_value =
- chan->properties.properties[tp_property_id].value;
-
- if (tp_property_value)
- {
- g_value_copy (tp_property_value, value);
- return;
- }
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-gabble_media_channel_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- const gchar *param_name;
- guint tp_property_id;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_free (priv->object_path);
- priv->object_path = g_value_dup_string (value);
- break;
- case PROP_HANDLE_TYPE:
- case PROP_HANDLE:
- case PROP_CHANNEL_TYPE:
- /* these properties are writable in the interface, but not actually
- * meaningfully changable on this channel, so we do nothing */
- break;
- case PROP_CONNECTION:
- priv->conn = g_value_get_object (value);
- break;
- case PROP_CREATOR:
- priv->creator = g_value_get_uint (value);
- break;
- case PROP_FACTORY:
- priv->factory = g_value_get_object (value);
- break;
- default:
- param_name = g_param_spec_get_name (pspec);
-
- if (tp_properties_mixin_has_property (object, param_name,
- &tp_property_id))
- {
- tp_properties_mixin_change_value (object, tp_property_id, value,
- NULL);
- tp_properties_mixin_change_flags (object, tp_property_id,
- TP_PROPERTY_FLAG_READ,
- 0, NULL);
-
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void gabble_media_channel_dispose (GObject *object);
-static void gabble_media_channel_finalize (GObject *object);
-static gboolean gabble_media_channel_remove_member (GObject *obj,
- TpHandle handle, const gchar *message, GError **error);
-
-static void
-gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class)
-{
- static TpDBusPropertiesMixinPropImpl channel_props[] = {
- { "TargetHandleType", "handle-type", NULL },
- { "TargetHandle", "handle", NULL },
- { "ChannelType", "channel-type", NULL },
- { "Interfaces", "interfaces", NULL },
- { NULL }
- };
- static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
- { TP_IFACE_CHANNEL,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- channel_props,
- },
- { NULL }
- };
- GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class);
- GParamSpec *param_spec;
-
- g_type_class_add_private (gabble_media_channel_class,
- sizeof (GabbleMediaChannelPrivate));
-
- object_class->constructor = gabble_media_channel_constructor;
-
- object_class->get_property = gabble_media_channel_get_property;
- object_class->set_property = gabble_media_channel_set_property;
-
- object_class->dispose = gabble_media_channel_dispose;
- object_class->finalize = gabble_media_channel_finalize;
-
- g_object_class_override_property (object_class, PROP_OBJECT_PATH,
- "object-path");
- g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
- "channel-type");
- g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
- "handle-type");
- g_object_class_override_property (object_class, PROP_HANDLE, "handle");
-
- param_spec = g_param_spec_object ("connection", "GabbleConnection object",
- "Gabble connection object that owns this media channel object.",
- GABBLE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
-
- param_spec = g_param_spec_uint ("creator", "Channel creator",
- "The TpHandle representing the contact who created the channel.",
- 0, G_MAXUINT32, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_CREATOR, param_spec);
-
- param_spec = g_param_spec_object ("factory", "GabbleMediaFactory object",
- "The factory that created this object.",
- GABBLE_TYPE_MEDIA_FACTORY,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_FACTORY, param_spec);
-
- param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
- "Additional Channel.Interface.* interfaces",
- G_TYPE_STRV,
- G_PARAM_READABLE |
- G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NAME);
- g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
-
- param_spec = g_param_spec_string ("nat-traversal", "NAT traversal",
- "NAT traversal mechanism.",
- "gtalk-p2p",
- G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL,
- param_spec);
-
- param_spec = g_param_spec_string ("stun-server", "STUN server",
- "IP or address of STUN server.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);
-
- param_spec = g_param_spec_uint ("stun-port", "STUN port",
- "UDP port of STUN server.",
- 0, G_MAXUINT16, 0,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);
-
- param_spec = g_param_spec_string ("gtalk-p2p-relay-token",
- "GTalk P2P Relay Token",
- "Magic token to authenticate with the Google Talk relay server.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_GTALK_P2P_RELAY_TOKEN,
- param_spec);
-
- tp_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (GabbleMediaChannelClass, properties_class),
- channel_property_signatures, NUM_CHAN_PROPS, NULL);
-
- gabble_media_channel_class->dbus_props_class.interfaces = prop_interfaces;
- tp_dbus_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (GabbleMediaChannelClass, dbus_props_class));
-
- tp_group_mixin_class_init (object_class,
- G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class),
- _gabble_media_channel_add_member,
- gabble_media_channel_remove_member);
- tp_group_mixin_init_dbus_properties (object_class);
-}
-
-void
-gabble_media_channel_dispose (GObject *object)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- if (priv->dispose_has_run)
- return;
-
- priv->dispose_has_run = TRUE;
-
- /** In this we set the state to ENDED, then the callback unrefs
- * the session
- */
-
- if (!priv->closed)
- gabble_media_channel_close (self);
-
- g_assert (priv->closed);
- g_assert (priv->session == NULL);
- g_assert (priv->streams == NULL);
-
- if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose)
- G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object);
-}
-
-void
-gabble_media_channel_finalize (GObject *object)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- g_free (priv->object_path);
-
- tp_group_mixin_finalize (object);
- tp_properties_mixin_finalize (object);
-
- G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object);
-}
-
-
-/**
- * gabble_media_channel_close
- *
- * Implements D-Bus method Close
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_media_channel_close_async (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
-
- gabble_media_channel_close (self);
- tp_svc_channel_return_from_close (context);
-}
-
-void
-gabble_media_channel_close (GabbleMediaChannel *self)
-{
- GabbleMediaChannelPrivate *priv;
-
- DEBUG ("called on %p", self);
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- if (priv->closed)
- {
- return;
- }
-
- priv->closed = TRUE;
-
- if (priv->session)
- {
- _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL,
- TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
- }
-
- tp_svc_channel_emit_closed (self);
-}
-
-
-/**
- * gabble_media_channel_get_channel_type
- *
- * Implements D-Bus method GetChannelType
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_media_channel_get_channel_type (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_channel_type (context,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
-}
-
-
-/**
- * gabble_media_channel_get_handle
- *
- * Implements D-Bus method GetHandle
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_media_channel_get_handle (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_handle (context, 0, 0);
-}
-
-
-/**
- * gabble_media_channel_get_interfaces
- *
- * Implements D-Bus method GetInterfaces
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_media_channel_get_interfaces (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_interfaces (context,
- gabble_media_channel_interfaces);
-}
-
-
-/**
- * gabble_media_channel_get_session_handlers
- *
- * Implements D-Bus method GetSessionHandlers
- * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling
- */
-static void
-gabble_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling *iface,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv;
- GPtrArray *ret;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- if (priv->session)
- {
- GValue handler = { 0, };
- TpHandle member;
- gchar *path;
-
- g_value_init (&handler, GABBLE_TP_SESSION_HANDLER_SET_TYPE);
- g_value_take_boxed (&handler,
- dbus_g_type_specialized_construct (GABBLE_TP_SESSION_HANDLER_SET_TYPE));
-
- g_object_get (priv->session,
- "peer", &member,
- "object-path", &path,
- NULL);
-
- dbus_g_type_struct_set (&handler,
- 0, path,
- 1, "rtp",
- G_MAXUINT);
-
- g_free (path);
-
- ret = g_ptr_array_sized_new (1);
- g_ptr_array_add (ret, g_value_get_boxed (&handler));
- }
- else
- {
- ret = g_ptr_array_sized_new (0);
- }
-
- tp_svc_channel_interface_media_signalling_return_from_get_session_handlers (
- context, ret);
- g_ptr_array_free (ret, TRUE);
-}
-
-
-static GPtrArray *
-make_stream_list (GabbleMediaChannel *self,
- GPtrArray *streams)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
- GPtrArray *ret;
- guint i;
-
- ret = g_ptr_array_sized_new (streams->len);
-
- for (i = 0; i < streams->len; i++)
- {
- GabbleMediaStream *stream = g_ptr_array_index (streams, i);
- GValue entry = { 0, };
- guint id;
- TpHandle peer;
- TpMediaStreamType type;
- TpMediaStreamState connection_state;
- CombinedStreamDirection combined_direction;
-
- g_object_get (stream,
- "id", &id,
- "media-type", &type,
- "connection-state", &connection_state,
- "combined-direction", &combined_direction,
- NULL);
-
- g_object_get (priv->session, "peer", &peer, NULL);
-
- g_value_init (&entry, GABBLE_TP_CHANNEL_STREAM_TYPE);
- g_value_take_boxed (&entry,
- dbus_g_type_specialized_construct (GABBLE_TP_CHANNEL_STREAM_TYPE));
-
- dbus_g_type_struct_set (&entry,
- 0, id,
- 1, peer,
- 2, type,
- 3, connection_state,
- 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction),
- 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction),
- G_MAXUINT);
-
- g_ptr_array_add (ret, g_value_get_boxed (&entry));
- }
-
- return ret;
-}
-
-/**
- * gabble_media_channel_list_streams
- *
- * Implements D-Bus method ListStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-gabble_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv;
- GPtrArray *ret;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- /* no session yet? return an empty array */
- if (priv->session == NULL)
- {
- ret = g_ptr_array_new ();
- }
- else
- {
- ret = make_stream_list (self, priv->streams);
- }
-
- tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret);
- g_ptr_array_free (ret, TRUE);
-}
-
-
-static GabbleMediaStream *
-_find_stream_by_id (GabbleMediaChannel *chan, guint stream_id)
-{
- GabbleMediaChannelPrivate *priv;
- guint i;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (chan));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
-
- for (i = 0; i < priv->streams->len; i++)
- {
- GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
- guint id;
-
- g_object_get (stream, "id", &id, NULL);
- if (id == stream_id)
- return stream;
- }
-
- return NULL;
-}
-
-/**
- * gabble_media_channel_remove_streams
- *
- * Implements DBus method RemoveStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-gabble_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
- const GArray * streams,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *obj = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv;
- GPtrArray *stream_objs;
- GError *error = NULL;
- guint i;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (obj));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (obj);
-
- stream_objs = g_ptr_array_sized_new (streams->len);
-
- /* check that all stream ids are valid and at the same time build an array
- * of stream objects so we don't have to look them up again after verifying
- * all stream identifiers. */
- for (i = 0; i < streams->len; i++)
- {
- guint id = g_array_index (streams, guint, i);
- GabbleMediaStream *stream;
- guint j;
-
- stream = _find_stream_by_id (obj, id);
- if (stream == NULL)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "given stream id %u does not exist", id);
- goto OUT;
- }
-
- /* make sure we don't allow the client to repeatedly remove the same
- stream */
- for (j = 0; j < stream_objs->len; j++)
- {
- GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j);
-
- if (tmp == stream)
- {
- stream = NULL;
- break;
- }
- }
-
- if (stream != NULL)
- g_ptr_array_add (stream_objs, stream);
- }
-
- /* groovy, it's all good dude, let's remove them */
- if (stream_objs->len > 0)
- _gabble_media_session_remove_streams (priv->session, (GabbleMediaStream **)
- stream_objs->pdata, stream_objs->len);
-
-OUT:
- g_ptr_array_free (stream_objs, TRUE);
-
- if (error)
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- }
- else
- {
- tp_svc_channel_type_streamed_media_return_from_remove_streams (context);
- }
-}
-
-
-/**
- * gabble_media_channel_request_stream_direction
- *
- * Implements D-Bus method RequestStreamDirection
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-gabble_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface,
- guint stream_id,
- guint stream_direction,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv;
- GabbleMediaStream *stream;
- GError *error = NULL;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "given stream direction %u is not valid", stream_direction);
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- stream = _find_stream_by_id (self, stream_id);
- if (stream == NULL)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "given stream id %u does not exist", stream_id);
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- /* streams with no session? I think not... */
- g_assert (priv->session != NULL);
-
- if (_gabble_media_session_request_stream_direction (priv->session, stream,
- stream_direction, &error))
- {
- tp_svc_channel_type_streamed_media_return_from_request_stream_direction (
- context);
- }
- else
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- }
-}
-
-
-/**
- * gabble_media_channel_request_streams
- *
- * Implements D-Bus method RequestStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-gabble_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface,
- guint contact_handle,
- const GArray *types,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv;
- TpBaseConnection *conn;
- GPtrArray *streams;
- GError *error = NULL;
- GPtrArray *ret;
- TpHandleRepoIface *contact_handles;
-
- g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
-
- /* FIXME: disallow this if we've put the other guy on hold? */
-
- priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
- conn = (TpBaseConnection *)priv->conn;
- contact_handles = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
-
- if (!tp_handle_is_valid (contact_handles, contact_handle, &error))
- goto error;
-
- if (priv->session == NULL)
- {
- if (create_session (self, contact_handle, NULL, NULL, &error)
- == NULL)
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
- }
- else
- {
- TpHandle peer;
-
- g_object_get (priv->session,
- "peer", &peer,
- NULL);
-
- if (peer != contact_handle)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "cannot add streams for %u: this channel's peer is %u",
- contact_handle, peer);
- goto error;
- }
- }
-
- g_assert (priv->session != NULL);
-
- if (!_gabble_media_session_request_streams (priv->session, types, &streams,
- &error))
- goto error;
-
- ret = make_stream_list (self, streams);
-
- g_ptr_array_free (streams, TRUE);
-
- tp_svc_channel_type_streamed_media_return_from_request_streams (context, ret);
- g_ptr_array_free (ret, TRUE);
- return;
-
-error:
- dbus_g_method_return_error (context, error);
- g_error_free (error);
-}
-
-
-gboolean
-_gabble_media_channel_add_member (GObject *obj,
- TpHandle handle,
- const gchar *message,
- GError **error)
-{
- GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
-
- /* did we create this channel? */
- if (priv->creator == mixin->self_handle)
- {
- TpIntSet *set;
-
- /* yes: invite the peer */
-
- if (priv->session == NULL)
- {
- /* create a new session */
- if (create_session (chan, handle, NULL, NULL, error) == NULL)
- return FALSE;
- }
- else
- {
- TpHandle peer;
-
- g_object_get (priv->session,
- "peer", &peer,
- NULL);
-
- if (peer != handle)
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "handle %u cannot be added: this channel's peer is %u",
- handle, peer);
- return FALSE;
- }
- }
-
- /* make the peer remote pending */
- set = tp_intset_new ();
- tp_intset_add (set, handle);
-
- tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, 0, 0);
-
- tp_intset_destroy (set);
-
- /* and update flags accordingly */
- tp_group_mixin_change_flags (obj,
- TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD);
-
- return TRUE;
- }
- else
- {
- /* no: has a session been created, is the handle being added ours,
- * and are we in local pending? */
-
- if (priv->session &&
- handle == mixin->self_handle &&
- tp_handle_set_is_member (mixin->local_pending, handle))
- {
- /* yes: accept the request */
-
- TpIntSet *set;
-
- /* make us a member */
- set = tp_intset_new ();
- tp_intset_add (set, handle);
-
- tp_group_mixin_change_members (obj,
- "", set, NULL, NULL, NULL, 0, 0);
-
- tp_intset_destroy (set);
-
- /* update flags */
- tp_group_mixin_change_flags (obj,
- 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
-
- /* signal acceptance */
- _gabble_media_session_accept (priv->session);
-
- return TRUE;
- }
- }
-
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "handle %u cannot be added in the current state", handle);
- return FALSE;
-}
-
-static gboolean
-gabble_media_channel_remove_member (GObject *obj,
- TpHandle handle,
- const gchar *message,
- GError **error)
-{
- GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
- TpIntSet *set;
-
- if (priv->session == NULL)
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "handle %u cannot be removed in the current state", handle);
-
- return FALSE;
- }
-
- if (priv->creator != mixin->self_handle &&
- handle != mixin->self_handle)
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED,
- "handle %u cannot be removed because you are not the creator of the"
- " channel", handle);
-
- return FALSE;
- }
-
- _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL,
- TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
-
- /* remove the member */
- set = tp_intset_new ();
- tp_intset_add (set, handle);
-
- tp_group_mixin_change_members (obj, "", NULL, set, NULL, NULL, 0, 0);
-
- tp_intset_destroy (set);
-
- /* and update flags accordingly */
- tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD,
- TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
-
- return TRUE;
-}
-
-static void
-session_terminated_cb (GabbleMediaSession *session,
- guint terminator,
- guint reason,
- gpointer user_data)
-{
- GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data;
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
- TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
- gchar *sid;
- JingleSessionState state;
- TpHandle peer;
- TpIntSet *set;
-
- g_object_get (session,
- "state", &state,
- "peer", &peer,
- NULL);
-
- set = tp_intset_new ();
-
- /* remove us and the peer from the member list */
- tp_intset_add (set, mixin->self_handle);
- tp_intset_add (set, peer);
-
- tp_group_mixin_change_members ((GObject *)channel,
- "", NULL, set, NULL, NULL, terminator, reason);
-
- /* update flags accordingly -- allow adding, deny removal */
- tp_group_mixin_change_flags ((GObject *)channel,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD,
- TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
-
- /* free the session ID */
- g_object_get (priv->session, "session-id", &sid, NULL);
- _gabble_media_factory_free_sid (priv->factory, sid);
- g_free (sid);
-
- /* unref streams */
- if (priv->streams != NULL)
- {
- GPtrArray *tmp = priv->streams;
-
- /* move priv->streams aside so that the stream_close_cb
- * doesn't double unref */
- priv->streams = NULL;
- g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL);
- g_ptr_array_free (tmp, TRUE);
- }
-
- /* remove the session */
- g_object_unref (priv->session);
- priv->session = NULL;
-
- /* close the channel */
- gabble_media_channel_close (channel);
-}
-
-
-static void
-session_state_changed_cb (GabbleMediaSession *session,
- GParamSpec *arg1,
- GabbleMediaChannel *channel)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
- TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
- JingleSessionState state;
- TpHandle peer;
- TpIntSet *set;
-
- g_object_get (session,
- "state", &state,
- "peer", &peer,
- NULL);
-
- set = tp_intset_new ();
-
- tp_intset_add (set, peer);
-
- if (state >= JS_STATE_PENDING_INITIATE_SENT &&
- state < JS_STATE_ACTIVE &&
- !tp_handle_set_is_member (mixin->members, peer))
- {
- /* The first time we send anything to the other user, they materialise
- * in remote-pending if necessary */
-
- tp_group_mixin_change_members ((GObject *) channel,
- "", NULL, NULL, NULL, set, 0, 0);
-
- tp_group_mixin_change_flags ((GObject *) channel,
- TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD);
- }
-
- if (state == JS_STATE_ACTIVE &&
- priv->creator == mixin->self_handle)
- {
- /* add the peer to the member list */
- tp_group_mixin_change_members ((GObject *)channel,
- "", set, NULL, NULL, NULL, 0, 0);
-
- /* update flags accordingly -- allow removal, deny adding and
- * rescinding */
- tp_group_mixin_change_flags ((GObject *) channel,
- TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
- TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
- }
-
- tp_intset_destroy (set);
-}
-
-
-static void
-inform_peer_of_unhold (GabbleMediaChannel *self)
-{
- /* FIXME: when we upgrade to current Jingle, signal to the peer that
- * we've taken them off hold, via a session-info message;
- * ignore success or failure, since there's nothing we could really
- * do differently, and the message is only advisory.
- *
- * For now, we don't signal the unhold in the XMPP stream */
- DEBUG ("TODO: tell peer we've taken them off hold");
-}
-
-
-static void
-inform_peer_of_hold (GabbleMediaChannel *self)
-{
- /* FIXME: when we upgrade to current Jingle, signal to the peer that
- * we're putting them on hold, via a session-info message;
- * ignore success or failure, since there's nothing we could really
- * do differently, and the message is only advisory.
- *
- * For now, we don't signal the hold in the XMPP stream */
- DEBUG ("TODO: tell peer we're putting them on hold");
-}
-
-
-static void
-stream_hold_state_changed (GabbleMediaStream *stream G_GNUC_UNUSED,
- GParamSpec *unused G_GNUC_UNUSED,
- gpointer data)
-{
- GabbleMediaChannel *self = data;
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
- gboolean all_held = TRUE, any_held = FALSE;
- guint i;
-
- for (i = 0; i < priv->streams->len; i++)
- {
- gboolean its_hold;
-
- g_object_get (g_ptr_array_index (priv->streams, i),
- "local-hold", &its_hold,
- NULL);
-
- DEBUG ("Stream at index %u has local-hold=%u", i, (guint) its_hold);
-
- all_held = all_held && its_hold;
- any_held = any_held || its_hold;
- }
-
- DEBUG ("all_held=%u, any_held=%u", (guint) all_held, (guint) any_held);
-
- if (all_held)
- {
- /* Move to state HELD */
-
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
- {
- /* nothing changed */
- return;
- }
- else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD)
- {
- /* This can happen if the user asks us to hold, then changes their
- * mind. We make no particular guarantees about stream states when
- * in PENDING_UNHOLD state, so keep claiming to be in that state */
- return;
- }
- else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD)
- {
- /* We wanted to hold, and indeed we have. Yay! Keep whatever
- * reason code we used for going to PENDING_HOLD */
- priv->hold_state = TP_LOCAL_HOLD_STATE_HELD;
- }
- else
- {
- /* We were previously UNHELD. So why have we gone on hold now? */
- DEBUG ("Unexpectedly entered HELD state!");
- priv->hold_state = TP_LOCAL_HOLD_STATE_HELD;
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
- }
- }
- else if (any_held)
- {
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
- {
- /* The streaming client has spontaneously changed its stream
- * state. Why? We just don't know */
- DEBUG ("Unexpectedly entered PENDING_UNHOLD state!");
- priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD;
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
- }
- else if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
- {
- /* Likewise */
- DEBUG ("Unexpectedly entered PENDING_HOLD state!");
- priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
- }
- else
- {
- /* nothing particularly interesting - we're trying to change hold
- * state already, so nothing to signal */
- return;
- }
-
- /* Tell the peer what's happened */
- inform_peer_of_unhold (self);
- }
- else
- {
- /* Move to state UNHELD */
-
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
- {
- /* nothing changed */
- return;
- }
- else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD)
- {
- /* This can happen if the user asks us to unhold, then changes their
- * mind. We make no particular guarantees about stream states when
- * in PENDING_HOLD state, so keep claiming to be in that state */
- return;
- }
- else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD)
- {
- /* We wanted to hold, and indeed we have. Yay! Keep whatever
- * reason code we used for going to PENDING_UNHOLD */
- priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD;
- }
- else
- {
- /* We were previously HELD. So why have we gone off hold now? */
- DEBUG ("Unexpectedly entered UNHELD state!");
- priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD;
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
- }
- }
-
- tp_svc_channel_interface_hold_emit_hold_state_changed (self,
- priv->hold_state, priv->hold_state_reason);
-}
-
-
-static void
-stream_unhold_failed (GabbleMediaStream *stream,
- gpointer data)
-{
- GabbleMediaChannel *self = data;
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
- guint i;
-
- DEBUG ("%p: %p", self, stream);
-
- /* Unholding failed - let's roll back to Hold state */
- priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE;
- tp_svc_channel_interface_hold_emit_hold_state_changed (self,
- priv->hold_state, priv->hold_state_reason);
-
- /* The stream's state may have changed from unheld to held, so re-poll.
- * It's possible that all streams are now held, in which case we can stop. */
- stream_hold_state_changed (stream, NULL, self);
-
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
- return;
-
- /* There should be no need to notify the peer, who already thinks they're
- * on hold, so just tell the streaming client what to do. */
-
- for (i = 0; i < priv->streams->len; i++)
- {
- gabble_media_stream_hold (g_ptr_array_index (priv->streams, i),
- TRUE);
- }
-}
-
-
-static void
-stream_close_cb (GabbleMediaStream *stream,
- GabbleMediaChannel *chan)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- guint id;
-
- g_object_get (stream,
- "id", &id,
- NULL);
-
- tp_svc_channel_type_streamed_media_emit_stream_removed (chan, id);
-
- if (priv->streams != NULL)
- {
- g_ptr_array_remove (priv->streams, stream);
-
- /* A stream closing might cause the "total" hold state to change:
- * if there's one held and one unheld, and the unheld one closes,
- * then our state changes from indeterminate to held. */
- stream_hold_state_changed (stream, NULL, chan);
-
- g_object_unref (stream);
- }
-}
-
-static void
-stream_error_cb (GabbleMediaStream *stream,
- TpMediaStreamError errno,
- const gchar *message,
- GabbleMediaChannel *chan)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
- guint id;
-
- /* emit signal */
- g_object_get (stream, "id", &id, NULL);
- tp_svc_channel_type_streamed_media_emit_stream_error (chan, id, errno,
- message);
-
- /* remove stream from session */
- _gabble_media_session_remove_streams (priv->session, &stream, 1);
-}
-
-static void
-stream_state_changed_cb (GabbleMediaStream *stream,
- GParamSpec *pspec,
- GabbleMediaChannel *chan)
-{
- guint id;
- TpMediaStreamState connection_state;
-
- g_object_get (stream,
- "id", &id,
- "connection-state", &connection_state,
- NULL);
-
- tp_svc_channel_type_streamed_media_emit_stream_state_changed (chan,
- id, connection_state);
-}
-
-static void
-stream_direction_changed_cb (GabbleMediaStream *stream,
- GParamSpec *pspec,
- GabbleMediaChannel *chan)
-{
- guint id;
- CombinedStreamDirection combined;
- TpMediaStreamDirection direction;
- TpMediaStreamPendingSend pending_send;
-
- g_object_get (stream,
- "id", &id,
- "combined-direction", &combined,
- NULL);
-
- direction = COMBINED_DIRECTION_GET_DIRECTION (combined);
- pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined);
-
- tp_svc_channel_type_streamed_media_emit_stream_direction_changed (
- chan, id, direction, pending_send);
-}
-
-static void
-session_stream_added_cb (GabbleMediaSession *session,
- GabbleMediaStream *stream,
- GabbleMediaChannel *chan)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
-
- guint id, handle, type;
-
- /* keep track of the stream */
- g_object_ref (stream);
- g_ptr_array_add (priv->streams, stream);
-
- g_signal_connect (stream, "close",
- (GCallback) stream_close_cb, chan);
- g_signal_connect (stream, "error",
- (GCallback) stream_error_cb, chan);
- g_signal_connect (stream, "unhold-failed",
- (GCallback) stream_unhold_failed, chan);
- g_signal_connect (stream, "notify::connection-state",
- (GCallback) stream_state_changed_cb, chan);
- g_signal_connect (stream, "notify::combined-direction",
- (GCallback) stream_direction_changed_cb, chan);
- g_signal_connect (stream, "notify::local-hold",
- (GCallback) stream_hold_state_changed, chan);
-
- /* emit StreamAdded */
- g_object_get (session, "peer", &handle, NULL);
- g_object_get (stream, "id", &id, "media-type", &type, NULL);
-
- tp_svc_channel_type_streamed_media_emit_stream_added (
- chan, id, handle, type);
-
- /* A stream being added might cause the "total" hold state to change */
- stream_hold_state_changed (stream, NULL, chan);
-}
-
-guint
-_gabble_media_channel_get_stream_id (GabbleMediaChannel *chan)
-{
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
-
- return priv->next_stream_id++;
-}
-
-#define GTALK_CAPS \
- ( PRESENCE_CAP_GOOGLE_VOICE )
-
-#define JINGLE_CAPS \
- ( PRESENCE_CAP_JINGLE \
- | PRESENCE_CAP_GOOGLE_TRANSPORT_P2P )
-
-#define JINGLE_AUDIO_CAPS \
- ( PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO )
-
-#define JINGLE_VIDEO_CAPS \
- ( PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO )
-
-GabblePresenceCapabilities
-_gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags)
-{
- GabblePresenceCapabilities caps = 0;
-
- /* currently we can only signal any (GTalk or Jingle calls) using
- * the GTalk-P2P transport */
- if (flags & TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P)
- {
- caps |= JINGLE_CAPS;
-
- if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO)
- caps |= GTALK_CAPS | JINGLE_AUDIO_CAPS;
-
- if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)
- caps |= JINGLE_VIDEO_CAPS;
- }
-
- return caps;
-}
-
-TpChannelMediaCapabilities
-_gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps)
-{
- TpChannelMediaCapabilities typeflags = 0;
-
- /* this is intentionally asymmetric to the previous function - we don't
- * require the other end to advertise the GTalk-P2P transport capability
- * separately because old GTalk clients didn't do that - having Google voice
- * implied Google session and GTalk-P2P */
- if ((caps & GTALK_CAPS) == GTALK_CAPS)
- typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
-
- if ((caps & JINGLE_CAPS) == JINGLE_CAPS)
- {
- if ((caps & JINGLE_AUDIO_CAPS) == JINGLE_AUDIO_CAPS)
- typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
-
- if ((caps & JINGLE_VIDEO_CAPS) == JINGLE_VIDEO_CAPS)
- typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO;
- }
-
- return typeflags;
-}
-
-static void
-gabble_media_channel_get_call_states (TpSvcChannelInterfaceCallState *iface,
- DBusGMethodInvocation *context)
-{
- GHashTable *states;
-
- /* stub implementation: nobody has any call-state flags */
- states = g_hash_table_new (g_direct_hash, g_direct_equal);
- tp_svc_channel_interface_call_state_return_from_get_call_states (context,
- states);
- g_hash_table_destroy (states);
-}
-
-static void
-gabble_media_channel_get_hold_state (TpSvcChannelInterfaceHold *iface,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = (GabbleMediaChannel *) iface;
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- tp_svc_channel_interface_hold_return_from_get_hold_state (context,
- priv->hold_state, priv->hold_state_reason);
-}
-
-
-static void
-gabble_media_channel_request_hold (TpSvcChannelInterfaceHold *iface,
- gboolean hold,
- DBusGMethodInvocation *context)
-{
- GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
- GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
- guint i;
- TpLocalHoldState old_state = priv->hold_state;
-
- DEBUG ("%p: RequestHold(%u)", self, !!hold);
-
- if (hold)
- {
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
- {
- DEBUG ("No-op");
- tp_svc_channel_interface_hold_return_from_request_hold (context);
- return;
- }
-
- inform_peer_of_hold (self);
-
- priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
- }
- else
- {
- if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
- {
- DEBUG ("No-op");
- tp_svc_channel_interface_hold_return_from_request_hold (context);
- return;
- }
-
- priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD;
- }
-
- if (old_state != priv->hold_state ||
- priv->hold_state_reason != TP_LOCAL_HOLD_STATE_REASON_REQUESTED)
- {
- tp_svc_channel_interface_hold_emit_hold_state_changed (self,
- priv->hold_state, TP_LOCAL_HOLD_STATE_REASON_REQUESTED);
- priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED;
- }
-
- /* Tell streaming client to release or reacquire resources */
-
- for (i = 0; i < priv->streams->len; i++)
- {
- gabble_media_stream_hold (g_ptr_array_index (priv->streams, i), hold);
- }
-
- tp_svc_channel_interface_hold_return_from_request_hold (context);
-}
-
-
-static void
-channel_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
-
-#define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\
- klass, gabble_media_channel_##x##suffix)
- IMPLEMENT(close,_async);
- IMPLEMENT(get_channel_type,);
- IMPLEMENT(get_handle,);
- IMPLEMENT(get_interfaces,);
-#undef IMPLEMENT
-}
-
-static void
-streamed_media_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelTypeStreamedMediaClass *klass =
- (TpSvcChannelTypeStreamedMediaClass *)g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\
- klass, gabble_media_channel_##x)
- IMPLEMENT(list_streams);
- IMPLEMENT(remove_streams);
- IMPLEMENT(request_stream_direction);
- IMPLEMENT(request_streams);
-#undef IMPLEMENT
-}
-
-static void
-media_signalling_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelInterfaceMediaSignallingClass *klass =
- (TpSvcChannelInterfaceMediaSignallingClass *)g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\
- klass, gabble_media_channel_##x)
- IMPLEMENT(get_session_handlers);
-#undef IMPLEMENT
-}
-
-static void
-call_state_iface_init (gpointer g_iface,
- gpointer iface_data G_GNUC_UNUSED)
-{
- TpSvcChannelInterfaceCallStateClass *klass = g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_interface_call_state_implement_##x (\
- klass, gabble_media_channel_##x)
- IMPLEMENT(get_call_states);
-#undef IMPLEMENT
-}
-
-static void
-hold_iface_init (gpointer g_iface,
- gpointer iface_data G_GNUC_UNUSED)
-{
- TpSvcChannelInterfaceHoldClass *klass = g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_interface_hold_implement_##x (\
- klass, gabble_media_channel_##x)
- IMPLEMENT(get_hold_state);
- IMPLEMENT(request_hold);
-#undef IMPLEMENT
-}
diff --git a/src/gabble-media-session.c b/src/gabble-media-session.c
index e20303c..6001797 100644
--- a/src/gabble-media-session.c
+++ b/src/gabble-media-session.c
@@ -2546,7 +2546,7 @@ _gabble_media_session_request_streams (GabbleMediaSession *session,
{
/* if you change the caps in this function, you almost certainly also need
* to change _gabble_media_channel_typeflags_to_caps and
- * _gabble_media_channel_caps_to_typeflags in gabble-media-channel.c */
+ * _gabble_media_channel_caps_to_typeflags in media-channel.c */
static GabblePresenceCapabilities google_audio_caps =
PRESENCE_CAP_GOOGLE_VOICE;
static GabblePresenceCapabilities jingle_audio_caps =
diff --git a/src/media-channel.c b/src/media-channel.c
new file mode 100644
index 0000000..8bb71d8
--- /dev/null
+++ b/src/media-channel.c
@@ -0,0 +1,1874 @@
+/*
+ * gabble-media-channel.c - Source for GabbleMediaChannel
+ * Copyright (C) 2006 Collabora Ltd.
+ * Copyright (C) 2006 Nokia Corporation
+ * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas at collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "media-channel.h"
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "connection.h"
+#include "media-session.h"
+#include "presence.h"
+#include "presence-cache.h"
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/channel-iface.h>
+#include <telepathy-glib/svc-channel.h>
+#include <telepathy-glib/svc-properties-interface.h>
+
+#include "media-session.h"
+#include "media-stream.h"
+
+#include "media-factory.h"
+
+#define GABBLE_TP_SESSION_HANDLER_SET_TYPE (dbus_g_type_get_struct ("GValueArray", \
+ DBUS_TYPE_G_OBJECT_PATH, \
+ G_TYPE_STRING, \
+ G_TYPE_INVALID))
+
+#define GABBLE_TP_CHANNEL_STREAM_TYPE (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_INVALID))
+
+static void call_state_iface_init (gpointer, gpointer);
+static void channel_iface_init (gpointer, gpointer);
+static void hold_iface_init (gpointer, gpointer);
+static void media_signalling_iface_init (gpointer, gpointer);
+static void streamed_media_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CALL_STATE,
+ call_state_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
+ tp_group_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD,
+ hold_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
+ media_signalling_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA,
+ streamed_media_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE,
+ tp_properties_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+ tp_dbus_properties_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
+
+static const gchar *gabble_media_channel_interfaces[] = {
+ /* FIXME: our implementation of CallState is a stub, so it doesn't
+ appear in GetInterfaces' output to avoid confusing clients
+ TP_IFACE_CHANNEL_INTERFACE_CALL_STATE,
+ */
+ TP_IFACE_CHANNEL_INTERFACE_GROUP,
+ TP_IFACE_CHANNEL_INTERFACE_HOLD,
+ TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
+ TP_IFACE_PROPERTIES_INTERFACE,
+ NULL
+};
+
+/* properties */
+enum
+{
+ PROP_OBJECT_PATH = 1,
+ PROP_CHANNEL_TYPE,
+ PROP_HANDLE_TYPE,
+ PROP_HANDLE,
+ PROP_CONNECTION,
+ PROP_CREATOR,
+ PROP_FACTORY,
+ PROP_INTERFACES,
+ /* TP properties (see also below) */
+ PROP_NAT_TRAVERSAL,
+ PROP_STUN_SERVER,
+ PROP_STUN_PORT,
+ PROP_GTALK_P2P_RELAY_TOKEN,
+ LAST_PROPERTY
+};
+
+/* TP properties */
+enum
+{
+ CHAN_PROP_NAT_TRAVERSAL = 0,
+ CHAN_PROP_STUN_SERVER,
+ CHAN_PROP_STUN_PORT,
+ CHAN_PROP_GTALK_P2P_RELAY_TOKEN,
+ NUM_CHAN_PROPS,
+ INVALID_CHAN_PROP
+};
+
+const TpPropertySignature channel_property_signatures[NUM_CHAN_PROPS] = {
+ { "nat-traversal", G_TYPE_STRING },
+ { "stun-server", G_TYPE_STRING },
+ { "stun-port", G_TYPE_UINT },
+ { "gtalk-p2p-relay-token", G_TYPE_STRING }
+};
+
+struct _GabbleMediaChannelPrivate
+{
+ GabbleConnection *conn;
+ gchar *object_path;
+ TpHandle creator;
+
+ GabbleMediaFactory *factory;
+ GabbleMediaSession *session;
+ GPtrArray *streams;
+
+ guint next_stream_id;
+
+ TpLocalHoldState hold_state;
+ TpLocalHoldStateReason hold_state_reason;
+
+ gboolean closed:1;
+ gboolean dispose_has_run:1;
+};
+
+#define GABBLE_MEDIA_CHANNEL_GET_PRIVATE(obj) (obj->priv)
+
+static void
+gabble_media_channel_init (GabbleMediaChannel *self)
+{
+ GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate);
+
+ self->priv = priv;
+
+ priv->next_stream_id = 1;
+
+ /* initialize properties mixin */
+ tp_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET (
+ GabbleMediaChannel, properties));
+}
+
+static GObject *
+gabble_media_channel_constructor (GType type, guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *obj;
+ GabbleMediaChannelPrivate *priv;
+ TpBaseConnection *conn;
+ DBusGConnection *bus;
+ TpIntSet *set;
+ TpHandleRepoIface *contact_handles;
+
+ obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)->
+ constructor (type, n_props, props);
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (GABBLE_MEDIA_CHANNEL (obj));
+ conn = (TpBaseConnection *)priv->conn;
+ contact_handles = tp_base_connection_get_handles (conn,
+ TP_HANDLE_TYPE_CONTACT);
+
+ /* register object on the bus */
+ bus = tp_get_bus ();
+ dbus_g_connection_register_g_object (bus, priv->object_path, obj);
+
+ tp_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group),
+ contact_handles, conn->self_handle);
+
+ /* automatically add creator to channel */
+ set = tp_intset_new ();
+ tp_intset_add (set, priv->creator);
+
+ tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0);
+
+ tp_intset_destroy (set);
+
+ /* Allow member adding; also, we implement the 0.17.6 properties correctly */
+ tp_group_mixin_change_flags (obj,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0);
+
+ return obj;
+}
+
+static void session_state_changed_cb (GabbleMediaSession *session,
+ GParamSpec *arg1, GabbleMediaChannel *channel);
+static void session_stream_added_cb (GabbleMediaSession *session,
+ GabbleMediaStream *stream, GabbleMediaChannel *chan);
+static void session_terminated_cb (GabbleMediaSession *session,
+ guint terminator, guint reason, gpointer user_data);
+
+/**
+ * create_session
+ *
+ * Creates a GabbleMediaSession object for given peer.
+ *
+ * If sid is set to NULL a unique sid is generated and
+ * the "initiator" property of the newly created
+ * GabbleMediaSession is set to our own handle.
+ */
+static GabbleMediaSession *
+create_session (GabbleMediaChannel *channel,
+ TpHandle peer,
+ const gchar *peer_resource,
+ const gchar *sid,
+ GError **error)
+{
+ GabbleMediaChannelPrivate *priv;
+ GabbleMediaSession *session;
+ gchar *object_path;
+ JingleInitiator initiator;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (channel));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
+
+ g_assert (priv->session == NULL);
+
+ object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer);
+
+ if (sid == NULL)
+ {
+ /* We are the initiator */
+ GabblePresence *presence;
+#ifdef ENABLE_DEBUG
+ TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
+ conn, TP_HANDLE_TYPE_CONTACT);
+#endif
+
+ initiator = INITIATOR_LOCAL;
+
+ presence = gabble_presence_cache_get (priv->conn->presence_cache, peer);
+
+ if (presence == NULL)
+ {
+ DEBUG ("failed to add contact %d (%s) to media channel: "
+ "no presence available", peer,
+ tp_handle_inspect (contact_handles, peer));
+ goto NO_CAPS;
+ }
+
+ if ((_gabble_media_channel_caps_to_typeflags (presence->caps) &
+ (TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
+ TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)) == 0)
+ {
+ DEBUG ("failed to add contact %d (%s) to media channel: "
+ "caps %x aren't sufficient", peer,
+ tp_handle_inspect (contact_handles, peer),
+ presence->caps);
+ goto NO_CAPS;
+ }
+
+ sid = _gabble_media_factory_allocate_sid (priv->factory, channel);
+ }
+ else
+ {
+ initiator = INITIATOR_REMOTE;
+ _gabble_media_factory_register_sid (priv->factory, sid, channel);
+ }
+
+ session = g_object_new (GABBLE_TYPE_MEDIA_SESSION,
+ "connection", priv->conn,
+ "media-channel", channel,
+ "object-path", object_path,
+ "session-id", sid,
+ "initiator", initiator,
+ "peer", peer,
+ "peer-resource", peer_resource,
+ NULL);
+
+ g_signal_connect (session, "notify::state",
+ (GCallback) session_state_changed_cb, channel);
+ g_signal_connect (session, "stream-added",
+ (GCallback) session_stream_added_cb, channel);
+ g_signal_connect (session, "terminated",
+ (GCallback) session_terminated_cb, channel);
+
+ priv->session = session;
+
+ priv->streams = g_ptr_array_sized_new (1);
+
+ tp_svc_channel_interface_media_signalling_emit_new_session_handler (
+ channel, object_path, "rtp");
+
+ g_free (object_path);
+
+ return session;
+
+NO_CAPS:
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "handle %u has no media capabilities", peer);
+ return NULL;
+}
+
+gboolean
+_gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan,
+ TpHandle peer,
+ const gchar *peer_resource,
+ const gchar *sid,
+ LmMessage *message,
+ LmMessageNode *session_node,
+ const gchar *action,
+ GError **error)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ GabbleMediaSession *session = priv->session;
+ gboolean session_is_new = FALSE;
+
+ /* If this assertion fails, create_session() would think we're the
+ * initiator. However, GabbleMediaFactory checks this, so it can't fail */
+ g_return_val_if_fail (sid != NULL, FALSE);
+
+ if (session == NULL)
+ {
+ TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
+ TpIntSet *set;
+
+ session = create_session (chan, peer, peer_resource, sid, NULL);
+ g_assert (session != NULL);
+ session_is_new = TRUE;
+
+ /* make us local pending */
+ set = tp_intset_new ();
+ tp_intset_add (set, mixin->self_handle);
+
+ tp_group_mixin_change_members ((GObject *)chan,
+ "", NULL, NULL, set, NULL, peer, 0);
+
+ tp_intset_destroy (set);
+
+ /* and update flags accordingly */
+ tp_group_mixin_change_flags ((GObject *)chan,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
+ 0);
+ }
+
+ g_object_ref (session);
+
+ if (_gabble_media_session_handle_action (session, message, session_node,
+ action, error))
+ {
+ g_object_unref (session);
+ return TRUE;
+ }
+ else
+ {
+ if (session_is_new)
+ _gabble_media_session_terminate (session, INITIATOR_LOCAL,
+ TP_CHANNEL_GROUP_CHANGE_REASON_ERROR);
+
+ g_object_unref (session);
+ return FALSE;
+ }
+}
+
+static void
+gabble_media_channel_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ const gchar *param_name;
+ guint tp_property_id;
+
+ switch (property_id) {
+ case PROP_OBJECT_PATH:
+ g_value_set_string (value, priv->object_path);
+ break;
+ case PROP_CHANNEL_TYPE:
+ g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ break;
+ case PROP_HANDLE_TYPE:
+ g_value_set_uint (value, TP_HANDLE_TYPE_NONE);
+ break;
+ case PROP_HANDLE:
+ g_value_set_uint (value, 0);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ case PROP_CREATOR:
+ g_value_set_uint (value, priv->creator);
+ break;
+ case PROP_FACTORY:
+ g_value_set_object (value, priv->factory);
+ break;
+ case PROP_INTERFACES:
+ g_value_set_boxed (value, gabble_media_channel_interfaces);
+ break;
+ default:
+ param_name = g_param_spec_get_name (pspec);
+
+ if (tp_properties_mixin_has_property (object, param_name,
+ &tp_property_id))
+ {
+ GValue *tp_property_value =
+ chan->properties.properties[tp_property_id].value;
+
+ if (tp_property_value)
+ {
+ g_value_copy (tp_property_value, value);
+ return;
+ }
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_media_channel_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ const gchar *param_name;
+ guint tp_property_id;
+
+ switch (property_id) {
+ case PROP_OBJECT_PATH:
+ g_free (priv->object_path);
+ priv->object_path = g_value_dup_string (value);
+ break;
+ case PROP_HANDLE_TYPE:
+ case PROP_HANDLE:
+ case PROP_CHANNEL_TYPE:
+ /* these properties are writable in the interface, but not actually
+ * meaningfully changable on this channel, so we do nothing */
+ break;
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ case PROP_CREATOR:
+ priv->creator = g_value_get_uint (value);
+ break;
+ case PROP_FACTORY:
+ priv->factory = g_value_get_object (value);
+ break;
+ default:
+ param_name = g_param_spec_get_name (pspec);
+
+ if (tp_properties_mixin_has_property (object, param_name,
+ &tp_property_id))
+ {
+ tp_properties_mixin_change_value (object, tp_property_id, value,
+ NULL);
+ tp_properties_mixin_change_flags (object, tp_property_id,
+ TP_PROPERTY_FLAG_READ,
+ 0, NULL);
+
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void gabble_media_channel_dispose (GObject *object);
+static void gabble_media_channel_finalize (GObject *object);
+static gboolean gabble_media_channel_remove_member (GObject *obj,
+ TpHandle handle, const gchar *message, GError **error);
+
+static void
+gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class)
+{
+ static TpDBusPropertiesMixinPropImpl channel_props[] = {
+ { "TargetHandleType", "handle-type", NULL },
+ { "TargetHandle", "handle", NULL },
+ { "ChannelType", "channel-type", NULL },
+ { "Interfaces", "interfaces", NULL },
+ { NULL }
+ };
+ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CHANNEL,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ channel_props,
+ },
+ { NULL }
+ };
+ GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (gabble_media_channel_class,
+ sizeof (GabbleMediaChannelPrivate));
+
+ object_class->constructor = gabble_media_channel_constructor;
+
+ object_class->get_property = gabble_media_channel_get_property;
+ object_class->set_property = gabble_media_channel_set_property;
+
+ object_class->dispose = gabble_media_channel_dispose;
+ object_class->finalize = gabble_media_channel_finalize;
+
+ g_object_class_override_property (object_class, PROP_OBJECT_PATH,
+ "object-path");
+ g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
+ "channel-type");
+ g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
+ "handle-type");
+ g_object_class_override_property (object_class, PROP_HANDLE, "handle");
+
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object that owns this media channel object.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_uint ("creator", "Channel creator",
+ "The TpHandle representing the contact who created the channel.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CREATOR, param_spec);
+
+ param_spec = g_param_spec_object ("factory", "GabbleMediaFactory object",
+ "The factory that created this object.",
+ GABBLE_TYPE_MEDIA_FACTORY,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_FACTORY, param_spec);
+
+ param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+ "Additional Channel.Interface.* interfaces",
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NAME);
+ g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+ param_spec = g_param_spec_string ("nat-traversal", "NAT traversal",
+ "NAT traversal mechanism.",
+ "gtalk-p2p",
+ G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL,
+ param_spec);
+
+ param_spec = g_param_spec_string ("stun-server", "STUN server",
+ "IP or address of STUN server.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);
+
+ param_spec = g_param_spec_uint ("stun-port", "STUN port",
+ "UDP port of STUN server.",
+ 0, G_MAXUINT16, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);
+
+ param_spec = g_param_spec_string ("gtalk-p2p-relay-token",
+ "GTalk P2P Relay Token",
+ "Magic token to authenticate with the Google Talk relay server.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_GTALK_P2P_RELAY_TOKEN,
+ param_spec);
+
+ tp_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (GabbleMediaChannelClass, properties_class),
+ channel_property_signatures, NUM_CHAN_PROPS, NULL);
+
+ gabble_media_channel_class->dbus_props_class.interfaces = prop_interfaces;
+ tp_dbus_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (GabbleMediaChannelClass, dbus_props_class));
+
+ tp_group_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class),
+ _gabble_media_channel_add_member,
+ gabble_media_channel_remove_member);
+ tp_group_mixin_init_dbus_properties (object_class);
+}
+
+void
+gabble_media_channel_dispose (GObject *object)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /** In this we set the state to ENDED, then the callback unrefs
+ * the session
+ */
+
+ if (!priv->closed)
+ gabble_media_channel_close (self);
+
+ g_assert (priv->closed);
+ g_assert (priv->session == NULL);
+ g_assert (priv->streams == NULL);
+
+ if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object);
+}
+
+void
+gabble_media_channel_finalize (GObject *object)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ g_free (priv->object_path);
+
+ tp_group_mixin_finalize (object);
+ tp_properties_mixin_finalize (object);
+
+ G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object);
+}
+
+
+/**
+ * gabble_media_channel_close
+ *
+ * Implements D-Bus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_media_channel_close_async (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+
+ gabble_media_channel_close (self);
+ tp_svc_channel_return_from_close (context);
+}
+
+void
+gabble_media_channel_close (GabbleMediaChannel *self)
+{
+ GabbleMediaChannelPrivate *priv;
+
+ DEBUG ("called on %p", self);
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->closed)
+ {
+ return;
+ }
+
+ priv->closed = TRUE;
+
+ if (priv->session)
+ {
+ _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL,
+ TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+ }
+
+ tp_svc_channel_emit_closed (self);
+}
+
+
+/**
+ * gabble_media_channel_get_channel_type
+ *
+ * Implements D-Bus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_media_channel_get_channel_type (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_channel_type (context,
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+}
+
+
+/**
+ * gabble_media_channel_get_handle
+ *
+ * Implements D-Bus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_media_channel_get_handle (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_handle (context, 0, 0);
+}
+
+
+/**
+ * gabble_media_channel_get_interfaces
+ *
+ * Implements D-Bus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_media_channel_get_interfaces (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_interfaces (context,
+ gabble_media_channel_interfaces);
+}
+
+
+/**
+ * gabble_media_channel_get_session_handlers
+ *
+ * Implements D-Bus method GetSessionHandlers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling
+ */
+static void
+gabble_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv;
+ GPtrArray *ret;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->session)
+ {
+ GValue handler = { 0, };
+ TpHandle member;
+ gchar *path;
+
+ g_value_init (&handler, GABBLE_TP_SESSION_HANDLER_SET_TYPE);
+ g_value_take_boxed (&handler,
+ dbus_g_type_specialized_construct (GABBLE_TP_SESSION_HANDLER_SET_TYPE));
+
+ g_object_get (priv->session,
+ "peer", &member,
+ "object-path", &path,
+ NULL);
+
+ dbus_g_type_struct_set (&handler,
+ 0, path,
+ 1, "rtp",
+ G_MAXUINT);
+
+ g_free (path);
+
+ ret = g_ptr_array_sized_new (1);
+ g_ptr_array_add (ret, g_value_get_boxed (&handler));
+ }
+ else
+ {
+ ret = g_ptr_array_sized_new (0);
+ }
+
+ tp_svc_channel_interface_media_signalling_return_from_get_session_handlers (
+ context, ret);
+ g_ptr_array_free (ret, TRUE);
+}
+
+
+static GPtrArray *
+make_stream_list (GabbleMediaChannel *self,
+ GPtrArray *streams)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+ GPtrArray *ret;
+ guint i;
+
+ ret = g_ptr_array_sized_new (streams->len);
+
+ for (i = 0; i < streams->len; i++)
+ {
+ GabbleMediaStream *stream = g_ptr_array_index (streams, i);
+ GValue entry = { 0, };
+ guint id;
+ TpHandle peer;
+ TpMediaStreamType type;
+ TpMediaStreamState connection_state;
+ CombinedStreamDirection combined_direction;
+
+ g_object_get (stream,
+ "id", &id,
+ "media-type", &type,
+ "connection-state", &connection_state,
+ "combined-direction", &combined_direction,
+ NULL);
+
+ g_object_get (priv->session, "peer", &peer, NULL);
+
+ g_value_init (&entry, GABBLE_TP_CHANNEL_STREAM_TYPE);
+ g_value_take_boxed (&entry,
+ dbus_g_type_specialized_construct (GABBLE_TP_CHANNEL_STREAM_TYPE));
+
+ dbus_g_type_struct_set (&entry,
+ 0, id,
+ 1, peer,
+ 2, type,
+ 3, connection_state,
+ 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction),
+ 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction),
+ G_MAXUINT);
+
+ g_ptr_array_add (ret, g_value_get_boxed (&entry));
+ }
+
+ return ret;
+}
+
+/**
+ * gabble_media_channel_list_streams
+ *
+ * Implements D-Bus method ListStreams
+ * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
+ */
+static void
+gabble_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv;
+ GPtrArray *ret;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ /* no session yet? return an empty array */
+ if (priv->session == NULL)
+ {
+ ret = g_ptr_array_new ();
+ }
+ else
+ {
+ ret = make_stream_list (self, priv->streams);
+ }
+
+ tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret);
+ g_ptr_array_free (ret, TRUE);
+}
+
+
+static GabbleMediaStream *
+_find_stream_by_id (GabbleMediaChannel *chan, guint stream_id)
+{
+ GabbleMediaChannelPrivate *priv;
+ guint i;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (chan));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+
+ for (i = 0; i < priv->streams->len; i++)
+ {
+ GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
+ guint id;
+
+ g_object_get (stream, "id", &id, NULL);
+ if (id == stream_id)
+ return stream;
+ }
+
+ return NULL;
+}
+
+/**
+ * gabble_media_channel_remove_streams
+ *
+ * Implements DBus method RemoveStreams
+ * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
+ */
+static void
+gabble_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
+ const GArray * streams,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *obj = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv;
+ GPtrArray *stream_objs;
+ GError *error = NULL;
+ guint i;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (obj));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (obj);
+
+ stream_objs = g_ptr_array_sized_new (streams->len);
+
+ /* check that all stream ids are valid and at the same time build an array
+ * of stream objects so we don't have to look them up again after verifying
+ * all stream identifiers. */
+ for (i = 0; i < streams->len; i++)
+ {
+ guint id = g_array_index (streams, guint, i);
+ GabbleMediaStream *stream;
+ guint j;
+
+ stream = _find_stream_by_id (obj, id);
+ if (stream == NULL)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "given stream id %u does not exist", id);
+ goto OUT;
+ }
+
+ /* make sure we don't allow the client to repeatedly remove the same
+ stream */
+ for (j = 0; j < stream_objs->len; j++)
+ {
+ GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j);
+
+ if (tmp == stream)
+ {
+ stream = NULL;
+ break;
+ }
+ }
+
+ if (stream != NULL)
+ g_ptr_array_add (stream_objs, stream);
+ }
+
+ /* groovy, it's all good dude, let's remove them */
+ if (stream_objs->len > 0)
+ _gabble_media_session_remove_streams (priv->session, (GabbleMediaStream **)
+ stream_objs->pdata, stream_objs->len);
+
+OUT:
+ g_ptr_array_free (stream_objs, TRUE);
+
+ if (error)
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+ else
+ {
+ tp_svc_channel_type_streamed_media_return_from_remove_streams (context);
+ }
+}
+
+
+/**
+ * gabble_media_channel_request_stream_direction
+ *
+ * Implements D-Bus method RequestStreamDirection
+ * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
+ */
+static void
+gabble_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface,
+ guint stream_id,
+ guint stream_direction,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv;
+ GabbleMediaStream *stream;
+ GError *error = NULL;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "given stream direction %u is not valid", stream_direction);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ stream = _find_stream_by_id (self, stream_id);
+ if (stream == NULL)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "given stream id %u does not exist", stream_id);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* streams with no session? I think not... */
+ g_assert (priv->session != NULL);
+
+ if (_gabble_media_session_request_stream_direction (priv->session, stream,
+ stream_direction, &error))
+ {
+ tp_svc_channel_type_streamed_media_return_from_request_stream_direction (
+ context);
+ }
+ else
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+
+/**
+ * gabble_media_channel_request_streams
+ *
+ * Implements D-Bus method RequestStreams
+ * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
+ */
+static void
+gabble_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface,
+ guint contact_handle,
+ const GArray *types,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv;
+ TpBaseConnection *conn;
+ GPtrArray *streams;
+ GError *error = NULL;
+ GPtrArray *ret;
+ TpHandleRepoIface *contact_handles;
+
+ g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
+
+ /* FIXME: disallow this if we've put the other guy on hold? */
+
+ priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+ conn = (TpBaseConnection *)priv->conn;
+ contact_handles = tp_base_connection_get_handles (conn,
+ TP_HANDLE_TYPE_CONTACT);
+
+ if (!tp_handle_is_valid (contact_handles, contact_handle, &error))
+ goto error;
+
+ if (priv->session == NULL)
+ {
+ if (create_session (self, contact_handle, NULL, NULL, &error)
+ == NULL)
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+ }
+ else
+ {
+ TpHandle peer;
+
+ g_object_get (priv->session,
+ "peer", &peer,
+ NULL);
+
+ if (peer != contact_handle)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "cannot add streams for %u: this channel's peer is %u",
+ contact_handle, peer);
+ goto error;
+ }
+ }
+
+ g_assert (priv->session != NULL);
+
+ if (!_gabble_media_session_request_streams (priv->session, types, &streams,
+ &error))
+ goto error;
+
+ ret = make_stream_list (self, streams);
+
+ g_ptr_array_free (streams, TRUE);
+
+ tp_svc_channel_type_streamed_media_return_from_request_streams (context, ret);
+ g_ptr_array_free (ret, TRUE);
+ return;
+
+error:
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+}
+
+
+gboolean
+_gabble_media_channel_add_member (GObject *obj,
+ TpHandle handle,
+ const gchar *message,
+ GError **error)
+{
+ GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
+
+ /* did we create this channel? */
+ if (priv->creator == mixin->self_handle)
+ {
+ TpIntSet *set;
+
+ /* yes: invite the peer */
+
+ if (priv->session == NULL)
+ {
+ /* create a new session */
+ if (create_session (chan, handle, NULL, NULL, error) == NULL)
+ return FALSE;
+ }
+ else
+ {
+ TpHandle peer;
+
+ g_object_get (priv->session,
+ "peer", &peer,
+ NULL);
+
+ if (peer != handle)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "handle %u cannot be added: this channel's peer is %u",
+ handle, peer);
+ return FALSE;
+ }
+ }
+
+ /* make the peer remote pending */
+ set = tp_intset_new ();
+ tp_intset_add (set, handle);
+
+ tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, 0, 0);
+
+ tp_intset_destroy (set);
+
+ /* and update flags accordingly */
+ tp_group_mixin_change_flags (obj,
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD);
+
+ return TRUE;
+ }
+ else
+ {
+ /* no: has a session been created, is the handle being added ours,
+ * and are we in local pending? */
+
+ if (priv->session &&
+ handle == mixin->self_handle &&
+ tp_handle_set_is_member (mixin->local_pending, handle))
+ {
+ /* yes: accept the request */
+
+ TpIntSet *set;
+
+ /* make us a member */
+ set = tp_intset_new ();
+ tp_intset_add (set, handle);
+
+ tp_group_mixin_change_members (obj,
+ "", set, NULL, NULL, NULL, 0, 0);
+
+ tp_intset_destroy (set);
+
+ /* update flags */
+ tp_group_mixin_change_flags (obj,
+ 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
+
+ /* signal acceptance */
+ _gabble_media_session_accept (priv->session);
+
+ return TRUE;
+ }
+ }
+
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "handle %u cannot be added in the current state", handle);
+ return FALSE;
+}
+
+static gboolean
+gabble_media_channel_remove_member (GObject *obj,
+ TpHandle handle,
+ const gchar *message,
+ GError **error)
+{
+ GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
+ TpIntSet *set;
+
+ if (priv->session == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "handle %u cannot be removed in the current state", handle);
+
+ return FALSE;
+ }
+
+ if (priv->creator != mixin->self_handle &&
+ handle != mixin->self_handle)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED,
+ "handle %u cannot be removed because you are not the creator of the"
+ " channel", handle);
+
+ return FALSE;
+ }
+
+ _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL,
+ TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+
+ /* remove the member */
+ set = tp_intset_new ();
+ tp_intset_add (set, handle);
+
+ tp_group_mixin_change_members (obj, "", NULL, set, NULL, NULL, 0, 0);
+
+ tp_intset_destroy (set);
+
+ /* and update flags accordingly */
+ tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD,
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
+
+ return TRUE;
+}
+
+static void
+session_terminated_cb (GabbleMediaSession *session,
+ guint terminator,
+ guint reason,
+ gpointer user_data)
+{
+ GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data;
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
+ TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
+ gchar *sid;
+ JingleSessionState state;
+ TpHandle peer;
+ TpIntSet *set;
+
+ g_object_get (session,
+ "state", &state,
+ "peer", &peer,
+ NULL);
+
+ set = tp_intset_new ();
+
+ /* remove us and the peer from the member list */
+ tp_intset_add (set, mixin->self_handle);
+ tp_intset_add (set, peer);
+
+ tp_group_mixin_change_members ((GObject *)channel,
+ "", NULL, set, NULL, NULL, terminator, reason);
+
+ /* update flags accordingly -- allow adding, deny removal */
+ tp_group_mixin_change_flags ((GObject *)channel,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD,
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
+
+ /* free the session ID */
+ g_object_get (priv->session, "session-id", &sid, NULL);
+ _gabble_media_factory_free_sid (priv->factory, sid);
+ g_free (sid);
+
+ /* unref streams */
+ if (priv->streams != NULL)
+ {
+ GPtrArray *tmp = priv->streams;
+
+ /* move priv->streams aside so that the stream_close_cb
+ * doesn't double unref */
+ priv->streams = NULL;
+ g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL);
+ g_ptr_array_free (tmp, TRUE);
+ }
+
+ /* remove the session */
+ g_object_unref (priv->session);
+ priv->session = NULL;
+
+ /* close the channel */
+ gabble_media_channel_close (channel);
+}
+
+
+static void
+session_state_changed_cb (GabbleMediaSession *session,
+ GParamSpec *arg1,
+ GabbleMediaChannel *channel)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
+ TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
+ JingleSessionState state;
+ TpHandle peer;
+ TpIntSet *set;
+
+ g_object_get (session,
+ "state", &state,
+ "peer", &peer,
+ NULL);
+
+ set = tp_intset_new ();
+
+ tp_intset_add (set, peer);
+
+ if (state >= JS_STATE_PENDING_INITIATE_SENT &&
+ state < JS_STATE_ACTIVE &&
+ !tp_handle_set_is_member (mixin->members, peer))
+ {
+ /* The first time we send anything to the other user, they materialise
+ * in remote-pending if necessary */
+
+ tp_group_mixin_change_members ((GObject *) channel,
+ "", NULL, NULL, NULL, set, 0, 0);
+
+ tp_group_mixin_change_flags ((GObject *) channel,
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD);
+ }
+
+ if (state == JS_STATE_ACTIVE &&
+ priv->creator == mixin->self_handle)
+ {
+ /* add the peer to the member list */
+ tp_group_mixin_change_members ((GObject *)channel,
+ "", set, NULL, NULL, NULL, 0, 0);
+
+ /* update flags accordingly -- allow removal, deny adding and
+ * rescinding */
+ tp_group_mixin_change_flags ((GObject *) channel,
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
+ TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
+ }
+
+ tp_intset_destroy (set);
+}
+
+
+static void
+inform_peer_of_unhold (GabbleMediaChannel *self)
+{
+ /* FIXME: when we upgrade to current Jingle, signal to the peer that
+ * we've taken them off hold, via a session-info message;
+ * ignore success or failure, since there's nothing we could really
+ * do differently, and the message is only advisory.
+ *
+ * For now, we don't signal the unhold in the XMPP stream */
+ DEBUG ("TODO: tell peer we've taken them off hold");
+}
+
+
+static void
+inform_peer_of_hold (GabbleMediaChannel *self)
+{
+ /* FIXME: when we upgrade to current Jingle, signal to the peer that
+ * we're putting them on hold, via a session-info message;
+ * ignore success or failure, since there's nothing we could really
+ * do differently, and the message is only advisory.
+ *
+ * For now, we don't signal the hold in the XMPP stream */
+ DEBUG ("TODO: tell peer we're putting them on hold");
+}
+
+
+static void
+stream_hold_state_changed (GabbleMediaStream *stream G_GNUC_UNUSED,
+ GParamSpec *unused G_GNUC_UNUSED,
+ gpointer data)
+{
+ GabbleMediaChannel *self = data;
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+ gboolean all_held = TRUE, any_held = FALSE;
+ guint i;
+
+ for (i = 0; i < priv->streams->len; i++)
+ {
+ gboolean its_hold;
+
+ g_object_get (g_ptr_array_index (priv->streams, i),
+ "local-hold", &its_hold,
+ NULL);
+
+ DEBUG ("Stream at index %u has local-hold=%u", i, (guint) its_hold);
+
+ all_held = all_held && its_hold;
+ any_held = any_held || its_hold;
+ }
+
+ DEBUG ("all_held=%u, any_held=%u", (guint) all_held, (guint) any_held);
+
+ if (all_held)
+ {
+ /* Move to state HELD */
+
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
+ {
+ /* nothing changed */
+ return;
+ }
+ else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD)
+ {
+ /* This can happen if the user asks us to hold, then changes their
+ * mind. We make no particular guarantees about stream states when
+ * in PENDING_UNHOLD state, so keep claiming to be in that state */
+ return;
+ }
+ else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD)
+ {
+ /* We wanted to hold, and indeed we have. Yay! Keep whatever
+ * reason code we used for going to PENDING_HOLD */
+ priv->hold_state = TP_LOCAL_HOLD_STATE_HELD;
+ }
+ else
+ {
+ /* We were previously UNHELD. So why have we gone on hold now? */
+ DEBUG ("Unexpectedly entered HELD state!");
+ priv->hold_state = TP_LOCAL_HOLD_STATE_HELD;
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
+ }
+ }
+ else if (any_held)
+ {
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
+ {
+ /* The streaming client has spontaneously changed its stream
+ * state. Why? We just don't know */
+ DEBUG ("Unexpectedly entered PENDING_UNHOLD state!");
+ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD;
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
+ }
+ else if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
+ {
+ /* Likewise */
+ DEBUG ("Unexpectedly entered PENDING_HOLD state!");
+ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
+ }
+ else
+ {
+ /* nothing particularly interesting - we're trying to change hold
+ * state already, so nothing to signal */
+ return;
+ }
+
+ /* Tell the peer what's happened */
+ inform_peer_of_unhold (self);
+ }
+ else
+ {
+ /* Move to state UNHELD */
+
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
+ {
+ /* nothing changed */
+ return;
+ }
+ else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD)
+ {
+ /* This can happen if the user asks us to unhold, then changes their
+ * mind. We make no particular guarantees about stream states when
+ * in PENDING_HOLD state, so keep claiming to be in that state */
+ return;
+ }
+ else if (priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD)
+ {
+ /* We wanted to hold, and indeed we have. Yay! Keep whatever
+ * reason code we used for going to PENDING_UNHOLD */
+ priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD;
+ }
+ else
+ {
+ /* We were previously HELD. So why have we gone off hold now? */
+ DEBUG ("Unexpectedly entered UNHELD state!");
+ priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD;
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE;
+ }
+ }
+
+ tp_svc_channel_interface_hold_emit_hold_state_changed (self,
+ priv->hold_state, priv->hold_state_reason);
+}
+
+
+static void
+stream_unhold_failed (GabbleMediaStream *stream,
+ gpointer data)
+{
+ GabbleMediaChannel *self = data;
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+ guint i;
+
+ DEBUG ("%p: %p", self, stream);
+
+ /* Unholding failed - let's roll back to Hold state */
+ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE;
+ tp_svc_channel_interface_hold_emit_hold_state_changed (self,
+ priv->hold_state, priv->hold_state_reason);
+
+ /* The stream's state may have changed from unheld to held, so re-poll.
+ * It's possible that all streams are now held, in which case we can stop. */
+ stream_hold_state_changed (stream, NULL, self);
+
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
+ return;
+
+ /* There should be no need to notify the peer, who already thinks they're
+ * on hold, so just tell the streaming client what to do. */
+
+ for (i = 0; i < priv->streams->len; i++)
+ {
+ gabble_media_stream_hold (g_ptr_array_index (priv->streams, i),
+ TRUE);
+ }
+}
+
+
+static void
+stream_close_cb (GabbleMediaStream *stream,
+ GabbleMediaChannel *chan)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ guint id;
+
+ g_object_get (stream,
+ "id", &id,
+ NULL);
+
+ tp_svc_channel_type_streamed_media_emit_stream_removed (chan, id);
+
+ if (priv->streams != NULL)
+ {
+ g_ptr_array_remove (priv->streams, stream);
+
+ /* A stream closing might cause the "total" hold state to change:
+ * if there's one held and one unheld, and the unheld one closes,
+ * then our state changes from indeterminate to held. */
+ stream_hold_state_changed (stream, NULL, chan);
+
+ g_object_unref (stream);
+ }
+}
+
+static void
+stream_error_cb (GabbleMediaStream *stream,
+ TpMediaStreamError errno,
+ const gchar *message,
+ GabbleMediaChannel *chan)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ guint id;
+
+ /* emit signal */
+ g_object_get (stream, "id", &id, NULL);
+ tp_svc_channel_type_streamed_media_emit_stream_error (chan, id, errno,
+ message);
+
+ /* remove stream from session */
+ _gabble_media_session_remove_streams (priv->session, &stream, 1);
+}
+
+static void
+stream_state_changed_cb (GabbleMediaStream *stream,
+ GParamSpec *pspec,
+ GabbleMediaChannel *chan)
+{
+ guint id;
+ TpMediaStreamState connection_state;
+
+ g_object_get (stream,
+ "id", &id,
+ "connection-state", &connection_state,
+ NULL);
+
+ tp_svc_channel_type_streamed_media_emit_stream_state_changed (chan,
+ id, connection_state);
+}
+
+static void
+stream_direction_changed_cb (GabbleMediaStream *stream,
+ GParamSpec *pspec,
+ GabbleMediaChannel *chan)
+{
+ guint id;
+ CombinedStreamDirection combined;
+ TpMediaStreamDirection direction;
+ TpMediaStreamPendingSend pending_send;
+
+ g_object_get (stream,
+ "id", &id,
+ "combined-direction", &combined,
+ NULL);
+
+ direction = COMBINED_DIRECTION_GET_DIRECTION (combined);
+ pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined);
+
+ tp_svc_channel_type_streamed_media_emit_stream_direction_changed (
+ chan, id, direction, pending_send);
+}
+
+static void
+session_stream_added_cb (GabbleMediaSession *session,
+ GabbleMediaStream *stream,
+ GabbleMediaChannel *chan)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+
+ guint id, handle, type;
+
+ /* keep track of the stream */
+ g_object_ref (stream);
+ g_ptr_array_add (priv->streams, stream);
+
+ g_signal_connect (stream, "close",
+ (GCallback) stream_close_cb, chan);
+ g_signal_connect (stream, "error",
+ (GCallback) stream_error_cb, chan);
+ g_signal_connect (stream, "unhold-failed",
+ (GCallback) stream_unhold_failed, chan);
+ g_signal_connect (stream, "notify::connection-state",
+ (GCallback) stream_state_changed_cb, chan);
+ g_signal_connect (stream, "notify::combined-direction",
+ (GCallback) stream_direction_changed_cb, chan);
+ g_signal_connect (stream, "notify::local-hold",
+ (GCallback) stream_hold_state_changed, chan);
+
+ /* emit StreamAdded */
+ g_object_get (session, "peer", &handle, NULL);
+ g_object_get (stream, "id", &id, "media-type", &type, NULL);
+
+ tp_svc_channel_type_streamed_media_emit_stream_added (
+ chan, id, handle, type);
+
+ /* A stream being added might cause the "total" hold state to change */
+ stream_hold_state_changed (stream, NULL, chan);
+}
+
+guint
+_gabble_media_channel_get_stream_id (GabbleMediaChannel *chan)
+{
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
+
+ return priv->next_stream_id++;
+}
+
+#define GTALK_CAPS \
+ ( PRESENCE_CAP_GOOGLE_VOICE )
+
+#define JINGLE_CAPS \
+ ( PRESENCE_CAP_JINGLE \
+ | PRESENCE_CAP_GOOGLE_TRANSPORT_P2P )
+
+#define JINGLE_AUDIO_CAPS \
+ ( PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO )
+
+#define JINGLE_VIDEO_CAPS \
+ ( PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO )
+
+GabblePresenceCapabilities
+_gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags)
+{
+ GabblePresenceCapabilities caps = 0;
+
+ /* currently we can only signal any (GTalk or Jingle calls) using
+ * the GTalk-P2P transport */
+ if (flags & TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P)
+ {
+ caps |= JINGLE_CAPS;
+
+ if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO)
+ caps |= GTALK_CAPS | JINGLE_AUDIO_CAPS;
+
+ if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)
+ caps |= JINGLE_VIDEO_CAPS;
+ }
+
+ return caps;
+}
+
+TpChannelMediaCapabilities
+_gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps)
+{
+ TpChannelMediaCapabilities typeflags = 0;
+
+ /* this is intentionally asymmetric to the previous function - we don't
+ * require the other end to advertise the GTalk-P2P transport capability
+ * separately because old GTalk clients didn't do that - having Google voice
+ * implied Google session and GTalk-P2P */
+ if ((caps & GTALK_CAPS) == GTALK_CAPS)
+ typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
+
+ if ((caps & JINGLE_CAPS) == JINGLE_CAPS)
+ {
+ if ((caps & JINGLE_AUDIO_CAPS) == JINGLE_AUDIO_CAPS)
+ typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
+
+ if ((caps & JINGLE_VIDEO_CAPS) == JINGLE_VIDEO_CAPS)
+ typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO;
+ }
+
+ return typeflags;
+}
+
+static void
+gabble_media_channel_get_call_states (TpSvcChannelInterfaceCallState *iface,
+ DBusGMethodInvocation *context)
+{
+ GHashTable *states;
+
+ /* stub implementation: nobody has any call-state flags */
+ states = g_hash_table_new (g_direct_hash, g_direct_equal);
+ tp_svc_channel_interface_call_state_return_from_get_call_states (context,
+ states);
+ g_hash_table_destroy (states);
+}
+
+static void
+gabble_media_channel_get_hold_state (TpSvcChannelInterfaceHold *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = (GabbleMediaChannel *) iface;
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ tp_svc_channel_interface_hold_return_from_get_hold_state (context,
+ priv->hold_state, priv->hold_state_reason);
+}
+
+
+static void
+gabble_media_channel_request_hold (TpSvcChannelInterfaceHold *iface,
+ gboolean hold,
+ DBusGMethodInvocation *context)
+{
+ GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface);
+ GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
+ guint i;
+ TpLocalHoldState old_state = priv->hold_state;
+
+ DEBUG ("%p: RequestHold(%u)", self, !!hold);
+
+ if (hold)
+ {
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD)
+ {
+ DEBUG ("No-op");
+ tp_svc_channel_interface_hold_return_from_request_hold (context);
+ return;
+ }
+
+ inform_peer_of_hold (self);
+
+ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD;
+ }
+ else
+ {
+ if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)
+ {
+ DEBUG ("No-op");
+ tp_svc_channel_interface_hold_return_from_request_hold (context);
+ return;
+ }
+
+ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD;
+ }
+
+ if (old_state != priv->hold_state ||
+ priv->hold_state_reason != TP_LOCAL_HOLD_STATE_REASON_REQUESTED)
+ {
+ tp_svc_channel_interface_hold_emit_hold_state_changed (self,
+ priv->hold_state, TP_LOCAL_HOLD_STATE_REASON_REQUESTED);
+ priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED;
+ }
+
+ /* Tell streaming client to release or reacquire resources */
+
+ for (i = 0; i < priv->streams->len; i++)
+ {
+ gabble_media_stream_hold (g_ptr_array_index (priv->streams, i), hold);
+ }
+
+ tp_svc_channel_interface_hold_return_from_request_hold (context);
+}
+
+
+static void
+channel_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
+
+#define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\
+ klass, gabble_media_channel_##x##suffix)
+ IMPLEMENT(close,_async);
+ IMPLEMENT(get_channel_type,);
+ IMPLEMENT(get_handle,);
+ IMPLEMENT(get_interfaces,);
+#undef IMPLEMENT
+}
+
+static void
+streamed_media_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcChannelTypeStreamedMediaClass *klass =
+ (TpSvcChannelTypeStreamedMediaClass *)g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\
+ klass, gabble_media_channel_##x)
+ IMPLEMENT(list_streams);
+ IMPLEMENT(remove_streams);
+ IMPLEMENT(request_stream_direction);
+ IMPLEMENT(request_streams);
+#undef IMPLEMENT
+}
+
+static void
+media_signalling_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcChannelInterfaceMediaSignallingClass *klass =
+ (TpSvcChannelInterfaceMediaSignallingClass *)g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\
+ klass, gabble_media_channel_##x)
+ IMPLEMENT(get_session_handlers);
+#undef IMPLEMENT
+}
+
+static void
+call_state_iface_init (gpointer g_iface,
+ gpointer iface_data G_GNUC_UNUSED)
+{
+ TpSvcChannelInterfaceCallStateClass *klass = g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_interface_call_state_implement_##x (\
+ klass, gabble_media_channel_##x)
+ IMPLEMENT(get_call_states);
+#undef IMPLEMENT
+}
+
+static void
+hold_iface_init (gpointer g_iface,
+ gpointer iface_data G_GNUC_UNUSED)
+{
+ TpSvcChannelInterfaceHoldClass *klass = g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_interface_hold_implement_##x (\
+ klass, gabble_media_channel_##x)
+ IMPLEMENT(get_hold_state);
+ IMPLEMENT(request_hold);
+#undef IMPLEMENT
+}
--
1.5.6.3
More information about the Telepathy-commits
mailing list