summaryrefslogtreecommitdiff
path: root/telepathy-farstream/content.c
diff options
context:
space:
mode:
Diffstat (limited to 'telepathy-farstream/content.c')
-rw-r--r--telepathy-farstream/content.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/telepathy-farstream/content.c b/telepathy-farstream/content.c
new file mode 100644
index 000000000..09989cd02
--- /dev/null
+++ b/telepathy-farstream/content.c
@@ -0,0 +1,551 @@
+#include "config.h"
+
+#include "content.h"
+#include "content-priv.h"
+
+#include <farstream/fs-conference.h>
+
+#include "channel.h"
+
+
+/**
+ * SECTION:content
+ * @short_description: Represent the Content of a channel handled by #TfChannel
+ *
+ * Objects of this class allow the user to handle the media side of a Telepathy
+ * channel handled by #TfChannel.
+ *
+ * This object is created by the #TfChannel and the user is notified
+ * of its creation by the #TfChannel::content-added signal. In the
+ * callback for this signal, the user should connect to the
+ * #TfContent::src-pad-added signal.
+ *
+ */
+
+
+G_DEFINE_ABSTRACT_TYPE (TfContent, tf_content, G_TYPE_OBJECT);
+
+
+enum
+{
+ PROP_TF_CHANNEL = 1,
+ PROP_FS_CONFERENCE,
+ PROP_FS_SESSION,
+ PROP_MEDIA_TYPE,
+ PROP_SINK_PAD,
+ PROP_OBJECT_PATH
+};
+
+enum
+{
+ SIGNAL_START_SENDING,
+ SIGNAL_STOP_SENDING,
+ SIGNAL_SRC_PAD_ADDED,
+ SIGNAL_START_RECEIVING,
+ SIGNAL_STOP_RECEIVING,
+ SIGNAL_RESTART_SOURCE,
+ SIGNAL_COUNT
+};
+
+static guint signals[SIGNAL_COUNT] = {0};
+
+static void
+tf_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ /* Other properties need to be overwritten */
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+tf_content_class_init (TfContentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = tf_content_get_property;
+
+ g_object_class_install_property (object_class, PROP_TF_CHANNEL,
+ g_param_spec_object ("tf-channel",
+ "Parent TfChannel object ",
+ "The Telepathy-Farstream Channel for this object",
+ TF_TYPE_CHANNEL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_FS_CONFERENCE,
+ g_param_spec_object ("fs-conference",
+ "Farstream FsConference used by the Content ",
+ "The Farstream conference for this content "
+ "(could be the same as other contents)",
+ FS_TYPE_CONFERENCE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_FS_SESSION,
+ g_param_spec_object ("fs-session",
+ "Farstream FsSession ",
+ "The Farstream session for this content",
+ FS_TYPE_SESSION,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_SINK_PAD,
+ g_param_spec_object ("sink-pad",
+ "Sink Pad",
+ "Sink GstPad for this content",
+ GST_TYPE_PAD,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_MEDIA_TYPE,
+ g_param_spec_enum ("media-type",
+ "MediaType",
+ "The FsMediaType for this content",
+ FS_TYPE_MEDIA_TYPE,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_OBJECT_PATH,
+ g_param_spec_string ("object-path",
+ "content object path",
+ "D-Bus object path of the Telepathy content which this content"
+ " operates on",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+
+ /**
+ * TfContent::start-sending:
+ * @content: the #TfContent
+ *
+ * This signal is emitted when the connection manager ask to send media.
+ * For example, this can be used to open a camera, start recording from a
+ * microphone or play back a file. The application should start
+ * sending data on the #TfContent:sink-pad
+ *
+ * Returns: %TRUE if the application can start providing data or %FALSE
+ * otherwise
+ */
+
+ signals[SIGNAL_START_SENDING] =
+ g_signal_new ("start-sending",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled, NULL, NULL,
+ G_TYPE_BOOLEAN, 0);
+
+ /**
+ * TfContent::stop-sending:
+ * @content: the #TfContent
+ *
+ * This signal is emitted when the connection manager ask to stop
+ * sending media
+ */
+
+ signals[SIGNAL_STOP_SENDING] =
+ g_signal_new ("stop-sending",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * TfContent::src-pad-added:
+ * @content: the #TfContent
+ * @handle: the handle of the remote party producing the content on this pad
+ * or 0 if unknown
+ * @stream: the #FsStream for this pad
+ * @pad: a #GstPad
+ * @codec: the #FsCodec for this pad
+ *
+ * This signal is emitted when a data is coming on a new pad. This signal
+ * is not emitted on the main thread, so special care must be made to lock
+ * the relevant data. When the callback returns from this signal, data will
+ * start flowing through the pad, so the application MUST connect a sink.
+ */
+
+ signals[SIGNAL_SRC_PAD_ADDED] =
+ g_signal_new ("src-pad-added",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 4,
+ G_TYPE_UINT, FS_TYPE_STREAM, GST_TYPE_PAD, FS_TYPE_CODEC);
+
+ /**
+ * TfContent::start-receiving:
+ * @content: the #TfContent
+ * @handles: a 0-terminated array of #guint containing the handles
+ * @handle_count: The number of handles in the @handles array
+ *
+ * This signal is emitted when the connection managers requests that the
+ * application prepares itself to start receiving data again from certain
+ * handles.
+ *
+ * This signal will only be emitted after the #TfContent::stop-receiving
+ * signal has succeeded. It will not be emitted right after
+ * #TfContent::src-pad-added.
+ *
+ * Returns: %TRUE if the application can start receiving data or %FALSE
+ * otherwise
+ */
+
+ signals[SIGNAL_START_RECEIVING] =
+ g_signal_new ("start-receiving",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled, NULL, NULL,
+ G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_UINT);
+
+ /**
+ * TfContent::stop-receiving:
+ * @content: the #TfContent
+ * @handles: a 0-terminated array of #guint containing the handles
+ * @handle_count: The number of handles in the @handles array
+ *
+ * This signal is emitted when the connection manager wants to tell the
+ * application that it is now allowed to stop receiving.
+ */
+
+ signals[SIGNAL_STOP_RECEIVING] =
+ g_signal_new ("stop-receiving",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
+
+ /**
+ * TfContent::restart-source:
+ * @content: the #TfContent
+ *
+ * This signal requests that the source be restarted so that the caps can
+ * be renegotiated with a new resolutions and framerate.
+ */
+
+ signals[SIGNAL_RESTART_SOURCE] =
+ g_signal_new ("restart-source",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+
+static void
+tf_content_init (TfContent *self)
+{
+}
+
+
+gboolean
+_tf_content_start_sending (TfContent *self)
+{
+ GValue instance = {0};
+ GValue sending_success_val = {0,};
+ gboolean sending_success;
+
+
+ if (self->sending_count)
+ {
+ self->sending_count ++;
+ return TRUE;
+ }
+
+ g_value_init (&sending_success_val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&sending_success_val, TRUE);
+
+ g_value_init (&instance, TF_TYPE_CONTENT);
+ g_value_set_object (&instance, self);
+
+ g_debug ("Requesting that the application start sending");
+
+ g_signal_emitv (&instance, signals[SIGNAL_START_SENDING], 0,
+ &sending_success_val);
+ sending_success = g_value_get_boolean (&sending_success_val);
+
+ g_value_unset (&instance);
+
+ g_debug ("Request to start sending %s",
+ sending_success ? "succeeded" : "failed");
+
+ self->sending_count = 1;
+
+ return sending_success;
+}
+
+void
+_tf_content_stop_sending (TfContent *self)
+{
+ self->sending_count --;
+
+ if (self->sending_count == 0)
+ {
+ g_signal_emit (self, signals[SIGNAL_STOP_SENDING], 0);
+ }
+}
+
+
+void
+_tf_content_emit_src_pad_added (TfContent *self, guint handle,
+ FsStream *stream, GstPad *pad, FsCodec *codec)
+{
+ g_signal_emit (self, signals[SIGNAL_SRC_PAD_ADDED], 0, handle,
+ stream, pad, codec);
+}
+
+/**
+ * tf_content_error_literal:
+ * @content: a #TfContent
+ * @message: error Message
+ *
+ * Send a fatal streaming error to the Content to the CM, the effect is most
+ * likely that the content will be removed.
+ *
+ * Rename to: tf_content_error
+ */
+
+void
+tf_content_error_literal (TfContent *content,
+ const gchar *message)
+{
+ TfContentClass *klass = TF_CONTENT_GET_CLASS (content);
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message != NULL);
+
+ if (klass->content_error)
+ klass->content_error (content, message);
+ else
+ GST_WARNING ("content_error not defined in class: %s", message);
+}
+
+/**
+ * tf_content_error:
+ * @content: a #TfContent
+ * @message_format: error Message with printf style formatting
+ * @...: Parameters to insert into the @message_format string
+ *
+ * Send a fatal streaming error to the Content to the CM, the effect is most
+ * likely that the content will be removed.
+ */
+
+void
+tf_content_error (TfContent *content,
+ const gchar *message_format,
+ ...)
+{
+ gchar *message;
+ va_list valist;
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message_format != NULL);
+
+ va_start (valist, message_format);
+ message = g_strdup_vprintf (message_format, valist);
+ va_end (valist);
+
+ tf_content_error_literal (content, message);
+ g_free (message);
+}
+
+/**
+ * tf_content_iterate_src_pads:
+ * @content: a #TfContent
+ * @handles: a 0 terminated array of #guint representing Telepathy handles
+ * @handle_count: the numner of handles in @handles
+ *
+ * Provides a iterator that can be used to iterate through all of the src
+ * pads that are are used to receive from a group of Telepathy handles.
+ *
+ * Returns: a #GstIterator
+ */
+
+GstIterator *
+tf_content_iterate_src_pads (TfContent *content, guint *handles,
+ guint handle_count)
+{
+ TfContentClass *klass = TF_CONTENT_GET_CLASS (content);
+
+ g_return_val_if_fail (content != NULL, NULL);
+
+ if (klass->iterate_src_pads)
+ return klass->iterate_src_pads (content, handles, handle_count);
+ else
+ GST_WARNING ("iterate_src_pads not defined in class");
+
+ return NULL;
+}
+
+gboolean
+_tf_content_start_receiving (TfContent *self, guint *handles,
+ guint handle_count)
+{
+ GValue instance_and_params[3] = {{0} , {0}, {0}};
+ GValue receiving_success_val = {0,};
+ gboolean receiving_success;
+
+ g_value_init (&receiving_success_val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&receiving_success_val, TRUE);
+
+ g_value_init (&instance_and_params[0], TF_TYPE_CONTENT);
+ g_value_set_object (&instance_and_params[0], self);
+
+ g_value_init (&instance_and_params[1], G_TYPE_POINTER);
+ g_value_set_pointer (&instance_and_params[1], handles);
+
+ g_value_init (&instance_and_params[2], G_TYPE_UINT);
+ g_value_set_uint (&instance_and_params[2], handle_count);
+
+ g_debug ("Requesting that the application start receiving");
+
+ g_signal_emitv (instance_and_params, signals[SIGNAL_START_RECEIVING], 0,
+ &receiving_success_val);
+ receiving_success = g_value_get_boolean (&receiving_success_val);
+
+ g_value_unset (&instance_and_params[0]);
+
+ g_debug ("Request to start receiving %s",
+ receiving_success ? "succeeded" : "failed");
+
+ return receiving_success;
+}
+
+void
+_tf_content_stop_receiving (TfContent *self, guint *handles,
+ guint handle_count)
+{
+ g_debug ("Requesting that the application stop receiving");
+ g_signal_emit (self, signals[SIGNAL_STOP_RECEIVING], 0, handles,
+ handle_count);
+}
+
+
+/**
+ * tf_content_sending_failed_literal:
+ * @content: a #TfContent
+ * @message: The error message
+ *
+ * Informs the Connection Manager that sending has failed for this
+ * content. This is a transient error and it may or not not end the Content
+ * and the call.
+ *
+ * Rename to: tf_content_sending_failed
+ */
+
+void
+tf_content_sending_failed_literal (TfContent *content,
+ const gchar *message)
+{
+ TfContentClass *klass = TF_CONTENT_GET_CLASS (content);
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message != NULL);
+
+ if (klass->content_error)
+ klass->sending_failed (content, message);
+ else
+ GST_WARNING ("sending_failed not defined in class, ignoring error: %s",
+ message);
+}
+
+/**
+ * tf_content_sending_failed:
+ * @content: a #TfContent
+ * @message_format: Message with printf style formatting
+ * @...: Parameters to insert into the @message_format string
+ *
+ * Informs the Connection Manager that sending has failed for this
+ * content. This is a transient error and it may or not not end the Content
+ * and the call.
+ */
+
+void
+tf_content_sending_failed (TfContent *content,
+ const gchar *message_format, ...)
+{
+ gchar *message;
+ va_list valist;
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message_format != NULL);
+
+ va_start (valist, message_format);
+ message = g_strdup_vprintf (message_format, valist);
+ va_end (valist);
+
+ tf_content_sending_failed_literal (content, message);
+ g_free (message);
+}
+
+/**
+ * tf_content_receiving_failed_literal:
+ * @content: a #TfContent
+ * @handles: an array of #guint representing Telepathy handles, may be %NULL
+ * @handle_count: the numner of handles in @handles
+ * @message: The error message
+ *
+ * Informs the Connection Manager that receiving has failed for this
+ * content. This is a transient error and it may or not not end the Content
+ * and the call.
+ *
+ * If handles are not specific, it assumes that it is valid for all handles.
+ *
+ * Rename to: tf_content_receiving_failed
+ */
+
+void
+tf_content_receiving_failed_literal (TfContent *content,
+ guint *handles, guint handle_count,
+ const gchar *message)
+{
+ TfContentClass *klass = TF_CONTENT_GET_CLASS (content);
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message != NULL);
+
+ if (klass->content_error)
+ klass->receiving_failed (content, handles, handle_count, message);
+ else
+ GST_WARNING ("receiving_failed not defined in class, ignoring error: %s",
+ message);
+}
+
+
+/**
+ * tf_content_receiving_failed:
+ * @content: a #TfContent
+ * @handles: an array of #guint representing Telepathy handles, may be %NULL
+ * @handle_count: the numner of handles in @handles
+ * @message_format: Message with printf style formatting
+ * @...: Parameters to insert into the @message_format string
+ *
+ * Informs the Connection Manager that receiving has failed for this
+ * content. This is a transient error and it may or not not end the Content
+ * and the call.
+ *
+ * If handles are not specific, it assumes that it is valid for all handles.
+ */
+
+void
+tf_content_receiving_failed (TfContent *content,
+ guint *handles, guint handle_count,
+ const gchar *message_format, ...)
+{
+ gchar *message;
+ va_list valist;
+
+ g_return_if_fail (content != NULL);
+ g_return_if_fail (message_format != NULL);
+
+ va_start (valist, message_format);
+ message = g_strdup_vprintf (message_format, valist);
+ va_end (valist);
+
+ tf_content_receiving_failed_literal (content, handles, handle_count, message);
+ g_free (message);
+}