mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 10:41:04 +00:00
utils: Add util functions to parse simple mathematical expressions
And make use of it to set the start of a seek
This commit is contained in:
parent
75356d4bbe
commit
a89f32725b
4 changed files with 549 additions and 4 deletions
|
@ -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 = \
|
||||
|
|
|
@ -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
|
||||
|
|
467
validate/gst/validate/gst-validate-utils.c
Normal file
467
validate/gst/validate/gst-validate-utils.c
Normal file
|
@ -0,0 +1,467 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
|
||||
*
|
||||
* 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<math.h>
|
||||
#include<ctype.h>
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
#include<stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
37
validate/gst/validate/gst-validate-utils.h
Normal file
37
validate/gst/validate/gst-validate-utils.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
|
||||
*
|
||||
* 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<setjmp.h>
|
||||
#include<stdlib.h>
|
||||
#include<glib.h>
|
||||
|
||||
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
|
Loading…
Reference in a new issue