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:
Benjamin Otte 2003-04-08 21:59:44 +00:00
parent 64f71f6961
commit 3a897e2f30
5 changed files with 913 additions and 849 deletions

View file

@ -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);
} }

View file

@ -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 */

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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__ */