aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefano Sabatini <stefano.sabatini-lala@poste.it>2011-06-25 13:06:24 +0200
committerStefano Sabatini <stefasab@gmail.com>2011-12-31 11:43:10 +0100
commit8fb03b4d7043712733d4a306b1d31f6e6bfa464e (patch)
tree7379dd5aaf00ad6018c87ef821afc77d6c3c313d
parent549b53e8cbc45d20a52a90a181d40d86c751585d (diff)
downloadffmpeg-8fb03b4d7043712733d4a306b1d31f6e6bfa464e.tar.gz
lavfi: port tinterlace filter from MPlayer
Port MPlayer tinterlace filter from MPlayer, with some ideas taken from the FFmbc/libavfilter port, with the following main differences: * added support for full-scale YUVJ formats * added support for YUVA420P * request_frame() on the filter is forced to return a frame * some code factorization (related to the copy_picture_fields() function) * fixed black padding values for mode 3
-rw-r--r--Changelog1
-rwxr-xr-xconfigure1
-rw-r--r--doc/filters.texi34
-rw-r--r--libavfilter/Makefile1
-rw-r--r--libavfilter/allfilters.c1
-rw-r--r--libavfilter/avfilter.h4
-rw-r--r--libavfilter/vf_tinterlace.c325
7 files changed, 365 insertions, 2 deletions
diff --git a/Changelog b/Changelog
index 4ab788237d..c65ce07068 100644
--- a/Changelog
+++ b/Changelog
@@ -11,6 +11,7 @@ version next:
- thumbnail video filter
- XML output in ffprobe
- asplit audio filter
+- tinterlace video filter
version 0.9:
diff --git a/configure b/configure
index d4fb20e756..6a19d602d7 100755
--- a/configure
+++ b/configure
@@ -1652,6 +1652,7 @@ mptestsrc_filter_deps="gpl"
negate_filter_deps="lut_filter"
ocv_filter_deps="libopencv"
scale_filter_deps="swscale"
+tinterlace_filter_deps="gpl"
yadif_filter_deps="gpl"
# libraries
diff --git a/doc/filters.texi b/doc/filters.texi
index da36e8b1b8..387a05e6a7 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -2433,6 +2433,40 @@ Complete example of a thumbnail creation with @command{ffmpeg}:
ffmpeg -i in.avi -vf thumbnail,scale=300:200 -frames:v 1 out.png
@end example
+@section tinterlace
+
+Perform various types of temporal field interlacing.
+
+Frames are counted starting from 1, so the first input frame is
+considered odd.
+
+This filter accepts a single parameter specifying the mode. Available
+modes are:
+
+@table @samp
+@item 0
+Move odd frames into the upper field, even into the lower field,
+generating a double height frame at half framerate.
+
+@item 1
+Only output even frames, odd frames are dropped, generating a frame with
+unchanged height at half framerate.
+
+@item 2
+Only output odd frames, even frames are dropped, generating a frame with
+unchanged height at half framerate.
+
+@item 3
+Expand each frame to full height, but pad alternate lines with black,
+generating a frame with double height at the same input framerate.
+
+@item 4
+Interleave the upper field from odd frames with the lower field from
+even frames, generating a frame with unchanged height at half framerate.
+@end table
+
+Default mode is 0.
+
@section transpose
Transpose rows with columns in the input video and optionally flip it.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index c8ce1d6405..75f9b85c79 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -81,6 +81,7 @@ OBJS-$(CONFIG_SHOWINFO_FILTER) += vf_showinfo.o
OBJS-$(CONFIG_SLICIFY_FILTER) += vf_slicify.o
OBJS-$(CONFIG_SPLIT_FILTER) += vf_split.o
OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o
+OBJS-$(CONFIG_TINTERLACE_FILTER) += vf_tinterlace.o
OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o
OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o
OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index adc78b197b..1637be811c 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -91,6 +91,7 @@ void avfilter_register_all(void)
REGISTER_FILTER (SLICIFY, slicify, vf);
REGISTER_FILTER (SPLIT, split, vf);
REGISTER_FILTER (THUMBNAIL, thumbnail, vf);
+ REGISTER_FILTER (TINTERLACE, tinterlace, vf);
REGISTER_FILTER (TRANSPOSE, transpose, vf);
REGISTER_FILTER (UNSHARP, unsharp, vf);
REGISTER_FILTER (VFLIP, vflip, vf);
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index fb2ce2bbcb..0daa84b0cd 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -30,8 +30,8 @@
#include "libavcodec/avcodec.h"
#define LIBAVFILTER_VERSION_MAJOR 2
-#define LIBAVFILTER_VERSION_MINOR 55
-#define LIBAVFILTER_VERSION_MICRO 101
+#define LIBAVFILTER_VERSION_MINOR 56
+#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \
diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c
new file mode 100644
index 0000000000..7ba164e0ba
--- /dev/null
+++ b/libavfilter/vf_tinterlace.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2011 Stefano Sabatini
+ * Copyright (c) 2010 Baptiste Coudurier
+ * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ * temporal field interlace filter, ported from MPlayer/libmpcodecs
+ */
+
+#include "libavutil/imgutils.h"
+#include "avfilter.h"
+#include "internal.h"
+
+typedef struct {
+ int mode; ///< interlace mode selected
+ int frame; ///< number of the output frame
+ int vsub; ///< chroma vertical subsampling
+ AVFilterBufferRef *cur;
+ AVFilterBufferRef *next;
+ uint8_t *black_data[4]; ///< buffer used to fill padded lines
+ int black_linesize[4];
+} TInterlaceContext;
+
+#define FULL_SCALE_YUVJ_FORMATS \
+ PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
+
+static enum PixelFormat full_scale_yuvj_pix_fmts[] = {
+ FULL_SCALE_YUVJ_FORMATS, PIX_FMT_NONE
+};
+
+static int query_formats(AVFilterContext *ctx)
+{
+ static const enum PixelFormat pix_fmts[] = {
+ PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV444P,
+ PIX_FMT_YUV444P, PIX_FMT_YUV410P, PIX_FMT_YUVA420P,
+ PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
+ PIX_FMT_NONE
+ };
+
+ avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
+ return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+ TInterlaceContext *tinterlace = ctx->priv;
+ int n;
+ tinterlace->mode = 0;
+
+ if (args) {
+ n = sscanf(args, "%d", &tinterlace->mode);
+
+ if (n != 1 || tinterlace->mode < 0 || tinterlace->mode > 4) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Invalid mode '%s', use an integer between 0 and 4\n", args);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ TInterlaceContext *tinterlace = ctx->priv;
+
+ if (tinterlace->cur ) avfilter_unref_buffer(tinterlace->cur );
+ if (tinterlace->next) avfilter_unref_buffer(tinterlace->next);
+
+ av_freep(&tinterlace->black_data[0]);
+}
+
+static int config_out_props(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ AVFilterLink *inlink = outlink->src->inputs[0];
+ const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[outlink->format];
+ TInterlaceContext *tinterlace = ctx->priv;
+
+ tinterlace->vsub = desc->log2_chroma_h;
+ outlink->w = inlink->w;
+ outlink->h = tinterlace->mode == 0 || tinterlace->mode == 3 ?
+ inlink->h*2 : inlink->h;
+
+ if (tinterlace->mode == 3) {
+ uint8_t black[4] = { 16, 128, 128, 16 };
+ int i, ret;
+ if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
+ black[0] = black[3] = 0;
+ ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
+ outlink->w, outlink->h, outlink->format, 1);
+ if (ret < 0)
+ return ret;
+
+ /* fill black picture with black */
+ for (i = 0; i < 4 && tinterlace->black_data[i]; i++) {
+ int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h;
+ memset(tinterlace->black_data[i], black[i],
+ tinterlace->black_linesize[i] * h);
+ }
+ }
+ av_log(ctx, AV_LOG_INFO, "mode:%d h:%d -> h:%d\n",
+ tinterlace->mode, inlink->h, outlink->h);
+
+ return 0;
+}
+
+#define FIELD_UPPER 0
+#define FIELD_LOWER 1
+#define FIELD_UPPER_AND_LOWER 2
+
+/**
+ * Copy picture field from src to dst.
+ *
+ * @param src_field copy from upper, lower field or both
+ * @param interleave leave a padding line between each copied field
+ * @param dst_field copy to upper or lower field,
+ * only meaningful when interleave is selected
+ */
+static inline
+void copy_picture_field(uint8_t *dst[4], int dst_linesize[4],
+ uint8_t *src[4], int src_linesize[4],
+ enum PixelFormat format, int w, int src_h,
+ int src_field, int interleave, int dst_field)
+{
+ const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[format];
+ int plane, vsub = desc->log2_chroma_h;
+ int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
+
+ for (plane = 0; plane < desc->nb_components; plane++) {
+ int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h;
+ int linesize = av_image_get_linesize(format, w, plane);
+ uint8_t *dstp = dst[plane];
+ uint8_t *srcp = src[plane];
+ lines /= k;
+ if (src_field == FIELD_LOWER)
+ srcp += src_linesize[plane];
+ if (interleave && dst_field == FIELD_LOWER)
+ dstp += dst_linesize[plane];
+ av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1),
+ srcp, src_linesize[plane]*k, linesize, lines);
+ }
+}
+
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
+{
+ AVFilterContext *ctx = inlink->dst;
+ TInterlaceContext *tinterlace = ctx->priv;
+
+ if (tinterlace->cur)
+ avfilter_unref_buffer(tinterlace->cur);
+ tinterlace->cur = tinterlace->next;
+ tinterlace->next = picref;
+}
+
+static void end_frame(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ TInterlaceContext *tinterlace = ctx->priv;
+ AVFilterBufferRef *cur = tinterlace->cur;
+ AVFilterBufferRef *next = tinterlace->next;
+ AVFilterBufferRef *out = NULL;
+ int field;
+
+ /* we need at least two frames */
+ if (!tinterlace->cur)
+ return;
+
+ switch (tinterlace->mode) {
+ case 0: /* move the odd frame into the upper field of the new image, even into
+ * the lower field, generating a double-height video at half framerate */
+ out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
+ avfilter_copy_buffer_ref_props(out, cur);
+ out->video->h = outlink->h;
+ out->video->interlaced = 1;
+ out->video->top_field_first = 1;
+
+ /* write odd frame lines into the upper field of the new frame */
+ copy_picture_field(out->data, out->linesize,
+ cur->data, cur->linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER);
+ /* write even frame lines into the lower field of the new frame */
+ copy_picture_field(out->data, out->linesize,
+ next->data, next->linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER);
+ avfilter_unref_buffer(tinterlace->next);
+ tinterlace->next = NULL;
+ break;
+
+ case 1: /* only output even frames, odd frames are dropped; height unchanged, half framerate */
+ case 2: /* only output odd frames, even frames are dropped; height unchanged, half framerate */
+ out = avfilter_ref_buffer(tinterlace->mode == 2 ? cur : next, AV_PERM_READ);
+ avfilter_unref_buffer(tinterlace->next);
+ tinterlace->next = NULL;
+ break;
+
+ case 3: /* expand each frame to double height, but pad alternate
+ * lines with black; framerate unchanged */
+ out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
+ avfilter_copy_buffer_ref_props(out, cur);
+ out->video->h = outlink->h;
+
+ field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
+ /* copy upper and lower fields */
+ copy_picture_field(out->data, out->linesize,
+ cur->data, cur->linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_UPPER_AND_LOWER, 1, field);
+ /* pad with black the other field */
+ copy_picture_field(out->data, out->linesize,
+ tinterlace->black_data, tinterlace->black_linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_UPPER_AND_LOWER, 1, !field);
+ break;
+
+ case 4: /* interleave upper lines from odd frames with lower lines from even frames,
+ * halving the frame rate and preserving image height */
+ out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
+ avfilter_copy_buffer_ref_props(out, cur);
+ out->video->interlaced = 1;
+ out->video->top_field_first = 1;
+
+ /* copy upper field from cur */
+ copy_picture_field(out->data, out->linesize,
+ cur->data, cur->linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_UPPER, 1, FIELD_UPPER);
+ /* copy lower fields from next */
+ copy_picture_field(out->data, out->linesize,
+ next->data, next->linesize,
+ inlink->format, inlink->w, inlink->h,
+ FIELD_LOWER, 1, FIELD_LOWER);
+ avfilter_unref_buffer(tinterlace->next);
+ tinterlace->next = NULL;
+ break;
+ }
+
+ avfilter_start_frame(outlink, out);
+ avfilter_draw_slice(outlink, 0, outlink->h, 1);
+ avfilter_end_frame(outlink);
+
+ tinterlace->frame++;
+}
+
+static int poll_frame(AVFilterLink *outlink)
+{
+ TInterlaceContext *tinterlace = outlink->src->priv;
+ AVFilterLink *inlink = outlink->src->inputs[0];
+ int ret, val;
+
+ val = avfilter_poll_frame(inlink);
+
+ if (val == 1 && !tinterlace->next) {
+ if ((ret = avfilter_request_frame(inlink)) < 0)
+ return ret;
+ val = avfilter_poll_frame(inlink);
+ }
+ assert(tinterlace->next);
+
+ return val;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+ TInterlaceContext *tinterlace = outlink->src->priv;
+ AVFilterLink *inlink = outlink->src->inputs[0];
+
+ do {
+ int ret;
+
+ if ((ret = avfilter_request_frame(inlink)) < 0)
+ return ret;
+ } while (!tinterlace->cur);
+
+ return 0;
+}
+
+static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
+
+AVFilter avfilter_vf_tinterlace = {
+ .name = "tinterlace",
+ .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
+ .priv_size = sizeof(TInterlaceContext),
+ .init = init,
+ .uninit = uninit,
+ .query_formats = query_formats,
+
+ .inputs = (const AVFilterPad[]) {
+ { .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .start_frame = start_frame,
+ .draw_slice = null_draw_slice,
+ .end_frame = end_frame, },
+ { .name = NULL}
+ },
+ .outputs = (const AVFilterPad[]) {
+ { .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_out_props,
+ .poll_frame = poll_frame,
+ .request_frame = request_frame },
+ { .name = NULL}
+ },
+};