[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