On 2/2/16, Paul B Mahol <one...@gmail.com> wrote:
> Hi,
>
> patch attached.
>

Much more useful version attached.

Simple demo demonstrating functionality currently missing in lavfi:

lavfi.log(0, "Lavfi script example", lavfi.script_name, lavfi.frame_count(0));
if (lavfi.frame_count(0) >= 50) then
    lavfi.filter({"aa"}, "vflip", nil, {"a"})
    lavfi.filter({"bb"}, "hflip", nil, {"b"})
else
    lavfi.filter({"aa"}, "negate", nil, {"a"})
    lavfi.filter({"bb"}, "vflip",  nil, {"b"})
end

lavfi.filter({"a", "b"}, "hstack", "2", {"out0"});
From a84f155924b2235162d690664c97b55477d0a4c2 Mon Sep 17 00:00:00 2001
From: Paul B Mahol <one...@gmail.com>
Date: Tue, 2 Feb 2016 10:09:50 +0100
Subject: [PATCH] avfilter: add luascript filter

Signed-off-by: Paul B Mahol <one...@gmail.com>
---
 configure                   |   4 +
 libavfilter/Makefile        |   1 +
 libavfilter/allfilters.c    |   1 +
 libavfilter/src_luascript.c | 793 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 799 insertions(+)
 create mode 100644 libavfilter/src_luascript.c

diff --git a/configure b/configure
index c415d5a..289c62e 100755
--- a/configure
+++ b/configure
@@ -275,6 +275,7 @@ External library support:
   --disable-lzma           disable lzma [autodetect]
   --enable-decklink        enable Blackmagic DeckLink I/O support [no]
   --enable-mmal            enable decoding via MMAL [no]
+  --enable-lua51           enable lua5.1, needed for luascript filter [no]
   --enable-netcdf          enable NetCDF, needed for sofalizer filter [no]
   --enable-nvenc           enable NVIDIA NVENC support [no]
   --enable-openal          enable OpenAL 1.1 capture support [no]
@@ -1494,6 +1495,7 @@ EXTERNAL_LIBRARY_LIST="
     libzimg
     libzmq
     libzvbi
+    lua51
     lzma
     mmal
     netcdf
@@ -2867,6 +2869,7 @@ hqdn3d_filter_deps="gpl"
 interlace_filter_deps="gpl"
 kerndeint_filter_deps="gpl"
 ladspa_filter_deps="ladspa dlopen"
+luascript_filter_deps="lua51"
 mcdeint_filter_deps="avcodec gpl"
 movie_filter_deps="avcodec avformat"
 mpdecimate_filter_deps="gpl"
@@ -5577,6 +5580,7 @@ enabled mmal &&
     (check_code cc interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS" ||
      die "ERROR: mmal firmware headers too old")
 
+enabled lua51             && require_pkg_config lua5.1 lua.h lua_newstate
 enabled netcdf            && require_pkg_config netcdf netcdf.h nc_inq_libvers
 enabled nvenc             && { check_header nvEncodeAPI.h || die "ERROR: nvEncodeAPI.h not found."; } &&
                              { check_cpp_condition nvEncodeAPI.h "NVENCAPI_MAJOR_VERSION >= 5" ||
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index e76d18e..bd9fce8 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -298,6 +298,7 @@ OBJS-$(CONFIG_SPECTRUMSYNTH_FILTER)          += vaf_spectrumsynth.o window_func.
 
 # multimedia sources
 OBJS-$(CONFIG_AMOVIE_FILTER)                 += src_movie.o
+OBJS-$(CONFIG_LUASCRIPT_FILTER)              += src_luascript.o
 OBJS-$(CONFIG_MOVIE_FILTER)                  += src_movie.o
 
 # Windows resource file
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 27d54bc..00eb8a4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -318,6 +318,7 @@ void avfilter_register_all(void)
 
     /* multimedia sources */
     REGISTER_FILTER(AMOVIE,         amovie,         avsrc);
+    REGISTER_FILTER(LUASCRIPT,      luascript,      avsrc);
     REGISTER_FILTER(MOVIE,          movie,          avsrc);
 
     /* those filters are part of public or internal API => registered
diff --git a/libavfilter/src_luascript.c b/libavfilter/src_luascript.c
new file mode 100644
index 0000000..2ff12ed
--- /dev/null
+++ b/libavfilter/src_luascript.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2016 Paul B Mahol
+ *
+ * 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
+ */
+
+#include <float.h>
+#include <stdint.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "libavutil/attributes.h"
+#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+#include "libavutil/timestamp.h"
+#include "libavformat/avformat.h"
+#include "audio.h"
+#include "avfilter.h"
+#include "buffersink.h"
+#include "buffersrc.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct FilterContext {
+    AVFilterContext *f;
+    int nb_inputs;
+    int nb_outputs;
+    const char *options;
+    const char *in[64];
+    const char *out[64];
+    uint8_t linked_out[64];
+    uint8_t linked_in[64];
+} FilterContext;
+
+typedef struct LuaScriptContext {
+    const AVClass *class;
+    char *script;
+
+    AVFilterGraph *graph;
+    FilterContext fctx[1024];
+    AVFilterContext *sink[64];
+    AVFilterContext *src[64];
+    int fsrcctx[64];
+    int fsinkctx[64];
+    int fsrcpad[64];
+    int fsinkpad[64];
+    int pending[64];
+    int first_audio;
+    int nbf;
+    int graph_configured;
+    int prev_oframe_count;
+
+    lua_State *L;
+} LuaScriptContext;
+
+#define OFFSET(x) offsetof(LuaScriptContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption luascript_options[]= {
+    { "script", "specify the script file", OFFSET(script), AV_OPT_TYPE_STRING, .flags = FLAGS },
+    { NULL },
+};
+
+static int luascript_push_frames(AVFilterContext *ctx, int out_id)
+{
+    LuaScriptContext *s = ctx->priv;
+    int i, ret = 0;
+
+    while (ret >= 0) {
+        AVFrame *frame = av_frame_alloc();
+
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        ret = av_buffersink_get_frame_flags(s->sink[out_id], frame,
+                                        ctx->nb_inputs > 0 ? AV_BUFFERSINK_FLAG_NO_REQUEST : 0);
+        if (ret == AVERROR(EAGAIN)) {
+            av_frame_free(&frame);
+            ret = 0;
+            goto exit;
+        } else if (ret < 0) {
+            av_frame_free(&frame);
+            return ret;
+        }
+
+        av_log(ctx, AV_LOG_DEBUG, "ff_filter_frame on outlink %d\n", out_id);
+        ret = ff_filter_frame(ctx->outputs[out_id], frame);
+    }
+
+exit:
+
+    av_log(ctx, AV_LOG_DEBUG, "clearing pending\n");
+    for (i = 0; i < ctx->nb_inputs; i++)
+        s->pending[i] = FFMAX(s->pending[i]--, 0);
+
+    return ret;
+}
+
+static int luascript_filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LuaScriptContext *s = ctx->priv;
+    unsigned in_id = FF_INLINK_IDX(inlink);
+    int ret;
+
+    av_log(ctx, AV_LOG_DEBUG, "filter_frame on inlink %d\n", in_id);
+
+    ret = av_buffersrc_add_frame_flags(s->src[in_id], frame,
+                                       AV_BUFFERSRC_FLAG_PUSH |
+                                       AV_BUFFERSRC_FLAG_KEEP_REF);
+    av_frame_free(&frame);
+    s->pending[in_id]++;
+    return ret;
+}
+
+static int link_filters(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    int ret, i, j, k, l;
+
+    for (i = 0; i < s->nbf; i++) {
+        FilterContext *fctx = &s->fctx[i];
+        for (j = 0; j < fctx->nb_outputs; j++) {
+            for (k = 0; k < s->nbf; k++) {
+                FilterContext *fctx2 = &s->fctx[k];
+                for (l = 0; l < fctx2->nb_inputs; l++) {
+                    if (!strcmp(fctx->out[j], fctx2->in[l])) {
+                        ret = avfilter_link(fctx->f, j, fctx2->f, l);
+                        if (ret < 0)
+                            return ret;
+                        av_log(ctx, AV_LOG_DEBUG, "%s->%s\n", fctx->f->name, fctx2->f->name);
+                        fctx->linked_out[j] = 1;
+                        fctx2->linked_in[l] = 1;
+                    }
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int link_src(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LuaScriptContext *s = ctx->priv;
+    unsigned in_id = FF_INLINK_IDX(inlink);
+    AVFilterContext *f = s->fctx[s->fsrcctx[in_id]].f;
+    int j = s->fsrcpad[in_id];
+    uint8_t ch_layout[64];
+    const char *src_name;
+    AVFilter *src;
+    int ret;
+
+    if (inlink->type == AVMEDIA_TYPE_VIDEO)
+        src = avfilter_get_by_name("buffer");
+    else
+        src = avfilter_get_by_name("abuffer");
+    if (!src) {
+        av_log(ctx, AV_LOG_ERROR, "Couldn't find src filter\n");
+        return AVERROR(EINVAL);
+    }
+
+    src_name = av_asprintf("src%d", in_id);
+    s->src[in_id] = avfilter_graph_alloc_filter(s->graph, src, src_name);
+    av_freep(&src_name);
+    if (!s->src[in_id]) {
+        av_log(ctx, AV_LOG_ERROR, "Error allocating src filter\n");
+        return AVERROR(ENOMEM);
+    }
+
+    if (inlink->type == AVMEDIA_TYPE_VIDEO) {
+        av_opt_set_int(s->src[in_id], "width",          inlink->w,                              AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(s->src[in_id], "height",         inlink->h,                              AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_q  (s->src[in_id], "time_base",      inlink->time_base,                      AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(s->src[in_id], "pix_fmt",        inlink->format,                         AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_q  (s->src[in_id], "sar",            inlink->sample_aspect_ratio,            AV_OPT_SEARCH_CHILDREN);
+    } else {
+        av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, inlink->channel_layout);
+        av_opt_set    (s->src[in_id], "channel_layout", ch_layout,                              AV_OPT_SEARCH_CHILDREN);
+        av_opt_set    (s->src[in_id], "sample_fmt",     av_get_sample_fmt_name(inlink->format), AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_q  (s->src[in_id], "time_base",      (AVRational){ 1, inlink->sample_rate }, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(s->src[in_id], "sample_rate",    inlink->sample_rate,                    AV_OPT_SEARCH_CHILDREN);
+    }
+
+    ret = avfilter_init_str(s->src[in_id], NULL);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = avfilter_link(s->src[in_id], 0, f, j);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static int link_sink(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
+    LuaScriptContext *s  = ctx->priv;
+    AVFilterContext *f = s->fctx[s->fsinkctx[out_id]].f;
+    int j = s->fsinkpad[out_id];
+    const char *sink_name;
+    AVFilter *sink;
+    int ret;
+
+    if (outlink->type == AVMEDIA_TYPE_VIDEO)
+        sink = avfilter_get_by_name("buffersink");
+    else
+        sink = avfilter_get_by_name("abuffersink");
+    if (!sink) {
+        av_log(ctx, AV_LOG_ERROR, "Couldn't find sink filter\n");
+        return AVERROR(EINVAL);
+    }
+
+    sink_name = av_asprintf("out%d", out_id);
+    s->sink[out_id] = avfilter_graph_alloc_filter(s->graph, sink, sink_name);
+    av_freep(&sink_name);
+    if (!s->sink[out_id]) {
+        av_log(ctx, AV_LOG_ERROR, "Error allocating sink filter\n");
+        return AVERROR(ENOMEM);
+    }
+
+    ret = avfilter_init_str(s->sink[out_id], NULL);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = avfilter_link(f, j, s->sink[out_id], 0);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static AVFilterContext *get_ctx(lua_State *L)
+{
+    lua_getfield(L, LUA_REGISTRYINDEX, "ctx");
+    AVFilterContext *ctx = lua_touserdata(L, -1);
+    lua_pop(L, 1);
+    av_assert0(ctx);
+    return ctx;
+}
+
+static int error_handler(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+
+    if (luaL_loadstring(L, "return debug.traceback('', 3)") == 0) {
+        lua_call(L, 0, 1);
+        const char *tr = lua_tostring(L, -1);
+        av_log(ctx, AV_LOG_ERROR, "%s\n", tr ? tr : "(unknown)");
+    }
+    lua_pop(L, 1);
+
+    return 1;
+}
+
+static int load_script(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    LuaScriptContext *s = ctx->priv;
+    int ret;
+
+    ret = luaL_loadfile(L, s->script);
+    if (ret) {
+        lua_error(L);
+    }
+    lua_call(L, 0, 0);
+
+    return ret;
+}
+
+static void add_inpad(FilterContext *fctx, const char *in)
+{
+    fctx->in[fctx->nb_inputs] = in;
+    fctx->nb_inputs++;
+}
+
+static void add_outpad(FilterContext *fctx, const char *out)
+{
+    fctx->out[fctx->nb_outputs] = out;
+    fctx->nb_outputs++;
+}
+
+static int script_filter(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    LuaScriptContext *s = ctx->priv;
+    const int inputs = lua_isnil(L, 1) ? 0 : lua_istable(L, 1);
+    const char *name = luaL_checkstring(L, 2);
+    const char *options = lua_isnil(L, 3) ? NULL : luaL_checkstring(L, 3);
+    const int outputs = lua_isnil(L, 4) ? 0 : lua_istable(L, 4);
+    const char *fname;
+    int ret = 0;
+
+    if (inputs > 0) {
+        lua_pushnil(L);
+        while (lua_next(L, 1) != 0) {
+            const char *in = luaL_checkstring(L, -1);
+            add_inpad(&s->fctx[s->nbf], in);
+            lua_pop(L, 1);
+        }
+    }
+
+    if (outputs > 0) {
+        lua_pushnil(L);
+        while (lua_next(L, 4) != 0) {
+            const char *out = luaL_checkstring(L, -1);
+            add_outpad(&s->fctx[s->nbf], out);
+            lua_pop(L, 1);
+        }
+    }
+
+    AVFilter *filter = avfilter_get_by_name(name);
+    if (!filter) {
+        luaL_error(L, "No such filter %s.", name);
+        return AVERROR(EINVAL);
+    }
+
+    fname = av_asprintf("%s%d", name, s->nbf);
+    s->fctx[s->nbf].f = avfilter_graph_alloc_filter(s->graph, filter, fname);
+    av_freep(&fname);
+    if (!s->fctx[s->nbf].f)
+        return luaL_error(L, "Failed to alloc filter: %s.", name);
+    ret = avfilter_init_str(s->fctx[s->nbf].f, options);
+    if (ret) {
+        luaL_error(L, "Failed to init filter: %s.", name);
+        return ret;
+    }
+
+    if (s->fctx[s->nbf].f->nb_inputs != s->fctx[s->nbf].nb_inputs) {
+        luaL_error(L, "Number of input pads provided does not match filter's: %s.", name);
+        ret = AVERROR(EINVAL);
+    }
+    if (s->fctx[s->nbf].f->nb_outputs != s->fctx[s->nbf].nb_outputs) {
+        luaL_error(L, "Number of output pads provided does not match filter's: %s.", name);
+        ret = AVERROR(EINVAL);
+    }
+
+    s->fctx[s->nbf].options = options;
+    s->nbf++;
+    return ret;
+}
+
+static int script_frame_count(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    const int out_id = luaL_checkinteger(L, 1);
+    int ret = 0;
+
+    if (ctx->nb_outputs > out_id && out_id >= 0) {
+        ret = ctx->outputs[out_id]->frame_count;
+    }
+
+    lua_pushinteger(L, ret);
+
+    return 1;
+}
+
+static int script_log(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    int msgl = luaL_checkinteger(L, 1);
+    int last = lua_gettop(L);
+
+    lua_getglobal(L, "tostring"); // args... tostring
+    for (int i = 2; i <= last; i++) {
+        lua_pushvalue(L, -1); // args... tostring tostring
+        lua_pushvalue(L, i); // args... tostring tostring args[i]
+        lua_call(L, 1, 1); // args... tostring str
+        const char *s = lua_tostring(L, -1);
+        if (s == NULL)
+            return luaL_error(L, "Invalid argument");
+        av_log(ctx, msgl, "%s%s", s, i > 0 ? " " : "");
+        lua_pop(L, 1);  // args... tostring
+    }
+    av_log(ctx, msgl, "\n");
+
+    return 0;
+}
+
+#define FN_ENTRY(name) {#name, script_ ## name}
+struct fn_entry {
+    const char *name;
+    int (*fn)(lua_State *L);
+};
+
+static const struct fn_entry main_fns[] = {
+    FN_ENTRY(log),
+    FN_ENTRY(frame_count),
+    FN_ENTRY(filter),
+    {0}
+};
+
+static void push_module_table(lua_State *L, const char *module)
+{
+    av_assert0(L);
+
+    lua_getglobal(L, "package"); // package
+    lua_getfield(L, -1, "loaded"); // package loaded
+    lua_remove(L, -2); // loaded
+    lua_getfield(L, -1, module); // loaded module
+    if (lua_isnil(L, -1)) {
+        lua_pop(L, 1); // loaded
+        lua_newtable(L); // loaded module
+        lua_pushvalue(L, -1); // loaded module module
+        lua_setfield(L, -3, module); // loaded module
+    }
+    lua_remove(L, -2); // module
+}
+
+static void register_package_fns(lua_State *L, const char *module,
+                                 const struct fn_entry *e)
+{
+    push_module_table(L, module);
+    for (int n = 0; e[n].name; n++) {
+        lua_pushcclosure(L, e[n].fn, 0);
+        lua_setfield(L, -2, e[n].name);
+    }
+    lua_pop(L, 1);
+}
+
+static void add_functions(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    lua_State *L = s->L;
+
+    register_package_fns(L, "lavfi", main_fns);
+    push_module_table(L, "lavfi");
+
+    lua_pop(L, 1);
+}
+
+static int run_lua(lua_State *L)
+{
+    AVFilterContext *ctx = lua_touserdata(L, -1);
+    LuaScriptContext *s = ctx->priv;
+    int ret = 0;
+
+    lua_pop(L, 1);
+
+    luaL_openlibs(L);
+
+    lua_pushlightuserdata(L, ctx);
+    lua_setfield(L, LUA_REGISTRYINDEX, "ctx");
+
+    add_functions(ctx);
+
+    push_module_table(L, "lavfi");
+
+    lua_pushvalue(L, -1);
+    lua_setglobal(L, "lavfi");
+
+    lua_pushstring(L, s->script);
+    lua_setfield(L, -2, "script_name");
+
+    lua_pushcfunction(L, error_handler);
+    lua_pushcfunction(L, load_script);
+    if (lua_pcall(L, 0, 0, -2)) {
+        const char *e = lua_tostring(L, -1);
+        av_log(ctx, AV_LOG_ERROR, "%s\n", e ? e : "(unknown)");
+        ret = AVERROR(EINVAL);
+    }
+
+    return ret;
+}
+
+static int run_script(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    const char *dump;
+    int ret, i;
+
+    if (s->L)
+        lua_close(s->L);
+
+    s->L = luaL_newstate();
+    if (!s->L)
+        return AVERROR(ENOMEM);
+
+    avfilter_graph_free(&s->graph);
+    memset(s->fctx, 0, sizeof(s->fctx));
+    s->nbf = 0;
+
+    s->graph = avfilter_graph_alloc();
+    if (!s->graph)
+        return AVERROR(ENOMEM);
+
+    if (lua_cpcall(s->L, run_lua, ctx)) {
+        const char *err = "unknown error";
+        if (lua_type(s->L, -1) == LUA_TSTRING)
+            err = lua_tostring(s->L, -1);
+        av_log(ctx, AV_LOG_ERROR, "%s\n", err);
+        return AVERROR(EINVAL);
+    }
+
+    link_filters(ctx);
+
+    for (i = 0; i < ctx->nb_inputs; i++) {
+        ret = link_src(ctx->inputs[i]);
+        if (ret < 0)
+            return ret;
+    }
+
+    for (i = 0; i < ctx->nb_outputs; i++) {
+        ret = link_sink(ctx->outputs[i]);
+        if (ret < 0)
+            return ret;
+    }
+
+    ret = avfilter_graph_config(s->graph, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error configuring the filter graph\n");
+        return ret;
+    }
+
+    // avfilter_config_links(ctx);
+
+    dump = avfilter_graph_dump(s->graph, NULL);
+    av_log(ctx, AV_LOG_DEBUG, "\n%s\n", dump);
+    av_freep(&dump);
+
+    return 0;
+}
+
+static int luascript_request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
+    LuaScriptContext *s = ctx->priv;
+    int i, ret, eof = 0;
+
+    if (s->graph_configured) {
+        if (outlink->frame_count - s->prev_oframe_count >= 50) {
+            s->graph_configured = 0;
+            s->prev_oframe_count = outlink->frame_count;
+        }
+    }
+    if (!s->graph_configured) {
+        ret = run_script(ctx);
+        if (ret < 0)
+            return ret;
+        s->graph_configured = 1;
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "request_frame on outlink %d\n", out_id);
+
+    for (i = 0; i < ctx->nb_inputs; i++) {
+        if (ctx->inputs[i]->status) {
+            eof++;
+        } else if (s->pending[i] == 0) {
+            av_log(ctx, AV_LOG_DEBUG, "request_frame from inlink %d\n", i);
+            ret = ff_request_frame(ctx->inputs[i]);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    if (eof == ctx->nb_inputs)
+        return AVERROR_EOF;
+
+    return luascript_push_frames(ctx, out_id);
+}
+
+static int luascript_config_input_props(AVFilterLink *inlink)
+{
+    return link_src(inlink);
+}
+
+static int luascript_config_output_props(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
+    LuaScriptContext *s  = ctx->priv;
+    AVFilterLink *sinklink;
+    int ret;
+
+    ret = run_script(ctx);
+    if (ret < 0)
+        return ret;
+
+    sinklink = s->sink[out_id]->inputs[0];
+    outlink->time_base = sinklink->time_base;
+    switch (outlink->type) {
+    case AVMEDIA_TYPE_AUDIO:
+        outlink->channel_layout = sinklink->channel_layout;
+        outlink->channels       = sinklink->channels;
+        outlink->sample_rate    = sinklink->sample_rate;
+        break;
+    case AVMEDIA_TYPE_VIDEO:
+        outlink->w                   = sinklink->w;
+        outlink->h                   = sinklink->h;
+        outlink->sample_aspect_ratio = sinklink->sample_aspect_ratio;
+        outlink->frame_rate          = sinklink->frame_rate;
+        break;
+    }
+
+    return 0;
+}
+
+static av_cold int luascript_init(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    lua_State *L = s->L;
+    int ret = 0;
+    int i, j;
+
+    if (!s->script) {
+        av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->graph = avfilter_graph_alloc();
+    if (!s->graph)
+        return AVERROR(ENOMEM);
+
+    s->L = L = luaL_newstate();
+    if (!L)
+        return AVERROR(ENOMEM);
+
+    if (lua_cpcall(L, run_lua, ctx)) {
+        const char *err = "unknown error";
+        if (lua_type(L, -1) == LUA_TSTRING)
+            err = lua_tostring(L, -1);
+        av_log(ctx, AV_LOG_ERROR, "%s\n", err);
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    link_filters(ctx);
+
+    for (i = 0; i < s->nbf; i++) {
+        FilterContext *fctx = &s->fctx[i];
+
+        for (j = 0; j < fctx->nb_inputs; j++) {
+            AVFilterContext *f = fctx->f;
+            AVFilterPad pad = { 0 };
+
+            if (fctx->linked_in[j])
+                continue;
+
+            s->fsrcctx[ctx->nb_inputs] = i;
+            s->fsrcpad[ctx->nb_inputs] = j;
+
+            av_log(ctx, AV_LOG_DEBUG, "input: %s:%s\n", f->name, fctx->in[j]);
+            pad.type          = avfilter_pad_get_type(f->input_pads, j);
+            pad.name          = av_strdup(avfilter_pad_get_name(f->input_pads, j));
+            if (!pad.name)
+                return AVERROR(ENOMEM);
+            pad.filter_frame     = luascript_filter_frame;
+            pad.config_props     = luascript_config_input_props;
+            ff_insert_inpad(ctx, ctx->nb_inputs, &pad);
+        }
+    }
+
+    for (i = 0; i < s->nbf; i++) {
+        FilterContext *fctx = &s->fctx[i];
+
+        for (j = 0; j < fctx->nb_outputs; j++) {
+            AVFilterContext *f = fctx->f;
+            AVFilterPad pad = { 0 };
+
+            if (fctx->linked_out[j])
+                continue;
+
+            s->fsinkctx[ctx->nb_outputs] = i;
+            s->fsinkpad[ctx->nb_outputs] = j;
+
+            av_log(ctx, AV_LOG_DEBUG, "output: %s:%s\n", f->name, fctx->out[j]);
+            pad.type          = avfilter_pad_get_type(f->output_pads, j);
+            pad.name          = av_strdup(fctx->out[j]);
+            if (!pad.name)
+                return AVERROR(ENOMEM);
+            pad.config_props  = luascript_config_output_props;
+            pad.request_frame = luascript_request_frame;
+            ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
+        }
+    }
+
+fail:
+    return ret;
+}
+
+static av_cold void luascript_uninit(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    int i;
+
+    if (s->L)
+        lua_close(s->L);
+    avfilter_graph_free(&s->graph);
+
+    for (i = 0; i < ctx->nb_inputs; i++) {
+        av_freep(&ctx->input_pads[i].name);
+    }
+
+    for (i = 0; i < ctx->nb_outputs; i++) {
+        av_freep(&ctx->output_pads[i].name);
+    }
+}
+
+static int luascript_query_formats(AVFilterContext *ctx)
+{
+    int i, ret;
+
+    for (i = 0; i < ctx->nb_outputs; i++) {
+        AVFilterFormats *formats, *rates = NULL;
+        AVFilterChannelLayouts *layouts = NULL;
+        AVFilterLink *outlink = ctx->outputs[i];
+
+        formats = ff_all_formats(outlink->type);
+        if ((ret = ff_formats_ref(formats,
+                                  &outlink->in_formats)) < 0)
+            return ret;
+        switch (outlink->type) {
+        case AVMEDIA_TYPE_AUDIO:
+            rates = ff_all_samplerates();
+            if ((ret = ff_formats_ref(rates,
+                                      &outlink->in_samplerates)) < 0)
+                return ret;
+            layouts = ff_all_channel_layouts();
+            if ((ret = ff_channel_layouts_ref(layouts,
+                                              &outlink->in_channel_layouts)) < 0)
+                return ret;
+            break;
+        }
+    }
+
+    for (i = 0; i < ctx->nb_inputs; i++) {
+        AVFilterFormats *formats, *rates = NULL;
+        AVFilterChannelLayouts *layouts = NULL;
+        AVFilterLink *inlink = ctx->inputs[i];
+
+        formats = ff_all_formats(inlink->type);
+        if ((ret = ff_formats_ref(formats,
+                                  &inlink->out_formats)) < 0)
+            return ret;
+        switch (inlink->type) {
+        case AVMEDIA_TYPE_AUDIO:
+            rates = ff_all_samplerates();
+            if ((ret = ff_formats_ref(rates,
+                                      &inlink->out_samplerates)) < 0)
+                return ret;
+            layouts = ff_all_channel_layouts();
+            if ((ret = ff_channel_layouts_ref(layouts,
+                                              &inlink->out_channel_layouts)) < 0)
+                return ret;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+AVFILTER_DEFINE_CLASS(luascript);
+
+AVFilter ff_avsrc_luascript = {
+    .name          = "luascript",
+    .description   = NULL_IF_CONFIG_SMALL("Lavfi lua script."),
+    .priv_size     = sizeof(LuaScriptContext),
+    .priv_class    = &luascript_class,
+    .init          = luascript_init,
+    .uninit        = luascript_uninit,
+    .query_formats = luascript_query_formats,
+    .inputs        = NULL,
+    .outputs       = NULL,
+    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+};
-- 
1.9.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to