summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TOOLS/vf_dlopen/Makefile2
-rw-r--r--TOOLS/vf_dlopen/ildetect.c292
-rwxr-xr-xTOOLS/vf_dlopen/ildetect.sh43
-rw-r--r--video/filter/vf_dlopen.c25
4 files changed, 357 insertions, 5 deletions
diff --git a/TOOLS/vf_dlopen/Makefile b/TOOLS/vf_dlopen/Makefile
index c1eb5e0c9f..8057676d57 100644
--- a/TOOLS/vf_dlopen/Makefile
+++ b/TOOLS/vf_dlopen/Makefile
@@ -19,7 +19,7 @@
# 02110-1301 USA
#
-FILTERS = showqscale telecine tile rectangle framestep
+FILTERS = showqscale telecine tile rectangle framestep ildetect
COMMON = filterutils.o
OBJECTS = $(patsubst %,%.o,$(FILTERS)) $(COMMON)
diff --git a/TOOLS/vf_dlopen/ildetect.c b/TOOLS/vf_dlopen/ildetect.c
new file mode 100644
index 0000000000..6aa62a1153
--- /dev/null
+++ b/TOOLS/vf_dlopen/ildetect.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2012 Rudolf Polzer <divVerent@xonotic.org>
+ *
+ * This file is part of mpv's vf_dlopen examples.
+ *
+ * mpv's vf_dlopen examples are free software; you can redistribute them and/or
+ * modify them 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.
+ *
+ * mpv's vf_dlopen examples are distributed in the hope that they 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 mpv's vf_dlopen examples; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "vf_dlopen.h"
+
+#include "filterutils.h"
+
+/*
+ * interlacing detector
+ *
+ * usage: -vf dlopen=./ildetect.so:<method>:<threshold>
+ *
+ * outputs an interlacing detection report at the end
+ *
+ * methods:
+ * 0 = transcode 32detect (default)
+ * 1 = decomb IsCombed
+ * 2 = IsCombedTIVTC
+ * 3 = simple average
+ *
+ * threshold:
+ * normalized at 1
+ */
+
+typedef struct {
+ int method;
+ double combing_threshold;
+ double motion_threshold;
+ double motion_amount;
+ double yes_threshold;
+ double no_threshold;
+ double total_yes_threshold;
+ double total_no_threshold;
+ double tc_threshold;
+ double decision_threshold;
+ double tc_decision_threshold;
+ double lastcombed;
+ int numtotalframes;
+ int numdecidedframes;
+ int totalcombedframes;
+ int numjumpingadjacentframes;
+ int numdecidedadjacentframes;
+ unsigned char *buffer_data;
+ size_t buffer_size;
+} ildetect_data_t;
+
+static int il_config(struct vf_dlopen_context *ctx)
+{
+ ctx->out_height -= 4;
+ ctx->out_d_height = ctx->out_height;
+ return 1;
+}
+
+static int il_decision(struct vf_dlopen_context *ctx,
+ int p0, int p1, int p2, int p3, int p4)
+{
+ ildetect_data_t *il = ctx->priv;
+
+ // model for threshold: p0, p2, p4 = 0; p1, p3 = t
+ switch (il->method) {
+ case 0: { // diff-diff (transcode 32detect)
+ int d12 = p1 - p2; // t
+ int d13 = p1 - p3; // 0
+ if (abs(d12) > 15 * il->combing_threshold &&
+ abs(d13) < 10 * il->combing_threshold)
+ return 1;
+ // true for t > 15
+ break;
+ }
+ case 1: { // multiply (decomb IsCombed)
+ int d12 = p1 - p2; // t
+ int d32 = p3 - p2; // t
+ if (d12 * d32 > pow(il->combing_threshold, 2) * (25*25))
+ return 1;
+ // true for t > 21
+ break;
+ }
+ case 2: { // blur-blur (IsCombedTIVTC)
+ int b024 = p0 + 6 * p2 + p4; // 0
+ int b13 = 4 * p1 + 4 * p3; // 8t
+ if (abs(b024 - b13) > il->combing_threshold * 8 * 20)
+ return 1;
+ // true for t > 20
+ break;
+ }
+ case 3: { // average-average
+ int d123 = p1 + p3 - 2 * p2; // 2t
+ int d024 = p0 + p4 - 2 * p2; // 0
+ if ((abs(d123) - abs(d024)) > il->combing_threshold * 30)
+ return 1;
+ // true for t > 15
+ break;
+ }
+ }
+ return 0;
+}
+
+static int il_put_image(struct vf_dlopen_context *ctx)
+{
+ ildetect_data_t *il = ctx->priv;
+ unsigned int x, y;
+ int first_frame = 0;
+
+ size_t sz = ctx->inpic.planestride[0] * ctx->inpic.planeheight[0];
+ if (sz != il->buffer_size) {
+ il->buffer_data = realloc(il->buffer_data, sz);
+ il->buffer_size = sz;
+ first_frame = 1;
+ }
+
+ assert(ctx->inpic.planes == 1);
+ assert(ctx->outpic[0].planes == 1);
+
+ assert(ctx->inpic.planewidth[0] == ctx->outpic[0].planewidth[0]);
+ assert(ctx->inpic.planeheight[0] == ctx->outpic[0].planeheight[0] + 4);
+
+ if (first_frame) {
+ printf("First frame\n");
+ il->lastcombed = -1;
+ } else {
+ // detect interlacing
+ // for each row of 5 pixels, compare:
+ // p2 vs (p1 + p3) / 2
+ // p2 vs (p0 + p4) / 2
+ unsigned int totalcombedframes = 0; // add 255 per combed pixel
+ unsigned int totalpixels = 0;
+ for (y = 0; y < ctx->inpic.planeheight[0] - 4; ++y) {
+ unsigned char *in_line =
+ &ctx->inpic.plane[0][ctx->inpic.planestride[0] * y];
+ unsigned char *buf_line =
+ &il->buffer_data[ctx->inpic.planestride[0] * y];
+ unsigned char *out_line =
+ &ctx->outpic->plane[0][ctx->outpic->planestride[0] * y];
+ for (x = 0; x < ctx->inpic.planewidth[0]; ++x) {
+ int b2 = buf_line[x + ctx->inpic.planestride[0] * 2];
+ int p0 = in_line[x];
+ int p1 = in_line[x + ctx->inpic.planestride[0]];
+ int p2 = in_line[x + ctx->inpic.planestride[0] * 2];
+ int p3 = in_line[x + ctx->inpic.planestride[0] * 3];
+ int p4 = in_line[x + ctx->inpic.planestride[0] * 4];
+ int is_moving = abs(b2 - p2) > il->motion_threshold;
+
+ if (!is_moving) {
+ out_line[x] = 128;
+ continue;
+ }
+
+ ++totalpixels;
+
+ int combed = il_decision(ctx, p0, p1, p2, p3, p4);
+ totalcombedframes += combed;
+ out_line[x] = 255 * combed;
+ }
+ }
+
+ double avgpixels = totalpixels / (double)
+ ((ctx->inpic.planeheight[0] - 4) * ctx->inpic.planewidth[0]);
+
+ if (avgpixels > il->motion_amount) {
+ double avgcombed = totalcombedframes / (double) totalpixels;
+
+ if (il->lastcombed >= 0) {
+ if (il->lastcombed > il->yes_threshold &&
+ avgcombed < il->no_threshold)
+ ++il->numjumpingadjacentframes;
+ if (il->lastcombed < il->no_threshold &&
+ avgcombed > il->yes_threshold)
+ ++il->numjumpingadjacentframes;
+ ++il->numdecidedadjacentframes;
+ }
+
+ il->lastcombed = avgcombed;
+
+ if (avgcombed > il->yes_threshold) {
+ ++il->numdecidedframes;
+ ++il->totalcombedframes;
+ } else if (avgcombed < il->no_threshold) {
+ ++il->numdecidedframes;
+ }
+ } else
+ il->lastcombed = -1;
+ }
+
+ ++il->numtotalframes;
+
+ copy_plane(
+ il->buffer_data, ctx->inpic.planestride[0],
+ ctx->inpic.plane[0], ctx->inpic.planestride[0],
+ ctx->inpic.planewidth[0],
+ ctx->inpic.planeheight[0]);
+
+ ctx->outpic[0].pts = ctx->inpic.pts;
+ return 1;
+}
+
+void il_uninit(struct vf_dlopen_context *ctx)
+{
+ ildetect_data_t *il = ctx->priv;
+
+ double avgdecided = il->numtotalframes
+ ? il->numdecidedframes / (double) il->numtotalframes : -1;
+ double avgadjacent = il->numdecidedframes
+ ? il->numdecidedadjacentframes / (double) il->numdecidedframes : -1;
+ double avgscore = il->numdecidedframes
+ ? il->totalcombedframes / (double) il->numdecidedframes : -1;
+ double avgjumps = il->numdecidedadjacentframes
+ ? il->numjumpingadjacentframes / (double) il->numdecidedadjacentframes : -1;
+
+ printf("ildetect: Avg decided: %f\n", avgdecided);
+ printf("ildetect: Avg adjacent decided: %f\n", avgadjacent);
+ printf("ildetect: Avg interlaced decided: %f\n", avgscore);
+ printf("ildetect: Avg interlaced/progressive adjacent decided: %f\n", avgjumps);
+
+ if (avgdecided < il->decision_threshold)
+ avgadjacent = avgscore = avgjumps = -1;
+
+ if (avgadjacent < il->tc_decision_threshold)
+ avgadjacent = avgjumps = -1;
+
+ if (avgscore < 0)
+ printf("ildetect: Content is probably: undecided\n");
+ else if (avgscore < il->total_no_threshold)
+ printf("ildetect: Content is probably: PROGRESSIVE\n");
+ else if (avgscore > il->total_yes_threshold && avgjumps < 0)
+ printf("ildetect: Content is probably: INTERLACED (possibly telecined)\n");
+ else if (avgjumps > il->tc_threshold)
+ printf("ildetect: Content is probably: TELECINED\n");
+ else if (avgscore > il->total_yes_threshold)
+ printf("ildetect: Content is probably: INTERLACED\n");
+ else
+ printf("ildetect: Content is probably: unknown\n");
+
+ free(ctx->priv);
+}
+
+int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv)
+{
+ VF_DLOPEN_CHECK_VERSION(ctx);
+ (void) argc;
+ (void) argv;
+
+ ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
+ memset(il, 0, sizeof(*il));
+
+#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
+ il->method = A(0, 0);
+ il->combing_threshold = A(1, 1);
+ il->motion_threshold = A(2, 6);
+ il->motion_amount = A(3, 0.1);
+ il->yes_threshold = A(4, 0.1);
+ il->no_threshold = A(5, 0.05);
+ il->total_yes_threshold = A(6, 0.1);
+ il->total_no_threshold = A(7, 0.05);
+ il->tc_threshold = A(8, 0.1);
+ il->decision_threshold = A(9, 0.2);
+ il->tc_decision_threshold = A(10, 0.2);
+
+ static struct vf_dlopen_formatpair map[] = {
+ { "y8", "y8" },
+ { NULL, NULL }
+ };
+ ctx->format_mapping = map;
+ ctx->config = il_config;
+ ctx->put_image = il_put_image;
+ ctx->uninit = il_uninit;
+ ctx->priv = il;
+ return 1;
+}
diff --git a/TOOLS/vf_dlopen/ildetect.sh b/TOOLS/vf_dlopen/ildetect.sh
new file mode 100755
index 0000000000..e9027077d2
--- /dev/null
+++ b/TOOLS/vf_dlopen/ildetect.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+case "$0" in
+ */*)
+ MYDIR=${0%/*}
+ ;;
+ *)
+ MYDIR=.
+ ;;
+esac
+
+set -e
+
+make -C "$MYDIR"
+
+testfun()
+{
+ ${MPV:-mpv} "$@" \
+ -vf dlopen="$MYDIR/ildetect.so" \
+ -o /dev/null -of nut -ovc rawvideo -no-audio \
+ | grep "^ildetect:"
+}
+
+out=`testfun "$@"`
+echo
+echo
+echo "$out"
+echo
+echo
+case "$out" in
+ *"probably: PROGRESSIVE"*)
+ ${MPV:-mpv} "$@"
+ ;;
+ *"probably: INTERLACED"*)
+ ${MPV:-mpv} "$@" -vf-pre yadif
+ ;;
+ *"probably: TELECINED"*)
+ ${MPV:-mpv} "$@" -vf-pre pullup
+ ;;
+ *)
+ false
+ ;;
+esac
diff --git a/video/filter/vf_dlopen.c b/video/filter/vf_dlopen.c
index 5bdd884c7a..2f13287e3d 100644
--- a/video/filter/vf_dlopen.c
+++ b/video/filter/vf_dlopen.c
@@ -48,7 +48,7 @@
static struct vf_priv_s {
const char *cfg_dllname;
int cfg_argc;
- const char *cfg_argv[4];
+ const char *cfg_argv[16];
void *dll;
struct vf_dlopen_context filter;
@@ -318,11 +318,16 @@ static int vf_open(vf_instance_t *vf, char *args)
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)
+ for (vf->priv->cfg_argc = sizeof(vf->priv->cfg_argv) / sizeof(vf->priv->cfg_argv[0]);
+ vf->priv->cfg_argc > 0 && !vf->priv->cfg_argv[vf->priv->cfg_argc - 1];
+ --vf->priv->cfg_argc)
;
+ // fix empty arguments
+ for (i = 0; i < vf->priv->cfg_argc; ++i)
+ if (vf->priv->cfg_argv[i] == NULL)
+ vf->priv->cfg_argv[i] = talloc_strdup (vf->priv, "");
+
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",
@@ -352,6 +357,18 @@ static m_option_t vf_opts_fields[] = {
{"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},
+ {"a4", ST_OFF(cfg_argv[4]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a5", ST_OFF(cfg_argv[5]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a6", ST_OFF(cfg_argv[6]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a7", ST_OFF(cfg_argv[7]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a8", ST_OFF(cfg_argv[8]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a9", ST_OFF(cfg_argv[9]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a10", ST_OFF(cfg_argv[10]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a11", ST_OFF(cfg_argv[11]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a12", ST_OFF(cfg_argv[12]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a13", ST_OFF(cfg_argv[13]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a14", ST_OFF(cfg_argv[14]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a15", ST_OFF(cfg_argv[15]), CONF_TYPE_STRING, 0, 0, 0, NULL},
{ NULL, NULL, 0, 0, 0, 0, NULL }
};