From patchwork Mon Feb 5 21:48:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: bmathew@gmail.com X-Patchwork-Id: 7521 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.150.1 with SMTP id c1csp4428667jai; Tue, 6 Feb 2018 10:19:46 -0800 (PST) X-Google-Smtp-Source: AH8x227z32k7BQAC0nrf8ppCYfDxxU7x3aqHoNaRomsCxi/cpkUzOMk2pzoQJURDt+59trdM9YWl X-Received: by 10.223.138.177 with SMTP id y46mr2956167wry.257.1517941186843; Tue, 06 Feb 2018 10:19:46 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517941186; cv=none; d=google.com; s=arc-20160816; b=S1cCpSDvRNo+S4dWX3fD1euZr5H+w7RM4dUhNEUEtFwXV+DnKRw/kq6LkQ6FzfuAgY McDh5iWd5vdCqRF7a4z9+KTGs2cPfLEpKtsIPsMduNefm8yW8Iwcvw0LbySdAqJP8Uak CfJzqBzzQLl7pAnkJ1ryL16lq2gRXXIogNihr/Te2ajesGkfP8sIuD0l7bA+ckOa8xX7 Gwp2LIvE3Qpl/vXBJPmo3N6IY7FfrG7MEq25pGF9i/8yqcKJQHQQ6qYl4TyptjI8S0Ur +MBpoDC4cJR0I6awdToiDTjQnhrM67w0XyAHwarWBiatSTWCgHl9aXc4yL5IKiwanx+9 sufw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=vGMlLkSG3oJLtGd4A27nmgq4p7roVMzNriCdyHdVAlI=; b=eMwhws8hcj805cwCycdYgoe6R8aMLARz2btr9ZG3PmQYLPnUWPHbWdplOTin5gl2Gt NJukZ6Mf5+n1ULCPa8qyULtyPyPTgJrJAFt1FTdzLVrXQsHnzje59oUlmpGWHyRLdRCY /JRhCaKwfT6qS/R8VdMQU83BoyRaBQvugrf9bsGc6UhQxUC60g7opUqqO6umVbk2nqsV jawbjDClHUiwVSIqihrZpL1tL5m05LnSWBTm3shafrenpvI8N2ZYnwrlDuRinp0L/4eK AGLBBvvuThknOHfCKGr1fuWrsuXvcHVu6kHdUDM3DCw58+XlVv+3oidvjnym1+Gz8XIr MyCQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=TvWKPgoN; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d11si9070509wrg.144.2018.02.06.10.19.46; Tue, 06 Feb 2018 10:19:46 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=TvWKPgoN; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3F085689BBF; Tue, 6 Feb 2018 20:19:33 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl0-f47.google.com (mail-pl0-f47.google.com [209.85.160.47]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3A193689BBF for ; Mon, 5 Feb 2018 23:48:47 +0200 (EET) Received: by mail-pl0-f47.google.com with SMTP id j19so12696104pll.2 for ; Mon, 05 Feb 2018 13:48:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=9mueB1c8MmFV9j/xfBz3dKVBoI0gZ7ASVB/jm/0vFBg=; b=TvWKPgoN3gLD2iC0Gzp/U6iy8Laxfo6vor+RcHKjIF3azY+qJOE0jhtOSt8z9BQYGv TCA9Vn6faKM3CZTyczavYf9QBEhoibBWyt2Yvx99BvI9rKeIN7gl/2njCxjF2Cjxp7mW zqTLfuONSIT/pPfOgLgt8gz6Y6hbSk59uNW77rrwmMmyMnKkC/td889801udJiQZFP07 UizBiMmH2A3h0QB0dr5Splh60/W+0faR6ZZvyAPBSLyHAbA9l2qBIJs2PJpnRXHr/1Ra VLwWFUJqXHzohZo9u9N2zXij2VDOOQXz9h5HtHzwB7hQceGi+p9qj1nU8YtFMAISdal1 Y5vQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=9mueB1c8MmFV9j/xfBz3dKVBoI0gZ7ASVB/jm/0vFBg=; b=tGiVPLdcbPCuD5DXfBiETytIts8s/i4yeBW13oJE8Bbxj3Q0Ie4M71MKhnvouS3BAg Tu0buM7pvp6fYt0goX6m1ykSVUUtPJYt+3tvyPBLbcZTG1l/LOMVNMqlgh6hgS+VJIfV toI4Ozs5lKlAght3/VlqN9aGrGeNeEtbrN8EJoskRJezIdUaquWQPlDenv/bt9MxoUu7 Yjv5/6OvcPn5OaJAdTg+SQvA6RjA3FibLTBhH6Lj0UTfPmRaQPaVSP24edfrWwSAYctg Ux1AFXIP9jn70O1kgEgsDT4J4bwqz7i8hzUbpb14n3CPjxgQtvRZ3YsnIG7ThzyDQYKA r9mA== X-Gm-Message-State: APf1xPDg5npW38PAX2qL0XojTJmZVfxOToo4HQan7mKO8Gi7KqqSlc9+ 8nivuXCUJUg9N7mgqUT78DaoAnW9cLg= X-Received: by 2002:a17:902:d83:: with SMTP id 3-v6mr243033plv.82.1517867333340; Mon, 05 Feb 2018 13:48:53 -0800 (PST) Received: from gauss.lan (c-67-180-121-39.hsd1.ca.comcast.net. [67.180.121.39]) by smtp.gmail.com with ESMTPSA id z6sm14030341pgu.49.2018.02.05.13.48.52 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 05 Feb 2018 13:48:52 -0800 (PST) From: bmathew@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Mon, 5 Feb 2018 16:48:45 -0500 Message-Id: <20180205214845.25243-2-bmathew@gmail.com> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20180205214845.25243-1-bmathew@gmail.com> References: <20180205214845.25243-1-bmathew@gmail.com> X-Mailman-Approved-At: Tue, 06 Feb 2018 20:19:32 +0200 Subject: [FFmpeg-devel] [PATCH 1/1] New helper expr for the select filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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_vparam[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]; } }