diff --git a/validate/gst/validate/Makefile.am b/validate/gst/validate/Makefile.am index cb49af17a9..269d626e3b 100644 --- a/validate/gst/validate/Makefile.am +++ b/validate/gst/validate/Makefile.am @@ -9,6 +9,7 @@ libgstvalidate_@GST_API_VERSION@_la_SOURCES = \ gst-validate-report.c \ gst-validate-scenario.c \ gst-validate-override.c \ + gst-validate-utils.c \ gst-validate-override-registry.c \ gst-validate-media-info.c \ validate.c @@ -28,6 +29,7 @@ noinst_HEADERS = \ gst-validate-report.h \ gst-validate-runner.h \ gst-validate-scenario.h \ + gst-validate-utils.h \ gst-validate-media-info.h lib_LTLIBRARIES = \ diff --git a/validate/gst/validate/gst-validate-scenario.c b/validate/gst/validate/gst-validate-scenario.c index 51279d3542..d556f2847c 100644 --- a/validate/gst/validate/gst-validate-scenario.c +++ b/validate/gst/validate/gst-validate-scenario.c @@ -33,6 +33,7 @@ #include "gst-validate-scenario.h" #include "gst-validate-reporter.h" #include "gst-validate-report.h" +#include "gst-validate-utils.h" #define GST_VALIDATE_SCENARIO_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioPrivate)) @@ -81,7 +82,6 @@ struct _GstValidateScenarioPrivate }; -/* Some helper method that are missing iin Json itscenario */ static guint get_flags_from_string (GType type, const gchar * str_flags) { @@ -124,11 +124,43 @@ _free_scenario_action (GstValidateAction * act) g_slice_free (GstValidateAction, act); } +static gboolean +_set_variable_func (const gchar *name, double *value, gpointer user_data) +{ + GstValidateScenario *scenario = GST_VALIDATE_SCENARIO (user_data); + + if (!g_strcmp0 (name, "duration")) { + gint64 duration; + + if (!gst_element_query_duration (scenario->priv->pipeline, + GST_FORMAT_TIME, &duration)) { + GST_WARNING_OBJECT (scenario, "Could not query duration"); + return FALSE; + } + *value = duration / GST_SECOND; + + return TRUE; + } else if (!g_strcmp0 (name, "position")) { + gint64 position; + + if (!gst_element_query_position (scenario->priv->pipeline, + GST_FORMAT_TIME, &position)) { + GST_WARNING_OBJECT (scenario, "Could not query position"); + return FALSE; + } + *value = ((double) position / GST_SECOND); + + return TRUE; + } + + return FALSE; +} + static gboolean _execute_seek (GstValidateScenario * scenario, GstValidateAction * action) { GstValidateScenarioPrivate *priv = scenario->priv; - const char *str_format, *str_flags, *str_start_type, *str_stop_type; + const char *str_format, *str_flags, *str_start_type, *str_stop_type, *str_start; gboolean ret = TRUE; gdouble rate = 1.0, dstart, dstop; @@ -141,9 +173,16 @@ _execute_seek (GstValidateScenario * scenario, GstValidateAction * action) GstEvent *seek; if (!gst_structure_get_double (action->structure, "start", &dstart)) { - GST_WARNING_OBJECT (scenario, "Could not find start for a seek, FAILED"); - return FALSE; + gchar *error = NULL; + + if (!(str_start = gst_structure_get_string(action->structure, "start"))) { + GST_WARNING_OBJECT (scenario, "Could not find start for a seek, FAILED"); + return FALSE; + } + dstart = parse_expression (str_start, _set_variable_func, + scenario, &error); } + if (dstart == -1.0) start = GST_CLOCK_TIME_NONE; else diff --git a/validate/gst/validate/gst-validate-utils.c b/validate/gst/validate/gst-validate-utils.c new file mode 100644 index 0000000000..d25cd6b07c --- /dev/null +++ b/validate/gst/validate/gst-validate-utils.c @@ -0,0 +1,467 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-utils.c - Some utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include "gst-validate-utils.h" + +#define PARSER_BOOLEAN_EQUALITY_THRESHOLD (1e-10) +#define PARSER_MAX_TOKEN_SIZE 256 +#define PARSER_MAX_ARGUMENT_COUNT 10 + +typedef struct +{ + const gchar *str; + gint len; + gint pos; + jmp_buf err_jmp_buf; + const gchar *error; + void *user_data; + ParseVariableFunc variable_func; +} MathParser; + +static gdouble _read_power (MathParser * parser); + +static void +_error (MathParser * parser, const gchar * err) +{ + parser->error = err; + longjmp (parser->err_jmp_buf, 1); +} + +static gchar +_peek (MathParser * parser) +{ + if (parser->pos < parser->len) + return parser->str[parser->pos]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gchar +_peek_n (MathParser * parser, gint n) +{ + if (parser->pos + n < parser->len) + return parser->str[parser->pos + n]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gchar +_next (MathParser * parser) +{ + if (parser->pos < parser->len) + return parser->str[parser->pos++]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gdouble +_read_double (MathParser * parser) +{ + gchar c, token[PARSER_MAX_TOKEN_SIZE]; + gint pos = 0; + gdouble val = 0.0; + + c = _peek (parser); + if (c == '+' || c == '-') + token[pos++] = _next (parser); + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == '.') + token[pos++] = _next (parser); + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == 'e' || c == 'E') { + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == '+' || c == '-') { + token[pos++] = _next (parser); + } + } + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + token[pos] = '\0'; + + if (pos == 0 || sscanf (token, "%lf", &val) != 1) + _error (parser, "Failed to read real number"); + + return val; +} + +static gdouble +_read_term (MathParser * parser) +{ + gdouble v0; + gchar c; + + v0 = _read_power (parser); + c = _peek (parser); + + while (c == '*' || c == '/') { + _next (parser); + if (c == '*') { + v0 *= _read_power (parser); + } else if (c == '/') { + v0 /= _read_power (parser); + } + c = _peek (parser); + } + return v0; +} + +static gdouble +_read_expr (MathParser * parser) +{ + gdouble v0 = 0.0; + gchar c; + + c = _peek (parser); + if (c == '+' || c == '-') { + _next (parser); + if (c == '+') + v0 += _read_term (parser); + else if (c == '-') + v0 -= _read_term (parser); + } else { + v0 = _read_term (parser); + } + + c = _peek (parser); + while (c == '+' || c == '-') { + _next (parser); + if (c == '+') { + v0 += _read_term (parser); + } else if (c == '-') { + v0 -= _read_term (parser); + } + + c = _peek (parser); + } + + return v0; +} + +static gdouble +_read_boolean_comparison (MathParser * parser) +{ + gchar c, oper[] = { '\0', '\0', '\0' }; + gdouble v0, v1; + + + v0 = _read_expr (parser); + c = _peek (parser); + if (c == '>' || c == '<') { + oper[0] = _next (parser); + c = _peek (parser); + if (c == '=') + oper[1] = _next (parser); + + + v1 = _read_expr (parser); + + if (g_strcmp0 (oper, "<") == 0) { + v0 = (v0 < v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, ">") == 0) { + v0 = (v0 > v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, "<=") == 0) { + v0 = (v0 <= v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, ">=") == 0) { + v0 = (v0 >= v1) ? 1.0 : 0.0; + } else { + _error (parser, "Unknown operation!"); + } + } + return v0; +} + +static gdouble +_read_boolean_equality (MathParser * parser) +{ + gchar c, oper[] = { '\0', '\0', '\0' }; + gdouble v0, v1; + + v0 = _read_boolean_comparison (parser); + c = _peek (parser); + if (c == '=' || c == '!') { + if (c == '!') { + if (_peek_n (parser, 1) == '=') { + oper[0] = _next (parser); + oper[1] = _next (parser); + } else { + return v0; + } + } else { + oper[0] = _next (parser); + c = _peek (parser); + if (c != '=') + _error (parser, "Expected a '=' for boolean '==' operator!"); + oper[1] = _next (parser); + } + v1 = _read_boolean_comparison (parser); + if (g_strcmp0 (oper, "==") == 0) { + v0 = (fabs (v0 - v1) < PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, "!=") == 0) { + v0 = (fabs (v0 - v1) > PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + } else { + _error (parser, "Unknown operation!"); + } + } + return v0; +} + +static gdouble +_read_boolean_and (MathParser * parser) +{ + gchar c; + gdouble v0, v1; + + v0 = _read_boolean_equality (parser); + + c = _peek (parser); + while (c == '&') { + _next (parser); + + c = _peek (parser); + if (c != '&') + _error (parser, "Expected '&' to follow '&' in logical and operation!"); + _next (parser); + + v1 = _read_boolean_equality (parser); + v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD + && fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + + c = _peek (parser); + } + + return v0; +} + +static gdouble +_read_boolean_or (MathParser * parser) +{ + gchar c; + gdouble v0, v1; + + v0 = _read_boolean_and (parser); + + c = _peek (parser); + while (c == '|') { + _next (parser); + c = _peek (parser); + if (c != '|') + _error (parser, "Expected '|' to follow '|' in logical or operation!"); + _next (parser); + v1 = _read_boolean_and (parser); + v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD + || fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + c = _peek (parser); + } + + return v0; +} + +static gboolean +_init (MathParser * parser, const gchar * str, + ParseVariableFunc variable_func, void *user_data) +{ + parser->str = str; + parser->len = strlen (str) + 1; + parser->pos = 0; + parser->error = NULL; + parser->user_data = user_data; + parser->variable_func = variable_func; + + return TRUE; +} + +static gdouble +_parse (MathParser * parser) +{ + gdouble result = 0.0; + + if (!setjmp (parser->err_jmp_buf)) { + result = _read_expr (parser); + if (parser->pos < parser->len - 1) { + _error (parser, + "Failed to reach end of input expression, likely malformed input"); + } else + return result; + } else { + return sqrt (-1.0); + } + return sqrt (-1.0); +} + +static gdouble +_read_argument (MathParser * parser) +{ + gchar c; + gdouble val; + + val = _read_expr (parser); + c = _peek (parser); + if (c == ',') + _next (parser); + + return val; +} + +static gdouble +_read_builtin (MathParser * parser) +{ + gdouble v0 = 0.0, v1 = 0.0; + gchar c, token[PARSER_MAX_TOKEN_SIZE]; + gint pos = 0; + + c = _peek (parser); + if (isalpha (c) || c == '_') { + while (isalpha (c) || isdigit (c) || c == '_') { + token[pos++] = _next (parser); + c = _peek (parser); + } + token[pos] = '\0'; + + if (_peek (parser) == '(') { + _next (parser); + if (g_strcmp0 (token, "min") == 0) { + v0 = _read_argument (parser); + v1 = _read_argument (parser); + v0 = MIN (v0, v1); + } else if (g_strcmp0 (token, "max") == 0) { + v0 = _read_argument (parser); + v1 = _read_argument (parser); + v0 = MAX (v0, v1); + } else { + _error (parser, "Tried to call unknown built-in function!"); + } + + if (_next (parser) != ')') + _error (parser, "Expected ')' in built-in call!"); + } else { + if (parser->variable_func != NULL + && parser->variable_func (token, &v1, parser->user_data)) { + v0 = v1; + } else { + _error (parser, "Could not look up value for variable %s!"); + } + } + } else { + v0 = _read_double (parser); + } + + return v0; +} + +static gdouble +_read_parenthesis (MathParser * parser) +{ + gdouble val; + + if (_peek (parser) == '(') { + _next (parser); + val = _read_boolean_or (parser); + if (_peek (parser) != ')') + _error (parser, "Expected ')'!"); + _next (parser); + } else { + val = _read_builtin (parser); + } + + return val; +} + +static gdouble +_read_unary (MathParser * parser) +{ + gchar c; + gdouble v0; + c = _peek (parser); + if (c == '!') { + _error (parser, "Expected '+' or '-' for unary expression, got '!'"); + } else if (c == '-') { + _next (parser); + v0 = -_read_parenthesis (parser); + } else if (c == '+') { + _next (parser); + v0 = _read_parenthesis (parser); + } else { + v0 = _read_parenthesis (parser); + } + return v0; +} + +static gdouble +_read_power (MathParser * parser) +{ + gdouble v0, v1 = 1.0, s = 1.0; + + v0 = _read_unary (parser); + + while (_peek (parser) == '^') { + _next (parser); + if (_peek (parser) == '-') { + _next (parser); + s = -1.0; + } + v1 = s * _read_power (parser); + v0 = pow (v0, v1); + } + + return v0; +} + +gdouble +parse_expression (const gchar * expr, ParseVariableFunc variable_func, + gpointer user_data, gchar ** error) +{ + gdouble val; + MathParser parser; + gchar **spl = g_strsplit (expr, " ", -1); + gchar *expr_nospace = g_strjoinv ("", spl); + + _init (&parser, expr_nospace, variable_func, user_data); + val = _parse (&parser); + g_strfreev (spl); + g_free (expr_nospace); + + if (error) { + if (parser.error) + *error = g_strdup (parser.error); + else + *error = NULL; + } + return val; +} diff --git a/validate/gst/validate/gst-validate-utils.h b/validate/gst/validate/gst-validate-utils.h new file mode 100644 index 0000000000..510a211108 --- /dev/null +++ b/validate/gst/validate/gst-validate-utils.h @@ -0,0 +1,37 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-utils.h - Some utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef EXPRESSION_PARSER_H +#define EXPRESSION_PARSER_H + +#include +#include +#include + +typedef int (*ParseVariableFunc) (const gchar *name, + double *value, gpointer user_data); + +gdouble parse_expression (const gchar *expr, + ParseVariableFunc variable_func, + gpointer user_data, + gchar **error); +#endif