/*
* Farstream - Farstream libnice Transmitter
*
* Copyright 2007-2008 Collabora Ltd.
* @author: Olivier Crete <olivier.crete@collabora.co.uk>
* Copyright 2007-2008 Nokia Corp.
*
* fs-nice-transmitter.c - A Farstream libnice transmitter
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* SECTION:fs-nice-transmitter
* @short_description: A transmitter for ICE using libnice
*
* The transmitter provides ICE (Interactive Connection Establishment) using
* libnice.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "fs-nice-transmitter.h"
#include "fs-nice-stream-transmitter.h"
#include "fs-nice-agent.h"
#include <farstream/fs-conference.h>
#include <farstream/fs-plugin.h>
#include <string.h>
#include <sys/types.h>
GST_DEBUG_CATEGORY (fs_nice_transmitter_debug);
#define GST_CAT_DEFAULT fs_nice_transmitter_debug
/* Signals */
enum
{
LAST_SIGNAL
};
/* props */
enum
{
PROP_0,
PROP_GST_SINK,
PROP_GST_SRC,
PROP_COMPONENTS,
PROP_TOS,
PROP_DO_TIMESTAMP
};
struct _FsNiceTransmitterPrivate
{
/* We hold references to this element */
GstElement *gst_sink;
GstElement *gst_src;
/* We don't hold a reference to these elements, they are owned
by the bins */
/* They are tables of pointers, one per component */
GstElement **src_funnels;
GstElement **sink_tees;
gint tos;
gboolean do_timestamp;
};
#define FS_NICE_TRANSMITTER_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_NICE_TRANSMITTER, \
FsNiceTransmitterPrivate))
static void fs_nice_transmitter_class_init (
FsNiceTransmitterClass *klass);
static void fs_nice_transmitter_init (FsNiceTransmitter *self);
static void fs_nice_transmitter_constructed (GObject *object);
static void fs_nice_transmitter_dispose (GObject *object);
static void fs_nice_transmitter_finalize (GObject *object);
static void fs_nice_transmitter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void fs_nice_transmitter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static FsStreamTransmitter *fs_nice_transmitter_new_stream_transmitter (
FsTransmitter *transmitter, FsParticipant *participant,
guint n_parameters, GParameter *parameters, GError **error);
static GType fs_nice_transmitter_get_stream_transmitter_type (
FsTransmitter *transmitter);
static GObjectClass *parent_class = NULL;
//static guint signals[LAST_SIGNAL] = { 0 };
/*
* Lets register the plugin
*/
static GType type = 0;
GType
fs_nice_transmitter_get_type (void)
{
g_assert (type);
return type;
}
static GType
fs_nice_transmitter_register_type (FsPlugin *module)
{
static const GTypeInfo info = {
sizeof (FsNiceTransmitterClass),
NULL,
NULL,
(GClassInitFunc) fs_nice_transmitter_class_init,
NULL,
NULL,
sizeof (FsNiceTransmitter),
0,
(GInstanceInitFunc) fs_nice_transmitter_init
};
GST_DEBUG_CATEGORY_INIT (fs_nice_transmitter_debug,
"fsnicetransmitter", 0,
"Farstream libnice transmitter");
fs_nice_stream_transmitter_register_type (module);
fs_nice_agent_register_type (module);
type = g_type_register_static (FS_TYPE_TRANSMITTER,
"FsNiceTransmitter", &info, 0);
return type;
}
FS_INIT_PLUGIN (nice, transmitter)
static void
fs_nice_transmitter_class_init (FsNiceTransmitterClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
FsTransmitterClass *transmitter_class = FS_TRANSMITTER_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property = fs_nice_transmitter_set_property;
gobject_class->get_property = fs_nice_transmitter_get_property;
gobject_class->constructed = fs_nice_transmitter_constructed;
g_object_class_override_property (gobject_class, PROP_GST_SRC, "gst-src");
g_object_class_override_property (gobject_class, PROP_GST_SINK, "gst-sink");
g_object_class_override_property (gobject_class, PROP_COMPONENTS,
"components");
g_object_class_override_property (gobject_class, PROP_TOS, "tos");
g_object_class_override_property (gobject_class, PROP_DO_TIMESTAMP,
"do-timestamp");
transmitter_class->new_stream_transmitter =
fs_nice_transmitter_new_stream_transmitter;
transmitter_class->get_stream_transmitter_type =
fs_nice_transmitter_get_stream_transmitter_type;
gobject_class->dispose = fs_nice_transmitter_dispose;
gobject_class->finalize = fs_nice_transmitter_finalize;
g_type_class_add_private (klass, sizeof (FsNiceTransmitterPrivate));
}
static void
fs_nice_transmitter_init (FsNiceTransmitter *self)
{
/* member init */
self->priv = FS_NICE_TRANSMITTER_GET_PRIVATE (self);
self->components = 2;
self->priv->do_timestamp = TRUE;
}
static void
fs_nice_transmitter_constructed (GObject *object)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER_CAST (object);
FsTransmitter *trans = FS_TRANSMITTER_CAST (self);
GstPad *pad = NULL, *pad2 = NULL;
GstPad *ghostpad = NULL;
gchar *padname;
GstPadLinkReturn ret;
int c; /* component_id */
/* We waste one space in order to have the index be the component_id */
self->priv->src_funnels = g_new0 (GstElement *, self->components+1);
self->priv->sink_tees = g_new0 (GstElement *, self->components+1);
/* First we need the src element */
self->priv->gst_src = gst_bin_new (NULL);
if (!self->priv->gst_src)
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not build the transmitter src bin");
return;
}
gst_object_ref (self->priv->gst_src);
/* Second, we do the sink element */
self->priv->gst_sink = gst_bin_new (NULL);
if (!self->priv->gst_sink)
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not build the transmitter sink bin");
return;
}
g_object_set (G_OBJECT (self->priv->gst_sink),
"async-handling", TRUE,
NULL);
gst_object_ref (self->priv->gst_sink);
for (c = 1; c <= self->components; c++)
{
GstElement *fakesink = NULL;
/* Lets create the RTP source funnel */
self->priv->src_funnels[c] = gst_element_factory_make ("funnel", NULL);
if (!self->priv->src_funnels[c])
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not make the funnel element");
return;
}
if (!gst_bin_add (GST_BIN (self->priv->gst_src),
self->priv->src_funnels[c]))
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not add the funnel element to the transmitter src bin");
}
pad = gst_element_get_static_pad (self->priv->src_funnels[c], "src");
padname = g_strdup_printf ("src_%u", c);
ghostpad = gst_ghost_pad_new (padname, pad);
g_free (padname);
gst_object_unref (pad);
gst_pad_set_active (ghostpad, TRUE);
gst_element_add_pad (self->priv->gst_src, ghostpad);
/* Lets create the RTP sink tee */
self->priv->sink_tees[c] = gst_element_factory_make ("tee", NULL);
if (!self->priv->sink_tees[c])
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not make the tee element");
return;
}
if (!gst_bin_add (GST_BIN (self->priv->gst_sink),
self->priv->sink_tees[c]))
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not add the tee element to the transmitter sink bin");
}
pad = gst_element_get_static_pad (self->priv->sink_tees[c], "sink");
padname = g_strdup_printf ("sink_%u", c);
ghostpad = gst_ghost_pad_new (padname, pad);
g_free (padname);
gst_object_unref (pad);
gst_pad_set_active (ghostpad, TRUE);
gst_element_add_pad (self->priv->gst_sink, ghostpad);
fakesink = gst_element_factory_make ("fakesink", NULL);
if (!fakesink)
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not make the fakesink element");
return;
}
g_object_set (fakesink,
"async", FALSE,
"sync" , FALSE,
NULL);
if (!gst_bin_add (GST_BIN (self->priv->gst_sink), fakesink))
{
gst_object_unref (fakesink);
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not add the fakesink element to the transmitter sink bin");
return;
}
pad = gst_element_get_request_pad (self->priv->sink_tees[c], "src_%u");
pad2 = gst_element_get_static_pad (fakesink, "sink");
ret = gst_pad_link (pad, pad2);
gst_object_unref (pad2);
gst_object_unref (pad);
if (GST_PAD_LINK_FAILED(ret))
{
trans->construction_error = g_error_new (FS_ERROR,
FS_ERROR_CONSTRUCTION,
"Could not link the tee to the fakesink");
return;
}
}
GST_CALL_PARENT (G_OBJECT_CLASS, constructed, (object));
}
static void
fs_nice_transmitter_dispose (GObject *object)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER (object);
if (self->priv->gst_src)
{
gst_object_unref (self->priv->gst_src);
self->priv->gst_src = NULL;
}
if (self->priv->gst_sink)
{
gst_object_unref (self->priv->gst_sink);
self->priv->gst_sink = NULL;
}
parent_class->dispose (object);
}
static void
fs_nice_transmitter_finalize (GObject *object)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER (object);
if (self->priv->src_funnels)
{
g_free (self->priv->src_funnels);
self->priv->src_funnels = NULL;
}
if (self->priv->sink_tees)
{
g_free (self->priv->sink_tees);
self->priv->sink_tees = NULL;
}
parent_class->finalize (object);
}
static void
fs_nice_transmitter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER (object);
switch (prop_id)
{
case PROP_GST_SINK:
g_value_set_object (value, self->priv->gst_sink);
break;
case PROP_GST_SRC:
g_value_set_object (value, self->priv->gst_src);
break;
case PROP_COMPONENTS:
g_value_set_uint (value, self->components);
break;
case PROP_TOS:
g_value_set_uint (value, self->priv->tos);
break;
case PROP_DO_TIMESTAMP:
g_value_set_boolean (value, self->priv->do_timestamp);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fs_nice_transmitter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER (object);
switch (prop_id)
{
case PROP_COMPONENTS:
self->components = g_value_get_uint (value);
break;
case PROP_TOS:
self->priv->tos = g_value_get_uint (value);
break;
case PROP_DO_TIMESTAMP:
self->priv->do_timestamp = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* fs_nice_transmitter_new_stream_nice_transmitter:
* @transmitter: a #FsTranmitter
* @participant: the #FsParticipant for which the #FsStream using this
* new #FsStreamTransmitter is created
*
* This function will create a new #FsStreamTransmitter element for a
* specific participant for this #FsNiceTransmitter
*
* Returns: a new #FsStreamTransmitter
*/
static FsStreamTransmitter *
fs_nice_transmitter_new_stream_transmitter (FsTransmitter *transmitter,
FsParticipant *participant, guint n_parameters, GParameter *parameters,
GError **error)
{
FsNiceTransmitter *self = FS_NICE_TRANSMITTER (transmitter);
return FS_STREAM_TRANSMITTER (fs_nice_stream_transmitter_newv (
self, participant, n_parameters, parameters, error));
}
static GType
fs_nice_transmitter_get_stream_transmitter_type (
FsTransmitter *transmitter)
{
return FS_TYPE_NICE_STREAM_TRANSMITTER;
}
static GstElement *
_create_sinksource (
gchar *elementname,
GstBin *bin,
GstElement *teefunnel,
NiceAgent *agent,
guint stream_id,
guint component_id,
GstPadDirection direction,
gboolean do_timestamp,
GstPadProbeCallback have_buffer_callback,
gpointer have_buffer_user_data,
gulong *buffer_probe_id,
GstPad **requested_pad,
GError **error)
{
GstElement *elem;
GstPadLinkReturn ret = GST_PAD_LINK_OK;
GstPad *elempad = NULL;
GstStateChangeReturn state_ret;
g_assert (direction == GST_PAD_SINK || direction == GST_PAD_SRC);
elem = gst_element_factory_make (elementname, NULL);
if (!elem)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not create the %s element", elementname);
return NULL;
}
g_object_set (elem,
"agent", agent,
"stream", stream_id,
"component", component_id,
NULL);
if (direction == GST_PAD_SINK)
g_object_set (elem,
"async", FALSE,
"sync", FALSE,
NULL);
else
g_object_set (elem,
"do-timestamp", do_timestamp,
NULL);
if (!gst_bin_add (bin, elem))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not add the %s element to the gst %s bin", elementname,
(direction == GST_PAD_SINK) ? "sink" : "src");
gst_object_unref (elem);
return NULL;
}
gst_object_ref (elem);
if (direction == GST_PAD_SINK)
*requested_pad = gst_element_get_request_pad (teefunnel, "src_%u");
else
*requested_pad = gst_element_get_request_pad (teefunnel, "sink_%u");
if (!*requested_pad)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not get the %s request pad from the %s",
(direction == GST_PAD_SINK) ? "src" : "sink",
(direction == GST_PAD_SINK) ? "tee" : "funnel");
goto error;
}
if (direction == GST_PAD_SINK)
elempad = gst_element_get_static_pad (elem, "sink");
else
elempad = gst_element_get_static_pad (elem, "src");
if (direction != GST_PAD_SINK)
ret = gst_pad_link (elempad, *requested_pad);
if (GST_PAD_LINK_FAILED(ret))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not link the new element %s (%d)", elementname, ret);
goto error;
}
if (have_buffer_callback && buffer_probe_id)
{
if (direction == GST_PAD_SINK)
{
g_object_set_data (G_OBJECT (*requested_pad), "component-id",
GUINT_TO_POINTER (component_id));
*buffer_probe_id = gst_pad_add_probe (*requested_pad,
GST_PAD_PROBE_TYPE_BUFFER,
have_buffer_callback,
have_buffer_user_data, NULL);
}
else
{
g_object_set_data (G_OBJECT (elempad), "component-id",
GUINT_TO_POINTER (component_id));
*buffer_probe_id = gst_pad_add_probe (elempad,
GST_PAD_PROBE_TYPE_BUFFER,
have_buffer_callback, have_buffer_user_data, NULL);
}
if (*buffer_probe_id == 0)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not create buffer probe as requested");
}
}
if (!gst_element_sync_state_with_parent (elem))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not sync the state of the new %s with its parent",
elementname);
goto error;
}
if (direction == GST_PAD_SINK)
ret = gst_pad_link (*requested_pad, elempad);
if (GST_PAD_LINK_FAILED(ret))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not link the new element %s (%d)", elementname, ret);
goto error;
}
gst_object_unref (elempad);
return elem;
error:
gst_element_set_locked_state (elem, TRUE);
state_ret = gst_element_set_state (elem, GST_STATE_NULL);
if (state_ret != GST_STATE_CHANGE_SUCCESS)
GST_ERROR ("On error, could not reset %s to state NULL (%s)", elementname,
gst_element_state_change_return_get_name (state_ret));
if (!gst_bin_remove (bin, elem))
GST_ERROR ("Could not remove element %s from bin on error", elementname);
if (elempad)
gst_object_unref (elempad);
if (elem)
gst_object_unref (elem);
return NULL;
}
struct _NiceGstStream {
GstElement **nicesrcs;
GstElement **nicesinks;
GstPad **requested_funnel_pads;
GstPad **requested_tee_pads;
gulong *probe_ids;
/* Protects the sending field and the addition/state of the elements */
GMutex mutex;
gboolean sending;
gboolean desired_sending;
gboolean modifying;
};
NiceGstStream *
fs_nice_transmitter_add_gst_stream (FsNiceTransmitter *self,
NiceAgent *agent,
guint stream_id,
GstPadProbeCallback have_buffer_callback,
gpointer have_buffer_user_data,
GError **error)
{
guint c;
NiceGstStream *ns = NULL;
ns = g_slice_new0 (NiceGstStream);
ns->sending = TRUE;
ns->desired_sending = TRUE;
g_mutex_init (&ns->mutex);
ns->nicesrcs = g_new0 (GstElement *, self->components + 1);
ns->nicesinks = g_new0 (GstElement *, self->components + 1);
ns->requested_tee_pads = g_new0 (GstPad *, self->components + 1);
ns->requested_funnel_pads = g_new0 (GstPad *, self->components + 1);
ns->probe_ids = g_new0 (gulong, self->components + 1);
for (c = 1; c <= self->components; c++)
{
ns->nicesrcs[c] = _create_sinksource ("nicesrc",
GST_BIN (self->priv->gst_src),
self->priv->src_funnels[c],
agent,
stream_id,
c,
GST_PAD_SRC,
self->priv->do_timestamp,
have_buffer_callback,
have_buffer_user_data,
&ns->probe_ids[c],
&ns->requested_funnel_pads[c],
error);
if (ns->nicesrcs[c] == NULL)
goto error;
ns->nicesinks[c] = _create_sinksource ("nicesink",
GST_BIN (self->priv->gst_sink),
self->priv->sink_tees[c],
agent,
stream_id,
c,
GST_PAD_SINK,
FALSE, NULL, NULL, NULL,
&ns->requested_tee_pads[c],
error);
if (ns->nicesinks[c] == NULL)
goto error;
}
return ns;
error:
fs_nice_transmitter_free_gst_stream (self, ns);
return NULL;
}
static void
remove_sink (FsNiceTransmitter *self, NiceGstStream *ns, guint component_id)
{
GstStateChangeReturn ret;
if (ns->requested_tee_pads[component_id] == NULL)
return;
gst_element_release_request_pad (self->priv->sink_tees[component_id],
ns->requested_tee_pads[component_id]);
gst_object_unref (ns->requested_tee_pads[component_id]);
ns->requested_tee_pads[component_id] = NULL;
gst_element_set_locked_state (ns->nicesinks[component_id], TRUE);
ret = gst_element_set_state (ns->nicesinks[component_id], GST_STATE_NULL);
if (ret != GST_STATE_CHANGE_SUCCESS)
GST_ERROR ("Error changing state of nicesink: %s",
gst_element_state_change_return_get_name (ret));
if (!gst_bin_remove (GST_BIN (self->priv->gst_sink),
ns->nicesinks[component_id]))
GST_ERROR ("Could not remove nicesink element from transmitter"
" sink");
}
void
fs_nice_transmitter_free_gst_stream (FsNiceTransmitter *self,
NiceGstStream *ns)
{
guint c;
for (c = 1; c <= self->components; c++)
{
if (ns->nicesrcs[c])
{
GstStateChangeReturn ret;
if (!gst_bin_remove (GST_BIN (self->priv->gst_src), ns->nicesrcs[c]))
GST_ERROR ("Could not remove nicesrc element from transmitter source");
ret = gst_element_set_state (ns->nicesrcs[c], GST_STATE_NULL);
if (ret != GST_STATE_CHANGE_SUCCESS)
GST_ERROR ("Error changing state of nicesrc: %s",
gst_element_state_change_return_get_name (ret));
gst_object_unref (ns->nicesrcs[c]);
}
if (ns->requested_funnel_pads[c])
{
gst_element_release_request_pad (self->priv->src_funnels[c],
ns->requested_funnel_pads[c]);
gst_object_unref (ns->requested_funnel_pads[c]);
}
if (ns->nicesinks[c])
{
remove_sink (self, ns, c);
gst_object_unref (ns->nicesinks[c]);
}
}
g_free (ns->nicesrcs);
g_free (ns->nicesinks);
g_free (ns->requested_tee_pads);
g_free (ns->requested_funnel_pads);
g_free (ns->probe_ids);
g_mutex_clear (&ns->mutex);
g_slice_free (NiceGstStream, ns);
}
void
fs_nice_transmitter_set_sending (FsNiceTransmitter *self,
NiceGstStream *ns, gboolean sending)
{
g_mutex_lock (&ns->mutex);
ns->desired_sending = sending;
GST_DEBUG ("Requesting gst stream sending status: %d", sending);
if (ns->modifying)
{
g_mutex_unlock (&ns->mutex);
return;
}
ns->modifying = TRUE;
while (ns->sending != ns->desired_sending)
{
gboolean current_sending = ns->sending;
g_mutex_unlock (&ns->mutex);
GST_DEBUG ("Changing gst stream sending status to %d", !current_sending);
if (current_sending)
{
remove_sink (self, ns, 1);
}
else
{
GstPadLinkReturn ret;
GstPad *elempad;
gst_element_set_locked_state (ns->nicesinks[1], FALSE);
if (!gst_bin_add (GST_BIN (self->priv->gst_sink), ns->nicesinks[1]))
GST_ERROR ("Could not add nicesink element to the transmitter"
" sink");
if (!gst_element_sync_state_with_parent (ns->nicesinks[1]))
GST_ERROR ("Could sync the state of the nicesink with its parent");
ns->requested_tee_pads[1] =
gst_element_get_request_pad (self->priv->sink_tees[1], "src_%u");
g_assert (ns->requested_tee_pads[1]);
elempad = gst_element_get_static_pad (ns->nicesinks[1], "sink");
ret = gst_pad_link (ns->requested_tee_pads[1], elempad);
if (GST_PAD_LINK_FAILED(ret))
GST_ERROR ("Could not link nicesink to its tee pad");
gst_object_unref (elempad);
fs_nice_transmitter_request_keyunit (self, ns, 1);
}
g_mutex_lock (&ns->mutex);
ns->sending = sending;
}
ns->modifying = FALSE;
g_mutex_unlock (&ns->mutex);
}
void
fs_nice_transmitter_request_keyunit (FsNiceTransmitter *self,
NiceGstStream *ns, guint component)
{
g_assert (component <= self->components);
gst_element_send_event (ns->nicesinks[component],
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new ("GstForceKeyUnit",
"all-headers", G_TYPE_BOOLEAN, TRUE,
NULL)));
}
void
fs_nice_transmitter_set_send_component_mux (FsNiceTransmitter *self,
NiceGstStream *ns, gboolean send_component_mux)
{
guint component;
for (component = 2; component <= self->components; component++)
{
g_object_set (ns->nicesinks[component],
"component", send_component_mux ? 1 : component, NULL);
}
}