2 * This file is part of the Nice GLib ICE library.
4 * (C) 2010, 2013 Collabora Ltd.
5 * Contact: Youness Alaoui
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is the Nice GLib ICE library.
19 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
20 * Corporation. All Rights Reserved.
23 * Youness Alaoui, Collabora Ltd.
24 * Philip Withnall, Collabora Ltd.
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
38 * SECTION:nice_io_stream
39 * @short_description: #GIOStream implementation for libnice
40 * @see_also: #NiceAgent
41 * @include: iostream.h
44 * #NiceIOStream is a #GIOStream wrapper for a single reliable stream and
45 * component of a #NiceAgent. Given an existing reliable #NiceAgent, plus the
46 * IDs of an existing stream and component in the agent, it will provide a
47 * streaming input and output interface for communication over the given
50 * A single #NiceIOStream can only be used with a single agent, stream and
51 * component triple, and will be closed as soon as that stream is removed from
52 * the agent (e.g. if nice_agent_remove_stream() is called from another thread).
53 * If g_io_stream_close() is called on a #NiceIOStream, the I/O stream and
54 * underlying #NiceAgent stream will be closed in both directions, but the
55 * underlying stream will not be removed. Use nice_agent_remove_stream() to do
56 * that, but only do so after g_io_stream_close() has completed, or the stream
57 * will return broken pipe errors.
67 #include "inputstream.h"
68 #include "outputstream.h"
70 G_DEFINE_TYPE (NiceIOStream, nice_io_stream, G_TYPE_IO_STREAM);
79 struct _NiceIOStreamPrivate
81 GWeakRef/*<NiceAgent>*/ agent_ref;
85 GInputStream *input_stream; /* owned */
86 GOutputStream *output_stream; /* owned */
89 static void nice_io_stream_dispose (GObject *object);
90 static void nice_io_stream_get_property (GObject *object, guint prop_id,
91 GValue *value, GParamSpec *pspec);
92 static void nice_io_stream_set_property (GObject *object, guint prop_id,
93 const GValue *value, GParamSpec *pspec);
94 static GInputStream *nice_io_stream_get_input_stream (GIOStream *stream);
95 static GOutputStream *nice_io_stream_get_output_stream (GIOStream *stream);
97 static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
101 nice_io_stream_class_init (NiceIOStreamClass *klass)
103 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104 GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass);
106 g_type_class_add_private (klass, sizeof (NiceIOStreamPrivate));
108 gobject_class->set_property = nice_io_stream_set_property;
109 gobject_class->get_property = nice_io_stream_get_property;
110 gobject_class->dispose = nice_io_stream_dispose;
112 stream_class->get_input_stream = nice_io_stream_get_input_stream;
113 stream_class->get_output_stream = nice_io_stream_get_output_stream;
116 * NiceIOStream:agent:
118 * The #NiceAgent to wrap with an I/O stream. This must be an existing
121 * A reference is not held on the #NiceAgent. If the agent is destroyed before
122 * the #NiceIOStream, %G_IO_ERROR_CLOSED will be returned for all subsequent
123 * operations on the stream.
127 g_object_class_install_property (gobject_class, PROP_AGENT,
128 g_param_spec_object ("agent",
130 "The underlying NiceAgent",
132 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135 * NiceIOStream:stream-id:
137 * ID of the stream to use in the #NiceIOStream:agent.
141 g_object_class_install_property (gobject_class, PROP_STREAM_ID,
145 "The ID of the agent’s stream to wrap.",
148 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 * NiceIOStream:component-id:
153 * ID of the component to use in the #NiceIOStream:agent.
157 g_object_class_install_property (gobject_class, PROP_COMPONENT_ID,
160 "Agent’s component ID",
161 "The ID of the agent’s component to wrap.",
164 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
168 nice_io_stream_init (NiceIOStream *self)
170 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NICE_TYPE_IO_STREAM,
171 NiceIOStreamPrivate);
173 g_weak_ref_init (&self->priv->agent_ref, NULL);
175 /* Invalidate the stream/component IDs to begin with. */
176 self->priv->stream_id = 0;
177 self->priv->component_id = 0;
181 nice_io_stream_dispose (GObject *object)
183 NiceIOStream *self = NICE_IO_STREAM (object);
186 /* Ensure the stream is closed before continuing. Otherwise, if the input or
187 * output streams haven’t yet been lazily created, closing the stream in
188 * g_io_stream_dispose() will lazily create them, but NiceAgent will be NULL
189 * by that point and things will explode. */
190 if (!g_io_stream_is_closed (G_IO_STREAM (object)))
191 g_io_stream_close (G_IO_STREAM (object), NULL, NULL);
193 /* Clear everything away. */
194 if (self->priv->input_stream != NULL)
195 g_object_unref (self->priv->input_stream);
196 self->priv->input_stream = NULL;
198 if (self->priv->output_stream != NULL)
199 g_object_unref (self->priv->output_stream);
200 self->priv->output_stream = NULL;
202 agent = g_weak_ref_get (&self->priv->agent_ref);
204 g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
205 g_object_unref (agent);
208 g_weak_ref_clear (&self->priv->agent_ref);
210 G_OBJECT_CLASS (nice_io_stream_parent_class)->dispose (object);
214 nice_io_stream_get_property (GObject *object, guint prop_id,
215 GValue *value, GParamSpec *pspec)
217 NiceIOStream *self = NICE_IO_STREAM (object);
221 g_value_take_object (value, g_weak_ref_get (&self->priv->agent_ref));
224 g_value_set_uint (value, self->priv->stream_id);
226 case PROP_COMPONENT_ID:
227 g_value_set_uint (value, self->priv->component_id);
230 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235 nice_io_stream_set_property (GObject *object, guint prop_id,
236 const GValue *value, GParamSpec *pspec)
238 NiceIOStream *self = NICE_IO_STREAM (object);
242 /* Construct only. */
243 NiceAgent *agent = g_value_dup_object (value);
244 g_weak_ref_set (&self->priv->agent_ref, agent);
245 g_signal_connect (agent, "streams-removed",
246 (GCallback) streams_removed_cb, self);
247 g_object_unref (agent);
252 /* Construct only. */
253 self->priv->stream_id = g_value_get_uint (value);
255 case PROP_COMPONENT_ID:
256 /* Construct only. */
257 self->priv->component_id = g_value_get_uint (value);
260 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
265 * nice_io_stream_new:
266 * @agent: A #NiceAgent
267 * @stream_id: The ID of the agent’s stream to wrap
268 * @component_id: The ID of the agent’s component to wrap
270 * Create a new #NiceIOStream wrapping the given stream/component from @agent,
271 * which must be a reliable #NiceAgent.
273 * The constructed #NiceIOStream will not hold a reference to @agent. If @agent
274 * is destroyed before the I/O stream, %G_IO_ERROR_CLOSED will be returned for
275 * all subsequent operations on the stream.
277 * Returns: The new #NiceIOStream object
282 nice_io_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
284 g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
285 g_return_val_if_fail (stream_id > 0, NULL);
286 g_return_val_if_fail (component_id > 0, NULL);
288 return g_object_new (NICE_TYPE_IO_STREAM,
290 "stream-id", stream_id,
291 "component-id", component_id,
295 static GInputStream *
296 nice_io_stream_get_input_stream (GIOStream *stream)
298 NiceIOStream *self = NICE_IO_STREAM (stream);
300 if (G_UNLIKELY (self->priv->input_stream == NULL)) {
303 /* Note that agent may be NULL here. NiceInputStream must support
304 * construction with a NULL agent. */
305 agent = g_weak_ref_get (&self->priv->agent_ref);
306 self->priv->input_stream = G_INPUT_STREAM (nice_input_stream_new (
307 agent, self->priv->stream_id, self->priv->component_id));
309 g_object_unref (agent);
312 return self->priv->input_stream;
315 static GOutputStream *
316 nice_io_stream_get_output_stream (GIOStream *stream)
318 NiceIOStream *self = NICE_IO_STREAM (stream);
320 if (G_UNLIKELY (self->priv->output_stream == NULL)) {
323 /* Note that agent may be NULL here. NiceOutputStream must support
324 * construction with a NULL agent. */
325 agent = g_weak_ref_get (&self->priv->agent_ref);
326 self->priv->output_stream = g_object_new (NICE_TYPE_OUTPUT_STREAM,
328 "stream-id", self->priv->stream_id,
329 "component-id", self->priv->component_id,
333 g_object_unref (agent);
336 return self->priv->output_stream;
340 streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
342 NiceIOStream *self = NICE_IO_STREAM (user_data);
345 for (i = 0; stream_ids[i] != 0; i++) {
346 if (stream_ids[i] == self->priv->stream_id) {
347 /* The socket has been closed. */
348 g_io_stream_close (G_IO_STREAM (self), NULL, NULL);