Added a new expr/function get(n, filename) in libavutil/eval.c that is meant to be used in conjunction with the select filter and greatly enhances the generality of the select filter.
The select filter as it exists now can be used to selectively enable frames during transcoding. e.g.: select='not(mod(n\,100))' will enable one frame every 100. It is often required to do much more complex frame by frame selection under the control of an external program. For example, a face detector might indicate frames on which certain individuals have been detected and we wish to get a video with only frames where those individuals are present. Another example is video of lab experiments synchronized with some external sensor and we wish to extract the frames where the sensor detected some event. The get operator may be used as follows: -vf 'select=get(n\,"frame_select.en")' frame_select.en is a file that contains 1 byte per frame of the input video. It can be generated by the external program (e.g.: a face detector, lab software that processes sensor data). The get operator looks up the n-th byte in the file and passes it on to select so that frames can be kept or deleted. Very general editing operations can be realized by having an external program generate the enable file. Implementation: New struct AVGetExprBuffer has been added that is used to load the contents of the frame select file only when a get() expression is present. eval_expr() was modified to return the appropriate frame enable value from the buffer. No changes to the libavutil API. The changes are completely contained within eval.c --- libavutil/eval.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/libavutil/eval.c b/libavutil/eval.c index 5da9a6d83b..58e3742245 100644 --- a/libavutil/eval.c +++ b/libavutil/eval.c @@ -142,6 +142,40 @@ double av_strtod(const char *numstr, char **tail) return d; } +static int parse_string_literal(Parser *p, char **tail, char **literal_str) +{ + char *parse_str = p->s; + char *next = parse_str; + char *literal = NULL; + int nbyte; + + if(parse_str[0] == '\'' || parse_str[0] == '"') { + + next = strchr(parse_str + 1, parse_str[0]); + + if(next == NULL) { + av_log(p, AV_LOG_ERROR, "Unterminated string in '%s'\n", parse_str); + return AVERROR(EINVAL); + } else { + nbyte = next - parse_str; /* Includes space for \0 */ + literal = av_mallocz(nbyte); + if (!literal) + return AVERROR(ENOMEM); + + strncpy(literal, parse_str+1, nbyte-1); + literal[nbyte-1] = '\0'; + next++; /* step past the ending quote */ + } + } + + /* if requested, fill in tail with the position after the last parsed + character */ + if (tail) + *tail = next; + *literal_str = literal; + return 0; +} + #define IS_IDENTIFIER_CHAR(c) ((c) - '0' <= 9U || (c) - 'a' <= 25U || (c) - 'A' <= 25U || (c) == '_') static int strmatch(const char *s, const char *prefix) @@ -154,17 +188,27 @@ static int strmatch(const char *s, const char *prefix) return !IS_IDENTIFIER_CHAR(s[i]); } + +struct AVGetExprBuffer { + int ndata; + char *data; +}; + struct AVExpr { enum { - e_value, e_const, e_func0, e_func1, e_func2, + e_value, e_const, e_string, e_buffer, 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_round, e_sqrt, e_not, e_random, e_hypot, e_gcd, e_if, e_ifnot, e_print, e_bitand, e_bitor, e_between, e_clip, e_atan2, e_lerp, + e_get, } type; - double value; // is sign in other types + union { + double value; // is sign in other types + void *pointer; + }; union { int const_index; double (*func0)(double); @@ -300,6 +344,12 @@ static double eval_expr(Parser *p, AVExpr *e) p->var[0] = var0; return -low_v<high_v ? low : high; } + case e_get: { + struct AVGetExprBuffer * buffer = (struct AVGetExprBuffer * )e->param[1]->pointer; + int index = (int)eval_expr(p, e->param[0]); + index = (index < 0)?0:((index >= buffer->ndata)?buffer->ndata-1:index); + return e->value * buffer->data[index]; + } default: { double d = eval_expr(p, e->param[0]); double d2 = eval_expr(p, e->param[1]); @@ -345,11 +395,23 @@ static int parse_primary(AVExpr **e, Parser *p) { AVExpr *d = av_mallocz(sizeof(AVExpr)); char *next = p->s, *s0 = p->s; + char * literal_str; int ret, i; if (!d) return AVERROR(ENOMEM); + if((ret = parse_string_literal(p, &next, &literal_str)) != 0) + return ret; + + if(literal_str != NULL) { + d->type = e_string; + d->pointer = literal_str; + p->s = next; + *e = d; + return 0; + } + /* number */ d->value = av_strtod(p->s, &next); if (next != p->s) { @@ -470,6 +532,7 @@ static int parse_primary(AVExpr **e, Parser *p) else if (strmatch(next, "clip" )) d->type = e_clip; else if (strmatch(next, "atan2" )) d->type = e_atan2; else if (strmatch(next, "lerp" )) d->type = e_lerp; + else if (strmatch(next, "get" )) d->type = e_get; else { for (i=0; p->func1_names && p->func1_names[i]; i++) { if (strmatch(next, p->func1_names[i])) { @@ -637,12 +700,55 @@ static int parse_expr(AVExpr **e, Parser *p) return 0; } + +static int init_get_expr(AVExpr *e) +{ + FILE * fp; + char * fname = (char *)e->pointer; + struct AVGetExprBuffer * buffer; + int nbyte; + + if((fp = fopen(fname, "rb")) == NULL) + return 0; + fseek(fp, 0, SEEK_END); + nbyte = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buffer = av_malloc(nbyte + sizeof(struct AVGetExprBuffer)); + if(buffer == NULL) + { + fclose(fp); + return 0; + } + + buffer->ndata = nbyte; + buffer->data = (char *)(buffer + 1); /* Add sizeof the struct */ + + if(fread(buffer->data, nbyte, 1, fp) != 1) + { + fclose(fp); + av_free(buffer); + return 0; + } + + e->pointer = buffer; + e->type = e_buffer; + av_free(fname); + fclose(fp); + + return 1; +} + static int verify_expr(AVExpr *e) { if (!e) return 0; switch (e->type) { case e_value: case e_const: return 1; + /* string and buffer types are currently only exposed to e_get. + So if we got here, something is wrong. */ + case e_string: + case e_buffer: return 0; case e_func0: case e_func1: case e_squish: @@ -672,6 +778,14 @@ static int verify_expr(AVExpr *e) return verify_expr(e->param[0]) && verify_expr(e->param[1]) && verify_expr(e->param[2]); + case e_get: + if(!e->param[1] || e->param[1]->type != e_string) + return 0; + /* Since there is no separate init for the expression, and nothing + other than get currently needs initialization, initializing + from verify_expr() was the least amount of new code. */ + return verify_expr(e->param[0]) && init_get_expr(e->param[1]); + default: return verify_expr(e->param[0]) && verify_expr(e->param[1]) && !e->param[2]; } } -- 2.13.5 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel