mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-02 20:42:29 +01:00
The ffmpeg submodule held prebuilt libraries for every platform next to the sources (about 1.5 GB checked out), and nothing uses those any more. Only the needed sources are copied in, from the submodule's commit, along with every header and template they include. The path is new, so checking out commits on either side of this works. It's also cut down to what PPSSPP uses: the H.264, AAC, MP3, ATRAC3 and ATRAC3+ decoders, the H.264 parser, the demuxers as before, and the FFV1 encoder, AVI muxer and file protocol for AVI dumping. config.h takes its CONFIG_ values from FFmpeg 3.0.2's own configure run with just those enabled. The raw H.264 demuxer stays even though nothing opens raw H.264: probing PSMF data uses it to identify the video stream's codec, which otherwise comes out as none. On top of that: - The files nothing links (configure would build them, but they're never pulled out of the static library, going by the GCC link map) are left out: mostly libavutil's crypto and hashing and the alternative DCT/IDCT/FFT versions. - libswresample isn't built, now that nothing uses it. - The H.264 decoder only does 8-bit video, which is all the PSP has. The SPS parser rejects any other bit depth, and the DSP files no longer build their 9- to 16-bit variants, which the linker kept because the init functions pick one by bit depth at run time. That's 160 files to compile instead of 277, and 391 of FFmpeg's vendored, about 5.8 MB. The README in ext/ffmpeg has what was done, the PPSSPP patches in the copy, how to add a component, and the tradeoffs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
892 lines
28 KiB
C
892 lines
28 KiB
C
/*
|
|
* Copyright (c) 2002-2006 Michael Niedermayer <michaelni@gmx.at>
|
|
* Copyright (c) 2006 Oded Shimon <ods15@ods15.dyndns.org>
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* simple arithmetic expression evaluator.
|
|
*
|
|
* see http://joe.hotchkiss.com/programming/eval/eval.html
|
|
*/
|
|
|
|
#include <float.h>
|
|
#include "attributes.h"
|
|
#include "avutil.h"
|
|
#include "common.h"
|
|
#include "eval.h"
|
|
#include "internal.h"
|
|
#include "log.h"
|
|
#include "mathematics.h"
|
|
#include "time.h"
|
|
#include "avstring.h"
|
|
#include "timer.h"
|
|
|
|
typedef struct Parser {
|
|
const AVClass *class;
|
|
int stack_index;
|
|
char *s;
|
|
const double *const_values;
|
|
const char * const *const_names; // NULL terminated
|
|
double (* const *funcs1)(void *, double a); // NULL terminated
|
|
const char * const *func1_names; // NULL terminated
|
|
double (* const *funcs2)(void *, double a, double b); // NULL terminated
|
|
const char * const *func2_names; // NULL terminated
|
|
void *opaque;
|
|
int log_offset;
|
|
void *log_ctx;
|
|
#define VARS 10
|
|
double *var;
|
|
} Parser;
|
|
|
|
static const AVClass eval_class = { "Eval", av_default_item_name, NULL, LIBAVUTIL_VERSION_INT, offsetof(Parser,log_offset), offsetof(Parser,log_ctx) };
|
|
|
|
static const struct {
|
|
double bin_val;
|
|
double dec_val;
|
|
int8_t exp;
|
|
} si_prefixes['z' - 'E' + 1] = {
|
|
['y'-'E']= { 8.271806125530276749e-25, 1e-24, -24 },
|
|
['z'-'E']= { 8.4703294725430034e-22, 1e-21, -21 },
|
|
['a'-'E']= { 8.6736173798840355e-19, 1e-18, -18 },
|
|
['f'-'E']= { 8.8817841970012523e-16, 1e-15, -15 },
|
|
['p'-'E']= { 9.0949470177292824e-13, 1e-12, -12 },
|
|
['n'-'E']= { 9.3132257461547852e-10, 1e-9, -9 },
|
|
['u'-'E']= { 9.5367431640625e-7, 1e-6, -6 },
|
|
['m'-'E']= { 9.765625e-4, 1e-3, -3 },
|
|
['c'-'E']= { 9.8431332023036951e-3, 1e-2, -2 },
|
|
['d'-'E']= { 9.921256574801246e-2, 1e-1, -1 },
|
|
['h'-'E']= { 1.0159366732596479e2, 1e2, 2 },
|
|
['k'-'E']= { 1.024e3, 1e3, 3 },
|
|
['K'-'E']= { 1.024e3, 1e3, 3 },
|
|
['M'-'E']= { 1.048576e6, 1e6, 6 },
|
|
['G'-'E']= { 1.073741824e9, 1e9, 9 },
|
|
['T'-'E']= { 1.099511627776e12, 1e12, 12 },
|
|
['P'-'E']= { 1.125899906842624e15, 1e15, 15 },
|
|
['E'-'E']= { 1.152921504606847e18, 1e18, 18 },
|
|
['Z'-'E']= { 1.1805916207174113e21, 1e21, 21 },
|
|
['Y'-'E']= { 1.2089258196146292e24, 1e24, 24 },
|
|
};
|
|
|
|
static const struct {
|
|
const char *name;
|
|
double value;
|
|
} constants[] = {
|
|
{ "E", M_E },
|
|
{ "PI", M_PI },
|
|
{ "PHI", M_PHI },
|
|
{ "QP2LAMBDA", FF_QP2LAMBDA },
|
|
};
|
|
|
|
double av_strtod(const char *numstr, char **tail)
|
|
{
|
|
double d;
|
|
char *next;
|
|
if(numstr[0]=='0' && (numstr[1]|0x20)=='x') {
|
|
d = strtoul(numstr, &next, 16);
|
|
} else
|
|
d = strtod(numstr, &next);
|
|
/* if parsing succeeded, check for and interpret postfixes */
|
|
if (next!=numstr) {
|
|
if (next[0] == 'd' && next[1] == 'B') {
|
|
/* treat dB as decibels instead of decibytes */
|
|
d = ff_exp10(d / 20);
|
|
next += 2;
|
|
} else if (*next >= 'E' && *next <= 'z') {
|
|
int e= si_prefixes[*next - 'E'].exp;
|
|
if (e) {
|
|
if (next[1] == 'i') {
|
|
d*= si_prefixes[*next - 'E'].bin_val;
|
|
next+=2;
|
|
} else {
|
|
d*= si_prefixes[*next - 'E'].dec_val;
|
|
next++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*next=='B') {
|
|
d*=8;
|
|
next++;
|
|
}
|
|
}
|
|
/* if requested, fill in tail with the position after the last parsed
|
|
character */
|
|
if (tail)
|
|
*tail = next;
|
|
return d;
|
|
}
|
|
|
|
#define IS_IDENTIFIER_CHAR(c) ((c) - '0' <= 9U || (c) - 'a' <= 25U || (c) - 'A' <= 25U || (c) == '_')
|
|
|
|
static int strmatch(const char *s, const char *prefix)
|
|
{
|
|
int i;
|
|
for (i=0; prefix[i]; i++) {
|
|
if (prefix[i] != s[i]) return 0;
|
|
}
|
|
/* return 1 only if the s identifier is terminated */
|
|
return !IS_IDENTIFIER_CHAR(s[i]);
|
|
}
|
|
|
|
struct AVExpr {
|
|
enum {
|
|
e_value, e_const, e_func0, e_func1, e_func2,
|
|
e_squish, e_gauss, e_ld, e_isnan, e_isinf,
|
|
e_mod, e_max, e_min, e_eq, e_gt, e_gte, e_lte, e_lt,
|
|
e_pow, e_mul, e_div, e_add,
|
|
e_last, e_st, e_while, e_taylor, e_root, e_floor, e_ceil, e_trunc,
|
|
e_sqrt, e_not, e_random, e_hypot, e_gcd,
|
|
e_if, e_ifnot, e_print, e_bitand, e_bitor, e_between, e_clip
|
|
} type;
|
|
double value; // is sign in other types
|
|
union {
|
|
int const_index;
|
|
double (*func0)(double);
|
|
double (*func1)(void *, double);
|
|
double (*func2)(void *, double, double);
|
|
} a;
|
|
struct AVExpr *param[3];
|
|
double *var;
|
|
};
|
|
|
|
static double etime(double v)
|
|
{
|
|
return av_gettime() * 0.000001;
|
|
}
|
|
|
|
static double eval_expr(Parser *p, AVExpr *e)
|
|
{
|
|
switch (e->type) {
|
|
case e_value: return e->value;
|
|
case e_const: return e->value * p->const_values[e->a.const_index];
|
|
case e_func0: return e->value * e->a.func0(eval_expr(p, e->param[0]));
|
|
case e_func1: return e->value * e->a.func1(p->opaque, eval_expr(p, e->param[0]));
|
|
case e_func2: return e->value * e->a.func2(p->opaque, eval_expr(p, e->param[0]), eval_expr(p, e->param[1]));
|
|
case e_squish: return 1/(1+exp(4*eval_expr(p, e->param[0])));
|
|
case e_gauss: { double d = eval_expr(p, e->param[0]); return exp(-d*d/2)/sqrt(2*M_PI); }
|
|
case e_ld: return e->value * p->var[av_clip(eval_expr(p, e->param[0]), 0, VARS-1)];
|
|
case e_isnan: return e->value * !!isnan(eval_expr(p, e->param[0]));
|
|
case e_isinf: return e->value * !!isinf(eval_expr(p, e->param[0]));
|
|
case e_floor: return e->value * floor(eval_expr(p, e->param[0]));
|
|
case e_ceil : return e->value * ceil (eval_expr(p, e->param[0]));
|
|
case e_trunc: return e->value * trunc(eval_expr(p, e->param[0]));
|
|
case e_sqrt: return e->value * sqrt (eval_expr(p, e->param[0]));
|
|
case e_not: return e->value * (eval_expr(p, e->param[0]) == 0);
|
|
case e_if: return e->value * (eval_expr(p, e->param[0]) ? eval_expr(p, e->param[1]) :
|
|
e->param[2] ? eval_expr(p, e->param[2]) : 0);
|
|
case e_ifnot: return e->value * (!eval_expr(p, e->param[0]) ? eval_expr(p, e->param[1]) :
|
|
e->param[2] ? eval_expr(p, e->param[2]) : 0);
|
|
case e_clip: {
|
|
double x = eval_expr(p, e->param[0]);
|
|
double min = eval_expr(p, e->param[1]), max = eval_expr(p, e->param[2]);
|
|
if (isnan(min) || isnan(max) || isnan(x) || min > max)
|
|
return NAN;
|
|
return e->value * av_clipd(eval_expr(p, e->param[0]), min, max);
|
|
}
|
|
case e_between: {
|
|
double d = eval_expr(p, e->param[0]);
|
|
return e->value * (d >= eval_expr(p, e->param[1]) &&
|
|
d <= eval_expr(p, e->param[2]));
|
|
}
|
|
case e_print: {
|
|
double x = eval_expr(p, e->param[0]);
|
|
int level = e->param[1] ? av_clip(eval_expr(p, e->param[1]), INT_MIN, INT_MAX) : AV_LOG_INFO;
|
|
av_log(p, level, "%f\n", x);
|
|
return x;
|
|
}
|
|
case e_random:{
|
|
int idx= av_clip(eval_expr(p, e->param[0]), 0, VARS-1);
|
|
uint64_t r= isnan(p->var[idx]) ? 0 : p->var[idx];
|
|
r= r*1664525+1013904223;
|
|
p->var[idx]= r;
|
|
return e->value * (r * (1.0/UINT64_MAX));
|
|
}
|
|
case e_while: {
|
|
double d = NAN;
|
|
while (eval_expr(p, e->param[0]))
|
|
d=eval_expr(p, e->param[1]);
|
|
return d;
|
|
}
|
|
case e_taylor: {
|
|
double t = 1, d = 0, v;
|
|
double x = eval_expr(p, e->param[1]);
|
|
int id = e->param[2] ? av_clip(eval_expr(p, e->param[2]), 0, VARS-1) : 0;
|
|
int i;
|
|
double var0 = p->var[id];
|
|
for(i=0; i<1000; i++) {
|
|
double ld = d;
|
|
p->var[id] = i;
|
|
v = eval_expr(p, e->param[0]);
|
|
d += t*v;
|
|
if(ld==d && v)
|
|
break;
|
|
t *= x / (i+1);
|
|
}
|
|
p->var[id] = var0;
|
|
return d;
|
|
}
|
|
case e_root: {
|
|
int i, j;
|
|
double low = -1, high = -1, v, low_v = -DBL_MAX, high_v = DBL_MAX;
|
|
double var0 = p->var[0];
|
|
double x_max = eval_expr(p, e->param[1]);
|
|
for(i=-1; i<1024; i++) {
|
|
if(i<255) {
|
|
p->var[0] = ff_reverse[i&255]*x_max/255;
|
|
} else {
|
|
p->var[0] = x_max*pow(0.9, i-255);
|
|
if (i&1) p->var[0] *= -1;
|
|
if (i&2) p->var[0] += low;
|
|
else p->var[0] += high;
|
|
}
|
|
v = eval_expr(p, e->param[0]);
|
|
if (v<=0 && v>low_v) {
|
|
low = p->var[0];
|
|
low_v = v;
|
|
}
|
|
if (v>=0 && v<high_v) {
|
|
high = p->var[0];
|
|
high_v = v;
|
|
}
|
|
if (low>=0 && high>=0){
|
|
for (j=0; j<1000; j++) {
|
|
p->var[0] = (low+high)*0.5;
|
|
if (low == p->var[0] || high == p->var[0])
|
|
break;
|
|
v = eval_expr(p, e->param[0]);
|
|
if (v<=0) low = p->var[0];
|
|
if (v>=0) high= p->var[0];
|
|
if (isnan(v)) {
|
|
low = high = v;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
p->var[0] = var0;
|
|
return -low_v<high_v ? low : high;
|
|
}
|
|
default: {
|
|
double d = eval_expr(p, e->param[0]);
|
|
double d2 = eval_expr(p, e->param[1]);
|
|
switch (e->type) {
|
|
case e_mod: return e->value * (d - floor((!CONFIG_FTRAPV || d2) ? d / d2 : d * INFINITY) * d2);
|
|
case e_gcd: return e->value * av_gcd(d,d2);
|
|
case e_max: return e->value * (d > d2 ? d : d2);
|
|
case e_min: return e->value * (d < d2 ? d : d2);
|
|
case e_eq: return e->value * (d == d2 ? 1.0 : 0.0);
|
|
case e_gt: return e->value * (d > d2 ? 1.0 : 0.0);
|
|
case e_gte: return e->value * (d >= d2 ? 1.0 : 0.0);
|
|
case e_lt: return e->value * (d < d2 ? 1.0 : 0.0);
|
|
case e_lte: return e->value * (d <= d2 ? 1.0 : 0.0);
|
|
case e_pow: return e->value * pow(d, d2);
|
|
case e_mul: return e->value * (d * d2);
|
|
case e_div: return e->value * ((!CONFIG_FTRAPV || d2 ) ? (d / d2) : d * INFINITY);
|
|
case e_add: return e->value * (d + d2);
|
|
case e_last:return e->value * d2;
|
|
case e_st : return e->value * (p->var[av_clip(d, 0, VARS-1)]= d2);
|
|
case e_hypot:return e->value * hypot(d, d2);
|
|
case e_bitand: return isnan(d) || isnan(d2) ? NAN : e->value * ((long int)d & (long int)d2);
|
|
case e_bitor: return isnan(d) || isnan(d2) ? NAN : e->value * ((long int)d | (long int)d2);
|
|
}
|
|
}
|
|
}
|
|
return NAN;
|
|
}
|
|
|
|
static int parse_expr(AVExpr **e, Parser *p);
|
|
|
|
void av_expr_free(AVExpr *e)
|
|
{
|
|
if (!e) return;
|
|
av_expr_free(e->param[0]);
|
|
av_expr_free(e->param[1]);
|
|
av_expr_free(e->param[2]);
|
|
av_freep(&e->var);
|
|
av_freep(&e);
|
|
}
|
|
|
|
static int parse_primary(AVExpr **e, Parser *p)
|
|
{
|
|
AVExpr *d = av_mallocz(sizeof(AVExpr));
|
|
char *next = p->s, *s0 = p->s;
|
|
int ret, i;
|
|
|
|
if (!d)
|
|
return AVERROR(ENOMEM);
|
|
|
|
/* number */
|
|
d->value = av_strtod(p->s, &next);
|
|
if (next != p->s) {
|
|
d->type = e_value;
|
|
p->s= next;
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
d->value = 1;
|
|
|
|
/* named constants */
|
|
for (i=0; p->const_names && p->const_names[i]; i++) {
|
|
if (strmatch(p->s, p->const_names[i])) {
|
|
p->s+= strlen(p->const_names[i]);
|
|
d->type = e_const;
|
|
d->a.const_index = i;
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
}
|
|
for (i = 0; i < FF_ARRAY_ELEMS(constants); i++) {
|
|
if (strmatch(p->s, constants[i].name)) {
|
|
p->s += strlen(constants[i].name);
|
|
d->type = e_value;
|
|
d->value = constants[i].value;
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
p->s= strchr(p->s, '(');
|
|
if (!p->s) {
|
|
av_log(p, AV_LOG_ERROR, "Undefined constant or missing '(' in '%s'\n", s0);
|
|
p->s= next;
|
|
av_expr_free(d);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
p->s++; // "("
|
|
if (*next == '(') { // special case do-nothing
|
|
av_freep(&d);
|
|
if ((ret = parse_expr(&d, p)) < 0)
|
|
return ret;
|
|
if (p->s[0] != ')') {
|
|
av_log(p, AV_LOG_ERROR, "Missing ')' in '%s'\n", s0);
|
|
av_expr_free(d);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
p->s++; // ")"
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
if ((ret = parse_expr(&(d->param[0]), p)) < 0) {
|
|
av_expr_free(d);
|
|
return ret;
|
|
}
|
|
if (p->s[0]== ',') {
|
|
p->s++; // ","
|
|
parse_expr(&d->param[1], p);
|
|
}
|
|
if (p->s[0]== ',') {
|
|
p->s++; // ","
|
|
parse_expr(&d->param[2], p);
|
|
}
|
|
if (p->s[0] != ')') {
|
|
av_log(p, AV_LOG_ERROR, "Missing ')' or too many args in '%s'\n", s0);
|
|
av_expr_free(d);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
p->s++; // ")"
|
|
|
|
d->type = e_func0;
|
|
if (strmatch(next, "sinh" )) d->a.func0 = sinh;
|
|
else if (strmatch(next, "cosh" )) d->a.func0 = cosh;
|
|
else if (strmatch(next, "tanh" )) d->a.func0 = tanh;
|
|
else if (strmatch(next, "sin" )) d->a.func0 = sin;
|
|
else if (strmatch(next, "cos" )) d->a.func0 = cos;
|
|
else if (strmatch(next, "tan" )) d->a.func0 = tan;
|
|
else if (strmatch(next, "atan" )) d->a.func0 = atan;
|
|
else if (strmatch(next, "asin" )) d->a.func0 = asin;
|
|
else if (strmatch(next, "acos" )) d->a.func0 = acos;
|
|
else if (strmatch(next, "exp" )) d->a.func0 = exp;
|
|
else if (strmatch(next, "log" )) d->a.func0 = log;
|
|
else if (strmatch(next, "abs" )) d->a.func0 = fabs;
|
|
else if (strmatch(next, "time" )) d->a.func0 = etime;
|
|
else if (strmatch(next, "squish")) d->type = e_squish;
|
|
else if (strmatch(next, "gauss" )) d->type = e_gauss;
|
|
else if (strmatch(next, "mod" )) d->type = e_mod;
|
|
else if (strmatch(next, "max" )) d->type = e_max;
|
|
else if (strmatch(next, "min" )) d->type = e_min;
|
|
else if (strmatch(next, "eq" )) d->type = e_eq;
|
|
else if (strmatch(next, "gte" )) d->type = e_gte;
|
|
else if (strmatch(next, "gt" )) d->type = e_gt;
|
|
else if (strmatch(next, "lte" )) d->type = e_lte;
|
|
else if (strmatch(next, "lt" )) d->type = e_lt;
|
|
else if (strmatch(next, "ld" )) d->type = e_ld;
|
|
else if (strmatch(next, "isnan" )) d->type = e_isnan;
|
|
else if (strmatch(next, "isinf" )) d->type = e_isinf;
|
|
else if (strmatch(next, "st" )) d->type = e_st;
|
|
else if (strmatch(next, "while" )) d->type = e_while;
|
|
else if (strmatch(next, "taylor")) d->type = e_taylor;
|
|
else if (strmatch(next, "root" )) d->type = e_root;
|
|
else if (strmatch(next, "floor" )) d->type = e_floor;
|
|
else if (strmatch(next, "ceil" )) d->type = e_ceil;
|
|
else if (strmatch(next, "trunc" )) d->type = e_trunc;
|
|
else if (strmatch(next, "sqrt" )) d->type = e_sqrt;
|
|
else if (strmatch(next, "not" )) d->type = e_not;
|
|
else if (strmatch(next, "pow" )) d->type = e_pow;
|
|
else if (strmatch(next, "print" )) d->type = e_print;
|
|
else if (strmatch(next, "random")) d->type = e_random;
|
|
else if (strmatch(next, "hypot" )) d->type = e_hypot;
|
|
else if (strmatch(next, "gcd" )) d->type = e_gcd;
|
|
else if (strmatch(next, "if" )) d->type = e_if;
|
|
else if (strmatch(next, "ifnot" )) d->type = e_ifnot;
|
|
else if (strmatch(next, "bitand")) d->type = e_bitand;
|
|
else if (strmatch(next, "bitor" )) d->type = e_bitor;
|
|
else if (strmatch(next, "between"))d->type = e_between;
|
|
else if (strmatch(next, "clip" )) d->type = e_clip;
|
|
else {
|
|
for (i=0; p->func1_names && p->func1_names[i]; i++) {
|
|
if (strmatch(next, p->func1_names[i])) {
|
|
d->a.func1 = p->funcs1[i];
|
|
d->type = e_func1;
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (i=0; p->func2_names && p->func2_names[i]; i++) {
|
|
if (strmatch(next, p->func2_names[i])) {
|
|
d->a.func2 = p->funcs2[i];
|
|
d->type = e_func2;
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
av_log(p, AV_LOG_ERROR, "Unknown function in '%s'\n", s0);
|
|
av_expr_free(d);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
*e = d;
|
|
return 0;
|
|
}
|
|
|
|
static AVExpr *make_eval_expr(int type, int value, AVExpr *p0, AVExpr *p1)
|
|
{
|
|
AVExpr *e = av_mallocz(sizeof(AVExpr));
|
|
if (!e)
|
|
return NULL;
|
|
e->type =type ;
|
|
e->value =value ;
|
|
e->param[0] =p0 ;
|
|
e->param[1] =p1 ;
|
|
return e;
|
|
}
|
|
|
|
static int parse_pow(AVExpr **e, Parser *p, int *sign)
|
|
{
|
|
*sign= (*p->s == '+') - (*p->s == '-');
|
|
p->s += *sign&1;
|
|
return parse_primary(e, p);
|
|
}
|
|
|
|
static int parse_dB(AVExpr **e, Parser *p, int *sign)
|
|
{
|
|
/* do not filter out the negative sign when parsing a dB value.
|
|
for example, -3dB is not the same as -(3dB) */
|
|
if (*p->s == '-') {
|
|
char *next;
|
|
double av_unused ignored = strtod(p->s, &next);
|
|
if (next != p->s && next[0] == 'd' && next[1] == 'B') {
|
|
*sign = 0;
|
|
return parse_primary(e, p);
|
|
}
|
|
}
|
|
return parse_pow(e, p, sign);
|
|
}
|
|
|
|
static int parse_factor(AVExpr **e, Parser *p)
|
|
{
|
|
int sign, sign2, ret;
|
|
AVExpr *e0, *e1, *e2;
|
|
if ((ret = parse_dB(&e0, p, &sign)) < 0)
|
|
return ret;
|
|
while(p->s[0]=='^'){
|
|
e1 = e0;
|
|
p->s++;
|
|
if ((ret = parse_dB(&e2, p, &sign2)) < 0) {
|
|
av_expr_free(e1);
|
|
return ret;
|
|
}
|
|
e0 = make_eval_expr(e_pow, 1, e1, e2);
|
|
if (!e0) {
|
|
av_expr_free(e1);
|
|
av_expr_free(e2);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
if (e0->param[1]) e0->param[1]->value *= (sign2|1);
|
|
}
|
|
if (e0) e0->value *= (sign|1);
|
|
|
|
*e = e0;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_term(AVExpr **e, Parser *p)
|
|
{
|
|
int ret;
|
|
AVExpr *e0, *e1, *e2;
|
|
if ((ret = parse_factor(&e0, p)) < 0)
|
|
return ret;
|
|
while (p->s[0]=='*' || p->s[0]=='/') {
|
|
int c= *p->s++;
|
|
e1 = e0;
|
|
if ((ret = parse_factor(&e2, p)) < 0) {
|
|
av_expr_free(e1);
|
|
return ret;
|
|
}
|
|
e0 = make_eval_expr(c == '*' ? e_mul : e_div, 1, e1, e2);
|
|
if (!e0) {
|
|
av_expr_free(e1);
|
|
av_expr_free(e2);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
}
|
|
*e = e0;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_subexpr(AVExpr **e, Parser *p)
|
|
{
|
|
int ret;
|
|
AVExpr *e0, *e1, *e2;
|
|
if ((ret = parse_term(&e0, p)) < 0)
|
|
return ret;
|
|
while (*p->s == '+' || *p->s == '-') {
|
|
e1 = e0;
|
|
if ((ret = parse_term(&e2, p)) < 0) {
|
|
av_expr_free(e1);
|
|
return ret;
|
|
}
|
|
e0 = make_eval_expr(e_add, 1, e1, e2);
|
|
if (!e0) {
|
|
av_expr_free(e1);
|
|
av_expr_free(e2);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
};
|
|
|
|
*e = e0;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_expr(AVExpr **e, Parser *p)
|
|
{
|
|
int ret;
|
|
AVExpr *e0, *e1, *e2;
|
|
if (p->stack_index <= 0) //protect against stack overflows
|
|
return AVERROR(EINVAL);
|
|
p->stack_index--;
|
|
|
|
if ((ret = parse_subexpr(&e0, p)) < 0)
|
|
return ret;
|
|
while (*p->s == ';') {
|
|
p->s++;
|
|
e1 = e0;
|
|
if ((ret = parse_subexpr(&e2, p)) < 0) {
|
|
av_expr_free(e1);
|
|
return ret;
|
|
}
|
|
e0 = make_eval_expr(e_last, 1, e1, e2);
|
|
if (!e0) {
|
|
av_expr_free(e1);
|
|
av_expr_free(e2);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
};
|
|
|
|
p->stack_index++;
|
|
*e = e0;
|
|
return 0;
|
|
}
|
|
|
|
static int verify_expr(AVExpr *e)
|
|
{
|
|
if (!e) return 0;
|
|
switch (e->type) {
|
|
case e_value:
|
|
case e_const: return 1;
|
|
case e_func0:
|
|
case e_func1:
|
|
case e_squish:
|
|
case e_ld:
|
|
case e_gauss:
|
|
case e_isnan:
|
|
case e_isinf:
|
|
case e_floor:
|
|
case e_ceil:
|
|
case e_trunc:
|
|
case e_sqrt:
|
|
case e_not:
|
|
case e_random:
|
|
return verify_expr(e->param[0]) && !e->param[1];
|
|
case e_print:
|
|
return verify_expr(e->param[0])
|
|
&& (!e->param[1] || verify_expr(e->param[1]));
|
|
case e_if:
|
|
case e_ifnot:
|
|
case e_taylor:
|
|
return verify_expr(e->param[0]) && verify_expr(e->param[1])
|
|
&& (!e->param[2] || verify_expr(e->param[2]));
|
|
case e_between:
|
|
case e_clip:
|
|
return verify_expr(e->param[0]) &&
|
|
verify_expr(e->param[1]) &&
|
|
verify_expr(e->param[2]);
|
|
default: return verify_expr(e->param[0]) && verify_expr(e->param[1]) && !e->param[2];
|
|
}
|
|
}
|
|
|
|
int av_expr_parse(AVExpr **expr, const char *s,
|
|
const char * const *const_names,
|
|
const char * const *func1_names, double (* const *funcs1)(void *, double),
|
|
const char * const *func2_names, double (* const *funcs2)(void *, double, double),
|
|
int log_offset, void *log_ctx)
|
|
{
|
|
Parser p = { 0 };
|
|
AVExpr *e = NULL;
|
|
char *w = av_malloc(strlen(s) + 1);
|
|
char *wp = w;
|
|
const char *s0 = s;
|
|
int ret = 0;
|
|
|
|
if (!w)
|
|
return AVERROR(ENOMEM);
|
|
|
|
while (*s)
|
|
if (!av_isspace(*s++)) *wp++ = s[-1];
|
|
*wp++ = 0;
|
|
|
|
p.class = &eval_class;
|
|
p.stack_index=100;
|
|
p.s= w;
|
|
p.const_names = const_names;
|
|
p.funcs1 = funcs1;
|
|
p.func1_names = func1_names;
|
|
p.funcs2 = funcs2;
|
|
p.func2_names = func2_names;
|
|
p.log_offset = log_offset;
|
|
p.log_ctx = log_ctx;
|
|
|
|
if ((ret = parse_expr(&e, &p)) < 0)
|
|
goto end;
|
|
if (*p.s) {
|
|
av_log(&p, AV_LOG_ERROR, "Invalid chars '%s' at the end of expression '%s'\n", p.s, s0);
|
|
ret = AVERROR(EINVAL);
|
|
goto end;
|
|
}
|
|
if (!verify_expr(e)) {
|
|
ret = AVERROR(EINVAL);
|
|
goto end;
|
|
}
|
|
e->var= av_mallocz(sizeof(double) *VARS);
|
|
if (!e->var) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto end;
|
|
}
|
|
*expr = e;
|
|
e = NULL;
|
|
end:
|
|
av_expr_free(e);
|
|
av_free(w);
|
|
return ret;
|
|
}
|
|
|
|
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
|
|
{
|
|
Parser p = { 0 };
|
|
p.var= e->var;
|
|
|
|
p.const_values = const_values;
|
|
p.opaque = opaque;
|
|
return eval_expr(&p, e);
|
|
}
|
|
|
|
int av_expr_parse_and_eval(double *d, const char *s,
|
|
const char * const *const_names, const double *const_values,
|
|
const char * const *func1_names, double (* const *funcs1)(void *, double),
|
|
const char * const *func2_names, double (* const *funcs2)(void *, double, double),
|
|
void *opaque, int log_offset, void *log_ctx)
|
|
{
|
|
AVExpr *e = NULL;
|
|
int ret = av_expr_parse(&e, s, const_names, func1_names, funcs1, func2_names, funcs2, log_offset, log_ctx);
|
|
|
|
if (ret < 0) {
|
|
*d = NAN;
|
|
return ret;
|
|
}
|
|
*d = av_expr_eval(e, const_values, opaque);
|
|
av_expr_free(e);
|
|
return isnan(*d) ? AVERROR(EINVAL) : 0;
|
|
}
|
|
|
|
#ifdef TEST
|
|
#include <string.h>
|
|
|
|
static const double const_values[] = {
|
|
M_PI,
|
|
M_E,
|
|
0
|
|
};
|
|
|
|
static const char *const const_names[] = {
|
|
"PI",
|
|
"E",
|
|
0
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
double d;
|
|
const char *const *expr;
|
|
static const char *const exprs[] = {
|
|
"",
|
|
"1;2",
|
|
"-20",
|
|
"-PI",
|
|
"+PI",
|
|
"1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
|
|
"80G/80Gi",
|
|
"1k",
|
|
"1Gi",
|
|
"1gi",
|
|
"1GiFoo",
|
|
"1k+1k",
|
|
"1Gi*3foo",
|
|
"foo",
|
|
"foo(",
|
|
"foo()",
|
|
"foo)",
|
|
"sin",
|
|
"sin(",
|
|
"sin()",
|
|
"sin)",
|
|
"sin 10",
|
|
"sin(1,2,3)",
|
|
"sin(1 )",
|
|
"1",
|
|
"1foo",
|
|
"bar + PI + E + 100f*2 + foo",
|
|
"13k + 12f - foo(1, 2)",
|
|
"1gi",
|
|
"1Gi",
|
|
"st(0, 123)",
|
|
"st(1, 123); ld(1)",
|
|
"lte(0, 1)",
|
|
"lte(1, 1)",
|
|
"lte(1, 0)",
|
|
"lt(0, 1)",
|
|
"lt(1, 1)",
|
|
"gt(1, 0)",
|
|
"gt(2, 7)",
|
|
"gte(122, 122)",
|
|
/* compute 1+2+...+N */
|
|
"st(0, 1); while(lte(ld(0), 100), st(1, ld(1)+ld(0));st(0, ld(0)+1)); ld(1)",
|
|
/* compute Fib(N) */
|
|
"st(1, 1); st(2, 2); st(0, 1); while(lte(ld(0),10), st(3, ld(1)+ld(2)); st(1, ld(2)); st(2, ld(3)); st(0, ld(0)+1)); ld(3)",
|
|
"while(0, 10)",
|
|
"st(0, 1); while(lte(ld(0),100), st(1, ld(1)+ld(0)); st(0, ld(0)+1))",
|
|
"isnan(1)",
|
|
"isnan(NAN)",
|
|
"isnan(INF)",
|
|
"isinf(1)",
|
|
"isinf(NAN)",
|
|
"isinf(INF)",
|
|
"floor(NAN)",
|
|
"floor(123.123)",
|
|
"floor(-123.123)",
|
|
"trunc(123.123)",
|
|
"trunc(-123.123)",
|
|
"ceil(123.123)",
|
|
"ceil(-123.123)",
|
|
"sqrt(1764)",
|
|
"isnan(sqrt(-1))",
|
|
"not(1)",
|
|
"not(NAN)",
|
|
"not(0)",
|
|
"6.0206dB",
|
|
"-3.0103dB",
|
|
"pow(0,1.23)",
|
|
"pow(PI,1.23)",
|
|
"PI^1.23",
|
|
"pow(-1,1.23)",
|
|
"if(1, 2)",
|
|
"if(1, 1, 2)",
|
|
"if(0, 1, 2)",
|
|
"ifnot(0, 23)",
|
|
"ifnot(1, NaN) + if(0, 1)",
|
|
"ifnot(1, 1, 2)",
|
|
"ifnot(0, 1, 2)",
|
|
"taylor(1, 1)",
|
|
"taylor(eq(mod(ld(1),4),1)-eq(mod(ld(1),4),3), PI/2, 1)",
|
|
"root(sin(ld(0))-1, 2)",
|
|
"root(sin(ld(0))+6+sin(ld(0)/12)-log(ld(0)), 100)",
|
|
"7000000B*random(0)",
|
|
"squish(2)",
|
|
"gauss(0.1)",
|
|
"hypot(4,3)",
|
|
"gcd(30,55)*print(min(9,1))",
|
|
"bitor(42, 12)",
|
|
"bitand(42, 12)",
|
|
"bitand(NAN, 1)",
|
|
"between(10, -3, 10)",
|
|
"between(-4, -2, -1)",
|
|
"between(1,2)",
|
|
"clip(0, 2, 1)",
|
|
"clip(0/0, 1, 2)",
|
|
"clip(0, 0/0, 1)",
|
|
NULL
|
|
};
|
|
|
|
for (expr = exprs; *expr; expr++) {
|
|
printf("Evaluating '%s'\n", *expr);
|
|
av_expr_parse_and_eval(&d, *expr,
|
|
const_names, const_values,
|
|
NULL, NULL, NULL, NULL, NULL, 0, NULL);
|
|
if (isnan(d))
|
|
printf("'%s' -> nan\n\n", *expr);
|
|
else
|
|
printf("'%s' -> %f\n\n", *expr, d);
|
|
}
|
|
|
|
av_expr_parse_and_eval(&d, "1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
|
|
const_names, const_values,
|
|
NULL, NULL, NULL, NULL, NULL, 0, NULL);
|
|
printf("%f == 12.7\n", d);
|
|
av_expr_parse_and_eval(&d, "80G/80Gi",
|
|
const_names, const_values,
|
|
NULL, NULL, NULL, NULL, NULL, 0, NULL);
|
|
printf("%f == 0.931322575\n", d);
|
|
|
|
if (argc > 1 && !strcmp(argv[1], "-t")) {
|
|
for (i = 0; i < 1050; i++) {
|
|
START_TIMER;
|
|
av_expr_parse_and_eval(&d, "1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
|
|
const_names, const_values,
|
|
NULL, NULL, NULL, NULL, NULL, 0, NULL);
|
|
STOP_TIMER("av_expr_parse_and_eval");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|