[telepathy-gabble/master] Add first dummy implementation of a Call channel

Sjoerd Simons sjoerd.simons at collabora.co.uk
Tue Dec 29 05:34:36 PST 2009


---
 src/Makefile.am     |    2 +
 src/call-channel.c  |  544 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/call-channel.h  |   64 ++++++
 src/media-factory.c |  107 ++++++++++-
 4 files changed, 715 insertions(+), 2 deletions(-)
 create mode 100644 src/call-channel.c
 create mode 100644 src/call-channel.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 304e788..9dcd82c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,6 +25,8 @@ libgabble_convenience_la_SOURCES = \
     bytestream-multiple.c \
     bytestream-socks5.h \
     bytestream-socks5.c \
+    call-channel.h \
+    call-channel.c \
     capabilities.h \
     capabilities.c \
     caps-hash.h \
diff --git a/src/call-channel.c b/src/call-channel.c
new file mode 100644
index 0000000..fd5fd00
--- /dev/null
+++ b/src/call-channel.c
@@ -0,0 +1,544 @@
+/*
+ * gabble-call-channel.c - Source for GabbleCallChannel
+ * Copyright (C) 2009 Collabora Ltd.
+ * @author Sjoerd Simons <sjoerd.simons 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 <stdio.h>
+#include <stdlib.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/enums.h>
+#include <telepathy-glib/exportable-channel.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 <telepathy-glib/base-connection.h>
+
+#include <extensions/extensions.h>
+
+#include "call-channel.h"
+
+#include "connection.h"
+#include "jingle-session.h"
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+
+static void channel_iface_init (gpointer, gpointer);
+static void call_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE(GabbleCallChannel, gabble_call_channel,
+  G_TYPE_OBJECT,
+  G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
+  G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CHANNEL_TYPE_CALL,
+        call_iface_init);
+   G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+    tp_dbus_properties_mixin_iface_init);
+   G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL);
+   G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)
+  );
+
+static const gchar *gabble_call_channel_interfaces[] = {
+    NULL
+};
+
+/* properties */
+enum
+{
+  PROP_OBJECT_PATH = 1,
+  PROP_CHANNEL_TYPE,
+  PROP_HANDLE_TYPE,
+  PROP_TARGET_HANDLE,
+  PROP_TARGET_ID,
+
+  PROP_REQUESTED,
+  PROP_CONNECTION,
+  PROP_CREATOR,
+  PROP_CREATOR_ID,
+
+  PROP_INTERFACES,
+  PROP_CHANNEL_DESTROYED,
+  PROP_CHANNEL_PROPERTIES,
+  PROP_INITIAL_AUDIO,
+  PROP_INITIAL_VIDEO,
+  PROP_MUTABLE_CONTENTS,
+
+  PROP_SESSION,
+  LAST_PROPERTY
+};
+
+
+/* private structure */
+struct _GabbleCallChannelPrivate
+{
+  GabbleConnection *conn;
+  gchar *object_path;
+  GabbleJingleSession *session;
+
+  TpHandle creator;
+  TpHandle target;
+
+  gboolean closed;
+
+  gboolean initial_audio;
+  gboolean initial_video;
+  gboolean mutable_contents;
+
+  gboolean dispose_has_run;
+};
+
+static GObject *
+gabble_call_channel_constructor (GType type,
+    guint n_props,
+    GObjectConstructParam *props)
+{
+  GObject *obj;
+  GabbleCallChannelPrivate *priv;
+  TpBaseConnection *conn;
+  DBusGConnection *bus;
+  TpHandleRepoIface *contact_handles;
+
+  obj = G_OBJECT_CLASS (gabble_call_channel_parent_class)->
+           constructor (type, n_props, props);
+
+  priv = GABBLE_CALL_CHANNEL (obj)->priv;
+  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 ();
+  DEBUG ("Registering %s", priv->object_path);
+  dbus_g_connection_register_g_object (bus, priv->object_path, obj);
+
+  if (priv->session != NULL)
+      priv->creator = priv->session->peer;
+  else
+      priv->creator = conn->self_handle;
+
+  /* automatically add creator to channel, but also ref them again (because
+   * priv->creator is the InitiatorHandle) */
+  g_assert (priv->creator != 0);
+  tp_handle_ref (contact_handles, priv->creator);
+
+  return obj;
+}
+
+static void
+gabble_call_channel_init (GabbleCallChannel *self)
+{
+  GabbleCallChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelPrivate);
+
+  self->priv = priv;
+}
+
+static void gabble_call_channel_dispose (GObject *object);
+static void gabble_call_channel_finalize (GObject *object);
+
+static void
+gabble_call_channel_get_property (GObject    *object,
+    guint       property_id,
+    GValue     *value,
+    GParamSpec *pspec)
+{
+  GabbleCallChannel *chan = GABBLE_CALL_CHANNEL (object);
+  GabbleCallChannelPrivate *priv = chan->priv;
+  TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
+
+  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, GABBLE_IFACE_CHANNEL_TYPE_CALL);
+        break;
+      case PROP_HANDLE_TYPE:
+        g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
+        break;
+      case PROP_TARGET_HANDLE:
+        g_value_set_uint (value, priv->target);
+        break;
+      case PROP_TARGET_ID:
+        {
+          TpHandleRepoIface *repo = tp_base_connection_get_handles (
+              base_conn, TP_HANDLE_TYPE_CONTACT);
+          const gchar *target_id = tp_handle_inspect (repo, priv->target);
+
+          g_value_set_string (value, target_id);
+        }
+        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_CREATOR_ID:
+        {
+          TpHandleRepoIface *repo = tp_base_connection_get_handles (
+               base_conn, TP_HANDLE_TYPE_CONTACT);
+
+          g_value_set_string (value, tp_handle_inspect (repo, priv->creator));
+        }
+       break;
+     case PROP_REQUESTED:
+       g_value_set_boolean (value, (priv->creator == base_conn->self_handle));
+       break;
+     case PROP_INTERFACES:
+       g_value_set_boxed (value, gabble_call_channel_interfaces);
+       break;
+     case PROP_CHANNEL_DESTROYED:
+       g_value_set_boolean (value, priv->closed);
+       break;
+     case PROP_CHANNEL_PROPERTIES:
+       g_value_take_boxed (value,
+           tp_dbus_properties_mixin_make_properties_hash (object,
+               TP_IFACE_CHANNEL, "TargetHandle",
+               TP_IFACE_CHANNEL, "TargetHandleType",
+               TP_IFACE_CHANNEL, "ChannelType",
+               TP_IFACE_CHANNEL, "TargetID",
+               TP_IFACE_CHANNEL, "InitiatorHandle",
+               TP_IFACE_CHANNEL, "InitiatorID",
+               TP_IFACE_CHANNEL, "Requested",
+               TP_IFACE_CHANNEL, "Interfaces",
+               GABBLE_IFACE_CHANNEL_TYPE_CALL, "InitialAudio",
+               GABBLE_IFACE_CHANNEL_TYPE_CALL, "InitialVideo",
+               GABBLE_IFACE_CHANNEL_TYPE_CALL, "MutableContents",
+               NULL));
+       break;
+     case PROP_SESSION:
+       g_value_set_object (value, priv->session);
+       break;
+     case PROP_INITIAL_AUDIO:
+       g_value_set_boolean (value, priv->initial_audio);
+       break;
+     case PROP_INITIAL_VIDEO:
+        g_value_set_boolean (value, priv->initial_video);
+        break;
+      case PROP_MUTABLE_CONTENTS:
+        g_value_set_boolean (value, priv->mutable_contents);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+gabble_call_channel_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  GabbleCallChannel *chan = GABBLE_CALL_CHANNEL (object);
+  GabbleCallChannelPrivate *priv = chan->priv;
+
+  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_CHANNEL_TYPE:
+        /* these properties are writable in the interface, but not actually
+        * meaningfully changable on this channel, so we do nothing */
+        break;
+      case PROP_TARGET_HANDLE:
+        priv->target = g_value_get_uint (value);
+        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_SESSION:
+        g_assert (priv->session == NULL);
+        priv->session = g_value_dup_object (value);
+        break;
+      case PROP_INITIAL_AUDIO:
+        priv->initial_audio = g_value_get_boolean (value);
+        break;
+      case PROP_INITIAL_VIDEO:
+        priv->initial_video = g_value_get_boolean (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+  }
+}
+
+
+static void
+gabble_call_channel_class_init (
+    GabbleCallChannelClass *gabble_call_channel_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_channel_class);
+  GParamSpec *param_spec;
+  static TpDBusPropertiesMixinPropImpl channel_props[] = {
+      { "TargetHandleType", "handle-type", NULL },
+      { "TargetHandle", "handle", NULL },
+      { "TargetID", "target-id", NULL },
+      { "ChannelType", "channel-type", NULL },
+      { "Interfaces", "interfaces", NULL },
+      { "Requested", "requested", NULL },
+      { "InitiatorHandle", "creator", NULL },
+      { "InitiatorID", "creator-id", NULL },
+      { NULL }
+  };
+  static TpDBusPropertiesMixinPropImpl call_props[] = {
+      { "MutableContents", "mutable-contents", NULL },
+      { "InitialAudio", "initial-audio", NULL },
+      { "InitialVideo", "initial-video", NULL },
+      { NULL }
+  };
+
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+      { TP_IFACE_CHANNEL,
+        tp_dbus_properties_mixin_getter_gobject_properties,
+        NULL,
+        channel_props,
+      },
+      { GABBLE_IFACE_CHANNEL_TYPE_CALL,
+        tp_dbus_properties_mixin_getter_gobject_properties,
+        NULL,
+        call_props,
+      },
+      { NULL }
+  };
+
+  g_type_class_add_private (gabble_call_channel_class,
+      sizeof (GabbleCallChannelPrivate));
+
+  object_class->constructor = gabble_call_channel_constructor;
+
+  object_class->get_property = gabble_call_channel_get_property;
+  object_class->set_property = gabble_call_channel_set_property;
+
+  object_class->dispose = gabble_call_channel_dispose;
+  object_class->finalize = gabble_call_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_TARGET_HANDLE,
+      "handle");
+
+  g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
+      "channel-destroyed");
+  g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
+      "channel-properties");
+
+  param_spec = g_param_spec_string ("target-id", "Target JID",
+      "Target JID of the call" ,
+      NULL,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
+
+  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_STRINGS);
+  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_STRINGS);
+  g_object_class_install_property (object_class, PROP_CREATOR, param_spec);
+
+  param_spec = g_param_spec_string ("creator-id", "Creator bare JID",
+      "The bare JID obtained by inspecting the creator handle.",
+      NULL,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CREATOR_ID, param_spec);
+
+  param_spec = g_param_spec_boolean ("requested", "Requested?",
+      "True if this channel was requested by the local user",
+      FALSE,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_REQUESTED, 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_STRINGS);
+  g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+  param_spec = g_param_spec_object ("session", "GabbleJingleSession object",
+      "Jingle session associated with this media channel object.",
+      GABBLE_TYPE_JINGLE_SESSION,
+      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_SESSION, param_spec);
+
+  param_spec = g_param_spec_boolean ("initial-audio", "InitialAudio",
+      "Whether the channel initially contained an audio stream",
+      FALSE,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
+      param_spec);
+
+  param_spec = g_param_spec_boolean ("initial-video", "InitialVideo",
+      "Whether the channel initially contained an video stream",
+      FALSE,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
+      param_spec);
+
+  param_spec = g_param_spec_boolean ("mutable-contents", "MutableContents",
+      "Whether the set of streams on this channel are mutable once requested",
+      FALSE,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_MUTABLE_CONTENTS,
+      param_spec);
+
+  gabble_call_channel_class->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (GabbleCallChannelClass, dbus_props_class));
+}
+
+void
+gabble_call_channel_dispose (GObject *object)
+{
+  GabbleCallChannel *self = GABBLE_CALL_CHANNEL (object);
+
+  if (self->priv->dispose_has_run)
+    return;
+
+  self->priv->dispose_has_run = TRUE;
+
+  /* release any references held by the object here */
+  if (G_OBJECT_CLASS (gabble_call_channel_parent_class)->dispose)
+    G_OBJECT_CLASS (gabble_call_channel_parent_class)->dispose (object);
+}
+
+void
+gabble_call_channel_finalize (GObject *object)
+{
+  GabbleCallChannel *self = GABBLE_CALL_CHANNEL (object);
+
+  /* free any data held directly by the object here */
+  g_free (self->priv->object_path);
+
+  G_OBJECT_CLASS (gabble_call_channel_parent_class)->finalize (object);
+}
+
+void
+gabble_call_channel_close (GabbleCallChannel *self)
+{
+  GabbleCallChannelPrivate *priv = self->priv;
+  DEBUG ("Closing media channel %s", self->priv->object_path);
+
+  if (!priv->closed)
+    {
+      priv->closed = TRUE;
+      if (priv->session != NULL)
+        gabble_jingle_session_terminate (priv->session,
+            TP_CHANNEL_GROUP_CHANGE_REASON_NONE, NULL, NULL);
+
+      tp_svc_channel_emit_closed (self);
+    }
+}
+
+/**
+ * gabble_call_channel_close_async:
+ *
+ * Implements D-Bus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_call_channel_close_async (TpSvcChannel *iface,
+                                  DBusGMethodInvocation *context)
+{
+  GabbleCallChannel *self = GABBLE_CALL_CHANNEL (iface);
+
+  DEBUG ("called");
+  gabble_call_channel_close (self);
+  tp_svc_channel_return_from_close (context);
+}
+
+/**
+ * gabble_call_channel_get_channel_type
+ *
+ * Implements D-Bus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_call_channel_get_channel_type (TpSvcChannel *iface,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_return_from_get_channel_type (context,
+      GABBLE_IFACE_CHANNEL_TYPE_CALL);
+}
+
+/**
+ * gabble_call_channel_get_handle
+ *
+ * Implements D-Bus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_call_channel_get_handle (TpSvcChannel *iface,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
+    GABBLE_CALL_CHANNEL (iface)->priv->target);
+}
+
+/**
+ * gabble_call_channel_get_interfaces
+ *
+ * Implements D-Bus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_call_channel_get_interfaces (TpSvcChannel *iface,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_return_from_get_interfaces (context,
+      gabble_call_channel_interfaces);
+}
+
+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_call_channel_##x##suffix)
+    IMPLEMENT(close,_async);
+    IMPLEMENT(get_channel_type,);
+    IMPLEMENT(get_handle,);
+    IMPLEMENT(get_interfaces,);
+#undef IMPLEMENT
+}
+
+static void
+call_iface_init (gpointer g_iface, gpointer iface_data)
+{
+}
diff --git a/src/call-channel.h b/src/call-channel.h
new file mode 100644
index 0000000..7998ad1
--- /dev/null
+++ b/src/call-channel.h
@@ -0,0 +1,64 @@
+/*
+ * gabble-call-channel.h - Header for GabbleCallChannel
+ * Copyright (C) 2009 Collabora Ltd.
+ * @author Sjoerd Simons <sjoerd.simons 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
+ */
+
+#ifndef __GABBLE_CALL_CHANNEL_H__
+#define __GABBLE_CALL_CHANNEL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleCallChannel GabbleCallChannel;
+typedef struct _GabbleCallChannelPrivate GabbleCallChannelPrivate;
+typedef struct _GabbleCallChannelClass GabbleCallChannelClass;
+
+struct _GabbleCallChannelClass {
+    GObjectClass parent_class;
+
+    TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _GabbleCallChannel {
+    GObject parent;
+
+    GabbleCallChannelPrivate *priv;
+};
+
+GType gabble_call_channel_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_CALL_CHANNEL \
+  (gabble_call_channel_get_type ())
+#define GABBLE_CALL_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannel))
+#define GABBLE_CALL_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelClass))
+#define GABBLE_IS_CALL_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_CHANNEL))
+#define GABBLE_IS_CALL_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_CHANNEL))
+#define GABBLE_CALL_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelClass))
+
+void gabble_call_channel_close (GabbleCallChannel *self);
+
+G_END_DECLS
+
+#endif /* #ifndef __GABBLE_CALL_CHANNEL_H__*/
diff --git a/src/media-factory.c b/src/media-factory.c
index 66fbd20..6e5f963 100644
--- a/src/media-factory.c
+++ b/src/media-factory.c
@@ -43,6 +43,7 @@
 #include "jingle-media-rtp.h"
 #include "jingle-session.h"
 
+#include "call-channel.h"
 #include "media-channel.h"
 #include "namespaces.h"
 #include "util.h"
@@ -70,6 +71,7 @@ struct _GabbleMediaFactoryPrivate
   gulong status_changed_id;
 
   GList *media_channels;
+  GList *call_channels;
   guint channel_index;
 
   gboolean dispose_has_run;
@@ -107,6 +109,7 @@ gabble_media_factory_dispose (GObject *object)
 
   gabble_media_factory_close_all (fac);
   g_assert (priv->media_channels == NULL);
+  g_assert (priv->call_channels == NULL);
 
   if (G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose)
     G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose (object);
@@ -259,6 +262,14 @@ gabble_media_factory_close_all (GabbleMediaFactory *fac)
     gabble_media_channel_close (
       GABBLE_MEDIA_CHANNEL (priv->media_channels->data));
 
+  /* Close will cause the channel to be removed from the list indirectly..*/
+  while (priv->call_channels != NULL)
+    gabble_call_channel_close (
+      GABBLE_CALL_CHANNEL (priv->call_channels->data));
+
+  g_list_free (priv->call_channels);
+  priv->call_channels = NULL;
+
   if (priv->status_changed_id != 0)
     {
       g_signal_handler_disconnect (priv->conn,
@@ -352,8 +363,10 @@ gabble_media_factory_foreach_channel (TpChannelManager *manager,
 
   for (l = priv->media_channels; l != NULL; l = g_list_next (l))
     foreach (TP_EXPORTABLE_CHANNEL (l->data), user_data);
-}
 
+  for (l = priv->call_channels; l != NULL; l = g_list_next (l))
+    foreach (TP_EXPORTABLE_CHANNEL (l->data), user_data);
+}
 
 static const gchar * const media_channel_fixed_properties[] = {
     TP_IFACE_CHANNEL ".ChannelType",
@@ -687,6 +700,90 @@ error:
   return TRUE;
 }
 
+static void
+call_channel_closed_cb (GabbleCallChannel *chan, gpointer user_data)
+{
+  GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data);
+  GabbleMediaFactoryPrivate *priv = fac->priv;
+
+  tp_channel_manager_emit_channel_closed_for_object (fac,
+      TP_EXPORTABLE_CHANNEL (chan));
+
+  DEBUG ("removing media channel %p with ref count %d",
+      chan, G_OBJECT (chan)->ref_count);
+
+  priv->call_channels = g_list_remove (priv->call_channels, chan);
+  g_object_unref (chan);
+}
+
+static gboolean
+gabble_media_factory_create_call (TpChannelManager *manager,
+    gpointer request_token,
+    GHashTable *request_properties)
+{
+  GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (manager);
+  TpHandle target;
+  GabbleCallChannel *channel = NULL;
+  TpBaseConnection *conn;
+  GError *error = NULL;
+  GSList *tokens;
+  gboolean initial_audio, initial_video;
+  gchar *object_path;
+
+  conn = (TpBaseConnection *) self->priv->conn;
+
+  DEBUG ("Creating a new call channel");
+
+  if (tp_asv_get_uint32 (request_properties,
+      TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT)
+    return FALSE;
+
+  if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+              media_channel_fixed_properties, call_channel_allowed_properties,
+              &error))
+        goto error;
+
+  target  = tp_asv_get_uint32 (request_properties,
+      TP_IFACE_CHANNEL ".TargetHandle", NULL);
+
+  initial_audio = tp_asv_get_boolean (request_properties,
+      GABBLE_IFACE_CHANNEL_TYPE_CALL ".InitialAudio", NULL);
+  initial_video = tp_asv_get_boolean (request_properties,
+      GABBLE_IFACE_CHANNEL_TYPE_CALL ".InitialVideo", NULL);
+
+  /* FIXME need to check if there is at least one of InitialAudio/InitialVideo
+   * FIXME creating the channel should check and wait for the capabilities
+   */
+
+  object_path = g_strdup_printf ("%s/CallChannel%u",
+    conn->object_path, self->priv->channel_index);
+  self->priv->channel_index++;
+
+  channel = g_object_new (GABBLE_TYPE_CALL_CHANNEL,
+      "connection", conn,
+      "object-path", object_path,
+      "handle", target,
+      "initial-audio", initial_audio,
+      "initial-video", initial_video,
+      NULL);
+
+  self->priv->call_channels
+    = g_list_prepend (self->priv->call_channels, channel);
+  g_signal_connect (channel, "closed",
+      G_CALLBACK (call_channel_closed_cb), self);
+
+  tokens = g_slist_prepend (NULL, request_token);
+  tp_channel_manager_emit_new_channel (self,
+    TP_EXPORTABLE_CHANNEL (channel), tokens);
+  g_slist_free (tokens);
+
+  return TRUE;
+error:
+  tp_channel_manager_emit_request_failed (self, request_token,
+      error->domain, error->code, error->message);
+  g_error_free (error);
+  return TRUE;
+}
 
 static gboolean
 gabble_media_factory_request_channel (TpChannelManager *manager,
@@ -703,7 +800,13 @@ gabble_media_factory_create_channel (TpChannelManager *manager,
                                      gpointer request_token,
                                      GHashTable *request_properties)
 {
-  return gabble_media_factory_requestotron (manager, request_token,
+  if (!tp_strdiff (tp_asv_get_string (request_properties,
+          TP_IFACE_CHANNEL ".ChannelType"),
+        GABBLE_IFACE_CHANNEL_TYPE_CALL))
+    return gabble_media_factory_create_call (manager, request_token,
+      request_properties);
+  else
+    return gabble_media_factory_requestotron (manager, request_token,
       request_properties, METHOD_CREATE);
 }
 
-- 
1.5.6.5




More information about the telepathy-commits mailing list