/**
* SECTION:fs-app-stream-transmitter
- * @short_description: A stream transmitter object for Shared Memory
+ * @short_description: A stream transmitter object for application pipelines
*
* The name of this transmitter is "app".
*
- * This transmitter is meant to send and received the data from another process
- * on the same system while minimizing the memory pressure associated with the
- * use of sockets.
+ * This transmitter is meant to send and receive the data via a pipeline
+ * described by the application. The pipelines will typically start/end
+ * in an appsrc/appsink respectively, but a full pipeline description is
+ * required because codec and other conversions may be necessary when
+ * used with #FsRawConference, which does not handle those for itself.
*
- * Two sockets are used to control the shared memory areas. One is used to
- * send data and one to receive data. The receiver always connects to the
- * sender. The sender socket must exist before the receiver connects to it.
+ * The pipelines for send (e.g. to appsink) and receive (e.g. from appsrc)
+ * are provided in the "ip" and "username" properties of the #FsCandidate,
+ * respectively. These pipelines are instantiated with
+ * gst_parse_bin_from_description().
*
- * Negotiating the paths of the sockets can happen in two ways. If the
- * create-local-candidates is True then the transmitter will generate the
- * path of the local candidate and us it as the ip filed in #FsCandidate. The
- * transmitter will expect the path of the applications sender socket to be in
- * the "ip" field of the remote candidates #FsCandidate as well.
- *
- * Or alternatively, if create-local-candidates is false then
- * the sender socket can be created by giving the transmitter a candidate
- * with the path of the socket in the "ip" field of the #FsCandidate. This
- * #FsCandidate can be given to the #FsStreamTransmitter in two ways, either
- * by setting the #FsStreamTransmitter:preferred-local-candidates property
- * or by calling the fs_stream_transmitter_force_remote_candidates() function.
- * There can be only one single send socket per stream. When the send socket
- * is ready to be connected to, #FsStreamTransmitter::new-local-candidate signal
- * will be emitted.
- *
- * To connect the receive side to the other application, one must create a
- * #FsCandidate with the path of the sender's socket in the "username" field.
- * If the receiver can not connect to the sender,
- * the fs_stream_transmitter_force_remote_candidates() call will fail.
+ * Typically the application would give a unique name to the appsrc and
+ * appsink elements, so that gst_bin_get_by_name() can be used to find
+ * and interact with them.
*/
#ifdef HAVE_CONFIG_H
{
PROP_0,
PROP_SENDING,
- PROP_PREFERRED_LOCAL_CANDIDATES,
};
struct _FsAppStreamTransmitterPrivate
*/
FsAppTransmitter *transmitter;
- GList *preferred_local_candidates;
-
GMutex mutex;
/* Protected by the mutex */
/* Protected by the mutex */
FsCandidate **candidates;
- /* temporary socket directy in case we made one */
- gchar *socket_dir;
-
AppSrc **app_src;
AppSink **app_sink;
};
static gboolean fs_app_stream_transmitter_force_remote_candidates (
FsStreamTransmitter *streamtransmitter, GList *candidates,
GError **error);
-static gboolean fs_app_stream_transmitter_gather_local_candidates (
- FsStreamTransmitter *streamtransmitter,
- GError **error);
static gboolean
fs_app_stream_transmitter_add_sink (FsAppStreamTransmitter *self,
streamtransmitterclass->force_remote_candidates =
fs_app_stream_transmitter_force_remote_candidates;
- streamtransmitterclass->gather_local_candidates =
- fs_app_stream_transmitter_gather_local_candidates;
g_object_class_override_property (gobject_class, PROP_SENDING, "sending");
- g_object_class_override_property (gobject_class,
- PROP_PREFERRED_LOCAL_CANDIDATES, "preferred-local-candidates");
gobject_class->dispose = fs_app_stream_transmitter_dispose;
gobject_class->finalize = fs_app_stream_transmitter_finalize;
self->priv->app_sink[c] = NULL;
}
- if (self->priv->socket_dir != NULL)
- g_rmdir (self->priv->socket_dir);
- g_free (self->priv->socket_dir);
- self->priv->socket_dir = NULL;
-
parent_class->dispose (object);
}
{
FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER (object);
- fs_candidate_list_destroy (self->priv->preferred_local_candidates);
-
g_free (self->priv->app_src);
g_free (self->priv->app_sink);
g_mutex_clear (&self->priv->mutex);
g_value_set_boolean (value, self->priv->sending);
FS_APP_STREAM_TRANSMITTER_UNLOCK (self);
break;
- case PROP_PREFERRED_LOCAL_CANDIDATES:
- g_value_set_boxed (value, self->priv->preferred_local_candidates);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
self->priv->app_sink[1], self->priv->sending);
FS_APP_STREAM_TRANSMITTER_UNLOCK (self);
break;
- case PROP_PREFERRED_LOCAL_CANDIDATES:
- self->priv->preferred_local_candidates = g_value_dup_boxed (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
buffer);
}
-static void ready_cb (guint component, gchar *path, gpointer data)
+static void ready_cb (guint component, gchar *pipeline, gpointer data)
{
FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER_CAST (data);
FsCandidate *candidate = fs_candidate_new (NULL, component,
- FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, path, 0);
+ FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, pipeline, 0);
- GST_DEBUG ("Emitting new local candidate with path %s", path);
+ printf ("Emitting new local candidate with pipeline %s", pipeline);
g_signal_emit_by_name (self, "new-local-candidate", candidate);
g_signal_emit_by_name (self, "local-candidates-prepared");
fs_candidate_destroy (candidate);
}
-static void
-connected_cb (guint component, gint id, gpointer data)
-{
- FsAppStreamTransmitter *self = data;
- printf("emit state-changed for %p:%u\n", self, component);
- g_signal_emit_by_name (self, "state-changed", component,
- FS_STREAM_STATE_READY);
-}
-
static gboolean
fs_app_stream_transmitter_add_sink (FsAppStreamTransmitter *self,
FsCandidate *candidate, GError **error)
self->priv->app_sink[candidate->component_id] =
fs_app_transmitter_get_app_sink (self->priv->transmitter,
- candidate->component_id, candidate->ip, ready_cb, connected_cb,
- self, error);
+ candidate->component_id, candidate->ip, ready_cb, self, error);
if (self->priv->app_sink[candidate->component_id] == NULL)
return FALSE;
-#if 0
- if (candidate->component_id == 1) {
- fs_app_transmitter_sink_set_sending (self->priv->transmitter,
- self->priv->app_sink[candidate->component_id], self->priv->sending);
- connected_cb(1, 0, self);
- }
-#endif
return TRUE;
}
FsAppStreamTransmitter *self, FsCandidate *candidate,
GError **error)
{
- const gchar *path;
+ const gchar *pipeline;
if (!fs_app_stream_transmitter_add_sink (self, candidate, error))
return FALSE;
- path = candidate->username;
+ pipeline = candidate->username;
- if (path && path[0])
+ if (pipeline && pipeline[0])
{
if (self->priv->app_src[candidate->component_id])
{
if (fs_app_transmitter_check_app_src (self->priv->transmitter,
- self->priv->app_src[candidate->component_id], path))
+ self->priv->app_src[candidate->component_id], pipeline))
return TRUE;
self->priv->app_src[candidate->component_id] = NULL;
}
self->priv->app_src[candidate->component_id] =
fs_app_transmitter_get_app_src (self->priv->transmitter,
- candidate->component_id, path, got_buffer_func, disconnected_cb,
+ candidate->component_id, pipeline, got_buffer_func, disconnected_cb,
self, error);
if (self->priv->app_src[candidate->component_id] == NULL)
(!candidate->username || !candidate->username[0]))
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
- "The candidate does not have a SINK app segment in its ip"
- " or a SRC app segment in its username");
+ "The candidate does not have a SINK pipeline in its ip"
+ " or a SRC pipeline in its username");
return FALSE;
}
}
return streamtransmitter;
}
-
-
-static gboolean
-fs_app_stream_transmitter_gather_local_candidates (
- FsStreamTransmitter *streamtransmitter,
- GError **error)
-{
- FsAppStreamTransmitter *self =
- FS_APP_STREAM_TRANSMITTER (streamtransmitter);
- GList *item;
-
- for (item = self->priv->preferred_local_candidates;
- item;
- item = g_list_next (item))
- {
- FsCandidate *candidate = item->data;
-
- if (candidate->ip && candidate->ip[0])
- if (!fs_app_stream_transmitter_add_sink (self, candidate, error))
- return FALSE;
- }
-
- return TRUE;
-}
struct _AppSrc {
guint component;
- gchar *path;
+ gchar *pipeline;
GstElement *src;
GstPad *funnelpad;
AppSrc *
fs_app_transmitter_get_app_src (FsAppTransmitter *self,
guint component,
- const gchar *path,
+ const gchar *pipeline,
got_buffer got_buffer_func,
connection disconnected_func,
gpointer cb_data,
app->disconnected_func = disconnected_func;
app->cb_data = cb_data;
- app->path = g_strdup (path);
+ app->pipeline = g_strdup (pipeline);
- elem = gst_parse_bin_from_description(path, TRUE, NULL);
+ elem = gst_parse_bin_from_description(pipeline, TRUE, NULL);
if (!elem)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
goto error;
}
- g_object_set (elem,
- "socket-path", path,
- "do-timestamp", self->priv->do_timestamp,
- "is-live", TRUE,
- NULL);
-
if (app->disconnected_func)
g_signal_connect (self->priv->gst_src, "disconnected",
G_CALLBACK (disconnected_cb), app);
}
/*
- * Returns: %TRUE if the path is the same, other %FALSE and freeds the AppSrc
+ * Returns: %TRUE if the pipeline is the same, other %FALSE and freeds the AppSrc
*/
gboolean
fs_app_transmitter_check_app_src (FsAppTransmitter *self, AppSrc *app,
- const gchar *path)
+ const gchar *pipeline)
{
- if (path && !strcmp (path, app->path))
+ if (pipeline && !strcmp (pipeline, app->pipeline))
return TRUE;
if (app->buffer_probe)
}
app->src = NULL;
- g_free (app->path);
+ g_free (app->pipeline);
g_slice_free (AppSrc, app);
return FALSE;
struct _AppSink {
guint component;
- gchar *path;
+ gchar *pipeline;
GstElement *sink;
GstElement *recvonly_filter;
GstPad *teepad;
ready ready_func;
- connection connected_func;
gpointer cb_data;
};
static void
ready_cb (GstBin *bin, GstElement *elem, AppSink *app)
{
- gchar *path = NULL;
-
if (elem != app->sink)
return;
- g_object_get (elem, "socket-path", &path, NULL);
- app->ready_func (app->component, path, app->cb_data);
- g_free (path);
-}
-
-
-static void
-connected_cb (GstBin *bin, gint id, AppSink *app)
-{
- app->connected_func (app->component, id, app->cb_data);
+ app->ready_func (app->component, app->pipeline, app->cb_data);
}
AppSink *
fs_app_transmitter_get_app_sink (FsAppTransmitter *self,
guint component,
- const gchar *path,
+ const gchar *pipeline,
ready ready_func,
- connection connected_func,
gpointer cb_data,
GError **error)
{
GstElement *elem;
GstPad *pad;
- GST_DEBUG ("Trying to add app sink for c:%u path %s", component, path);
+ GST_DEBUG ("Trying to add app sink for c:%u pipeline '%s'", component, pipeline);
app->component = component;
- app->path = g_strdup (path);
+ app->pipeline = g_strdup (pipeline);
app->ready_func = ready_func;
- app->connected_func = connected_func;
app->cb_data = cb_data;
/* First add the sink */
- elem = gst_parse_bin_from_description(path, TRUE, NULL);
+ elem = gst_parse_bin_from_description(pipeline, TRUE, NULL);
if (!elem)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
g_signal_connect (self->priv->gst_sink, "ready", G_CALLBACK (ready_cb),
app);
- if (connected_func)
- g_signal_connect (elem, "client-connected", G_CALLBACK (connected_cb), app);
-
if (!gst_bin_add (GST_BIN (self->priv->gst_sink), elem))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
gboolean
fs_app_transmitter_check_app_sink (FsAppTransmitter *self, AppSink *app,
- const gchar *path)
+ const gchar *pipeline)
{
- if (path && !strcmp (path, app->path))
+ if (pipeline && !strcmp (pipeline, app->pipeline))
return TRUE;
- if (path)
- GST_DEBUG ("Replacing app socket %s with %s", app->path, path);
+ if (pipeline)
+ GST_DEBUG ("Replacing app pipeline '%s' with '%s'", app->pipeline, pipeline);
else
- GST_DEBUG ("Freeing app socket %s", app->path);
+ GST_DEBUG ("Freeing app pipeline '%s'", app->pipeline);
if (app->teepad)
{
}
app->recvonly_filter = NULL;
- g_free (app->path);
+ g_free (app->pipeline);
g_slice_free (AppSink, app);
return FALSE;