Hi,

patch attached.
From 5caafe2f74d6eb5563b1103193cc8d136edcfd0f Mon Sep 17 00:00:00 2001
From: Paul B Mahol <one...@gmail.com>
Date: Tue, 2 Feb 2016 10:09:50 +0100
Subject: [RFC][WIP][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 | 428 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 434 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..949256e
--- /dev/null
+++ b/libavfilter/src_luascript.c
@@ -0,0 +1,428 @@
+/*
+ * 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 <lua5.1/lua.h>
+#include <lua5.1/lualib.h>
+#include <lua5.1/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 "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct FiltersContext {
+    AVFilterContext *f;
+    char *options;
+    char *in;
+    char *out;
+} FiltersContext;
+
+typedef struct LuaScriptContext {
+    const AVClass *class;
+    char *script;
+    int width;
+    int height;
+
+    AVFilterGraph *graph;
+    FiltersContext fctx[1024];
+    AVFilterContext *sink[32];
+    int nbf;
+
+    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", NULL, OFFSET(script), AV_OPT_TYPE_STRING, .flags = FLAGS },
+    { NULL },
+};
+
+static int luascript_push_frame(AVFilterContext *ctx, int out_id)
+{
+    LuaScriptContext *s  = ctx->priv;
+    AVFrame *frame = av_frame_alloc();
+    int ret;
+
+    if (!frame)
+        return AVERROR(ENOMEM);
+
+    ret = av_buffersink_get_frame(s->sink[out_id], frame);
+    if (ret < 0)
+        return ret;
+
+    return ff_filter_frame(ctx->outputs[out_id], frame);
+}
+
+static int luascript_request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    unsigned out_id = FF_OUTLINK_IDX(outlink);
+
+    return luascript_push_frame(ctx, out_id);
+}
+
+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 = 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 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 int script_register_filter(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    const char *name = luaL_checkstring(L, 1);
+    AVFilter *filter = avfilter_get_by_name(name);
+
+    if (filter) {
+        avfilter_register(filter);
+        return 0;
+    } else {
+        lua_pushfstring(L, "No such filter %s", name);
+        return 1;
+    }
+}
+
+static int script_filter(lua_State *L)
+{
+    AVFilterContext *ctx = get_ctx(L);
+    LuaScriptContext *s = ctx->priv;
+    const char *name = luaL_checkstring(L, 1);
+    const char *options = luaL_checkstring(L, 2);
+    int ret;
+
+    AVFilter *filter = avfilter_get_by_name(name);
+    if (!filter)
+        return AVERROR(EINVAL);
+
+    s->fctx[s->nbf].f = avfilter_graph_alloc_filter(s->graph, filter, name);
+    if (!s->fctx[s->nbf].f)
+        return AVERROR(ENOMEM);
+    ret = avfilter_init_str(s->fctx[s->nbf].f, options);
+    if (ret)
+        return ret;
+    s->fctx[s->nbf].options = options;
+    s->nbf++;
+    return 0;
+}
+
+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(filter),
+    FN_ENTRY(register_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 av_cold int luascript_common_init(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    lua_State *L = s->L;
+    int ret = 0;
+    int i;
+
+    if (!s->script) {
+        av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->L = L = luaL_newstate();
+    if (!L)
+        return AVERROR(ENOMEM);
+
+    s->graph = avfilter_graph_alloc();
+    if (!s->graph)
+        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);
+    }
+
+    for (i = 0; i < s->fctx[0].f->nb_outputs; i++) {
+        AVFilterPad pad = { 0 };
+        AVFilter *sink;
+
+        pad.type          = avfilter_pad_get_type(s->fctx[0].f->output_pads, i);
+        pad.name          = av_strdup(avfilter_pad_get_name(s->fctx[0].f->output_pads, i));
+        if (!pad.name)
+            return AVERROR(ENOMEM);
+        pad.config_props  = luascript_config_output_props;
+        pad.request_frame = luascript_request_frame;
+        ff_insert_outpad(ctx, i, &pad);
+
+        if (pad.type == AVMEDIA_TYPE_VIDEO)
+            sink = avfilter_get_by_name("buffersink");
+        else
+            sink = avfilter_get_by_name("abuffersink");
+        if (!sink) {
+            return AVERROR(EINVAL);
+        }
+
+        s->sink[i] = avfilter_graph_alloc_filter(s->graph, sink, "sink");
+        if (!s->sink[i]) {
+            return AVERROR(ENOMEM);
+        }
+
+        ret = avfilter_init_str(s->sink[i], NULL);
+        if (ret < 0) {
+            return ret;
+        }
+
+        avfilter_link(s->fctx[0].f, i, s->sink[i], i);
+    }
+
+    ret = avfilter_graph_config(s->graph, NULL);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error configuring the filter graph\n");
+        return ret;
+    }
+
+    return ret;
+}
+
+static av_cold void luascript_uninit(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+
+    lua_close(s->L);
+    avfilter_graph_free(&s->graph);
+}
+
+static int luascript_query_formats(AVFilterContext *ctx)
+{
+    LuaScriptContext *s = ctx->priv;
+    int64_t list64[] = { 0, -1 };
+    int list[] = { 0, -1 };
+    int i, ret;
+
+    for (i = 0; i < ctx->nb_outputs; i++) {
+        AVFilterLink *outlink = ctx->outputs[i];
+        AVFilterLink *sinklink = s->sink[i]->inputs[0];
+
+        list[0] = sinklink->format;
+        if ((ret = ff_formats_ref(ff_make_format_list(list),
+                                  &outlink->in_formats)) < 0)
+            return ret;
+        switch (outlink->type) {
+        case AVMEDIA_TYPE_AUDIO:
+            list[0] = sinklink->sample_rate;
+            if ((ret = ff_formats_ref(ff_make_format_list(list),
+                                      &outlink->in_samplerates)) < 0)
+                return ret;
+            list64[0] = sinklink->channel_layout;
+            if ((ret = ff_channel_layouts_ref(avfilter_make_format64_list(list64),
+                                              &outlink->in_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_common_init,
+    .uninit        = luascript_uninit,
+    .query_formats = luascript_query_formats,
+    .inputs        = NULL,
+    .outputs       = NULL,
+    .flags         = 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