On Tue, 2 Feb 2016 23:25:27 +0100 Paul B Mahol <one...@gmail.com> wrote:
> 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 Lua and pkg-config is a sad state of affair. Almost every Linux distro and BSD variant use a different name for pkg-config. (Upstream Lua provides no .pc file.) > 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> This include path is not portable. > + > +#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, > +}; I'm not sure if I understand the implications. Do you have an example file? _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel