summaryrefslogtreecommitdiff
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to 'agent')
-rw-r--r--agent/Makefile.am15
-rw-r--r--agent/agent.h15
-rw-r--r--agent/inputstream.c314
-rw-r--r--agent/inputstream.h87
-rw-r--r--agent/iostream.c355
-rw-r--r--agent/iostream.h92
-rw-r--r--agent/outputstream.c417
-rw-r--r--agent/outputstream.h87
8 files changed, 1381 insertions, 1 deletions
diff --git a/agent/Makefile.am b/agent/Makefile.am
index 7975906..0506551 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -61,6 +61,12 @@ libagent_la_SOURCES = \
interfaces.h \
pseudotcp.h \
pseudotcp.c \
+ iostream.h \
+ iostream.c \
+ inputstream.h \
+ inputstream.c \
+ outputstream.h \
+ outputstream.c \
$(BUILT_SOURCES)
libagent_la_LIBADD = \
@@ -72,7 +78,14 @@ libagent_la_DEPENDENCIES = \
$(top_builddir)/socket/libsocket.la \
$(top_builddir)/stun/libstun.la
-pkginclude_HEADERS = agent.h candidate.h debug.h address.h interfaces.h pseudotcp.h
+pkginclude_HEADERS = \
+ agent.h \
+ candidate.h \
+ debug.h \
+ address.h \
+ interfaces.h \
+ pseudotcp.h \
+ $(NULL)
if WINDOWS
libagent_la_LIBADD += -liphlpapi -lws2_32
diff --git a/agent/agent.h b/agent/agent.h
index 9d7bb25..d888148 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -57,6 +57,18 @@
* given #NiceAgent). These IDs are guaranteed to be positive (i.e. non-zero)
* for valid streams/components.
*
+ * Each stream can receive data in one of two ways: using
+ * nice_agent_attach_recv() or nice_agent_recv() (and the derived
+ * #NiceInputStream and #NiceIOStream classes accessible using
+ * nice_agent_build_io_stream()). nice_agent_attach_recv() is non-blocking: it
+ * takes a user-provided callback function and attaches the stream’s socket to
+ * the provided #GMainContext, invoking the callback in that context for every
+ * packet received. nice_agent_recv() instead blocks on receiving a packet, and
+ * writes it directly into a user-provided buffer. This reduces the number of
+ * callback invokations and (potentially) buffer copies required to receive
+ * packets. nice_agent_recv() (or #NiceInputStream) is designed to be used in a
+ * blocking loop in a separate thread.
+ *
<example>
<title>Simple example on how to use libnice</title>
<programlisting>
@@ -675,6 +687,9 @@ nice_agent_restart (
* Attaches the stream's component's sockets to the Glib Mainloop Context in
* order to be notified whenever data becomes available for a component.
*
+ * This must not be used in combination with nice_agent_recv() (or
+ * #NiceIOStream or #NiceInputStream) on the same stream/component pair.
+ *
* Returns: %TRUE on success, %FALSE if the stream or component IDs are invalid.
*/
gboolean
diff --git a/agent/inputstream.c b/agent/inputstream.c
new file mode 100644
index 0000000..de65f66
--- /dev/null
+++ b/agent/inputstream.c
@@ -0,0 +1,314 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ * Philip Withnall, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+/**
+ * SECTION:nice_input_stream
+ * @short_description: #GInputStream implementation for libnice
+ * @see_also: #NiceAgent
+ * @include: inputstream.h
+ * @stability: Stable
+ *
+ * #NiceInputStream is a #GInputStream wrapper for a single reliable stream and
+ * component of a #NiceAgent. Given an existing reliable #NiceAgent, plus the
+ * IDs of an existing stream and component in the agent, it will provide a
+ * streaming input interface for reading from the given component.
+ *
+ * A single #NiceInputStream can only be used with a single agent, stream and
+ * component triple, and will be closed as soon as that stream is removed from
+ * the agent (e.g. if nice_agent_remove_stream() is called from another thread).
+ * If g_input_stream_close() is called on a #NiceInputStream, the input stream
+ * will be marked as closed, but the underlying #NiceAgent stream will not be
+ * removed. Use nice_agent_remove_stream() to do that.
+ *
+ * Since: 0.1.5
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "inputstream.h"
+#include "agent-priv.h"
+
+static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
+ gpointer user_data);
+
+G_DEFINE_TYPE (NiceInputStream, nice_input_stream, G_TYPE_INPUT_STREAM);
+
+enum
+{
+ PROP_AGENT = 1,
+ PROP_STREAM_ID,
+ PROP_COMPONENT_ID,
+};
+
+struct _NiceInputStreamPrivate
+{
+ GWeakRef/*<NiceAgent>*/ agent_ref;
+ guint stream_id;
+ guint component_id;
+};
+
+static void nice_input_stream_dispose (GObject *object);
+static void nice_input_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+static void nice_input_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static gssize nice_input_stream_read (GInputStream *stream, void *buffer,
+ gsize count, GCancellable *cancellable, GError **error);
+
+static void
+nice_input_stream_class_init (NiceInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NiceInputStreamPrivate));
+
+ gobject_class->set_property = nice_input_stream_set_property;
+ gobject_class->get_property = nice_input_stream_get_property;
+ gobject_class->dispose = nice_input_stream_dispose;
+
+ stream_class->read_fn = nice_input_stream_read;
+
+ /**
+ * NiceInputStream:agent:
+ *
+ * The #NiceAgent to wrap with an input stream. This must be an existing
+ * reliable agent.
+ *
+ * A reference is not held on the #NiceAgent. If the agent is destroyed before
+ * the #NiceInputStream, %G_IO_ERROR_CLOSED will be returned for all
+ * subsequent operations on the stream.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_AGENT,
+ g_param_spec_object ("agent",
+ "NiceAgent",
+ "The underlying NiceAgent",
+ NICE_TYPE_AGENT,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceInputStream:stream-id:
+ *
+ * ID of the stream to use in the #NiceInputStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_STREAM_ID,
+ g_param_spec_uint (
+ "stream-id",
+ "Agent’s stream ID",
+ "The ID of the agent’s stream to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceInputStream:component-id:
+ *
+ * ID of the component to use in the #NiceInputStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_COMPONENT_ID,
+ g_param_spec_uint (
+ "component-id",
+ "Agent’s component ID",
+ "The ID of the agent’s component to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+nice_input_stream_dispose (GObject *object)
+{
+ NiceInputStream *self = NICE_INPUT_STREAM (object);
+ NiceAgent *agent;
+
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ if (agent != NULL) {
+ g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
+ g_object_unref (agent);
+ }
+
+ g_weak_ref_clear (&self->priv->agent_ref);
+
+ G_OBJECT_CLASS (nice_input_stream_parent_class)->dispose (object);
+}
+
+static void
+nice_input_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NiceInputStream *self = NICE_INPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT:
+ g_value_take_object (value, g_weak_ref_get (&self->priv->agent_ref));
+ break;
+ case PROP_STREAM_ID:
+ g_value_set_uint (value, self->priv->stream_id);
+ break;
+ case PROP_COMPONENT_ID:
+ g_value_set_uint (value, self->priv->component_id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nice_input_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NiceInputStream *self = NICE_INPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT: {
+ /* Construct only. */
+ NiceAgent *agent = g_value_dup_object (value);
+ g_weak_ref_set (&self->priv->agent_ref, agent);
+
+ /* agent may be NULL if the stream is being constructed by
+ * nice_io_stream_get_input_stream() after the NiceIOStream’s agent has
+ * already been finalised. */
+ if (agent != NULL) {
+ g_signal_connect (agent, "streams-removed",
+ (GCallback) streams_removed_cb, self);
+ g_object_unref (agent);
+ }
+
+ break;
+ }
+ case PROP_STREAM_ID:
+ /* Construct only. */
+ self->priv->stream_id = g_value_get_uint (value);
+ break;
+ case PROP_COMPONENT_ID:
+ /* Construct only. */
+ self->priv->component_id = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nice_input_stream_init (NiceInputStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, NICE_TYPE_INPUT_STREAM,
+ NiceInputStreamPrivate);
+
+ g_weak_ref_init (&stream->priv->agent_ref, NULL);
+}
+
+/**
+ * nice_input_stream_new:
+ * @agent: A #NiceAgent
+ * @stream_id: The ID of the agent’s stream to wrap
+ * @component_id: The ID of the agent’s component to wrap
+ *
+ * Create a new #NiceInputStream wrapping the given stream/component from
+ * @agent, which must be a reliable #NiceAgent.
+ *
+ * The constructed #NiceInputStream will not hold a reference to @agent. If
+ * @agent is destroyed before the input stream, %G_IO_ERROR_CLOSED will be
+ * returned for all subsequent operations on the stream.
+ *
+ * Returns: The new #NiceInputStream object
+ *
+ * Since: 0.1.5
+ */
+NiceInputStream *
+nice_input_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
+{
+ g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
+ g_return_val_if_fail (stream_id >= 1, NULL);
+ g_return_val_if_fail (component_id >= 1, NULL);
+
+ return g_object_new (NICE_TYPE_INPUT_STREAM,
+ "agent", agent,
+ "stream-id", stream_id,
+ "component-id", component_id,
+ NULL);
+}
+
+static gssize
+nice_input_stream_read (GInputStream *stream, void *buffer, gsize count,
+ GCancellable *cancellable, GError **error)
+{
+ NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv;
+ NiceAgent *agent; /* owned */
+ gssize len;
+
+ /* Closed streams are not readable. */
+ if (g_input_stream_is_closed (stream))
+ return 0;
+
+ /* Has the agent disappeared? */
+ agent = g_weak_ref_get (&priv->agent_ref);
+ if (agent == NULL) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+ "Stream is closed due to the NiceAgent being finalised.");
+ return -1;
+ }
+
+ len = nice_agent_recv (agent, priv->stream_id, priv->component_id,
+ buffer, count, cancellable, error);
+
+ g_object_unref (agent);
+
+ return len;
+}
+
+static void
+streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
+{
+ NiceInputStream *self = NICE_INPUT_STREAM (user_data);
+ guint i;
+
+ for (i = 0; stream_ids[i] != 0; i++) {
+ if (stream_ids[i] == self->priv->stream_id) {
+ /* The socket has been closed. */
+ g_input_stream_close (G_INPUT_STREAM (self), NULL, NULL);
+ break;
+ }
+ }
+}
diff --git a/agent/inputstream.h b/agent/inputstream.h
new file mode 100644
index 0000000..e59dddf
--- /dev/null
+++ b/agent/inputstream.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+#ifndef __NICE_INPUT_STREAM_H__
+#define __NICE_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include "agent.h"
+
+G_BEGIN_DECLS
+
+/* TYPE MACROS */
+#define NICE_TYPE_INPUT_STREAM \
+ (nice_input_stream_get_type ())
+#define NICE_INPUT_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NICE_TYPE_INPUT_STREAM, \
+ NiceInputStream))
+#define NICE_INPUT_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NICE_TYPE_INPUT_STREAM, \
+ NiceInputStreamClass))
+#define NICE_IS_INPUT_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NICE_TYPE_INPUT_STREAM))
+#define NICE_IS_INPUT_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NICE_TYPE_INPUT_STREAM))
+#define NICE_INPUT_STREAM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_INPUT_STREAM, \
+ NiceInputStreamClass))
+
+
+typedef struct _NiceInputStreamPrivate NiceInputStreamPrivate;
+typedef struct _NiceInputStreamClass NiceInputStreamClass;
+typedef struct _NiceInputStream NiceInputStream;
+
+GType nice_input_stream_get_type (void);
+
+struct _NiceInputStreamClass
+{
+ GInputStreamClass parent_class;
+};
+
+struct _NiceInputStream
+{
+ GInputStream parent_instance;
+ NiceInputStreamPrivate *priv;
+};
+
+
+NiceInputStream *nice_input_stream_new (NiceAgent *agent,
+ guint stream_id, guint component_id);
+
+
+G_END_DECLS
+
+#endif /* __NICE_INPUT_STREAM_H__ */
diff --git a/agent/iostream.c b/agent/iostream.c
new file mode 100644
index 0000000..f1b6e94
--- /dev/null
+++ b/agent/iostream.c
@@ -0,0 +1,355 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ * Philip Withnall, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+/**
+ * SECTION:nice_io_stream
+ * @short_description: #GIOStream implementation for libnice
+ * @see_also: #NiceAgent
+ * @include: iostream.h
+ * @stability: Stable
+ *
+ * #NiceIOStream is a #GIOStream wrapper for a single reliable stream and
+ * component of a #NiceAgent. Given an existing reliable #NiceAgent, plus the
+ * IDs of an existing stream and component in the agent, it will provide a
+ * streaming input and output interface for communication over the given
+ * component.
+ *
+ * A single #NiceIOStream can only be used with a single agent, stream and
+ * component triple, and will be closed as soon as that stream is removed from
+ * the agent (e.g. if nice_agent_remove_stream() is called from another thread).
+ * If g_io_stream_close() is called on a #NiceIOStream, the I/O stream will be
+ * marked as closed in both directions, but the underlying #NiceAgent stream
+ * will not be removed. Use nice_agent_remove_stream() to do that.
+ *
+ * Since: 0.1.5
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "iostream.h"
+#include "inputstream.h"
+#include "outputstream.h"
+
+G_DEFINE_TYPE (NiceIOStream, nice_io_stream, G_TYPE_IO_STREAM);
+
+enum
+{
+ PROP_AGENT = 1,
+ PROP_STREAM_ID,
+ PROP_COMPONENT_ID,
+};
+
+struct _NiceIOStreamPrivate
+{
+ GWeakRef/*<NiceAgent>*/ agent_ref;
+ guint stream_id;
+ guint component_id;
+
+ GInputStream *input_stream; /* owned */
+ GOutputStream *output_stream; /* owned */
+};
+
+static void nice_io_stream_dispose (GObject *object);
+static void nice_io_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+static void nice_io_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static GInputStream *nice_io_stream_get_input_stream (GIOStream *stream);
+static GOutputStream *nice_io_stream_get_output_stream (GIOStream *stream);
+
+static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
+ gpointer user_data);
+
+static void
+nice_io_stream_class_init (NiceIOStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NiceIOStreamPrivate));
+
+ gobject_class->set_property = nice_io_stream_set_property;
+ gobject_class->get_property = nice_io_stream_get_property;
+ gobject_class->dispose = nice_io_stream_dispose;
+
+ stream_class->get_input_stream = nice_io_stream_get_input_stream;
+ stream_class->get_output_stream = nice_io_stream_get_output_stream;
+
+ /**
+ * NiceIOStream:agent:
+ *
+ * The #NiceAgent to wrap with an I/O stream. This must be an existing
+ * reliable agent.
+ *
+ * A reference is not held on the #NiceAgent. If the agent is destroyed before
+ * the #NiceIOStream, %G_IO_ERROR_CLOSED will be returned for all subsequent
+ * operations on the stream.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_AGENT,
+ g_param_spec_object ("agent",
+ "NiceAgent",
+ "The underlying NiceAgent",
+ NICE_TYPE_AGENT,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceIOStream:stream-id:
+ *
+ * ID of the stream to use in the #NiceIOStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_STREAM_ID,
+ g_param_spec_uint (
+ "stream-id",
+ "Agent’s stream ID",
+ "The ID of the agent’s stream to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceIOStream:component-id:
+ *
+ * ID of the component to use in the #NiceIOStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_COMPONENT_ID,
+ g_param_spec_uint (
+ "component-id",
+ "Agent’s component ID",
+ "The ID of the agent’s component to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+nice_io_stream_init (NiceIOStream *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NICE_TYPE_IO_STREAM,
+ NiceIOStreamPrivate);
+
+ g_weak_ref_init (&self->priv->agent_ref, NULL);
+
+ /* Invalidate the stream/component IDs to begin with. */
+ self->priv->stream_id = 0;
+ self->priv->component_id = 0;
+}
+
+static void
+nice_io_stream_dispose (GObject *object)
+{
+ NiceIOStream *self = NICE_IO_STREAM (object);
+ NiceAgent *agent;
+
+ /* Ensure the stream is closed before continuing. Otherwise, if the input or
+ * output streams haven’t yet been lazily created, closing the stream in
+ * g_io_stream_dispose() will lazily create them, but NiceAgent will be NULL
+ * by that point and things will explode. */
+ if (!g_io_stream_is_closed (G_IO_STREAM (object)))
+ g_io_stream_close (G_IO_STREAM (object), NULL, NULL);
+
+ /* Clear everything away. */
+ if (self->priv->input_stream != NULL)
+ g_object_unref (self->priv->input_stream);
+ self->priv->input_stream = NULL;
+
+ if (self->priv->output_stream != NULL)
+ g_object_unref (self->priv->output_stream);
+ self->priv->output_stream = NULL;
+
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ if (agent != NULL) {
+ g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
+ g_object_unref (agent);
+ }
+
+ g_weak_ref_clear (&self->priv->agent_ref);
+
+ G_OBJECT_CLASS (nice_io_stream_parent_class)->dispose (object);
+}
+
+static void
+nice_io_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NiceIOStream *self = NICE_IO_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT:
+ g_value_take_object (value, g_weak_ref_get (&self->priv->agent_ref));
+ break;
+ case PROP_STREAM_ID:
+ g_value_set_uint (value, self->priv->stream_id);
+ break;
+ case PROP_COMPONENT_ID:
+ g_value_set_uint (value, self->priv->component_id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nice_io_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NiceIOStream *self = NICE_IO_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT: {
+ /* Construct only. */
+ NiceAgent *agent = g_value_dup_object (value);
+ g_weak_ref_set (&self->priv->agent_ref, agent);
+ g_signal_connect (agent, "streams-removed",
+ (GCallback) streams_removed_cb, self);
+ g_object_unref (agent);
+
+ break;
+ }
+ case PROP_STREAM_ID:
+ /* Construct only. */
+ self->priv->stream_id = g_value_get_uint (value);
+ break;
+ case PROP_COMPONENT_ID:
+ /* Construct only. */
+ self->priv->component_id = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+/**
+ * nice_io_stream_new:
+ * @agent: A #NiceAgent
+ * @stream_id: The ID of the agent’s stream to wrap
+ * @component_id: The ID of the agent’s component to wrap
+ *
+ * Create a new #NiceIOStream wrapping the given stream/component from @agent,
+ * which must be a reliable #NiceAgent.
+ *
+ * The constructed #NiceIOStream will not hold a reference to @agent. If @agent
+ * is destroyed before the I/O stream, %G_IO_ERROR_CLOSED will be returned for
+ * all subsequent operations on the stream.
+ *
+ * Returns: The new #NiceIOStream object
+ *
+ * Since: 0.1.5
+ */
+GIOStream *
+nice_io_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
+{
+ gboolean reliable_agent;
+
+ g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
+ g_return_val_if_fail (stream_id > 0, NULL);
+ g_return_val_if_fail (component_id > 0, NULL);
+
+ g_object_get (agent, "reliable", &reliable_agent, NULL);
+ g_return_val_if_fail (reliable_agent, NULL);
+
+ return g_object_new (NICE_TYPE_IO_STREAM,
+ "agent", agent,
+ "stream-id", stream_id,
+ "component-id", component_id,
+ NULL);
+}
+
+static GInputStream *
+nice_io_stream_get_input_stream (GIOStream *stream)
+{
+ NiceIOStream *self = NICE_IO_STREAM (stream);
+
+ if (G_UNLIKELY (self->priv->input_stream == NULL)) {
+ NiceAgent *agent;
+
+ /* Note that agent may be NULL here. NiceInputStream must support
+ * construction with a NULL agent. */
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ self->priv->input_stream = G_INPUT_STREAM (nice_input_stream_new (
+ agent, self->priv->stream_id, self->priv->component_id));
+ if (agent != NULL)
+ g_object_unref (agent);
+ }
+
+ return self->priv->input_stream;
+}
+
+static GOutputStream *
+nice_io_stream_get_output_stream (GIOStream *stream)
+{
+ NiceIOStream *self = NICE_IO_STREAM (stream);
+
+ if (G_UNLIKELY (self->priv->output_stream == NULL)) {
+ NiceAgent *agent;
+
+ /* Note that agent may be NULL here. NiceOutputStream must support
+ * construction with a NULL agent. */
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ self->priv->output_stream = g_object_new (NICE_TYPE_OUTPUT_STREAM,
+ "agent", agent,
+ "stream-id", self->priv->stream_id,
+ "component-id", self->priv->component_id,
+ NULL);
+
+ if (agent != NULL)
+ g_object_unref (agent);
+ }
+
+ return self->priv->output_stream;
+}
+
+static void
+streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
+{
+ NiceIOStream *self = NICE_IO_STREAM (user_data);
+ guint i;
+
+ for (i = 0; stream_ids[i] != 0; i++) {
+ if (stream_ids[i] == self->priv->stream_id) {
+ /* The socket has been closed. */
+ g_io_stream_close (G_IO_STREAM (self), NULL, NULL);
+ break;
+ }
+ }
+}
diff --git a/agent/iostream.h b/agent/iostream.h
new file mode 100644
index 0000000..88f42c0
--- /dev/null
+++ b/agent/iostream.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+#ifndef __NICE_IO_STREAM_H__
+#define __NICE_IO_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+/* TYPE MACROS */
+
+/* IO Stream */
+#define NICE_TYPE_IO_STREAM \
+ (nice_io_stream_get_type ())
+#define NICE_IO_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NICE_TYPE_IO_STREAM, \
+ NiceIOStream))
+#define NICE_IO_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NICE_TYPE_IO_STREAM, \
+ NiceIOStreamClass))
+#define NICE_IS_IO_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NICE_TYPE_IO_STREAM))
+#define NICE_IS_IO_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NICE_TYPE_IO_STREAM))
+#define NICE_IO_STREAM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_IO_STREAM, \
+ NiceIOStreamClass))
+
+/* IO Stream */
+typedef struct _NiceIOStreamPrivate NiceIOStreamPrivate;
+typedef struct _NiceIOStreamClass NiceIOStreamClass;
+typedef struct _NiceIOStream NiceIOStream;
+
+#include "agent.h"
+#include "inputstream.h"
+#include "outputstream.h"
+
+/* IO Stream */
+GType nice_io_stream_get_type (void);
+
+struct _NiceIOStreamClass
+{
+ GIOStreamClass parent_class;
+
+};
+
+struct _NiceIOStream
+{
+ GIOStream parent_instance;
+ NiceIOStreamPrivate *priv;
+};
+
+GIOStream *nice_io_stream_new (NiceAgent *agent,
+ guint stream_id, guint component_id);
+
+G_END_DECLS
+
+#endif /* __NICE_IO_STREAM_H__ */
diff --git a/agent/outputstream.c b/agent/outputstream.c
new file mode 100644
index 0000000..85b29e2
--- /dev/null
+++ b/agent/outputstream.c
@@ -0,0 +1,417 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ * Philip Withnall, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+/**
+ * SECTION:nice_output_stream
+ * @short_description: #GOutputStream implementation for libnice
+ * @see_also: #NiceAgent
+ * @include: outputstream.h
+ * @stability: Stable
+ *
+ * #NiceOutputStream is a #GOutputStream wrapper for a single reliable stream
+ * and component of a #NiceAgent. Given an existing reliable #NiceAgent, plus
+ * the IDs of an existing stream and component in the agent, it will provide a
+ * streaming output interface for writing to the given component.
+ *
+ * A single #NiceOutputStream can only be used with a single agent, stream and
+ * component triple, and will be closed as soon as that stream is removed from
+ * the agent (e.g. if nice_agent_remove_stream() is called from another thread).
+ * If g_output_stream_close() is called on a #NiceOutputStream, the output
+ * stream will be marked as closed, but the underlying #NiceAgent stream will
+ * not be removed. Use nice_agent_remove_stream() to do that.
+ *
+ * The output stream can only be used once the
+ * #NiceAgent::reliable-transport-writable signal has been received for the
+ * stream/component pair. Any calls to g_output_stream_write() before then will
+ * return %G_IO_ERROR_BROKEN_PIPE.
+ *
+ * Since: 0.1.5
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "outputstream.h"
+
+static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
+ gpointer user_data);
+
+G_DEFINE_TYPE (NiceOutputStream,
+ nice_output_stream, G_TYPE_OUTPUT_STREAM);
+
+enum
+{
+ PROP_AGENT = 1,
+ PROP_STREAM_ID,
+ PROP_COMPONENT_ID,
+};
+
+struct _NiceOutputStreamPrivate
+{
+ GWeakRef/*<NiceAgent>*/ agent_ref;
+ guint stream_id;
+ guint component_id;
+};
+
+static void nice_output_stream_dispose (GObject *object);
+static void nice_output_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+static void nice_output_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+
+static gssize nice_output_stream_write (GOutputStream *stream,
+ const void *buffer, gsize count, GCancellable *cancellable, GError **error);
+
+
+
+/* Output Stream */
+static void
+nice_output_stream_class_init (NiceOutputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NiceOutputStreamPrivate));
+
+ stream_class->write_fn = nice_output_stream_write;
+
+ gobject_class->set_property = nice_output_stream_set_property;
+ gobject_class->get_property = nice_output_stream_get_property;
+ gobject_class->dispose = nice_output_stream_dispose;
+
+ /**
+ * NiceOutputStream:agent:
+ *
+ * The #NiceAgent to wrap with an output stream. This must be an existing
+ * reliable agent.
+ *
+ * A reference is not held on the #NiceAgent. If the agent is destroyed before
+ * the #NiceOutputStream, %G_IO_ERROR_CLOSED will be returned for all
+ * subsequent operations on the stream.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_AGENT,
+ g_param_spec_object ("agent",
+ "NiceAgent",
+ "The underlying NiceAgent",
+ NICE_TYPE_AGENT,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceOutputStream:stream-id:
+ *
+ * ID of the stream to use in the #NiceOutputStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_STREAM_ID,
+ g_param_spec_uint (
+ "stream-id",
+ "Agent’s stream ID",
+ "The ID of the agent’s stream to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NiceOutputStream:component-id:
+ *
+ * ID of the component to use in the #NiceOutputStream:agent.
+ *
+ * Since: 0.1.5
+ */
+ g_object_class_install_property (gobject_class, PROP_COMPONENT_ID,
+ g_param_spec_uint (
+ "component-id",
+ "Agent’s component ID",
+ "The ID of the agent’s component to wrap.",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+nice_output_stream_dispose (GObject *object)
+{
+ NiceOutputStream *self = NICE_OUTPUT_STREAM (object);
+ NiceAgent *agent;
+
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ if (agent != NULL) {
+ g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
+ g_object_unref (agent);
+ }
+
+ g_weak_ref_clear (&self->priv->agent_ref);
+
+ G_OBJECT_CLASS (nice_output_stream_parent_class)->dispose (object);
+}
+
+static void
+nice_output_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NiceOutputStream *self = NICE_OUTPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT:
+ g_value_take_object (value, g_weak_ref_get (&self->priv->agent_ref));
+ break;
+ case PROP_STREAM_ID:
+ g_value_set_uint (value, self->priv->stream_id);
+ break;
+ case PROP_COMPONENT_ID:
+ g_value_set_uint (value, self->priv->component_id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nice_output_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NiceOutputStream *self = NICE_OUTPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_AGENT: {
+ /* Construct only. */
+ NiceAgent *agent = g_value_dup_object (value);
+ g_weak_ref_set (&self->priv->agent_ref, agent);
+
+ /* agent may be NULL if the stream is being constructed by
+ * nice_io_stream_get_output_stream() after the NiceIOStream’s agent has
+ * already been finalised. */
+ if (agent != NULL) {
+ g_signal_connect (agent, "streams-removed",
+ (GCallback) streams_removed_cb, self);
+ g_object_unref (agent);
+ }
+
+ break;
+ }
+ case PROP_STREAM_ID:
+ /* Construct only. */
+ self->priv->stream_id = g_value_get_uint (value);
+ break;
+ case PROP_COMPONENT_ID:
+ /* Construct only. */
+ self->priv->component_id = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nice_output_stream_init (NiceOutputStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, NICE_TYPE_OUTPUT_STREAM,
+ NiceOutputStreamPrivate);
+
+ g_weak_ref_init (&stream->priv->agent_ref, NULL);
+}
+
+/**
+ * nice_output_stream_new:
+ * @agent: A #NiceAgent
+ * @stream_id: The ID of the agent’s stream to wrap
+ * @component_id: The ID of the agent’s component to wrap
+ *
+ * Create a new #NiceOutputStream wrapping the given stream/component from
+ * @agent, which must be a reliable #NiceAgent.
+ *
+ * The constructed #NiceOutputStream will not hold a reference to @agent. If
+ * @agent is destroyed before the output stream, %G_IO_ERROR_CLOSED will be
+ * returned for all subsequent operations on the stream.
+ *
+ * Returns: The new #NiceOutputStream object
+ *
+ * Since: 0.1.5
+ */
+NiceOutputStream *
+nice_output_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
+{
+ g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
+ g_return_val_if_fail (stream_id >= 1, NULL);
+ g_return_val_if_fail (component_id >= 1, NULL);
+
+ return g_object_new (NICE_TYPE_OUTPUT_STREAM,
+ "agent", agent,
+ "stream-id", stream_id,
+ "component-id", component_id,
+ NULL);
+}
+
+typedef struct {
+ GCond cond;
+ GMutex mutex;
+ GError **error;
+} WriteData;
+
+static void
+write_cancelled_cb (GCancellable *cancellable, gpointer user_data)
+{
+ WriteData *write_data = user_data;
+
+ g_mutex_lock (&write_data->mutex);
+ g_cond_broadcast (&write_data->cond);
+ g_mutex_unlock (&write_data->mutex);
+
+ g_cancellable_set_error_if_cancelled (cancellable, write_data->error);
+}
+
+static void
+reliable_transport_writeable_cb (NiceAgent *agent, guint stream_id,
+ guint component_id, gpointer user_data)
+{
+ WriteData *write_data = user_data;
+
+ g_mutex_lock (&write_data->mutex);
+ g_cond_broadcast (&write_data->cond);
+ g_mutex_unlock (&write_data->mutex);
+}
+
+static gssize
+nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count,
+ GCancellable *cancellable, GError **error)
+{
+ NiceOutputStream *self = NICE_OUTPUT_STREAM (stream);
+ gssize len = -1, _len;
+ GError *child_error = NULL;
+ NiceAgent *agent = NULL; /* owned */
+ gulong cancel_id = 0, writeable_id;
+ WriteData write_data;
+
+ /* Closed streams are not writeable. */
+ if (g_output_stream_is_closed (stream)) {
+ g_set_error_literal (&child_error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+ "Stream is closed.");
+ goto done;
+ }
+
+ /* Has the agent disappeared? */
+ agent = g_weak_ref_get (&self->priv->agent_ref);
+ if (agent == NULL) {
+ g_set_error_literal (&child_error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+ "Stream is closed due to the NiceAgent being finalised.");
+ goto done;
+ }
+
+ if (count == 0)
+ return 0;
+
+ /* FIXME: nice_agent_send_full() is non-blocking, which is a bit unexpected
+ * since nice_agent_recv() is blocking. Currently this uses a fairly dodgy
+ * GCond solution; would be much better for nice_agent_send() to block
+ * properly in the main loop. */
+ len = 0;
+ write_data.error = &child_error;
+
+ g_mutex_init (&write_data.mutex);
+ g_cond_init (&write_data.cond);
+
+ if (cancellable != NULL) {
+ cancel_id = g_cancellable_connect (cancellable,
+ (GCallback) write_cancelled_cb, &write_data, NULL);
+ }
+
+ writeable_id = g_signal_connect (G_OBJECT (agent),
+ "reliable-transport-writable",
+ (GCallback) reliable_transport_writeable_cb, &write_data);
+
+ g_mutex_lock (&write_data.mutex);
+
+ do {
+ _len = nice_agent_send_full (agent, self->priv->stream_id,
+ self->priv->component_id, (guint8 *) buffer + len, count - len,
+ cancellable, &child_error);
+
+ if (_len == -1 &&
+ g_error_matches (child_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ /* EWOULDBLOCK. */
+ g_clear_error (&child_error);
+ g_cond_wait (&write_data.cond, &write_data.mutex);
+ } else if (_len > 0) {
+ /* Success. */
+ len += _len;
+ } else {
+ /* Other error. */
+ len = _len;
+ break;
+ }
+ } while ((gsize) len < count);
+
+ g_mutex_unlock (&write_data.mutex);
+
+ g_signal_handler_disconnect (G_OBJECT (agent), writeable_id);
+
+ if (cancellable != NULL)
+ g_cancellable_disconnect (cancellable, cancel_id);
+
+ g_cond_clear (&write_data.cond);
+ g_mutex_clear (&write_data.mutex);
+
+done:
+ if (agent != NULL)
+ g_object_unref (agent);
+
+ g_assert ((child_error != NULL) == (len == -1));
+ g_assert (len != 0);
+
+ if (child_error != NULL)
+ g_propagate_error (error, child_error);
+
+ return len;
+}
+
+static void
+streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
+{
+ NiceOutputStream *self = NICE_OUTPUT_STREAM (user_data);
+ guint i;
+
+ for (i = 0; stream_ids[i] != 0; i++) {
+ if (stream_ids[i] == self->priv->stream_id) {
+ /* The socket has been closed. */
+ g_output_stream_close (G_OUTPUT_STREAM (self), NULL, NULL);
+ break;
+ }
+ }
+}
diff --git a/agent/outputstream.h b/agent/outputstream.h
new file mode 100644
index 0000000..eefdb3c
--- /dev/null
+++ b/agent/outputstream.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2010, 2013 Collabora Ltd.
+ * Contact: Youness Alaoui
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Nice GLib ICE library.
+ *
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
+ * Corporation. All Rights Reserved.
+ *
+ * Contributors:
+ * Youness Alaoui, Collabora Ltd.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
+ * case the provisions of LGPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * LGPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replace
+ * them with the notice and other provisions required by the LGPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the LGPL.
+ */
+
+#ifndef __NICE_OUTPUT_STREAM_H__
+#define __NICE_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include "agent.h"
+
+G_BEGIN_DECLS
+
+/* TYPE MACROS */
+#define NICE_TYPE_OUTPUT_STREAM \
+ (nice_output_stream_get_type ())
+#define NICE_OUTPUT_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NICE_TYPE_OUTPUT_STREAM, \
+ NiceOutputStream))
+#define NICE_OUTPUT_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NICE_TYPE_OUTPUT_STREAM, \
+ NiceOutputStreamClass))
+#define NICE_IS_OUTPUT_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NICE_TYPE_OUTPUT_STREAM))
+#define NICE_IS_OUTPUT_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NICE_TYPE_OUTPUT_STREAM))
+#define NICE_OUTPUT_STREAM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_OUTPUT_STREAM, \
+ NiceOutputStreamClass))
+
+
+typedef struct _NiceOutputStreamPrivate NiceOutputStreamPrivate;
+typedef struct _NiceOutputStreamClass NiceOutputStreamClass;
+typedef struct _NiceOutputStream NiceOutputStream;
+
+
+GType nice_output_stream_get_type (void);
+
+struct _NiceOutputStreamClass
+{
+ GOutputStreamClass parent_class;
+};
+
+struct _NiceOutputStream
+{
+ GOutputStream parent_instance;
+ NiceOutputStreamPrivate *priv;
+};
+
+
+NiceOutputStream *nice_output_stream_new (NiceAgent *agent,
+ guint stream_id, guint component_id);
+
+G_END_DECLS
+
+#endif /* __NICE_OUTPUT_STREAM_H__ */