[FFmpeg-devel] [PATCH] telecine filter

Paul B Mahol onemda at gmail.com
Mon Apr 8 02:33:00 CEST 2013


Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
 Changelog                 |   1 +
 doc/filters.texi          |  38 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/version.h     |   2 +-
 libavfilter/vf_telecine.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 289 insertions(+), 1 deletion(-)
 create mode 100644 libavfilter/vf_telecine.c

diff --git a/Changelog b/Changelog
index 5faa414..bee29c3 100644
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,7 @@ version <next>:
   filtergraph description to be read from a file
 - OpenCL support
 - audio phaser filter
+- telecine filter
 
 
 version 1.2:
diff --git a/doc/filters.texi b/doc/filters.texi
index 483d8a1..9093bf9 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -428,6 +428,44 @@ slope
 Determine how steep is the filter's shelf transition.
 @end table
 
+ at section telecine
+
+Apply telecine process to the video.
+
+The filter accepts parameters as a list of @var{key}=@var{value}
+pairs, separated by ":".
+
+A description of the accepted parameters follows.
+
+ at table @option
+ at item first_field
+ at table @option
+ at item t
+top field first
+ at item b
+bottom field first
+ at end table
+
+ at item pattern
+String representing for how many fields a frame is to be displayed.
+ at end table
+
+Some typical patterns:
+
+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
+
 @section treble
 
 Boost or cut treble (upper) frequencies of the audio using a two-pole
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 277ff71..8380499 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -163,6 +163,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
 OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
 OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
+OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
 OBJS-$(CONFIG_TILE_FILTER)                   += vf_tile.o
 OBJS-$(CONFIG_TINTERLACE_FILTER)             += vf_tinterlace.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4972322..799df05 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -158,6 +158,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(SUBTITLES,      subtitles,      vf);
     REGISTER_FILTER(SUPER2XSAI,     super2xsai,     vf);
     REGISTER_FILTER(SWAPUV,         swapuv,         vf);
+    REGISTER_FILTER(TELECINE,       telecine,       vf);
     REGISTER_FILTER(THUMBNAIL,      thumbnail,      vf);
     REGISTER_FILTER(TILE,           tile,           vf);
     REGISTER_FILTER(TINTERLACE,     tinterlace,     vf);
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 379b9ab..a285835 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -29,7 +29,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFILTER_VERSION_MAJOR  3
-#define LIBAVFILTER_VERSION_MINOR  49
+#define LIBAVFILTER_VERSION_MINOR  50
 #define LIBAVFILTER_VERSION_MICRO 100
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
diff --git a/libavfilter/vf_telecine.c b/libavfilter/vf_telecine.c
new file mode 100644
index 0000000..9dc5f23
--- /dev/null
+++ b/libavfilter/vf_telecine.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2012 Rudolf Polzer
+ * Copyright (c) 2013 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
+ */
+
+/**
+ * @file telecine filter, heavily based from mvp-player:TOOLS/vf_dlopen/telecine.c by
+ * Rudolf Polzer.
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct {
+    const AVClass *class;
+    char *pattern;
+    unsigned int pattern_pos;
+    int first_field;
+    AVRational pts;
+    int out_cnt;
+    int occupied;
+    int planeheight[4];
+    int stride[4];
+    int nb_planes;
+    AVFrame *frame[5];
+    AVFrame *temp;
+} TelecineContext;
+
+#define OFFSET(x) offsetof(TelecineContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption telecine_options[] = {
+    {"first_field", "", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"},
+        {"t", "top", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
+        {"b", "bottom", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
+    {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS},
+    {NULL}
+};
+
+AVFILTER_DEFINE_CLASS(telecine);
+
+static av_cold int init(AVFilterContext *ctx, const char *args)
+{
+    TelecineContext *tc = ctx->priv;
+    const char *p;
+    int max = 0;
+
+    for (p = tc->pattern; *p; p++)
+        if (*p < '0' || *p > '9') {
+            av_log(ctx, AV_LOG_ERROR, "invalid pattern\n");
+            return AVERROR(EINVAL);
+        }
+
+    for (p = tc->pattern; *p; p++) {
+        max = FFMAX(*p - '0', max);
+        tc->pts.num += 2;
+        tc->pts.den += *p - '0';
+    }
+
+    tc->out_cnt = (max + 1) / 2;
+    av_log(ctx, AV_LOG_INFO,
+        "telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n",
+        tc->pattern, tc->out_cnt, tc->pts.num, tc->pts.den);
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *pix_fmts = NULL;
+    int fmt;
+
+    for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) {
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
+        if (!(desc->flags & PIX_FMT_HWACCEL))
+            ff_add_format(&pix_fmts, fmt);
+    }
+
+    ff_set_common_formats(ctx, pix_fmts);
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    TelecineContext *tc = inlink->dst->priv;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    int i, ret;
+
+    tc->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h);
+    if (!tc->temp)
+            return AVERROR(ENOMEM);
+    for (i = 0; i < tc->out_cnt; i++) {
+        tc->frame[i] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
+        if (!tc->frame[i])
+            return AVERROR(ENOMEM);
+    }
+
+    if ((ret = av_image_fill_linesizes(tc->stride, inlink->format, inlink->w)) < 0)
+        return ret;
+
+    tc->planeheight[1] = tc->planeheight[2] = inlink->h >> desc->log2_chroma_h;
+    tc->planeheight[0] = tc->planeheight[3] = inlink->h;
+
+    tc->nb_planes = av_pix_fmt_count_planes(inlink->format);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    TelecineContext *tc = ctx->priv;
+    int i, len, ret = 0, nout = 0;
+
+    len = tc->pattern[tc->pattern_pos] - '0';
+
+    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 (i = 0; i < tc->nb_planes; i++) {
+            // fill in the EARLIER field from the buffered pic
+            av_image_copy_plane(
+                &tc->frame[nout]->data[i][tc->frame[nout]->linesize[i] * tc->first_field],
+                tc->frame[nout]->linesize[i] * 2,
+                &tc->temp->data[i][inpicref->linesize[i] * tc->first_field],
+                inpicref->linesize[i] * 2,
+                tc->stride[i],
+                (tc->planeheight[i] - tc->first_field + 1) / 2);
+            // fill in the LATER field from the new pic
+            av_image_copy_plane(
+                &tc->frame[nout]->data[i][tc->frame[nout]->linesize[i] * !tc->first_field],
+                tc->frame[nout]->linesize[i] * 2,
+                &inpicref->data[i][inpicref->linesize[i] * !tc->first_field],
+                inpicref->linesize[i] * 2,
+                tc->stride[i],
+                (tc->planeheight[i] - !tc->first_field + 1) / 2);
+        }
+        tc->frame[nout]->pts = AV_NOPTS_VALUE;
+        nout++;
+        len--;
+        tc->occupied = 0;
+    }
+
+    while (len >= 2) {
+        // output THIS image as-is
+        for (i = 0; i < tc->nb_planes; i++)
+            av_image_copy_plane(
+                tc->frame[nout]->data[i], tc->frame[nout]->linesize[i],
+                inpicref->data[i], inpicref->linesize[i],
+                tc->stride[i],
+                tc->planeheight[i]);
+        tc->frame[nout]->pts = AV_NOPTS_VALUE;
+        nout++;
+        len -= 2;
+    }
+
+    if (len >= 1) {
+        // copy THIS image to the buffer, we need it later
+        for (i = 0; i < tc->nb_planes; i++)
+            av_image_copy_plane(
+                &tc->temp->data[i][0], inpicref->linesize[i],
+                &inpicref->data[i][0], inpicref->linesize[i],
+                tc->stride[i],
+                tc->planeheight[i]);
+        tc->occupied = 1;
+    }
+
+    for (i = 0; i < nout; i++) {
+        AVFrame *out = av_frame_clone(tc->frame[i]);
+
+        av_frame_copy_props(out, inpicref);
+        ret = ff_filter_frame(outlink, out);
+    }
+    av_frame_free(&inpicref);
+
+    return ret;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    TelecineContext *tc = ctx->priv;
+    int i;
+
+    av_frame_free(&tc->temp);
+    for (i = 0; i < tc->out_cnt; i++)
+        av_frame_free(&tc->frame[i]);
+}
+
+static const AVFilterPad telecine_inputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .filter_frame  = filter_frame,
+        .config_props  = config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad telecine_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+static const char *const shorthand[] = { "first_field", "pattern", NULL };
+
+AVFilter avfilter_vf_telecine = {
+    .name          = "telecine",
+    .description   = NULL_IF_CONFIG_SMALL("Apply telecine process."),
+    .priv_size     = sizeof(TelecineContext),
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = telecine_inputs,
+    .outputs       = telecine_outputs,
+    .priv_class    = &telecine_class,
+    .shorthand     = shorthand,
+};
-- 
1.7.11.2



More information about the ffmpeg-devel mailing list