mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
rewritten pipeline parsing lands. Have fun breaking it.
Original commit message from CVS: rewritten pipeline parsing lands. Have fun breaking it. regressions: - No support for filtered links. If anybody needs this, please contact me and I'll add it as fast as possible. improvements: - The pipeline building actually works as expected. - syntax compatible nearly everywhere but more flexible - better property parsing (you can now set enums by name or nick) - uses locked_state to allow for delayed pads - can connect video pipelines without the need for queues - allows properties on bins - does not return a bin, but an element. You may want to read docs/random/company/gstparse to learn how it works.
This commit is contained in:
parent
64f71f6961
commit
3a897e2f30
5 changed files with 913 additions and 849 deletions
480
gst/gstparse.c
480
gst/gstparse.c
|
@ -26,24 +26,8 @@
|
||||||
#include "gstparse.h"
|
#include "gstparse.h"
|
||||||
#include "gstinfo.h"
|
#include "gstinfo.h"
|
||||||
#include "gstlog.h"
|
#include "gstlog.h"
|
||||||
#include "parse/types.h"
|
|
||||||
|
|
||||||
typedef struct _gst_parse_delayed_pad gst_parse_delayed_pad;
|
|
||||||
struct _gst_parse_delayed_pad
|
|
||||||
{
|
|
||||||
gchar *name;
|
|
||||||
GstPad *peer;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
gchar *srcpadname;
|
|
||||||
GstPad *target_pad;
|
|
||||||
GstElement *target_element;
|
|
||||||
GstElement *pipeline;
|
|
||||||
}
|
|
||||||
dynamic_link_t;
|
|
||||||
|
|
||||||
|
extern GstElement *_gst_parse_launch (const gchar *, GError **);
|
||||||
|
|
||||||
GQuark
|
GQuark
|
||||||
gst_parse_error_quark (void)
|
gst_parse_error_quark (void)
|
||||||
|
@ -54,413 +38,38 @@ gst_parse_error_quark (void)
|
||||||
return quark;
|
return quark;
|
||||||
}
|
}
|
||||||
|
|
||||||
G_GNUC_UNUSED static void
|
static gchar *_gst_parse_escape (const gchar *str)
|
||||||
dynamic_link (GstElement * element, GstPad * newpad, gpointer data)
|
|
||||||
{
|
{
|
||||||
dynamic_link_t *dc = (dynamic_link_t *) data;
|
GString *gstr = NULL;
|
||||||
gboolean warn = TRUE;
|
|
||||||
|
|
||||||
/* do we know the exact srcpadname? */
|
g_return_val_if_fail (str != NULL, NULL);
|
||||||
if (dc->srcpadname) {
|
|
||||||
GstPadTemplate *templ = gst_pad_get_pad_template (newpad);
|
|
||||||
|
|
||||||
/* see if this is the one */
|
gstr = g_string_sized_new (strlen (str));
|
||||||
if (strcmp (gst_pad_get_name (newpad), dc->srcpadname) &&
|
|
||||||
strcmp (gst_object_get_name (GST_OBJECT (templ)), dc->srcpadname)) {
|
while (*str) {
|
||||||
return;
|
if (*str == ' ')
|
||||||
}
|
g_string_append_c (gstr, '\\');
|
||||||
|
g_string_append_c (gstr, *str);
|
||||||
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* try to find a target pad if we don't know it yet */
|
return gstr->str;
|
||||||
if (!dc->target_pad) {
|
|
||||||
if (!GST_PAD_IS_LINKED (newpad)) {
|
|
||||||
dc->target_pad = gst_element_get_compatible_pad (dc->target_element, newpad);
|
|
||||||
warn = FALSE;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!GST_PAD_IS_LINKED (dc->target_pad) && !GST_PAD_IS_LINKED (newpad)) {
|
|
||||||
gst_element_set_state (dc->pipeline, GST_STATE_PAUSED);
|
|
||||||
if (!gst_pad_link (newpad, dc->target_pad) && warn) {
|
|
||||||
g_warning ("could not link %s:%s to %s:%s", GST_DEBUG_PAD_NAME (newpad),
|
|
||||||
GST_DEBUG_PAD_NAME (dc->target_pad));
|
|
||||||
}
|
|
||||||
gst_element_set_state (dc->pipeline, GST_STATE_PLAYING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
make_elements (graph_t *g, GError **error)
|
|
||||||
{
|
|
||||||
GList *l = NULL;
|
|
||||||
gchar *bin_type;
|
|
||||||
element_t *e;
|
|
||||||
|
|
||||||
if (!(g->bins || g->elements)) {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_SYNTAX,
|
|
||||||
"Empty bin");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g->current_bin_type)
|
|
||||||
bin_type = g->current_bin_type;
|
|
||||||
else
|
|
||||||
bin_type = "pipeline";
|
|
||||||
|
|
||||||
if (!(g->bin = gst_element_factory_make (bin_type, NULL))) {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
|
||||||
"No such bin type %s", bin_type);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
l = g->elements;
|
|
||||||
while (l) {
|
|
||||||
e = (element_t*)l->data;
|
|
||||||
if (!(e->element = gst_element_factory_make (e->type, NULL))) {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
|
||||||
"No such element %s", e->type);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
gst_bin_add (GST_BIN (g->bin), e->element);
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
l = g->bins;
|
|
||||||
while (l) {
|
|
||||||
if (!make_elements ((graph_t*)l->data, error))
|
|
||||||
return FALSE;
|
|
||||||
gst_bin_add (GST_BIN (g->bin), ((graph_t*)l->data)->bin);
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
set_properties (graph_t *g, GError **error)
|
|
||||||
{
|
|
||||||
GList *l, *l2;
|
|
||||||
element_t *e;
|
|
||||||
property_t *p;
|
|
||||||
GParamSpec *pspec;
|
|
||||||
|
|
||||||
l = g->elements;
|
|
||||||
while (l) {
|
|
||||||
e = (element_t*)l->data;
|
|
||||||
l2 = e->property_values;
|
|
||||||
while (l2) {
|
|
||||||
p = (property_t*)l2->data;
|
|
||||||
if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (e->element), p->name))) {
|
|
||||||
g_object_set_property (G_OBJECT (e->element), p->name, p->value);
|
|
||||||
} else {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_NO_SUCH_PROPERTY,
|
|
||||||
"No such property '%s' in element '%s'",
|
|
||||||
p->name, GST_OBJECT_NAME (GST_OBJECT (e->element)));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
l2 = g_list_next (l2);
|
|
||||||
}
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
l = g->bins;
|
|
||||||
while (l) {
|
|
||||||
if (!set_properties ((graph_t*)l->data, error))
|
|
||||||
return FALSE;
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstElement*
|
|
||||||
find_element_by_index_recurse (graph_t *g, gint i)
|
|
||||||
{
|
|
||||||
GList *l;
|
|
||||||
element_t *e;
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
l = g->elements;
|
|
||||||
while (l) {
|
|
||||||
e = (element_t*)l->data;
|
|
||||||
if (e->index == i) {
|
|
||||||
return e->element;
|
|
||||||
}
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
l = g->bins;
|
|
||||||
while (l) {
|
|
||||||
if ((element = find_element_by_index_recurse ((graph_t*)l->data, i)))
|
|
||||||
return element;
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstElement*
|
|
||||||
find_element_by_index (graph_t *g, gint i)
|
|
||||||
{
|
|
||||||
while (g->parent)
|
|
||||||
g = g->parent;
|
|
||||||
|
|
||||||
return find_element_by_index_recurse (g, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
make_links (graph_t *g, GError **error)
|
|
||||||
{
|
|
||||||
GList *l, *a, *b;
|
|
||||||
link_t *c;
|
|
||||||
dynamic_link_t *dc;
|
|
||||||
GstElement *src, *sink;
|
|
||||||
GstPad *p1, *p2;
|
|
||||||
GstPadTemplate *pt1, *pt2;
|
|
||||||
GstCaps *caps;
|
|
||||||
|
|
||||||
l = g->links;
|
|
||||||
while (l) {
|
|
||||||
c = (link_t*)l->data;
|
|
||||||
if (c->src_name) {
|
|
||||||
if (!(src = gst_bin_get_by_name (GST_BIN (g->bin), c->src_name))) {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
|
||||||
"No such element '%s'",
|
|
||||||
c->src_name);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
src = find_element_by_index (g, c->src_index);
|
|
||||||
g_assert (src);
|
|
||||||
}
|
|
||||||
if (c->sink_name) {
|
|
||||||
if (!(sink = gst_bin_get_by_name (GST_BIN (g->bin), c->sink_name))) {
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
|
||||||
"No such element '%s'",
|
|
||||||
c->sink_name);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sink = find_element_by_index (g, c->sink_index);
|
|
||||||
g_assert (sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
a = c->src_pads;
|
|
||||||
b = c->sink_pads;
|
|
||||||
caps = c->caps;
|
|
||||||
gst_caps_ref (caps);
|
|
||||||
gst_caps_sink (caps);
|
|
||||||
|
|
||||||
gst_caps_debug (caps, "foo");
|
|
||||||
/* g_print ("a: %p, b: %p\n", a, b); */
|
|
||||||
if (a && b) {
|
|
||||||
/* balanced multipad link */
|
|
||||||
while (a && b) {
|
|
||||||
p1 = gst_element_get_pad (src, (gchar*)a->data);
|
|
||||||
p2 = gst_element_get_pad (sink, (gchar*)b->data);
|
|
||||||
|
|
||||||
if (!p2)
|
|
||||||
goto could_not_get_pad_b;
|
|
||||||
|
|
||||||
if (!p1 && p2 && (pt1 = gst_element_get_pad_template (src, (gchar*)a->data)) &&
|
|
||||||
pt1->presence == GST_PAD_SOMETIMES) {
|
|
||||||
dc = g_new0 (dynamic_link_t, 1);
|
|
||||||
dc->srcpadname = (gchar*)a->data;
|
|
||||||
dc->target_pad = p2;
|
|
||||||
dc->target_element = sink;
|
|
||||||
dc->pipeline = g->bin;
|
|
||||||
|
|
||||||
GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s and %s:%s",
|
|
||||||
GST_OBJECT_NAME (GST_OBJECT (src)),
|
|
||||||
(gchar*)a->data, GST_DEBUG_PAD_NAME (p2));
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
|
|
||||||
} else if (!p1) {
|
|
||||||
goto could_not_get_pad_a;
|
|
||||||
} else if (!gst_pad_link_filtered (p1, p2, caps)) {
|
|
||||||
goto could_not_link_pads;
|
|
||||||
}
|
|
||||||
a = g_list_next (a);
|
|
||||||
b = g_list_next (b);
|
|
||||||
}
|
|
||||||
} else if (a) {
|
|
||||||
if ((pt1 = gst_element_get_pad_template (src, (gchar*)a->data))) {
|
|
||||||
if ((p1 = gst_element_get_pad (src, (gchar*)a->data)) || pt1->presence == GST_PAD_SOMETIMES) {
|
|
||||||
if (!p1) {
|
|
||||||
/* sigh, a hack until i fix the gstelement api... */
|
|
||||||
if ((pt2 = gst_element_get_compatible_pad_template (sink, pt1))) {
|
|
||||||
if ((p2 = gst_element_get_pad (sink, pt2->name_template))) {
|
|
||||||
dc = g_new0 (dynamic_link_t, 1);
|
|
||||||
dc->srcpadname = (gchar*)a->data;
|
|
||||||
dc->target_pad = p2;
|
|
||||||
dc->target_element = NULL;
|
|
||||||
dc->pipeline = g->bin;
|
|
||||||
|
|
||||||
GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s and %s:%s",
|
|
||||||
GST_OBJECT_NAME (GST_OBJECT (src)),
|
|
||||||
(gchar*)a->data, GST_DEBUG_PAD_NAME (p2));
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
|
|
||||||
goto next;
|
|
||||||
} else {
|
|
||||||
/* both pt1 and pt2 are sometimes templates. sheesh. */
|
|
||||||
goto both_templates_have_sometimes_presence;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* if the target pad has no padtemplate we will figure out a target
|
|
||||||
* pad later on */
|
|
||||||
dc = g_new0 (dynamic_link_t, 1);
|
|
||||||
dc->srcpadname = (gchar*)a->data;
|
|
||||||
dc->target_pad = NULL;
|
|
||||||
dc->target_element = sink;
|
|
||||||
dc->pipeline = g->bin;
|
|
||||||
|
|
||||||
GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s, and some pad in %s",
|
|
||||||
GST_OBJECT_NAME (GST_OBJECT (src)),
|
|
||||||
(gchar*)a->data, GST_OBJECT_NAME (sink));
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
goto could_not_get_compatible_to_a;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
goto could_not_get_pad_a;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
goto could_not_get_pad_a;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gst_pad_link_filtered (p1, p2, caps)) {
|
|
||||||
goto could_not_link_pads;
|
|
||||||
}
|
|
||||||
} else if (b) {
|
|
||||||
/* we don't support dynamic links on this side yet, if ever */
|
|
||||||
if (!(p2 = gst_element_get_pad (sink, (gchar*)b->data))) {
|
|
||||||
goto could_not_get_pad_b;
|
|
||||||
}
|
|
||||||
if (!(p1 = gst_element_get_compatible_pad (src, p2))) {
|
|
||||||
goto could_not_get_compatible_to_b;
|
|
||||||
}
|
|
||||||
if (!gst_pad_link_filtered (p1, p2, caps)) {
|
|
||||||
goto could_not_link_pads;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!gst_element_link_filtered (src, sink, caps)) {
|
|
||||||
goto could_not_link_elements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next:
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
l = g->bins;
|
|
||||||
while (l) {
|
|
||||||
if (!make_links ((graph_t*)l->data, error))
|
|
||||||
return FALSE;
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
could_not_get_pad_a:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not get a pad %s from element %s",
|
|
||||||
(gchar*)a->data, GST_OBJECT_NAME (src));
|
|
||||||
return FALSE;
|
|
||||||
could_not_get_pad_b:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not get a pad %s from element %s",
|
|
||||||
(gchar*)b->data, GST_OBJECT_NAME (sink));
|
|
||||||
return FALSE;
|
|
||||||
could_not_get_compatible_to_a:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not find a compatible pad in element %s to for %s:%s",
|
|
||||||
GST_OBJECT_NAME (sink), GST_OBJECT_NAME (src), (gchar*)a->data);
|
|
||||||
return FALSE;
|
|
||||||
could_not_get_compatible_to_b:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not find a compatible pad in element %s to for %s:%s",
|
|
||||||
GST_OBJECT_NAME (src), GST_OBJECT_NAME (sink), (gchar*)b->data);
|
|
||||||
return FALSE;
|
|
||||||
both_templates_have_sometimes_presence:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Both %s:%s and %s:%s have GST_PAD_SOMETIMES presence, operation not supported",
|
|
||||||
GST_OBJECT_NAME (src), pt1->name_template, GST_OBJECT_NAME (sink), pt2->name_template);
|
|
||||||
return FALSE;
|
|
||||||
could_not_link_pads:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not link %s:%s to %s:%s",
|
|
||||||
GST_DEBUG_PAD_NAME (p1),
|
|
||||||
GST_DEBUG_PAD_NAME (p2));
|
|
||||||
return FALSE;
|
|
||||||
could_not_link_elements:
|
|
||||||
g_set_error (error,
|
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_LINK,
|
|
||||||
"Could not link element %s to %s",
|
|
||||||
GST_OBJECT_NAME (src),
|
|
||||||
GST_OBJECT_NAME (sink));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstBin*
|
|
||||||
pipeline_from_graph (graph_t *g, GError **error)
|
|
||||||
{
|
|
||||||
if (!make_elements (g, error))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!set_properties (g, error))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!make_links (g, error))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (GstBin*)g->bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_parse_launchv:
|
* gst_parse_launchv:
|
||||||
* @argv: null-terminated array of arguments
|
* @argv: null-terminated array of arguments
|
||||||
* @error: pointer to GError
|
* @error: pointer to a #GError
|
||||||
*
|
*
|
||||||
* Create a new pipeline based on command line syntax.
|
* Create a new element based on command line syntax.
|
||||||
|
* #error will contain an error message if pipeline creation fails and can
|
||||||
|
* contain an error message, when a recoverable error happened.
|
||||||
*
|
*
|
||||||
* Returns: a new pipeline on success, NULL on failure and error
|
* Returns: a new element on success and NULL on failure.
|
||||||
* will contain the error message.
|
|
||||||
*/
|
*/
|
||||||
GstBin *
|
GstElement *
|
||||||
gst_parse_launchv (const gchar **argv, GError **error)
|
gst_parse_launchv (const gchar **argv, GError **error)
|
||||||
{
|
{
|
||||||
GstBin *pipeline;
|
GstElement *element;
|
||||||
GString *str;
|
GString *str;
|
||||||
const gchar **argvp, *arg;
|
const gchar **argvp, *arg;
|
||||||
gchar *tmp;
|
gchar *tmp;
|
||||||
|
@ -480,47 +89,11 @@ gst_parse_launchv (const gchar **argv, GError **error)
|
||||||
argvp++;
|
argvp++;
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline = gst_parse_launch (str->str, error);
|
element = gst_parse_launch (str->str, error);
|
||||||
|
|
||||||
g_string_free (str, TRUE);
|
g_string_free (str, TRUE);
|
||||||
|
|
||||||
return pipeline;
|
return element;
|
||||||
}
|
|
||||||
|
|
||||||
gchar *_gst_parse_escape (const gchar *str)
|
|
||||||
{
|
|
||||||
GString *gstr = NULL;
|
|
||||||
|
|
||||||
g_return_val_if_fail (str != NULL, NULL);
|
|
||||||
|
|
||||||
gstr = g_string_sized_new (strlen (str));
|
|
||||||
|
|
||||||
while (*str) {
|
|
||||||
if (*str == ' ')
|
|
||||||
g_string_append_c (gstr, '\\');
|
|
||||||
g_string_append_c (gstr, *str);
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gstr->str;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _gst_parse_unescape (gchar *str)
|
|
||||||
{
|
|
||||||
gchar *walk;
|
|
||||||
|
|
||||||
g_return_if_fail (str != NULL);
|
|
||||||
|
|
||||||
walk = str;
|
|
||||||
|
|
||||||
while (*walk) {
|
|
||||||
if (*walk == '\\')
|
|
||||||
walk++;
|
|
||||||
*str = *walk;
|
|
||||||
str++;
|
|
||||||
walk++;
|
|
||||||
}
|
|
||||||
*str = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,10 +106,10 @@ void _gst_parse_unescape (gchar *str)
|
||||||
* Returns: a new bin on success, NULL on failure. By default the bin is
|
* Returns: a new bin on success, NULL on failure. By default the bin is
|
||||||
* a GstPipeline, but it depends on the pipeline_description.
|
* a GstPipeline, but it depends on the pipeline_description.
|
||||||
*/
|
*/
|
||||||
GstBin *
|
GstElement *
|
||||||
gst_parse_launch (const gchar * pipeline_description, GError **error)
|
gst_parse_launch (const gchar * pipeline_description, GError **error)
|
||||||
{
|
{
|
||||||
graph_t *graph;
|
GstElement *element;
|
||||||
static GStaticMutex flex_lock = G_STATIC_MUTEX_INIT;
|
static GStaticMutex flex_lock = G_STATIC_MUTEX_INIT;
|
||||||
|
|
||||||
g_return_val_if_fail (pipeline_description != NULL, NULL);
|
g_return_val_if_fail (pipeline_description != NULL, NULL);
|
||||||
|
@ -546,11 +119,8 @@ gst_parse_launch (const gchar * pipeline_description, GError **error)
|
||||||
|
|
||||||
/* the need for the mutex will go away with flex 2.5.6 */
|
/* the need for the mutex will go away with flex 2.5.6 */
|
||||||
g_static_mutex_lock (&flex_lock);
|
g_static_mutex_lock (&flex_lock);
|
||||||
graph = _gst_parse_launch (pipeline_description, error);
|
element = _gst_parse_launch (pipeline_description, error);
|
||||||
g_static_mutex_unlock (&flex_lock);
|
g_static_mutex_unlock (&flex_lock);
|
||||||
|
|
||||||
if (!graph)
|
return element;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return pipeline_from_graph (graph, error);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,12 +37,13 @@ typedef enum
|
||||||
GST_PARSE_ERROR_SYNTAX,
|
GST_PARSE_ERROR_SYNTAX,
|
||||||
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
GST_PARSE_ERROR_NO_SUCH_ELEMENT,
|
||||||
GST_PARSE_ERROR_NO_SUCH_PROPERTY,
|
GST_PARSE_ERROR_NO_SUCH_PROPERTY,
|
||||||
GST_PARSE_ERROR_LINK
|
GST_PARSE_ERROR_LINK,
|
||||||
|
GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
|
||||||
} GstParseError;
|
} GstParseError;
|
||||||
|
|
||||||
|
|
||||||
GstBin* gst_parse_launch (const gchar *pipeline_description, GError **error);
|
GstElement* gst_parse_launch (const gchar *pipeline_description, GError **error);
|
||||||
GstBin* gst_parse_launchv (const gchar **argv, GError **error);
|
GstElement* gst_parse_launchv (const gchar **argv, GError **error);
|
||||||
|
|
||||||
#else /* GST_DISABLE_PARSE */
|
#else /* GST_DISABLE_PARSE */
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,478 @@
|
||||||
%{
|
%{
|
||||||
|
#include <glib-object.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../gstparse.h"
|
#include "../gstparse.h"
|
||||||
|
#include "../gstinfo.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define YYDEBUG 1
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define YYERROR_VERBOSE 1
|
#define YYERROR_VERBOSE 1
|
||||||
#define YYPARSE_PARAM pgraph
|
#define YYPARSE_PARAM graph
|
||||||
|
|
||||||
|
#ifdef __GST_PARSE_TRACE
|
||||||
|
static uint __strings;
|
||||||
|
static uint __links;
|
||||||
|
static uint __chains;
|
||||||
|
gchar *
|
||||||
|
__gst_parse_strdup (gchar *org)
|
||||||
|
{
|
||||||
|
__strings++;
|
||||||
|
/* g_print ("ALLOCATED: %p %s\n", org, org); */
|
||||||
|
return g_strdup (org);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
__gst_parse_strfree (gchar *str)
|
||||||
|
{
|
||||||
|
if (str) {
|
||||||
|
/* g_print ("FREEING : %p %s\n", str, str); */
|
||||||
|
g_free (str);
|
||||||
|
g_return_if_fail (__strings > 0);
|
||||||
|
__strings--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
link_t *__gst_parse_link_new ()
|
||||||
|
{
|
||||||
|
__links++;
|
||||||
|
return g_new0 (link_t, 1);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
__gst_parse_link_free (link_t *data)
|
||||||
|
{
|
||||||
|
if (data) {
|
||||||
|
g_free (data);
|
||||||
|
g_return_if_fail (__links > 0);
|
||||||
|
__links--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain_t *
|
||||||
|
__gst_parse_chain_new ()
|
||||||
|
{
|
||||||
|
__chains++;
|
||||||
|
return g_new0 (chain_t, 1);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
__gst_parse_chain_free (chain_t *data)
|
||||||
|
{
|
||||||
|
if (data) {
|
||||||
|
g_free (data);
|
||||||
|
g_return_if_fail (__chains > 0);
|
||||||
|
__chains--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __GST_PARSE_TRACE */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gchar *src_pad;
|
||||||
|
gchar *sink_pad;
|
||||||
|
GstElement *sink;
|
||||||
|
GstCaps *caps;
|
||||||
|
gulong signal_id;
|
||||||
|
/* FIXME: need to connect to "disposed" signal to clean up, but there is no such signal */
|
||||||
|
} DelayedLink;
|
||||||
|
|
||||||
|
#ifdef G_HAVE_ISO_VARARGS
|
||||||
|
#define SET_ERROR(error, type, ...) G_STMT_START{ \
|
||||||
|
if (error) { \
|
||||||
|
if (*(error)) { \
|
||||||
|
g_warning (__VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
g_set_error ((error), GST_PARSE_ERROR, (type), __VA_ARGS__); \
|
||||||
|
}\
|
||||||
|
} \
|
||||||
|
}G_STMT_END
|
||||||
|
#define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), __VA_ARGS__ )
|
||||||
|
#ifdef GST_DEBUG_ENABLED
|
||||||
|
# define YYDEBUG 1
|
||||||
|
# define YYFPRINTF(a, ...) GST_DEBUG (GST_CAT_PIPELINE, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(G_HAVE_GNUC_VARARGS)
|
||||||
|
|
||||||
|
#define SET_ERROR(error, type, args...) G_STMT_START{ \
|
||||||
|
if (error) { \
|
||||||
|
if (*(error)) { \
|
||||||
|
g_warning ( ## args ); \
|
||||||
|
} else { \
|
||||||
|
g_set_error ((error), GST_PARSE_ERROR, (type), ## args ); \
|
||||||
|
}\
|
||||||
|
} \
|
||||||
|
}G_STMT_END
|
||||||
|
#define ERROR(type, args...) SET_ERROR (((graph_t *) graph)->error, (type), ## args )
|
||||||
|
#ifdef GST_DEBUG_ENABLED
|
||||||
|
# define YYDEBUG 1
|
||||||
|
# define YYFPRINTF(a, args...) GST_DEBUG (GST_CAT_PIPELINE, ## args )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define SET_ERROR(error, type, ...) G_STMT_START{ \
|
||||||
|
if (error) { \
|
||||||
|
if (*(error)) { \
|
||||||
|
g_warning ("error while parsing"); \
|
||||||
|
} else { \
|
||||||
|
g_set_error ((error), GST_PARSE_ERROR, (type), "error while parsing"); \
|
||||||
|
}\
|
||||||
|
} \
|
||||||
|
}G_STMT_END
|
||||||
|
#define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), "error while parsing")
|
||||||
|
#ifdef GST_DEBUG_ENABLED
|
||||||
|
# define YYDEBUG 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // G_HAVE_ISO_VARARGS
|
||||||
|
|
||||||
|
#define GST_BIN_MAKE(res, type, chain, assign) G_STMT_START{ \
|
||||||
|
GSList *walk; \
|
||||||
|
GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \
|
||||||
|
if (!bin) { \
|
||||||
|
ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No bin \"%s\"", type); \
|
||||||
|
} else { \
|
||||||
|
walk = chain->elements; \
|
||||||
|
while (walk) { \
|
||||||
|
gst_bin_add (bin, GST_ELEMENT (walk->data)); \
|
||||||
|
walk = walk->next; \
|
||||||
|
} \
|
||||||
|
g_slist_free (chain->elements); \
|
||||||
|
chain->elements = g_slist_prepend (NULL, bin); \
|
||||||
|
res = chain; \
|
||||||
|
/* set the properties now */ \
|
||||||
|
walk = assign; \
|
||||||
|
while (walk) { \
|
||||||
|
gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \
|
||||||
|
walk = g_slist_next (walk); \
|
||||||
|
} \
|
||||||
|
g_slist_free (assign); \
|
||||||
|
} \
|
||||||
|
}G_STMT_END
|
||||||
|
|
||||||
|
#define MAKE_LINK(link, _src, _src_name, _src_pads, _sink, _sink_name, _sink_pads) G_STMT_START{ \
|
||||||
|
link = gst_parse_link_new (); \
|
||||||
|
link->src = _src; \
|
||||||
|
link->sink = _sink; \
|
||||||
|
link->src_name = _src_name; \
|
||||||
|
link->sink_name = _sink_name; \
|
||||||
|
link->src_pads = _src_pads; \
|
||||||
|
link->sink_pads = _sink_pads; \
|
||||||
|
link->caps = NULL; \
|
||||||
|
}G_STMT_END
|
||||||
|
|
||||||
|
#define MAKE_REF(link, _src, _pads) G_STMT_START{ \
|
||||||
|
gchar *padname = _src; \
|
||||||
|
GSList *pads = _pads; \
|
||||||
|
if (padname) { \
|
||||||
|
while (*padname != '.') padname++; \
|
||||||
|
*padname = '\0'; \
|
||||||
|
padname++; \
|
||||||
|
if (*padname != '\0') \
|
||||||
|
pads = g_slist_prepend (pads, gst_parse_strdup (padname)); \
|
||||||
|
} \
|
||||||
|
MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \
|
||||||
|
}G_STMT_END
|
||||||
|
|
||||||
|
static inline void gst_parse_unescape (gchar *str)
|
||||||
|
{
|
||||||
|
gchar *walk;
|
||||||
|
|
||||||
|
g_return_if_fail (str != NULL);
|
||||||
|
|
||||||
|
walk = str;
|
||||||
|
|
||||||
|
while (*walk) {
|
||||||
|
if (*walk == '\\')
|
||||||
|
walk++;
|
||||||
|
*str = *walk;
|
||||||
|
str++;
|
||||||
|
walk++;
|
||||||
|
}
|
||||||
|
*str = '\0';
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
|
||||||
|
{
|
||||||
|
GParamSpec *pspec;
|
||||||
|
gchar *pos = value;
|
||||||
|
/* parse the string, so the property name is null-terminated an pos points
|
||||||
|
to the beginning of the value */
|
||||||
|
while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++;
|
||||||
|
if (*pos == '=') {
|
||||||
|
*pos = '\0';
|
||||||
|
} else {
|
||||||
|
*pos = '\0';
|
||||||
|
pos++;
|
||||||
|
while (g_ascii_isspace (*pos)) pos++;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
while (g_ascii_isspace (*pos)) pos++;
|
||||||
|
if (*pos == '"') {
|
||||||
|
pos++;
|
||||||
|
pos[strlen (pos) - 1] = '\0';
|
||||||
|
}
|
||||||
|
gst_parse_unescape (pos);
|
||||||
|
if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), value))) {
|
||||||
|
GValue v = { 0, };
|
||||||
|
GValue v2 = { 0, };
|
||||||
|
g_value_init (&v, G_PARAM_SPEC_VALUE_TYPE(pspec));
|
||||||
|
switch (G_TYPE_FUNDAMENTAL (G_PARAM_SPEC_VALUE_TYPE (pspec))) {
|
||||||
|
case G_TYPE_STRING:
|
||||||
|
g_value_set_string (&v, pos);
|
||||||
|
break;
|
||||||
|
case G_TYPE_BOOLEAN:
|
||||||
|
if (g_ascii_strcasecmp (pos, "true") && g_ascii_strcasecmp (pos, "yes") && g_ascii_strcasecmp (pos, "1")) {
|
||||||
|
g_value_set_boolean (&v, FALSE);
|
||||||
|
} else {
|
||||||
|
g_value_set_boolean (&v, TRUE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case G_TYPE_ENUM: {
|
||||||
|
GEnumValue *en;
|
||||||
|
gchar **endptr;
|
||||||
|
GEnumClass *klass = (GEnumClass *) g_type_class_peek (G_PARAM_SPEC_VALUE_TYPE (pspec));
|
||||||
|
if (klass == NULL) goto error;
|
||||||
|
if (!(en = g_enum_get_value_by_name (klass, pos)))
|
||||||
|
en = g_enum_get_value_by_nick (klass, pos);
|
||||||
|
if (en) {
|
||||||
|
g_value_set_enum (&v, en->value);
|
||||||
|
} else {
|
||||||
|
gint i = strtol (value, endptr, 0);
|
||||||
|
if (**endptr == '\0') {
|
||||||
|
g_value_set_enum (&v, i);
|
||||||
|
} else {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case G_TYPE_INT:
|
||||||
|
case G_TYPE_LONG:
|
||||||
|
case G_TYPE_INT64:
|
||||||
|
g_value_init (&v2, G_TYPE_LONG);
|
||||||
|
g_value_set_long (&v2, strtol (pos, NULL, 0));
|
||||||
|
if (!g_value_transform (&v2, &v)) goto error;
|
||||||
|
break;
|
||||||
|
case G_TYPE_UINT:
|
||||||
|
case G_TYPE_ULONG:
|
||||||
|
case G_TYPE_UINT64:
|
||||||
|
g_value_init (&v2, G_TYPE_ULONG);
|
||||||
|
g_value_set_long (&v2, strtoul (pos, NULL, 0));
|
||||||
|
if (!g_value_transform (&v2, &v)) goto error;
|
||||||
|
break;
|
||||||
|
case G_TYPE_FLOAT:
|
||||||
|
case G_TYPE_DOUBLE:
|
||||||
|
g_value_init (&v2, G_TYPE_DOUBLE);
|
||||||
|
g_value_set_double (&v2, atof (pos));
|
||||||
|
if (!g_value_transform (&v2, &v)) goto error;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* add more */
|
||||||
|
g_warning ("property \"%s\" in element %s cannot be set", value, GST_ELEMENT_NAME (element));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
g_object_set_property (G_OBJECT (element), value, &v);
|
||||||
|
} else {
|
||||||
|
ERROR (GST_PARSE_ERROR_NO_SUCH_PROPERTY, "No property \"%s\" in element \"%s\"", value, GST_ELEMENT_NAME (element));
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
gst_parse_strfree (value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ERROR (GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY, "Could not set property \"%s\" in element \"%s\" to \"%s\"", value, GST_ELEMENT_NAME (element), pos);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
gst_parse_free_link (link_t *link)
|
||||||
|
{
|
||||||
|
gst_parse_strfree (link->src_name);
|
||||||
|
gst_parse_strfree (link->sink_name);
|
||||||
|
g_slist_foreach (link->src_pads, (GFunc) gst_parse_strfree, NULL);
|
||||||
|
g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL);
|
||||||
|
g_slist_free (link->src_pads);
|
||||||
|
g_slist_free (link->sink_pads);
|
||||||
|
gst_caps_unref (link->caps);
|
||||||
|
gst_parse_link_free (link);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
gst_parse_element_lock (GstElement *element, gboolean lock)
|
||||||
|
{
|
||||||
|
GstPad *pad;
|
||||||
|
GList *walk = (GList *) gst_element_get_pad_list (element);
|
||||||
|
gboolean unlocked_peer = FALSE;
|
||||||
|
|
||||||
|
if (gst_element_is_state_locked (element) == lock)
|
||||||
|
return;
|
||||||
|
/* check if we have an unlocked peer */
|
||||||
|
while (walk) {
|
||||||
|
pad = (GstPad *) GST_PAD_REALIZE (walk->data);
|
||||||
|
walk = walk->next;
|
||||||
|
if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) &&
|
||||||
|
!gst_element_is_state_locked (GST_PAD_PARENT (GST_PAD_PEER (pad)))) {
|
||||||
|
unlocked_peer = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lock && !unlocked_peer) {
|
||||||
|
gst_element_lock_state (element);
|
||||||
|
} else if (!lock) {
|
||||||
|
gst_element_unlock_state (element);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if there are other pads to (un)lock */
|
||||||
|
walk = (GList *) gst_element_get_pad_list (element);
|
||||||
|
while (walk) {
|
||||||
|
pad = (GstPad *) GST_PAD_REALIZE (walk->data);
|
||||||
|
walk = walk->next;
|
||||||
|
if (GST_PAD_IS_SRC (pad) && GST_PAD_PEER (pad)) {
|
||||||
|
GstElement *next = GST_ELEMENT (GST_OBJECT_PARENT (GST_PAD_PEER (pad)));
|
||||||
|
if (gst_element_is_state_locked (next) != lock)
|
||||||
|
gst_parse_element_lock (next, lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
|
||||||
|
{
|
||||||
|
DelayedLink *link = (DelayedLink *) data;
|
||||||
|
gboolean restart = FALSE;
|
||||||
|
|
||||||
|
GST_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s",
|
||||||
|
GST_ELEMENT_NAME (src), link->src_pad,
|
||||||
|
GST_ELEMENT_NAME (link->sink), link->sink_pad);
|
||||||
|
if (gst_element_get_state (src) == GST_STATE_PLAYING) {
|
||||||
|
restart = TRUE;
|
||||||
|
gst_element_set_state (src, GST_STATE_PAUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gst_element_link_pads_filtered (src, link->src_pad, link->sink, link->sink_pad, link->caps)) {
|
||||||
|
/* do this here, we don't want to get any problems later on when unlocking states */
|
||||||
|
GST_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked",
|
||||||
|
GST_ELEMENT_NAME (src), link->src_pad,
|
||||||
|
GST_ELEMENT_NAME (link->sink), link->sink_pad);
|
||||||
|
if (restart) {
|
||||||
|
gst_element_set_state (src, GST_STATE_PLAYING);
|
||||||
|
}
|
||||||
|
g_signal_handler_disconnect (src, link->signal_id);
|
||||||
|
g_free (link->src_pad);
|
||||||
|
g_free (link->sink_pad);
|
||||||
|
gst_caps_unref (link->caps);
|
||||||
|
if (!gst_element_is_state_locked (src))
|
||||||
|
gst_parse_element_lock (link->sink, FALSE);
|
||||||
|
g_free (link);
|
||||||
|
} else {
|
||||||
|
if (restart) {
|
||||||
|
gst_element_set_state (src, GST_STATE_PLAYING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* both padnames and the caps may be NULL */
|
||||||
|
static gboolean
|
||||||
|
gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
|
||||||
|
GstElement *sink, const gchar *sink_pad, GstCaps *caps)
|
||||||
|
{
|
||||||
|
GList *templs = gst_element_get_pad_template_list (src);
|
||||||
|
|
||||||
|
while (templs) {
|
||||||
|
GstPadTemplate *templ = (GstPadTemplate *) templs->data;
|
||||||
|
if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
|
||||||
|
{
|
||||||
|
/* TODO: maybe we should check if src_pad matches this template's names */
|
||||||
|
|
||||||
|
GST_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s",
|
||||||
|
GST_ELEMENT_NAME (src), src_pad, GST_ELEMENT_NAME (sink), sink_pad);
|
||||||
|
|
||||||
|
DelayedLink *data = g_new (DelayedLink, 1);
|
||||||
|
data->src_pad = g_strdup (src_pad);
|
||||||
|
data->sink = sink;
|
||||||
|
data->sink_pad = g_strdup (sink_pad);
|
||||||
|
data->caps = gst_caps_ref (caps);
|
||||||
|
data->signal_id = g_signal_connect (G_OBJECT (src), "new_pad",
|
||||||
|
G_CALLBACK (gst_parse_found_pad), data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
templs = g_list_next (templs);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* performs a link and frees the struct. src and sink elements must be given
|
||||||
|
* return values: 0 - link performed
|
||||||
|
* 1 - link delayed
|
||||||
|
* <0 - error
|
||||||
|
*/
|
||||||
|
static gint
|
||||||
|
gst_parse_perform_link (link_t *link, graph_t *graph)
|
||||||
|
{
|
||||||
|
GstElement *src = link->src;
|
||||||
|
GstElement *sink = link->sink;
|
||||||
|
GSList *srcs = link->src_pads;
|
||||||
|
GSList *sinks = link->sink_pads;
|
||||||
|
g_assert (GST_IS_ELEMENT (src));
|
||||||
|
g_assert (GST_IS_ELEMENT (sink));
|
||||||
|
|
||||||
|
GST_INFO (GST_CAT_PIPELINE, "linking %s(%s):%u to %s(%s):%u",
|
||||||
|
GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "---", g_slist_length (srcs),
|
||||||
|
GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "---", g_slist_length (sinks));
|
||||||
|
|
||||||
|
if (!srcs || !sinks) {
|
||||||
|
if (gst_element_link_pads_filtered (src, srcs ? (const gchar *) srcs->data : NULL,
|
||||||
|
sink, sinks ? (const gchar *) sinks->data : NULL,
|
||||||
|
link->caps)) {
|
||||||
|
gst_parse_element_lock (sink, gst_element_is_state_locked (src));
|
||||||
|
goto success;
|
||||||
|
} else {
|
||||||
|
if (gst_parse_perform_delayed_link (src, srcs ? (const gchar *) srcs->data : NULL,
|
||||||
|
sink, sinks ? (const gchar *) sinks->data : NULL,
|
||||||
|
link->caps)) {
|
||||||
|
gst_parse_element_lock (sink, TRUE);
|
||||||
|
goto success;
|
||||||
|
} else {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_slist_length (link->src_pads) != g_slist_length (link->src_pads)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
while (srcs && sinks) {
|
||||||
|
const gchar *src_pad = (const gchar *) srcs->data;
|
||||||
|
const gchar *sink_pad = (const gchar *) sinks->data;
|
||||||
|
srcs = g_slist_next (srcs);
|
||||||
|
sinks = g_slist_next (sinks);
|
||||||
|
if (gst_element_link_pads_filtered (src, src_pad, sink, sink_pad, link->caps)) {
|
||||||
|
gst_parse_element_lock (sink, gst_element_is_state_locked (src));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (gst_parse_perform_delayed_link (src, src_pad,
|
||||||
|
sink, sink_pad,
|
||||||
|
link->caps)) {
|
||||||
|
gst_parse_element_lock (sink, TRUE);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success:
|
||||||
|
gst_parse_free_link (link);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "could not link %s to %s", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink));
|
||||||
|
gst_parse_free_link (link);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int yylex (void *lvalp);
|
static int yylex (void *lvalp);
|
||||||
static int yyerror (const char *s);
|
static int yyerror (const char *s);
|
||||||
|
@ -16,111 +480,195 @@ static int yyerror (const char *s);
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
gchar *s;
|
gchar *s;
|
||||||
GValue *v;
|
chain_t *c;
|
||||||
|
link_t *l;
|
||||||
|
GstElement *e;
|
||||||
|
GSList *p;
|
||||||
graph_t *g;
|
graph_t *g;
|
||||||
link_t *c;
|
|
||||||
property_t *p;
|
|
||||||
element_t *e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%token <s> IDENTIFIER
|
%token <s> IDENTIFIER
|
||||||
%token <c> LINK BLINK FLINK
|
%left <s> REF PADREF BINREF
|
||||||
%token <v> VALUE
|
%token <s> ASSIGNMENT
|
||||||
|
|
||||||
%type <s> id
|
%type <g> graph
|
||||||
%type <g> graph bin
|
%type <c> chain bin
|
||||||
|
%type <l> reference
|
||||||
|
%type <l> linkpart link
|
||||||
|
%type <p> linklist
|
||||||
%type <e> element
|
%type <e> element
|
||||||
%type <p> property_value value
|
%type <p> padlist pads assignments
|
||||||
%type <c> link rlink
|
|
||||||
|
|
||||||
%left '{' '}' '(' ')'
|
%left '{' '}' '(' ')'
|
||||||
%left '!' '='
|
|
||||||
%left ','
|
%left ','
|
||||||
%left '.'
|
%right '.'
|
||||||
|
%left '!' '='
|
||||||
|
|
||||||
%pure_parser
|
%pure_parser
|
||||||
|
|
||||||
%start graph
|
%start graph
|
||||||
%%
|
%%
|
||||||
|
|
||||||
id: IDENTIFIER
|
element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL);
|
||||||
;
|
if (!$$) ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element \"%s\"", $1);
|
||||||
|
gst_parse_strfree ($1);
|
||||||
value: VALUE { $$ = g_new0 (property_t, 1); $$->value = $1; }
|
|
||||||
;
|
|
||||||
|
|
||||||
property_value: id '=' value { $$ = $3; $$->name = $1; }
|
|
||||||
;
|
|
||||||
|
|
||||||
element: id { static int i = 0; $$ = g_new0 (element_t, 1);
|
|
||||||
$$->type = $1; $$->index = ++i; }
|
|
||||||
;
|
|
||||||
|
|
||||||
graph: /* empty */ { $$ = g_new0 (graph_t, 1); *((graph_t**) pgraph) = $$; }
|
|
||||||
| graph element { GList *l;
|
|
||||||
$$ = $1; l = $$->links_pending;
|
|
||||||
$$->elements = g_list_append ($$->elements, $2);
|
|
||||||
$$->current = $2;
|
|
||||||
if (!$$->first)
|
|
||||||
$$->first = $$->current;
|
|
||||||
while (l) {
|
|
||||||
((link_t*) l->data)->sink_index = $$->current->index;
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
}
|
||||||
if ($$->links_pending) {
|
| element ASSIGNMENT { gst_parse_element_set ($2, $1, graph);
|
||||||
g_list_free ($$->links_pending);
|
$$ = $1;
|
||||||
$$->links_pending = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| graph bin { GList *l; $$ = $1; l = $$->links_pending;
|
|
||||||
*((graph_t**) pgraph) = $$;
|
|
||||||
$$->bins = g_list_append ($$->bins, $2);
|
|
||||||
$2->parent = $$;
|
|
||||||
$$->current = $2->first;
|
|
||||||
if (!$$->first)
|
|
||||||
$$->first = $$->current;
|
|
||||||
while (l) {
|
|
||||||
((link_t*) l->data)->sink_index = $$->current->index;
|
|
||||||
l = g_list_next (l);
|
|
||||||
}
|
|
||||||
if ($$->links_pending) {
|
|
||||||
g_list_free ($$->links_pending);
|
|
||||||
$$->links_pending = NULL;
|
|
||||||
}
|
|
||||||
$$->current = $2->current;
|
|
||||||
}
|
|
||||||
| graph link { $$ = $1;
|
|
||||||
$$->links = g_list_append ($$->links, $2);
|
|
||||||
if ($$->current)
|
|
||||||
$2->src_index = $$->current->index;
|
|
||||||
if (!$2->sink_name)
|
|
||||||
$$->links_pending = g_list_append ($$->links_pending, $2);
|
|
||||||
}
|
|
||||||
| graph property_value { $$ = $1;
|
|
||||||
if (!$$->current) {
|
|
||||||
fprintf (stderr, "error: property value assignments must be preceded by an element definition\n");
|
|
||||||
YYABORT;
|
|
||||||
}
|
|
||||||
$$->current->property_values = g_list_append ($$->current->property_values,
|
|
||||||
$2);
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
bin: '{' graph '}' { $$ = $2; $$->current_bin_type = "thread"; }
|
assignments: /* NOP */ { $$ = NULL; }
|
||||||
| id '.' '(' graph ')' { $$ = $4; $$->current_bin_type = $1; }
|
| assignments ASSIGNMENT { $$ = g_slist_prepend ($1, $2); }
|
||||||
|
|
||||||
|
bin: '{' assignments chain '}' { GST_BIN_MAKE ($$, "thread", $3, $2); }
|
||||||
|
| '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2); }
|
||||||
|
| BINREF assignments chain ')' { GST_BIN_MAKE ($$, $1, $3, $2);
|
||||||
|
gst_parse_strfree ($1);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
link: LINK
|
pads: PADREF { $$ = g_slist_prepend (NULL, $1); }
|
||||||
| rlink
|
| PADREF padlist { $$ = $2;
|
||||||
|
$$ = g_slist_prepend ($$, $1);
|
||||||
|
}
|
||||||
|
|
||||||
|
padlist: ',' IDENTIFIER { $$ = g_slist_prepend (NULL, $2); }
|
||||||
|
| ',' IDENTIFIER padlist { $$ = g_slist_prepend ($3, $2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
rlink: '!' { $$ = g_new0 (link_t, 1); }
|
reference: REF { MAKE_REF ($$, $1, NULL); }
|
||||||
| BLINK { $$ = $1; }
|
| REF padlist { MAKE_REF ($$, $1, $2); }
|
||||||
| FLINK { $$ = $1; }
|
;
|
||||||
| id ',' rlink ',' id
|
|
||||||
{ $$ = $3;
|
linkpart: reference { $$ = $1; }
|
||||||
$$->src_pads = g_list_prepend ($$->src_pads, $1);
|
| pads { MAKE_REF ($$, NULL, $1); }
|
||||||
$$->sink_pads = g_list_append ($$->sink_pads, $5);
|
| /* NOP */ { MAKE_REF ($$, NULL, NULL); }
|
||||||
|
;
|
||||||
|
|
||||||
|
link: linkpart '!' linkpart { $$ = $1;
|
||||||
|
$$->sink_name = $3->src_name;
|
||||||
|
$$->sink_pads = $3->src_pads;
|
||||||
|
gst_parse_link_free ($3);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
linklist: link { $$ = g_slist_prepend (NULL, $1); }
|
||||||
|
| link linklist { $$ = g_slist_prepend ($2, $1); }
|
||||||
|
;
|
||||||
|
|
||||||
|
chain: element { $$ = gst_parse_chain_new ();
|
||||||
|
$$->first = $$->last = $1;
|
||||||
|
$$->front = $$->back = NULL;
|
||||||
|
$$->elements = g_slist_prepend (NULL, $1);
|
||||||
|
}
|
||||||
|
| bin { $$ = $1; }
|
||||||
|
| chain chain { if ($1->back && $2->front) {
|
||||||
|
if (!$1->back->sink_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without source element");
|
||||||
|
gst_parse_free_link ($1->back);
|
||||||
|
} else {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
|
||||||
|
}
|
||||||
|
if (!$2->front->src_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
|
||||||
|
gst_parse_free_link ($2->front);
|
||||||
|
} else {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
|
||||||
|
}
|
||||||
|
$1->back = NULL;
|
||||||
|
} else if ($1->back) {
|
||||||
|
if (!$1->back->sink_name) {
|
||||||
|
$1->back->sink = $2->first;
|
||||||
|
} else {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
|
||||||
|
$1->back = NULL;
|
||||||
|
}
|
||||||
|
} else if ($2->front) {
|
||||||
|
if (!$2->front->src_name) {
|
||||||
|
$2->front->src = $1->last;
|
||||||
|
$1->back = $2->front;
|
||||||
|
} else {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($1->back)
|
||||||
|
gst_parse_perform_link ($1->back, (graph_t *) graph);
|
||||||
|
$1->last = $2->last;
|
||||||
|
$1->back = $2->back;
|
||||||
|
$1->elements = g_slist_concat ($1->elements, $2->elements);
|
||||||
|
gst_parse_chain_free ($2);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| link chain { if ($2->front) {
|
||||||
|
if (!$2->front->src_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without source element");
|
||||||
|
gst_parse_free_link ($2->front);
|
||||||
|
} else {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$1->sink_name) {
|
||||||
|
$1->sink = $2->first;
|
||||||
|
}
|
||||||
|
$2->front = $1;
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
|
||||||
|
| chain linklist { GSList *walk;
|
||||||
|
if ($1->back) {
|
||||||
|
g_slist_prepend ($2, $1->back);
|
||||||
|
$1->back = NULL;
|
||||||
|
} else {
|
||||||
|
if (!((link_t *) $2->data)->src_name) {
|
||||||
|
((link_t *) $2->data)->src = $1->last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk = $2;
|
||||||
|
while (walk) {
|
||||||
|
link_t *link = (link_t *) walk->data;
|
||||||
|
walk = walk->next;
|
||||||
|
if (!link->sink_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
|
||||||
|
gst_parse_free_link (link);
|
||||||
|
} else if (!link->src_name && !link->src) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without source element");
|
||||||
|
gst_parse_free_link (link);
|
||||||
|
} else {
|
||||||
|
if (walk) {
|
||||||
|
((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, link);
|
||||||
|
} else {
|
||||||
|
$1->back = link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_slist_free ($2);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
graph: chain { $$ = (graph_t *) graph;
|
||||||
|
if ($1->front) {
|
||||||
|
if (!$1->front->src_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without source element");
|
||||||
|
gst_parse_free_link ($1->front);
|
||||||
|
} else {
|
||||||
|
$$->links = g_slist_prepend ($$->links, $1->front);
|
||||||
|
}
|
||||||
|
$1->front = NULL;
|
||||||
|
}
|
||||||
|
if ($1->back) {
|
||||||
|
if (!$1->back->sink_name) {
|
||||||
|
ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
|
||||||
|
gst_parse_free_link ($1->back);
|
||||||
|
} else {
|
||||||
|
$$->links = g_slist_prepend ($$->links, $1->back);
|
||||||
|
}
|
||||||
|
$1->back = NULL;
|
||||||
|
}
|
||||||
|
$$->chain = $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -136,45 +684,141 @@ static int yylex (void *lvalp) {
|
||||||
static int
|
static int
|
||||||
yyerror (const char *s)
|
yyerror (const char *s)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "error: %s\n", s);
|
/* FIXME: This should go into the GError somehow, but how? */
|
||||||
|
g_warning ("error: %s\n", s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _gst_parse_yy_scan_string (char*);
|
int _gst_parse_yy_scan_string (char*);
|
||||||
|
GstElement *
|
||||||
graph_t * _gst_parse_launch (const gchar *str, GError **error)
|
_gst_parse_launch (const gchar *str, GError **error)
|
||||||
{
|
{
|
||||||
graph_t *g = NULL;
|
graph_t g;
|
||||||
gchar *dstr;
|
gchar *dstr;
|
||||||
|
GSList *walk;
|
||||||
|
GstBin *bin = NULL;
|
||||||
|
GstElement *ret;
|
||||||
|
|
||||||
g_return_val_if_fail (str != NULL, NULL);
|
g_return_val_if_fail (str != NULL, NULL);
|
||||||
|
|
||||||
|
g.chain = NULL;
|
||||||
|
g.links = NULL;
|
||||||
|
g.error = error;
|
||||||
|
|
||||||
|
#ifdef __GST_PARSE_TRACE
|
||||||
|
GST_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled");
|
||||||
|
__strings = __chains = __links = 0;
|
||||||
|
#endif /* __GST_PARSE_TRACE */
|
||||||
|
|
||||||
dstr = g_strdup (str);
|
dstr = g_strdup (str);
|
||||||
_gst_parse_yy_scan_string (dstr);
|
_gst_parse_yy_scan_string (dstr);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
_gst_parse_yydebug = 1;
|
yydebug = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (yyparse (&g) != 0) {
|
if (yyparse (&g) != 0) {
|
||||||
g_set_error (error,
|
SET_ERROR (error, GST_PARSE_ERROR_SYNTAX, "Unrecoverable syntax error while parsing pipeline");
|
||||||
GST_PARSE_ERROR,
|
|
||||||
GST_PARSE_ERROR_SYNTAX,
|
goto error1;
|
||||||
"Invalid syntax");
|
|
||||||
g_free (dstr);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert (g != NULL);
|
|
||||||
|
|
||||||
g_free (dstr);
|
g_free (dstr);
|
||||||
|
|
||||||
/* if the toplevel only contains one bin, make that bin top-level */
|
GST_INFO (GST_CAT_PIPELINE, "got %u elements and %u links", g.chain ? g_slist_length (g.chain->elements) : 0, g_slist_length (g.links));
|
||||||
if (g->elements == NULL && g->bins && g->bins->next == NULL) {
|
|
||||||
g = (graph_t*)g->bins->data;
|
|
||||||
g_free (g->parent);
|
|
||||||
g->parent = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g;
|
if (!g.chain) {
|
||||||
|
ret = NULL;
|
||||||
|
} else if (!(((chain_t *) g.chain)->elements->next)) {
|
||||||
|
/* only one toplevel element */
|
||||||
|
ret = (GstElement *) ((chain_t *) g.chain)->elements->data;
|
||||||
|
g_slist_free (((chain_t *) g.chain)->elements);
|
||||||
|
if (GST_IS_BIN (ret))
|
||||||
|
bin = GST_BIN (ret);
|
||||||
|
} else {
|
||||||
|
/* put all elements in our bin */
|
||||||
|
bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
|
||||||
|
g_assert (bin);
|
||||||
|
walk = g.chain->elements;
|
||||||
|
while (walk) {
|
||||||
|
gst_bin_add (bin, GST_ELEMENT (walk->data));
|
||||||
|
walk = g_slist_next (walk);
|
||||||
|
}
|
||||||
|
g_slist_free (g.chain->elements);
|
||||||
|
ret = GST_ELEMENT (bin);
|
||||||
|
}
|
||||||
|
gst_parse_chain_free (g.chain);
|
||||||
|
|
||||||
|
/* remove links */
|
||||||
|
walk = g.links;
|
||||||
|
while (walk) {
|
||||||
|
link_t *l = (link_t *) walk->data;
|
||||||
|
GstElement *sink;
|
||||||
|
walk = g_slist_next (walk);
|
||||||
|
if (!l->src) {
|
||||||
|
if (l->src_name) {
|
||||||
|
if (bin) {
|
||||||
|
l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name);
|
||||||
|
} else {
|
||||||
|
l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!l->src) {
|
||||||
|
SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->src_name);
|
||||||
|
gst_parse_free_link (l);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!l->sink) {
|
||||||
|
if (l->sink_name) {
|
||||||
|
if (bin) {
|
||||||
|
l->sink = gst_bin_get_by_name_recurse_up (bin, l->sink_name);
|
||||||
|
} else {
|
||||||
|
l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!l->sink) {
|
||||||
|
SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->sink_name);
|
||||||
|
gst_parse_free_link (l);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sink = l->sink;
|
||||||
|
gst_parse_perform_link (l, &g);
|
||||||
|
}
|
||||||
|
g_slist_free (g.links);
|
||||||
|
|
||||||
|
out:
|
||||||
|
#ifdef __GST_PARSE_TRACE
|
||||||
|
GST_DEBUG (GST_CAT_PIPELINE, "TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
|
||||||
|
if (__strings || __chains || __links) {
|
||||||
|
g_warning ("TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
|
||||||
|
}
|
||||||
|
#endif /* __GST_PARSE_TRACE */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error1:
|
||||||
|
g_free (dstr);
|
||||||
|
|
||||||
|
if (g.chain) {
|
||||||
|
walk = g.chain->elements;
|
||||||
|
while (walk) {
|
||||||
|
gst_object_unref (GST_OBJECT (walk->data));
|
||||||
|
walk = walk->next;
|
||||||
|
}
|
||||||
|
g_slist_free (g.chain->elements);
|
||||||
|
}
|
||||||
|
gst_parse_chain_free (g.chain);
|
||||||
|
|
||||||
|
walk = g.links;
|
||||||
|
while (walk) {
|
||||||
|
gst_parse_free_link ((link_t *) walk->data);
|
||||||
|
walk = walk->next;
|
||||||
|
}
|
||||||
|
g_slist_free (g.links);
|
||||||
|
|
||||||
|
g_assert (*error);
|
||||||
|
ret = NULL;
|
||||||
|
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,272 +3,98 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <grammar.tab.h>
|
#include "../gstinfo.h"
|
||||||
|
#include "grammar.tab.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef G_HAVE_ISO_VARARGS
|
#ifdef G_HAVE_ISO_VARARGS
|
||||||
|
# ifdef GST_DEBUG_ENABLED
|
||||||
#ifdef DEBUG
|
# define PRINT(...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "__VA_ARGS__)
|
||||||
# define PRINT(...) printf(__VAR_ARGS__)
|
|
||||||
#else
|
|
||||||
#define PRINT(...)
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#elif defined(G_HAVE_GNUC_VARARGS)
|
#elif defined(G_HAVE_GNUC_VARARGS)
|
||||||
|
# ifdef GST_DEBUG_ENABLED
|
||||||
#ifdef DEBUG
|
# define PRINT(...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "##args)
|
||||||
# define PRINT(a...) printf(##a)
|
# endif
|
||||||
#else
|
#else
|
||||||
#define PRINT(a...)
|
# ifdef GST_DEBUG_ENABLED
|
||||||
|
# define PRINT(a...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "##a)
|
||||||
# endif
|
# endif
|
||||||
|
#endif // G_HAVE_ISO_VARARGS
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CHAR(x) PRINT ("char: %c\n", *yytext); return *yytext;
|
|
||||||
|
|
||||||
#define YY_DECL int _gst_parse_yylex (YYSTYPE *lvalp)
|
#define YY_DECL int _gst_parse_yylex (YYSTYPE *lvalp)
|
||||||
#define YY_NO_UNPUT
|
#define YY_NO_UNPUT
|
||||||
%}
|
%}
|
||||||
|
|
||||||
_integer [-+]?[[:digit:]]+
|
_operators [(){}.:!,=]
|
||||||
_double [-+]?[[:digit:]]+"."*[[:digit:]]*
|
_identifier [[:alpha:]][[:alnum:]\-_%]*
|
||||||
_number {_integer}|{_double}
|
|
||||||
_boolean "true"|"false"|"TRUE"|"FALSE"
|
_char ("\\".)|([^[:space:]])
|
||||||
_identifier [[:alpha:]][[:alnum:]\-_%:]*
|
|
||||||
_char ([^[:space:]])|("\\".)
|
|
||||||
_string {_char}+|("\""([^\"]|"\\\"")*"\"")
|
_string {_char}+|("\""([^\"]|"\\\"")*"\"")
|
||||||
|
|
||||||
_comma [[:space:]]*","[[:space:]]*
|
_comma [[:space:]]*","[[:space:]]*
|
||||||
_assign [[:space:]]*"="[[:space:]]*
|
_assign [[:space:]]*"="[[:space:]]*
|
||||||
_caps_type_string "fourcc"|"string"
|
|
||||||
_caps_type_double "float"
|
/* we must do this here, because nearly everything matches a {_string} */
|
||||||
_caps_string {_string}{_assign}{_caps_type_string}[[:space:]]+{_string}
|
_assignment {_identifier}{_assign}{_string}
|
||||||
_caps_int {_string}{_assign}"int"[[:space:]]+{_integer}
|
|
||||||
_caps_double {_string}{_assign}{_caps_type_double}[[:space:]]+{_double}
|
/* get pad/element references and stuff with dots right */
|
||||||
_caps_boolean {_string}{_assign}"boolean"[[:space:]]+{_boolean}
|
_padref "."{_identifier}
|
||||||
_caps_pair {_caps_string}|{_caps_int}|{_caps_double}|{_caps_boolean}
|
_ref {_identifier}"."{_identifier}?
|
||||||
_caps {_string}({_comma}{_caps_pair})*
|
_binref {_identifier}[[:space:]]*"."[[:space:]]*"("
|
||||||
_llink ({_identifier}\.)?{_identifier}!
|
|
||||||
_rlink !({_identifier}\.)?{_identifier}
|
|
||||||
_blink ({_identifier}\.)?{_identifier}!({_identifier}\.)?{_identifier}
|
|
||||||
_flink ({_identifier}\.)?{_identifier}!{_caps}!({_identifier}\.)?{_identifier}
|
|
||||||
|
|
||||||
%x value
|
%x value
|
||||||
%option noyywrap
|
%option noyywrap
|
||||||
%%
|
%%
|
||||||
|
|
||||||
<value>{
|
{_assignment} {
|
||||||
{_integer} {
|
/* "=" */
|
||||||
PRINT ("An integer: %s (%d)\n", yytext,
|
PRINT ("ASSIGNMENT: %s\n", yytext);
|
||||||
atoi (yytext));
|
lvalp->s = gst_parse_strdup (yytext);
|
||||||
lvalp->v = g_new0 (GValue, 1);
|
|
||||||
g_value_init (lvalp->v, G_TYPE_INT);
|
|
||||||
g_value_set_int (lvalp->v, atoi (yytext));
|
|
||||||
BEGIN (INITIAL);
|
BEGIN (INITIAL);
|
||||||
return VALUE;
|
return ASSIGNMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
{_double} {
|
{_padref} {
|
||||||
PRINT ("A double: %s (%g)\n", yytext, atof (yytext));
|
|
||||||
lvalp->v = g_new0 (GValue, 1);
|
|
||||||
g_value_init (lvalp->v, G_TYPE_DOUBLE);
|
|
||||||
g_value_set_double (lvalp->v, atof (yytext));
|
|
||||||
BEGIN (INITIAL);
|
|
||||||
return VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
{_boolean} {
|
|
||||||
PRINT ("A boolean: %s (%d)\n", yytext, tolower (*yytext) == 't' ? 1 : 0);
|
|
||||||
lvalp->v = g_new0 (GValue, 1);
|
|
||||||
g_value_init (lvalp->v, G_TYPE_BOOLEAN);
|
|
||||||
g_value_set_boolean (lvalp->v, tolower (*yytext) == 't' ? TRUE : FALSE);
|
|
||||||
BEGIN (INITIAL);
|
|
||||||
return VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
{_string} {
|
|
||||||
if (*yytext == '"') {
|
|
||||||
yytext++;
|
yytext++;
|
||||||
*(yytext + strlen (yytext) - 1) = '\0';
|
PRINT ("PADREF: %s\n", yytext);
|
||||||
}
|
lvalp->s = gst_parse_strdup (yytext);
|
||||||
_gst_parse_unescape (yytext);
|
|
||||||
PRINT ("A string: \"%s\"\n", yytext);
|
|
||||||
lvalp->v = g_new0 (GValue, 1);
|
|
||||||
g_value_init (lvalp->v, G_TYPE_STRING);
|
|
||||||
g_value_set_string (lvalp->v, yytext);
|
|
||||||
BEGIN (INITIAL);
|
BEGIN (INITIAL);
|
||||||
return VALUE;
|
return PADREF;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[:space:]]+ { /* PRINT ("space: [%s]\n", yytext); */ }
|
{_ref} {
|
||||||
|
PRINT ("REF: %s\n", yytext);
|
||||||
|
lvalp->s = gst_parse_strdup (yytext);
|
||||||
|
BEGIN (INITIAL);
|
||||||
|
return REF;
|
||||||
}
|
}
|
||||||
|
|
||||||
{_llink} {
|
{_binref} {
|
||||||
gchar *d1, *q;
|
gchar *pos = yytext;
|
||||||
lvalp->c = g_new0 (link_t, 1);
|
while (!g_ascii_isspace (*pos) && (*pos != '.')) pos++;
|
||||||
PRINT ("An link: %s\n", yytext);
|
*pos = '\0';
|
||||||
q = strchr (yytext, '!');
|
PRINT ("BINREF: %s\n", yytext);
|
||||||
d1 = strchr (yytext, '.');
|
lvalp->s = gst_parse_strdup (yytext);
|
||||||
if (d1) {
|
BEGIN (INITIAL);
|
||||||
lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
|
return BINREF;
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q - d1 - 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q - yytext));
|
|
||||||
}
|
|
||||||
|
|
||||||
return LINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
{_rlink} {
|
|
||||||
gchar *d2;
|
|
||||||
lvalp->c = g_new0 (link_t, 1);
|
|
||||||
PRINT ("An rlink: %s\n", yytext);
|
|
||||||
d2 = strchr (yytext, '.');
|
|
||||||
if (d2) {
|
|
||||||
lvalp->c->sink_name = g_strndup (yytext + 1, d2 - yytext - 1);
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (yytext + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return LINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
{_blink} {
|
|
||||||
gchar *d1, *d2, *q;
|
|
||||||
lvalp->c = g_new0 (link_t, 1);
|
|
||||||
PRINT ("A blink: %s\n", yytext);
|
|
||||||
q = strchr (yytext, '!');
|
|
||||||
d1 = strchr (yytext, '.');
|
|
||||||
d2 = strchr (q, '.');
|
|
||||||
if (d1 && d1 < q) {
|
|
||||||
lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
|
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q - d1 - 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q - yytext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d2) {
|
|
||||||
lvalp->c->sink_name = g_strndup (q + 1, d2 - q - 1);
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (q + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return BLINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
{_flink} {
|
|
||||||
gchar *d1, *d2, *q1, *q2, *a1, *m1;
|
|
||||||
gchar *mime;
|
|
||||||
GstProps *props;
|
|
||||||
|
|
||||||
lvalp->c = g_new0 (link_t, 1);
|
|
||||||
PRINT ("An flink: %s\n", yytext);
|
|
||||||
q1 = strchr (yytext, '!');
|
|
||||||
d1 = strchr (yytext, '.');
|
|
||||||
q2 = strchr (q1+1, '!');
|
|
||||||
d2 = strchr (q2, '.');
|
|
||||||
if (d1 && d1 < q1) {
|
|
||||||
lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
|
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q1 - d1 - 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q1 - yytext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d2) {
|
|
||||||
lvalp->c->sink_name = g_strndup (q2 + 1, d2 - q2 - 1);
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
|
|
||||||
} else {
|
|
||||||
lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (q2 + 1));
|
|
||||||
}
|
|
||||||
/* parse mime type */
|
|
||||||
m1 = strchr (q1 + 1, ',');
|
|
||||||
mime = g_strndup (q1 + 1, m1 - q1 - 1);
|
|
||||||
|
|
||||||
props = gst_props_empty_new ();
|
|
||||||
|
|
||||||
a1 = strchr (m1 + 1, ',');
|
|
||||||
if (a1 == NULL)
|
|
||||||
a1 = q2;
|
|
||||||
|
|
||||||
while (a1 && a1 <= q2) {
|
|
||||||
gchar *key, *t, *v;
|
|
||||||
gchar *k1, *k2;
|
|
||||||
GstPropsEntry *entry = NULL;
|
|
||||||
|
|
||||||
k1 = strchr (m1, '=');
|
|
||||||
key = g_strstrip (g_strndup (m1 + 1, k1 - m1 -1));
|
|
||||||
|
|
||||||
k1++;
|
|
||||||
|
|
||||||
while (g_ascii_isspace (*k1)) k1++;
|
|
||||||
|
|
||||||
k2 = strchr (k1, ' ');
|
|
||||||
t = g_strstrip (g_strndup (k1, k2 - k1));
|
|
||||||
|
|
||||||
while (g_ascii_isspace (*k2)) k2++;
|
|
||||||
|
|
||||||
v = g_strstrip (g_strndup (k2, a1 - k2));
|
|
||||||
|
|
||||||
if (!strcmp (t, "string")) {
|
|
||||||
entry = gst_props_entry_new (key, GST_PROPS_STRING (v));
|
|
||||||
}
|
|
||||||
else if (!strcmp (t, "fourcc")) {
|
|
||||||
entry = gst_props_entry_new (key, GST_PROPS_FOURCC (GST_STR_FOURCC(v)));
|
|
||||||
}
|
|
||||||
else if (!strcmp (t, "float")) {
|
|
||||||
gfloat f;
|
|
||||||
sscanf (v, "%f", &f);
|
|
||||||
entry = gst_props_entry_new (key, GST_PROPS_FLOAT (f));
|
|
||||||
}
|
|
||||||
else if (!strcmp (t, "int")) {
|
|
||||||
gint i;
|
|
||||||
sscanf (v, "%d", &i);
|
|
||||||
entry = gst_props_entry_new (key, GST_PROPS_INT (i));
|
|
||||||
}
|
|
||||||
else if (!strcmp (t, "boolean")) {
|
|
||||||
gboolean b;
|
|
||||||
b = (!strcmp (v, "true") || ! strcmp (v, "TRUE"));
|
|
||||||
entry = gst_props_entry_new (key, GST_PROPS_BOOLEAN (b));
|
|
||||||
}
|
|
||||||
gst_props_add_entry (props, entry);
|
|
||||||
|
|
||||||
m1 = a1;
|
|
||||||
if (a1 < q2) {
|
|
||||||
a1 = strchr (m1 + 1, ',');
|
|
||||||
if (a1 == NULL)
|
|
||||||
a1 = q2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lvalp->c->caps = gst_caps_new ("parse_caps", mime, props);
|
|
||||||
|
|
||||||
return FLINK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{_identifier} {
|
{_identifier} {
|
||||||
PRINT ("An identifier: %s\n", yytext);
|
PRINT ("IDENTIFIER: %s\n", yytext);
|
||||||
lvalp->s = g_strdup (yytext);
|
lvalp->s = gst_parse_strdup (yytext);
|
||||||
|
BEGIN (INITIAL);
|
||||||
return IDENTIFIER;
|
return IDENTIFIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
"=" { BEGIN (value); CHAR ('='); }
|
{_operators} { PRINT ("OPERATOR: [%s]\n", yytext); return *yytext; }
|
||||||
"@" { CHAR ('@'); }
|
|
||||||
"." { CHAR ('.'); }
|
|
||||||
"," { CHAR (','); }
|
|
||||||
"{" { CHAR ('{'); }
|
|
||||||
"}" { CHAR ('}'); }
|
|
||||||
"[" { CHAR ('['); }
|
|
||||||
"]" { CHAR (']'); }
|
|
||||||
"(" { CHAR ('('); }
|
|
||||||
")" { CHAR (')'); }
|
|
||||||
"!" { CHAR ('!'); }
|
|
||||||
"+" { CHAR ('+'); }
|
|
||||||
|
|
||||||
[[:space:]]+ { PRINT ("space: [%s]\n", yytext); }
|
[[:space:]]+ { PRINT ("SPACE: [%s]\n", yytext); }
|
||||||
|
|
||||||
. {
|
. {
|
||||||
printf ("unknown: %s\n", yytext);
|
printf ("???: %s\n", yytext);
|
||||||
return *yytext;
|
return *yytext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,69 @@
|
||||||
|
#ifndef __GST_PARSE_TYPES_H__
|
||||||
|
#define __GST_PARSE_TYPES_H__
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include "../gstelement.h"
|
#include "../gstelement.h"
|
||||||
|
|
||||||
typedef struct {
|
#ifdef HAVE_CONFIG_H
|
||||||
gchar *type;
|
# include "config.h"
|
||||||
gint index;
|
#endif
|
||||||
GList *property_values;
|
|
||||||
GstElement *element;
|
|
||||||
} element_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gchar *name;
|
GstElement *src;
|
||||||
GValue *value;
|
GstElement *sink;
|
||||||
} property_t;
|
gchar *src_name;
|
||||||
|
gchar *sink_name;
|
||||||
typedef struct {
|
GSList *src_pads;
|
||||||
/* if the names are present, upon link we'll search out the pads of the
|
GSList *sink_pads;
|
||||||
proper name and use those. otherwise, we'll search for elements of
|
|
||||||
src_index and sink_index. */
|
|
||||||
char *src_name;
|
|
||||||
char *sink_name;
|
|
||||||
int src_index;
|
|
||||||
int sink_index;
|
|
||||||
GList *src_pads;
|
|
||||||
GList *sink_pads;
|
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
} link_t;
|
} link_t;
|
||||||
|
|
||||||
typedef struct _graph_t graph_t;
|
typedef struct {
|
||||||
|
GSList *elements;
|
||||||
|
GstElement *first;
|
||||||
|
GstElement *last;
|
||||||
|
link_t *front;
|
||||||
|
link_t *back;
|
||||||
|
} chain_t;
|
||||||
|
|
||||||
|
typedef struct _graph_t graph_t;
|
||||||
struct _graph_t {
|
struct _graph_t {
|
||||||
element_t *first;
|
chain_t *chain; /* links are supposed to be done now */
|
||||||
element_t *current;
|
GSList *links;
|
||||||
graph_t *parent;
|
GError **error;
|
||||||
gchar *current_bin_type;
|
|
||||||
GList *elements;
|
|
||||||
GList *links;
|
|
||||||
GList *links_pending;
|
|
||||||
GList *bins;
|
|
||||||
GstElement *bin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
graph_t * _gst_parse_launch (const gchar *str, GError **error);
|
|
||||||
|
|
||||||
gchar *_gst_parse_escape (const gchar *str);
|
/**
|
||||||
void _gst_parse_unescape (gchar *str);
|
* Memory checking. Should probably be done with gsttrace stuff, but that
|
||||||
|
* doesn't really work.
|
||||||
|
* This is not safe from reentrance issues, but that doesn't matter as long as
|
||||||
|
* we lock a mutex before parsing anyway.
|
||||||
|
*/
|
||||||
|
#ifdef GST_DEBUG_ENABLED
|
||||||
|
# define __GST_PARSE_TRACE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GST_PARSE_TRACE
|
||||||
|
gchar *__gst_parse_strdup (gchar *org);
|
||||||
|
void __gst_parse_strfree (gchar *str);
|
||||||
|
link_t *__gst_parse_link_new ();
|
||||||
|
void __gst_parse_link_free (link_t *data);
|
||||||
|
chain_t *__gst_parse_chain_new ();
|
||||||
|
void __gst_parse_chain_free (chain_t *data);
|
||||||
|
# define gst_parse_strdup __gst_parse_strdup
|
||||||
|
# define gst_parse_strfree __gst_parse_strfree
|
||||||
|
# define gst_parse_link_new __gst_parse_link_new
|
||||||
|
# define gst_parse_link_free __gst_parse_link_free
|
||||||
|
# define gst_parse_chain_new __gst_parse_chain_new
|
||||||
|
# define gst_parse_chain_free __gst_parse_chain_free
|
||||||
|
#else /* __GST_PARSE_TRACE */
|
||||||
|
# define gst_parse_strdup g_strdup
|
||||||
|
# define gst_parse_strfree g_free
|
||||||
|
# define gst_parse_link_new() g_new0 (link_t, 1)
|
||||||
|
# define gst_parse_link_free g_free
|
||||||
|
# define gst_parse_chain_new() g_new0 (chain_t, 1)
|
||||||
|
# define gst_parse_chain_free g_free
|
||||||
|
#endif /* __GST_PARSE_TRACE */
|
||||||
|
|
||||||
|
#endif /* __GST_PARSE_TYPES_H__ */
|
||||||
|
|
Loading…
Reference in a new issue