summaryrefslogtreecommitdiff
path: root/rakia/call-content.c
diff options
context:
space:
mode:
Diffstat (limited to 'rakia/call-content.c')
-rw-r--r--rakia/call-content.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/rakia/call-content.c b/rakia/call-content.c
new file mode 100644
index 0000000..314b04a
--- /dev/null
+++ b/rakia/call-content.c
@@ -0,0 +1,401 @@
+/*
+ * call-content.c - RakiaCallContent
+ * Copyright (C) 2011-2012 Collabora Ltd.
+ * @author Olivier Crete <olivier.crete@collabora.com>
+ *
+ * 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 "config.h"
+
+#include "rakia/call-content.h"
+
+#include "rakia/call-stream.h"
+
+#define DEBUG_FLAG RAKIA_DEBUG_MEDIA
+#include "debug.h"
+
+
+static void rakia_call_content_constructed (GObject *object);
+static void rakia_call_content_dispose (GObject *object);
+static void rakia_call_content_deinit (TpBaseCallContent *base);
+
+static void media_remote_codecs_updated_cb (RakiaSipMedia *media,
+ gboolean is_offer, RakiaCallContent *self);
+static void local_media_description_updated (RakiaCallContent *self,
+ TpHandle contact, GHashTable *properties, gpointer user_data);
+
+static void md_offer_cb (GObject *obj, GAsyncResult *res, gpointer user_data);
+
+
+G_DEFINE_TYPE (RakiaCallContent, rakia_call_content,
+ TP_TYPE_BASE_MEDIA_CALL_CONTENT);
+
+
+/* properties */
+enum
+{
+ PROP_CHANNEL = 1,
+ PROP_SIP_MEDIA,
+ LAST_PROPERTY
+};
+
+
+/* private structure */
+struct _RakiaCallContentPrivate
+{
+ RakiaCallChannel *channel;
+
+ RakiaSipMedia *media;
+
+ RakiaCallStream *stream;
+
+ guint codec_offer_id;
+};
+
+static void
+rakia_call_content_init (RakiaCallContent *self)
+{
+ RakiaCallContentPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ RAKIA_TYPE_CALL_CONTENT, RakiaCallContentPrivate);
+
+ self->priv = priv;
+}
+
+static void
+rakia_call_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RakiaCallContent *self = RAKIA_CALL_CONTENT (object);
+ RakiaCallContentPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_CHANNEL:
+ priv->channel = g_value_dup_object (value);
+ break;
+ case PROP_SIP_MEDIA:
+ priv->media = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+rakia_call_content_class_init (
+ RakiaCallContentClass *rakia_call_content_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (rakia_call_content_class);
+ TpBaseCallContentClass *bcc_class =
+ TP_BASE_CALL_CONTENT_CLASS (rakia_call_content_class);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (rakia_call_content_class,
+ sizeof (RakiaCallContentPrivate));
+
+ object_class->constructed = rakia_call_content_constructed;
+ object_class->dispose = rakia_call_content_dispose;
+ object_class->set_property = rakia_call_content_set_property;
+
+ bcc_class->deinit = rakia_call_content_deinit;
+
+ param_spec = g_param_spec_object ("channel", "RakiaCallChannel object",
+ "Call Channel.",
+ RAKIA_TYPE_CALL_CHANNEL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CHANNEL, param_spec);
+
+ param_spec = g_param_spec_object ("sip-media", "RakiaSipMedia object",
+ "SIP media object that is used for this SIP media channel object.",
+ RAKIA_TYPE_SIP_MEDIA,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SIP_MEDIA, param_spec);
+}
+
+static void
+rakia_call_content_constructed (GObject *object)
+{
+ RakiaCallContent *self = RAKIA_CALL_CONTENT (object);
+ RakiaCallContentPrivate *priv = self->priv;
+ TpBaseChannel *bchan = TP_BASE_CHANNEL (priv->channel);
+ TpBaseCallContent *bcc = TP_BASE_CALL_CONTENT (self);
+ TpBaseMediaCallContent *bmcc = TP_BASE_MEDIA_CALL_CONTENT (self);
+ TpHandle creator;
+ gchar *object_path;
+
+ g_signal_connect_object (priv->media, "remote-codec-offer-updated",
+ G_CALLBACK (media_remote_codecs_updated_cb), self, 0);
+
+ g_signal_connect (self, "local-media-description-updated",
+ G_CALLBACK (local_media_description_updated), NULL);
+
+ g_object_get (object, "creator", &creator, NULL);
+
+ if (creator == tp_base_channel_get_self_handle (bchan))
+ {
+ TpCallContentMediaDescription *md;
+ TpDBusDaemon *bus = tp_base_connection_get_dbus_daemon (
+ tp_base_call_content_get_connection (bcc));
+
+ object_path = g_strdup_printf ("%s/InitialOffer",
+ tp_base_call_content_get_object_path (bcc));
+
+ md = tp_call_content_media_description_new (bus, object_path,
+ tp_base_channel_get_target_handle (bchan), FALSE, TRUE);
+ g_free (object_path);
+
+ tp_base_media_call_content_offer_media_description_async (bmcc,
+ md, md_offer_cb, GUINT_TO_POINTER (TRUE));
+ }
+ else
+ {
+ media_remote_codecs_updated_cb (priv->media, TRUE, self);
+ }
+
+ G_OBJECT_CLASS (rakia_call_content_parent_class)->constructed (object);
+}
+
+static void
+rakia_call_content_dispose (GObject *object)
+{
+ RakiaCallContent *self = RAKIA_CALL_CONTENT (object);
+ RakiaCallContentPrivate *priv = self->priv;
+
+ tp_clear_object (&priv->media);
+
+ if (G_OBJECT_CLASS (rakia_call_content_parent_class)->dispose)
+ G_OBJECT_CLASS (rakia_call_content_parent_class)->dispose (object);
+}
+
+static void
+rakia_call_content_deinit (TpBaseCallContent *base)
+{
+ RakiaCallContent *self = RAKIA_CALL_CONTENT (base);
+ RakiaCallContentPrivate *priv = self->priv;
+ RakiaSipSession *session;
+
+ session = rakia_sip_media_get_session (priv->media);
+
+ /* If the media was removed, it means it's by user request, so we must
+ * do a re-invite
+ */
+ if (rakia_sip_session_remove_media (session, priv->media, 0, NULL))
+ rakia_sip_session_media_changed (session);
+
+
+ tp_clear_object (&priv->stream);
+ tp_clear_object (&priv->channel);
+
+ TP_BASE_CALL_CONTENT_CLASS (
+ rakia_call_content_parent_class)->deinit (base);
+}
+
+RakiaCallContent *
+rakia_call_content_new (RakiaCallChannel *channel,
+ RakiaSipMedia *media,
+ const gchar *object_path,
+ TpBaseConnection *connection,
+ const gchar *name,
+ TpMediaStreamType media_type,
+ TpHandle creator,
+ TpCallContentDisposition disposition)
+{
+ RakiaCallContent *content;
+
+ content = g_object_new (RAKIA_TYPE_CALL_CONTENT,
+ "channel", channel,
+ "sip-media", media,
+ "object-path", object_path,
+ "connection", connection,
+ "name", name,
+ "media-type", media_type,
+ "creator", creator,
+ "disposition", disposition,
+ "packetization", TP_CALL_CONTENT_PACKETIZATION_TYPE_RTP,
+ NULL);
+
+ return content;
+}
+
+RakiaSipMedia *
+rakia_call_content_get_media (RakiaCallContent *self)
+{
+ return self->priv->media;
+}
+
+static void
+set_telepathy_codecs (RakiaCallContent *self, GHashTable *md_properties)
+{
+ RakiaCallContentPrivate *priv = self->priv;
+ guint i;
+ GPtrArray *tpcodecs = tp_asv_get_boxed (md_properties,
+ TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_CODECS,
+ TP_ARRAY_TYPE_CODEC_LIST);
+ GPtrArray *sipcodecs = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) rakia_sip_codec_free);
+
+ for (i = 0; i < tpcodecs->len; i++)
+ {
+ GValueArray *tpcodec = g_ptr_array_index (tpcodecs, i);
+ guint id;
+ const gchar *name;
+ guint clock_rate;
+ guint channels;
+ gboolean updated;
+ GHashTable *extra_params;
+ RakiaSipCodec *sipcodec;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ tp_value_array_unpack (tpcodec, 6, &id, &name, &clock_rate,
+ &channels, &updated, &extra_params);
+
+ sipcodec = rakia_sip_codec_new (id, name, clock_rate, channels);
+
+ g_hash_table_iter_init (&iter, extra_params);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ rakia_sip_codec_add_param (sipcodec, key, value);
+
+ g_ptr_array_add (sipcodecs, sipcodec);
+ }
+
+ rakia_sip_media_take_local_codecs (priv->media, sipcodecs);
+}
+
+static void
+md_offer_cb (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+ RakiaCallContent *self = RAKIA_CALL_CONTENT (obj);
+ RakiaCallContentPrivate *priv = self->priv;
+ TpBaseMediaCallContent *bmcc = TP_BASE_MEDIA_CALL_CONTENT (self);
+ GError *error = NULL;
+ gboolean is_initial_offer = GPOINTER_TO_UINT (user_data);
+
+ if (tp_base_media_call_content_offer_media_description_finish (bmcc,
+ res, &error))
+ {
+ GHashTable *local_md =
+ tp_base_media_call_content_get_local_media_description (bmcc,
+ tp_base_channel_get_target_handle (TP_BASE_CHANNEL (priv->channel)));
+
+ set_telepathy_codecs (self, local_md);
+ }
+ else
+ {
+ /* Only reject if the codecs where rejected */
+ if (error->domain == TP_ERRORS &&
+ error->code == TP_ERROR_MEDIA_CODECS_INCOMPATIBLE)
+ {
+ g_assert (!is_initial_offer);
+
+ rakia_sip_media_codecs_rejected (priv->media);
+
+ DEBUG ("Codecs rejected: %s", error->message);
+
+ }
+
+ /* FIXME: We need to allow for partial failures */
+ g_clear_error (&error);
+ }
+}
+
+static void
+media_remote_codecs_updated_cb (RakiaSipMedia *media, gboolean is_offer,
+ RakiaCallContent *self)
+{
+ TpBaseCallContent *bcc = TP_BASE_CALL_CONTENT (self);
+ TpBaseMediaCallContent *bmcc = TP_BASE_MEDIA_CALL_CONTENT (self);
+ RakiaCallContentPrivate *priv = self->priv;
+ GPtrArray *remote_codecs =
+ rakia_sip_media_get_remote_codec_offer (priv->media);
+ TpCallContentMediaDescription *md;
+ TpDBusDaemon *bus = tp_base_connection_get_dbus_daemon (
+ tp_base_call_content_get_connection (bcc));
+ gchar *object_path;
+ guint i, j;
+
+ if (remote_codecs == NULL)
+ return;
+
+ object_path = g_strdup_printf ("%s/Offer%u",
+ tp_base_call_content_get_object_path (bcc), ++priv->codec_offer_id);
+
+ md = tp_call_content_media_description_new (bus, object_path,
+ tp_base_channel_get_target_handle (TP_BASE_CHANNEL (priv->channel)),
+ TRUE, is_offer);
+
+ g_free (object_path);
+
+ for (i = 0; i < remote_codecs->len; i++)
+ {
+ RakiaSipCodec *codec = g_ptr_array_index (remote_codecs, i);
+ GHashTable *parameters = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* No need to copy the values as .._append_codec() will */
+ if (codec->params)
+ for (j = 0; j < codec->params->len; j++)
+ {
+ RakiaSipCodecParam *param = g_ptr_array_index (codec->params, j);
+
+ g_hash_table_insert (parameters, param->name, param->value);
+ }
+
+ tp_call_content_media_description_append_codec (md, codec->id,
+ codec->encoding_name, codec->clock_rate, codec->channels, TRUE,
+ parameters);
+
+ g_hash_table_unref (parameters);
+ }
+
+ tp_base_media_call_content_offer_media_description_async (bmcc,
+ md, md_offer_cb, GUINT_TO_POINTER (FALSE));
+
+ g_object_unref (md);
+}
+
+void
+rakia_call_content_add_stream (RakiaCallContent *self)
+{
+ RakiaCallContentPrivate *priv = self->priv;
+ TpBaseCallContent *bcc = TP_BASE_CALL_CONTENT (self);
+ gchar *object_path;
+
+ object_path = g_strdup_printf ("%s/Stream",
+ tp_base_call_content_get_object_path (bcc));
+ priv->stream = rakia_call_stream_new (self, priv->media,
+ object_path, TP_STREAM_TRANSPORT_TYPE_RAW_UDP,
+ tp_base_call_content_get_connection (bcc));
+ g_free (object_path);
+
+ tp_base_call_content_add_stream (bcc, TP_BASE_CALL_STREAM (priv->stream));
+}
+
+static void
+local_media_description_updated (RakiaCallContent *self, TpHandle contact,
+ GHashTable *properties, gpointer user_data)
+{
+ set_telepathy_codecs (self, properties);
+}
+
+void
+rakia_call_content_remote_accept (RakiaCallContent *content)
+{
+ rakia_call_stream_update_direction (content->priv->stream);
+}