From 2adc81f0a2459a565437009ff6f4ace1dca3d46c Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Thu, 23 Aug 2012 12:32:13 +0200 Subject: vf_dlopen: add a generic filter to load external filters Usage: -vf dlopen=filename.so:args... Examples of such filters are provided in TOOLS/vf_dlopen/ --- DOCS/man/en/vf.rst | 20 +++ Makefile | 1 + TOOLS/vf_dlopen/Makefile | 24 +++ TOOLS/vf_dlopen/filterutils.c | 19 +++ TOOLS/vf_dlopen/filterutils.h | 6 + TOOLS/vf_dlopen/showqscale.c | 99 ++++++++++++ TOOLS/vf_dlopen/telecine.c | 242 ++++++++++++++++++++++++++++ TOOLS/vf_dlopen/tile.c | 157 +++++++++++++++++++ libmpcodecs/vf.c | 2 + libmpcodecs/vf_dlopen.c | 356 ++++++++++++++++++++++++++++++++++++++++++ libmpcodecs/vf_dlopen.h | 88 +++++++++++ 11 files changed, 1014 insertions(+) create mode 100644 TOOLS/vf_dlopen/Makefile create mode 100644 TOOLS/vf_dlopen/filterutils.c create mode 100644 TOOLS/vf_dlopen/filterutils.h create mode 100644 TOOLS/vf_dlopen/showqscale.c create mode 100644 TOOLS/vf_dlopen/telecine.c create mode 100644 TOOLS/vf_dlopen/tile.c create mode 100644 libmpcodecs/vf_dlopen.c create mode 100644 libmpcodecs/vf_dlopen.h diff --git a/DOCS/man/en/vf.rst b/DOCS/man/en/vf.rst index 2a93e8ec78..434c2faec3 100644 --- a/DOCS/man/en/vf.rst +++ b/DOCS/man/en/vf.rst @@ -1405,3 +1405,23 @@ fixpts[=options] *NOTE*: Using this filter together with any sort of seeking (including ``--ss``) may make demons fly out of your nose. + +dlopen=dll[:a0[:a1[:a2[:a3]]]] + Loads an external library to filter the image. The library interface + is the vf_dlopen interface specified using libmpcodecs/vf_dlopen.h. + + dll= + Specify the library to load. This may require a full file system path + in some cases! This argument is required. + + a0= + Specify the first parameter to pass to the library. + + a1= + Specify the second parameter to pass to the library. + + a2= + Specify the third parameter to pass to the library. + + a3= + Specify the fourth parameter to pass to the library. diff --git a/Makefile b/Makefile index 9b73377020..99b310d8d2 100644 --- a/Makefile +++ b/Makefile @@ -161,6 +161,7 @@ SRCS_COMMON = asxparser.c \ libmpcodecs/vf_detc.c \ libmpcodecs/vf_dint.c \ libmpcodecs/vf_divtc.c \ + libmpcodecs/vf_dlopen.c \ libmpcodecs/vf_down3dright.c \ libmpcodecs/vf_dsize.c \ libmpcodecs/vf_dvbscale.c \ diff --git a/TOOLS/vf_dlopen/Makefile b/TOOLS/vf_dlopen/Makefile new file mode 100644 index 0000000000..2fa4e740c2 --- /dev/null +++ b/TOOLS/vf_dlopen/Makefile @@ -0,0 +1,24 @@ +FILTERS = showqscale telecine tile +COMMON = filterutils.o + +OBJECTS = $(patsubst %,%.o,$(FILTERS)) $(COMMON) +HEADERS = $(wildcard *.h) +OUT = $(patsubst %,%.so,$(FILTERS)) + +CFLAGS ?= -Wall -Wextra -O3 -march=native -mtune=native + +CPPFLAGS += -I../../libmpcodecs +CFLAGS += -fPIC +LDFLAGS += -shared -fPIC + +all: $(OUT) + +clean: + $(RM) $(OBJECTS) $(OUT) + +%.so: %.o $(COMMON) + $(CC) $(LDFLAGS) $(LIBS) -o $@ $(COMMON) $< + +# FIXME replace this by real dependency tracking +%.o: %.c $(HEADERS) + diff --git a/TOOLS/vf_dlopen/filterutils.c b/TOOLS/vf_dlopen/filterutils.c new file mode 100644 index 0000000000..e2f14092de --- /dev/null +++ b/TOOLS/vf_dlopen/filterutils.c @@ -0,0 +1,19 @@ +#include +#include + +#include "filterutils.h" + +void copy_plane( + unsigned char *dest, unsigned dest_stride, + const unsigned char *src, unsigned src_stride, + unsigned length, + unsigned rows + ) +{ + unsigned i; + assert(dest_stride >= length); + assert(src_stride >= length); + for (i = 0; i < rows; ++i) + memcpy(&dest[dest_stride * i], &src[src_stride * i], length); +} + diff --git a/TOOLS/vf_dlopen/filterutils.h b/TOOLS/vf_dlopen/filterutils.h new file mode 100644 index 0000000000..4b4229d8ea --- /dev/null +++ b/TOOLS/vf_dlopen/filterutils.h @@ -0,0 +1,6 @@ +void copy_plane( + unsigned char *dest, unsigned dest_stride, + const unsigned char *src, unsigned src_stride, + unsigned length, + unsigned rows + ); diff --git a/TOOLS/vf_dlopen/showqscale.c b/TOOLS/vf_dlopen/showqscale.c new file mode 100644 index 0000000000..9bece60a4f --- /dev/null +++ b/TOOLS/vf_dlopen/showqscale.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +#include "vf_dlopen.h" + +/* + * qscale visualizer + * + * usage: -vf dlopen=./showqscale.so + * + * uses reddish colors for high QPs, and greenish colors for low QPs + */ + +#define PLANE_Y 0 +#define PLANE_U 1 +#define PLANE_V 2 + +static int qs_put_image(struct vf_dlopen_context *ctx) +{ + unsigned int x, y, p; + + assert(ctx->inpic.planes == ctx->outpic[0].planes); + + for (p = 0; p < ctx->outpic[0].planes; ++p) { + assert(ctx->inpic.planewidth[p] == ctx->outpic[0].planewidth[p]); + assert(ctx->inpic.planeheight[p] == ctx->outpic[0].planeheight[p]); + if ((p == PLANE_U || p == PLANE_V) && ctx->inpic_qscale) + continue; +#if 0 + // copy as is + for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y) + memcpy( + &ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y], + &inpic[ctx->outpic[0].planeofs[p] + ctx->inpic.planestride[p] * y], + ctx->outpic[0].planewidth[p] + ); +#else + // reduce contrast + for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y) + for (x = 0; x < ctx->outpic[0].planewidth[p]; ++x) + ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y + x] = + 0x20 + ((ctx->inpic.plane[p][ctx->inpic.planestride[p] * y + x] * 3) >> 2); +#endif + } + + if (ctx->inpic_qscale) { + int qmin = 255; + int qmax = -255; + + // clear U plane + p = PLANE_U; + for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y) + memset( + &ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y], + 0x80, + ctx->outpic[0].planewidth[p] + ); + + // replace V by the qp (0 = green, 12 = red) + p = PLANE_V; + for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y) + for (x = 0; x < ctx->outpic[0].planewidth[p]; ++x) { + int q = ctx->inpic_qscale[ + (x >> (ctx->inpic_qscaleshift - ctx->inpic.planexshift[p])) + + (y >> (ctx->inpic_qscaleshift - ctx->inpic.planeyshift[p])) * ctx->inpic_qscalestride]; + if (q < qmin) + qmin = q; + if (q > qmax) + qmax = q; + int v = 128 + 21 * (q - 6); // range: 0 = green, 12 = red + if (v < 0) + v = 0; + if (v > 255) + v = 255; + ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y + x] = v; + } + + // printf("qscale range: %d .. %d\n", qmin, qmax); + } + + ctx->outpic[0].pts = ctx->inpic.pts; + return 1; +} + +int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv) +{ + VF_DLOPEN_CHECK_VERSION(ctx); + (void) argc; + (void) argv; + static struct vf_dlopen_formatpair map[] = { + { "yv12", "yv12" }, + { NULL, NULL } + }; + ctx->format_mapping = map; + ctx->put_image = qs_put_image; + return 1; +} diff --git a/TOOLS/vf_dlopen/telecine.c b/TOOLS/vf_dlopen/telecine.c new file mode 100644 index 0000000000..109fef2d6b --- /dev/null +++ b/TOOLS/vf_dlopen/telecine.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include + +#include "vf_dlopen.h" +#include "filterutils.h" + +#define MIN(a,b) ((a)<(b)?(a):(b)) + +/* + * telecine filter + * + * usage: -vf dlopen=./telecine.so:t:32 + * + * Parameter: first parameter is "t" for top field first, "b" for bottom field first + * then digits (0-9) for how many fields a frame is to be displayed + * + * Typical patterns (see http://en.wikipedia.org/wiki/Telecine): + * + * NTSC output (30i): + * 27.5p: 32222 + * 24p: 23 (classic) + * 24p: 2332 (preferred) + * 20p: 33 + * 18p: 334 + * 16p: 3444 + * + * PAL output (25i): + * 27.5p: 12222 + * 24p: 222222222223 ("Euro pulldown") + * 16.67p: 33 + * 16p: 33333334 + */ + +typedef struct { + int firstfield; + const char *pattern; + unsigned int pattern_pos; + unsigned char *buffer_plane[4]; + size_t buffer_size[4]; + int pts_num; + int pts_denom; + int occupied; + double lastpts_in; + double lastpts_out; +} tc_data_t; + +static int tc_config(struct vf_dlopen_context *ctx) +{ + // we may return more than one pic! + tc_data_t *tc = ctx->priv; + const char *p; + int max = 0; + tc->pts_num = 0; + tc->pts_denom = 0; + for (p = tc->pattern; *p; ++p) { + if (*p - '0' > max) + max = *p - '0'; + tc->pts_num += 2; + tc->pts_denom += *p - '0'; + } + ctx->out_cnt = (max + 1) / 2; + printf( + "Telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n", + tc->pattern, ctx->out_cnt, tc->pts_num, tc->pts_denom); + return 1; +} + +static int tc_put_image(struct vf_dlopen_context *ctx) +{ + tc_data_t *tc = ctx->priv; + + unsigned p; + unsigned np = ctx->outpic[0].planes; + assert(ctx->inpic.planes == ctx->outpic[0].planes); + + int need_reinit = 0; + + // fix buffers + for (p = 0; p < np; ++p) { + size_t sz = ctx->inpic.planestride[p] * ctx->inpic.planeheight[p]; + if (sz != tc->buffer_size[p]) { + if (p == 0 && tc->buffer_plane[p]) + printf("WARNING: reinitializing telecine buffers.\n"); + tc->buffer_plane[p] = realloc(tc->buffer_plane[p], sz); + tc->buffer_size[p] = sz; + need_reinit = 1; + } + } + + // too big pts change? reinit + if (ctx->inpic.pts < tc->lastpts_in || ctx->inpic.pts > tc->lastpts_in + 0.5) + need_reinit = 1; + + if (need_reinit) { + // initialize telecine + tc->pattern_pos = 0; + tc->occupied = 0; + tc->lastpts_in = ctx->inpic.pts; + tc->lastpts_out = ctx->inpic.pts; + } + + int len = tc->pattern[tc->pattern_pos] - '0'; + unsigned nout; + double delta = ctx->inpic.pts - tc->lastpts_in; + tc->lastpts_in = ctx->inpic.pts; + + for (nout = 0; nout < ctx->out_cnt; ++nout) { + for (p = 0; p < np; ++p) { + assert(ctx->inpic.planewidth[p] == ctx->outpic[nout].planewidth[p]); + assert(ctx->inpic.planeheight[p] == ctx->outpic[nout].planeheight[p]); + } + } + nout = 0; + + if (tc->pattern_pos == 0 && !tc->occupied) { + // at the start of the pattern, reset pts + double newpts = ctx->inpic.pts - (delta * tc->pts_num) / tc->pts_denom; + // printf("pts reset: %f -> %f (delta: %f)\n", tc->lastpts_out, newpts, newpts - tc->lastpts_out); + tc->lastpts_out = newpts; + } + ++tc->pattern_pos; + if (!tc->pattern[tc->pattern_pos]) + tc->pattern_pos = 0; + + if (len == 0) { + // do not output any field from this frame + return 0; + } + + if (tc->occupied) { + for (p = 0; p < np; ++p) { + // fill in the EARLIER field from the buffered pic + copy_plane( + &ctx->outpic[nout].plane[p][ctx->outpic[nout].planestride[p] * tc->firstfield], + ctx->outpic[nout].planestride[p] * 2, + &tc->buffer_plane[p][ctx->inpic.planestride[p] * tc->firstfield], + ctx->inpic.planestride[p] * 2, + MIN(ctx->inpic.planestride[p], ctx->outpic[nout].planestride[p]), + (ctx->inpic.planeheight[p] - tc->firstfield + 1) / 2 + ); + // fill in the LATER field from the new pic + copy_plane( + &ctx->outpic[nout].plane[p][ctx->outpic[nout].planestride[p] * !tc->firstfield], + ctx->outpic[nout].planestride[p] * 2, + &ctx->inpic.plane[p][ctx->inpic.planestride[p] * !tc->firstfield], + ctx->inpic.planestride[p] * 2, + MIN(ctx->inpic.planestride[p], ctx->outpic[nout].planestride[p]), + (ctx->inpic.planeheight[p] - !tc->firstfield + 1) / 2 + ); + } + tc->lastpts_out += (delta * tc->pts_num) / tc->pts_denom; + ctx->outpic[nout].pts = tc->lastpts_out; + // printf("pts written: %f\n", ctx->outpic[nout].pts); + ++nout; + --len; + tc->occupied = 0; + } + + while (len >= 2) { + // output THIS image as-is + for (p = 0; p < np; ++p) + copy_plane( + ctx->outpic[nout].plane[p], ctx->outpic[nout].planestride[p], + ctx->inpic.plane[p], ctx->inpic.planestride[p], + MIN(ctx->inpic.planestride[p], ctx->outpic[nout].planestride[p]), + ctx->inpic.planeheight[p] + ); + tc->lastpts_out += (delta * tc->pts_num) / tc->pts_denom; + ctx->outpic[nout].pts = tc->lastpts_out; + // printf("pts written: %f\n", ctx->outpic[nout].pts); + ++nout; + len -= 2; + } + + if (len >= 1) { + // copy THIS image to the buffer, we need it later + for (p = 0; p < np; ++p) + copy_plane( + &tc->buffer_plane[p][0], ctx->inpic.planestride[p], + &ctx->inpic.plane[p][0], ctx->inpic.planestride[p], + ctx->inpic.planestride[p], + ctx->inpic.planeheight[p] + ); + tc->occupied = 1; + } + + return nout; +} + +void tc_uninit(struct vf_dlopen_context *ctx) +{ + tc_data_t *tc = ctx->priv; + free(tc->buffer_plane[3]); + free(tc->buffer_plane[2]); + free(tc->buffer_plane[1]); + free(tc->buffer_plane[0]); + free(tc); +} + +int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv) +{ + VF_DLOPEN_CHECK_VERSION(ctx); + + const char *a0 = (argc < 1) ? "t" : argv[0]; + const char *a1 = (argc < 2) ? "23" : argv[1]; + + if (!a0[0] || a0[1] || !a1[0] || argc > 2) + return -1; + + tc_data_t *tc = malloc(sizeof(tc_data_t)); + memset(tc, 0, sizeof(*tc)); + + if (a0[0] == 't') + tc->firstfield = 0; + else if (a0[0] == 'b') + tc->firstfield = 1; + else { + printf("telecine: invalid first field\n"); + free(tc); + return -1; + } + + tc->pattern = a1; + + const char *p; + for (p = tc->pattern; *p; ++p) + if (*p < '0' || *p > '9') { + printf("telecine: invalid pattern\n"); + free(tc); + return -1; + } + + ctx->priv = tc; + ctx->format_mapping = NULL; // anything goes + ctx->config = tc_config; + ctx->put_image = tc_put_image; + ctx->uninit = tc_uninit; + + return 1; +} diff --git a/TOOLS/vf_dlopen/tile.c b/TOOLS/vf_dlopen/tile.c new file mode 100644 index 0000000000..0e9a14fb62 --- /dev/null +++ b/TOOLS/vf_dlopen/tile.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include + +#include "vf_dlopen.h" +#include "filterutils.h" + +/* + * tile filter + * + * usage: -vf dlopen=./tile.so:4:3 + * + * only supports rgb24 and yv12 for now + * in theory can support any format where rows are a multiple of bytes, and the + * multiple is known + */ + +#define ALLFORMATS \ + /* format bytes xmul ymul */ \ + FORMAT("rgb24", 3, 1, 1) \ + FORMAT("yv12", 1, 2, 2) + +typedef struct { + int rows, cols; + unsigned char *buffer_plane[4]; + size_t buffer_size[4]; + int pos; + int pixelbytes; +} tile_data_t; + +static int tile_config(struct vf_dlopen_context *ctx) +{ + // we may return more than one pic! + tile_data_t *tile = ctx->priv; + + ctx->out_width = tile->cols * ctx->in_width; + ctx->out_height = tile->rows * ctx->in_height; + ctx->out_d_width = tile->cols * ctx->in_d_width; + ctx->out_d_height = tile->rows * ctx->in_d_height; + +#define FORMAT(fmt,sz,xmul,ymul) \ + if (!strcmp(ctx->in_fmt, fmt)) { \ + if (ctx->in_width % xmul || ctx->in_height % ymul) { \ + printf("Format " fmt " requires width to be a multiple of %d and height to be a multiple of %d\n", \ + xmul, ymul); \ + return -1; \ + } \ + tile->pixelbytes = sz; \ + } else + ALLFORMATS +#undef FORMAT + { + printf("Format %s is not in the list, how come?\n", ctx->in_fmt); + return -1; + } + + return 1; +} + +static int tile_put_image(struct vf_dlopen_context *ctx) +{ + tile_data_t *tile = ctx->priv; + + unsigned p; + unsigned np = ctx->outpic[0].planes; + assert(ctx->inpic.planes == ctx->outpic[0].planes); + + // fix buffers + for (p = 0; p < np; ++p) { + size_t sz = ctx->outpic->planestride[p] * ctx->outpic->planeheight[p]; + if (sz != tile->buffer_size[p]) { + if (p == 0 && tile->buffer_plane[p]) + printf("WARNING: reinitializing output buffers.\n"); + tile->buffer_plane[p] = realloc(tile->buffer_plane[p], sz); + tile->buffer_size[p] = sz; + tile->pos = 0; + } + } + + for (p = 0; p < np; ++p) { + assert(ctx->inpic.planewidth[p] * tile->cols == ctx->outpic->planewidth[p]); + assert(ctx->inpic.planeheight[p] * tile->rows == ctx->outpic->planeheight[p]); + } + + // copy this frame + for (p = 0; p < np; ++p) + copy_plane( + &tile->buffer_plane[p][ctx->outpic->planestride[p] * ctx->inpic.planeheight[p] * (tile->pos / tile->cols) + tile->pixelbytes * ctx->inpic.planewidth[p] * (tile->pos % tile->cols)], + ctx->outpic->planestride[p], + ctx->inpic.plane[p], + ctx->inpic.planestride[p], + tile->pixelbytes * ctx->inpic.planewidth[p], + ctx->inpic.planeheight[p] + ); + + ++tile->pos; + if (tile->pos == tile->rows * tile->cols) { + // copy THIS image to the buffer, we need it later + for (p = 0; p < np; ++p) + copy_plane( + ctx->outpic->plane[p], ctx->outpic->planestride[p], + &tile->buffer_plane[p][0], ctx->outpic->planestride[p], + tile->pixelbytes * ctx->outpic->planewidth[p], + ctx->outpic->planeheight[p] + ); + ctx->outpic->pts = ctx->inpic.pts; + tile->pos = 0; + return 1; + } + + return 0; +} + +void tile_uninit(struct vf_dlopen_context *ctx) +{ + tile_data_t *tile = ctx->priv; + free(tile->buffer_plane[3]); + free(tile->buffer_plane[2]); + free(tile->buffer_plane[1]); + free(tile->buffer_plane[0]); + free(tile); +} + +int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv) +{ + VF_DLOPEN_CHECK_VERSION(ctx); + + if (argc != 2) + return -1; + + tile_data_t *tile = malloc(sizeof(tile_data_t)); + memset(tile, 0, sizeof(*tile)); + + tile->cols = atoi(argv[0]); + tile->rows = atoi(argv[1]); + + if (!tile->rows || !tile->cols) { + printf("tile: invalid rows/cols\n"); + free(tile); + return -1; + } + + ctx->priv = tile; + static struct vf_dlopen_formatpair map[] = { +#define FORMAT(fmt,sz,xmul,ymul) {fmt, NULL}, + ALLFORMATS +#undef FORMAT + {NULL, NULL} + }; + ctx->format_mapping = map; + ctx->config = tile_config; + ctx->put_image = tile_put_image; + ctx->uninit = tile_uninit; + + return 1; +} diff --git a/libmpcodecs/vf.c b/libmpcodecs/vf.c index 8017b6b510..4dc4da1835 100644 --- a/libmpcodecs/vf.c +++ b/libmpcodecs/vf.c @@ -114,6 +114,7 @@ extern const vf_info_t vf_info_geq; extern const vf_info_t vf_info_ow; extern const vf_info_t vf_info_fixpts; extern const vf_info_t vf_info_stereo3d; +extern const vf_info_t vf_info_dlopen; // list of available filters: static const vf_info_t *const filter_list[] = { @@ -205,6 +206,7 @@ static const vf_info_t *const filter_list[] = { &vf_info_ow, &vf_info_fixpts, &vf_info_stereo3d, + &vf_info_dlopen, NULL }; diff --git a/libmpcodecs/vf_dlopen.c b/libmpcodecs/vf_dlopen.c new file mode 100644 index 0000000000..1468a05998 --- /dev/null +++ b/libmpcodecs/vf_dlopen.c @@ -0,0 +1,356 @@ +/* + * This file is part of mplayer. + * + * mplayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mplayer 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mplayer. If not, see . + */ + +#include +#include +#include +#include + +#include "config.h" +#include "mp_msg.h" + +#include "img_format.h" +#include "mp_image.h" +#include "vf.h" + +#include "m_option.h" +#include "m_struct.h" + +#include "vf_dlopen.h" + +#ifdef _WIN32 +# include +# define DLLOpen(name) LoadLibrary(name) +# define DLLClose(handle) FreeLibrary(handle) +# define DLLSymbol(handle, name) ((void *)GetProcAddress(handle, name)) +#else +# include +# define DLLOpen(name) dlopen(name, RTLD_NOW) +# define DLLClose(handle) dlclose(handle) +# define DLLSymbol(handle, name) dlsym(handle, name) +#endif + +static struct vf_priv_s { + const char *cfg_dllname; + int cfg_argc; + const char *cfg_argv[4]; + void *dll; + struct vf_dlopen_context filter; + + // output mp_image_t stuff + mp_image_t *outpic[FILTER_MAX_OUTCNT]; + + // multi frame output + unsigned int outbufferpos; + unsigned int outbufferlen; + mp_image_t *outbuffermpi; + + // qscale buffer + unsigned char *qbuffer; + size_t qbuffersize; + + unsigned int outfmt; + + int argc; +} const vf_priv_dflt = {}; + +//===========================================================================// + +static void set_imgprop(struct vf_dlopen_picdata *out, const mp_image_t *mpi) +{ + int i; + out->planes = mpi->num_planes; + for (i = 0; i < mpi->num_planes; ++i) { + out->plane[i] = mpi->planes[i]; + out->planestride[i] = mpi->stride[i]; + out->planewidth[i] = + i ? (/*mpi->chroma_width*/ mpi->w >> mpi->chroma_x_shift) : mpi->w; + out->planeheight[i] = + i ? (/*mpi->chroma_height*/ mpi->h >> mpi->chroma_y_shift) : mpi->h; + out->planexshift[i] = i ? mpi->chroma_x_shift : 0; + out->planeyshift[i] = i ? mpi->chroma_y_shift : 0; + } +} + +static int config(struct vf_instance *vf, + int width, int height, int d_width, int d_height, + unsigned int flags, unsigned int fmt) +{ + vf->priv->filter.in_width = width; + vf->priv->filter.in_height = height; + vf->priv->filter.in_d_width = d_width; + vf->priv->filter.in_d_height = d_height; + vf->priv->filter.in_fmt = mp_imgfmt_to_name(fmt); + vf->priv->filter.out_width = width; + vf->priv->filter.out_height = height; + vf->priv->filter.out_d_width = d_width; + vf->priv->filter.out_d_height = d_height; + vf->priv->filter.out_fmt = NULL; + vf->priv->filter.out_cnt = 1; + + if (!vf->priv->filter.in_fmt) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "invalid input/output format\n"); + return 0; + } + if (vf->priv->filter.config && vf->priv->filter.config(&vf->priv->filter) < 0) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "filter config failed\n"); + return 0; + } + + if (vf->priv->filter.out_fmt) + vf->priv->outfmt = mp_imgfmt_from_name(vf->priv->filter.out_fmt); + else { + struct vf_dlopen_formatpair *p = vf->priv->filter.format_mapping; + vf->priv->outfmt = 0; + if (p) { + for (; p->from; ++p) { + // TODO support pixel format classes in matching + if (!strcmp(p->from, vf->priv->filter.in_fmt)) { + vf->priv->outfmt = p->to ? mp_imgfmt_from_name(p->to) : fmt; + break; + } + } + } else + vf->priv->outfmt = fmt; + vf->priv->filter.out_fmt = mp_imgfmt_to_name(vf->priv->outfmt); + } + + if (!vf->priv->outfmt) { + mp_msg(MSGT_VFILTER, MSGL_ERR, + "filter config wants an unsupported output format\n"); + return 0; + } + if (!vf->priv->filter.out_cnt || vf->priv->filter.out_cnt > + FILTER_MAX_OUTCNT) { + mp_msg(MSGT_VFILTER, MSGL_ERR, + "filter config wants to yield zero or too many output frames\n"); + return 0; + } + + int i; + for (i = 0; i < vf->priv->filter.out_cnt; ++i) { + vf->priv->outpic[i] = + alloc_mpi(vf->priv->filter.out_width, vf->priv->filter.out_height, + vf->priv->outfmt); + set_imgprop(&vf->priv->filter.outpic[i], vf->priv->outpic[i]); + } + + return vf_next_config(vf, vf->priv->filter.out_width, + vf->priv->filter.out_height, + vf->priv->filter.out_d_width, + vf->priv->filter.out_d_height, + flags, vf->priv->outfmt); +} + +static void uninit(struct vf_instance *vf) +{ + if (vf->priv->filter.uninit) + vf->priv->filter.uninit(&vf->priv->filter); + memset(&vf->priv->filter, 0, sizeof(&vf->priv->filter)); + if (vf->priv->dll) { + DLLClose(vf->priv->dll); + vf->priv->dll = NULL; + } + int i; + for (i = 0; i < vf->priv->filter.out_cnt; ++i) { + free_mp_image(vf->priv->outpic[i]); + vf->priv->outpic[i] = NULL; + } + if (vf->priv->qbuffer) { + free(vf->priv->qbuffer); + vf->priv->qbuffer = NULL; + } +} + +static int continue_put_image(struct vf_instance *vf) +{ + int k; + int ret = 0; + + mp_image_t *dmpi = + vf_get_image(vf->next, vf->priv->outfmt, MP_IMGTYPE_EXPORT, 0, + vf->priv->outpic[vf->priv->outbufferpos]->w, + vf->priv->outpic[vf->priv->outbufferpos]->h); + for (k = 0; k < vf->priv->outpic[vf->priv->outbufferpos]->num_planes; + ++k) { + dmpi->planes[k] = vf->priv->outpic[vf->priv->outbufferpos]->planes[k]; + dmpi->stride[k] = vf->priv->outpic[vf->priv->outbufferpos]->stride[k]; + } + + // pass through qscale if we can + vf_clone_mpi_attributes(dmpi, vf->priv->outbuffermpi); + + ret = + vf_next_put_image(vf, dmpi, + vf->priv->filter.outpic[vf->priv->outbufferpos].pts); + + ++vf->priv->outbufferpos; + + // more frames left? + if (vf->priv->outbufferpos < vf->priv->outbufferlen) + vf_queue_frame(vf, continue_put_image); + + return ret; +} + +static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) +{ + int i, k; + + set_imgprop(&vf->priv->filter.inpic, mpi); + if (mpi->qscale) { + if (mpi->qscale_type != 0) { + k = mpi->qstride * ((mpi->height + 15) >> 4); + if (vf->priv->qbuffersize != k) { + vf->priv->qbuffer = realloc(vf->priv->qbuffer, k); + vf->priv->qbuffersize = k; + } + for (i = 0; i < k; ++i) + vf->priv->qbuffer[i] = norm_qscale(mpi->qscale[i], + mpi->qscale_type); + vf->priv->filter.inpic_qscale = vf->priv->qbuffer; + } else + vf->priv->filter.inpic_qscale = mpi->qscale; + vf->priv->filter.inpic_qscalestride = mpi->qstride; + vf->priv->filter.inpic_qscaleshift = 4; + } else { + vf->priv->filter.inpic_qscale = NULL; + vf->priv->filter.inpic_qscalestride = 0; + vf->priv->filter.inpic_qscaleshift = 0; + } + vf->priv->filter.inpic.pts = pts; + + int ret = vf->priv->filter.put_image(&vf->priv->filter); + if (ret <= 0) + return ret; + + vf->priv->outbuffermpi = mpi; + vf->priv->outbufferlen = ret; + vf->priv->outbufferpos = 0; + return continue_put_image(vf); +} + +//===========================================================================// + +static int query_format(struct vf_instance *vf, unsigned int fmt) +{ + if (IMGFMT_IS_HWACCEL(fmt) || fmt == IMGFMT_MJPEG || fmt == IMGFMT_MPEGPES) + return 0; // these can't really be filtered + if (fmt == IMGFMT_RGB8 || fmt == IMGFMT_BGR8) + return 0; // we don't have palette support, sorry + const char *fmtname = mp_imgfmt_to_name(fmt); + if (!fmtname) + return 0; + struct vf_dlopen_formatpair *p = vf->priv->filter.format_mapping; + unsigned int outfmt = 0; + if (p) { + for (; p->from; ++p) { + // TODO support pixel format classes in matching + if (!strcmp(p->from, fmtname)) { + outfmt = p->to ? mp_imgfmt_from_name(p->to) : fmt; + break; + } + } + } else + outfmt = fmt; + if (!outfmt) + return 0; + return vf_next_query_format(vf, outfmt); +} + +static int vf_open(vf_instance_t *vf, char *args) +{ + if (!vf->priv->cfg_dllname) { + mp_msg(MSGT_VFILTER, MSGL_ERR, + "usage: -vf dlopen=filename.so:function:args\n"); + return 0; + } + + vf->priv->dll = DLLOpen(vf->priv->cfg_dllname); + if (!vf->priv->dll) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "library not found: %s\n", + vf->priv->cfg_dllname); + return 0; + } + + vf_dlopen_getcontext_func *func = + (vf_dlopen_getcontext_func *) DLLSymbol(vf->priv->dll, "vf_dlopen_getcontext"); + if (!func) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "library is not a filter: %s\n", + vf->priv->cfg_dllname); + return 0; + } + + memset(&vf->priv->filter, 0, sizeof(vf->priv->filter)); + vf->priv->filter.major_version = VF_DLOPEN_MAJOR_VERSION; + vf->priv->filter.minor_version = VF_DLOPEN_MINOR_VERSION; + + // count arguments + for (vf->priv->cfg_argc = 0; + vf->priv->cfg_argc < sizeof(vf->priv->cfg_argv) / sizeof(vf->priv->cfg_argv[0]) && vf->priv->cfg_argv[vf->priv->cfg_argc]; + ++vf->priv->cfg_argc) + ; + + if (func(&vf->priv->filter, vf->priv->cfg_argc, vf->priv->cfg_argv) < 0) { + mp_msg(MSGT_VFILTER, MSGL_ERR, + "function did not create a filter: %s\n", + vf->priv->cfg_dllname); + return 0; + } + + if (!vf->priv->filter.put_image) { + mp_msg(MSGT_VFILTER, MSGL_ERR, + "function did not create a filter that can put images: %s\n", + vf->priv->cfg_dllname); + return 0; + } + + vf->put_image = put_image; + vf->query_format = query_format; + vf->config = config; + vf->uninit = uninit; + + return 1; +} + +#define ST_OFF(f) M_ST_OFF(struct vf_priv_s, f) +static m_option_t vf_opts_fields[] = { + {"dll", ST_OFF(cfg_dllname), CONF_TYPE_STRING, 0, 0, 0, NULL}, + {"a0", ST_OFF(cfg_argv[0]), CONF_TYPE_STRING, 0, 0, 0, NULL}, + {"a1", ST_OFF(cfg_argv[1]), CONF_TYPE_STRING, 0, 0, 0, NULL}, + {"a2", ST_OFF(cfg_argv[2]), CONF_TYPE_STRING, 0, 0, 0, NULL}, + {"a3", ST_OFF(cfg_argv[3]), CONF_TYPE_STRING, 0, 0, 0, NULL}, + { NULL, NULL, 0, 0, 0, 0, NULL } +}; + +static const m_struct_t vf_opts = { + "dlopen", + sizeof(struct vf_priv_s), + &vf_priv_dflt, + vf_opts_fields +}; + +const vf_info_t vf_info_dlopen = { + "Dynamic library filter", + "dlopen", + "Rudolf Polzer", + "", + vf_open, + &vf_opts +}; + +//===========================================================================// diff --git a/libmpcodecs/vf_dlopen.h b/libmpcodecs/vf_dlopen.h new file mode 100644 index 0000000000..962605ca28 --- /dev/null +++ b/libmpcodecs/vf_dlopen.h @@ -0,0 +1,88 @@ +#ifndef VF_DLOPEN_H +#define VF_DLOPEN_H + +// when doing a two-way compatible change, don't change these +// when doing a backwards compatible change, bump minor version +// when doing an incompatible change, bump major version and zero minor version +#define VF_DLOPEN_MAJOR_VERSION 1 +#define VF_DLOPEN_MINOR_VERSION 0 + +#if VF_DLOPEN_MINOR_VERSION > 0 +# define VF_DLOPEN_CHECK_VERSION(ctx) \ + do { \ + if (ctx->major_version != VF_DLOPEN_MAJOR_VERSION || \ + ctx->minor_version < VF_DLOPEN_MINOR_VERSION) \ + return -1; \ + } while (0) +#else +// workaround for "comparison is always false" warning +# define VF_DLOPEN_CHECK_VERSION(ctx) \ + do { \ + if (ctx->major_version != VF_DLOPEN_MAJOR_VERSION) \ + return -1; \ + } while (0) +#endif + +// valid pixel format names: +// "yv12": planar YUV, U and V planes have an xshift and yshift of 1 +// "rgb24": packed RGB24 +struct vf_dlopen_formatpair { + const char *from; // (LATER) can also be a name of a format class + const char *to; // if NULL, this means identical format as source +}; + +#define FILTER_MAX_OUTCNT 16 + +struct vf_dlopen_picdata { + unsigned int planes; + unsigned char *plane[4]; + signed int planestride[4]; + unsigned int planewidth[4]; + unsigned int planeheight[4]; + unsigned int planexshift[4]; + unsigned int planeyshift[4]; + double pts; +}; + +struct vf_dlopen_context { + unsigned short major_version; + unsigned short minor_version; + + void *priv; + + struct vf_dlopen_formatpair *format_mapping; + // {NULL, NULL} terminated list of supported format pairs + // if NULL, anything goes + + int (*config)(struct vf_dlopen_context *ctx); // -1 = error + // image config is put into the in_* members before calling this + // fills in the out_* members (which are preinitialized for an identity vf_dlopen_context) + + int (*put_image)(struct vf_dlopen_context *ctx); // returns number of images written, or negative on error + // before this is called, inpic_* and outpic_* are filled + + void (*uninit)(struct vf_dlopen_context *ctx); + + unsigned int in_width; + unsigned int in_height; + unsigned int in_d_width; + unsigned int in_d_height; + const char *in_fmt; + unsigned int out_width; + unsigned int out_height; + unsigned int out_d_width; + unsigned int out_d_height; + const char *out_fmt; + unsigned int out_cnt; + + struct vf_dlopen_picdata inpic; + char *inpic_qscale; + unsigned int inpic_qscalestride; + unsigned int inpic_qscaleshift; + + struct vf_dlopen_picdata outpic[FILTER_MAX_OUTCNT]; +}; +typedef int (vf_dlopen_getcontext_func)(struct vf_dlopen_context *ctx, int argc, const char **argv); // negative on error +vf_dlopen_getcontext_func vf_dlopen_getcontext; + +#endif -- cgit v1.2.3