[FFmpeg-devel] [PATCH] add fieldorder video filter
Stefano Sabatini
stefano.sabatini-lala at poste.it
Thu Mar 31 16:15:22 CEST 2011
On date Thursday 2011-03-31 08:47:38 +0100, Mark Himsley encoded:
> Second version of this filter. Renamed and updated from the
> suggestions given for the first version.
>
>
> Converting to and from interlaced PAL DV files, with their
> bottom-field-first interlace field order, can be a pain. Converting
> tff files to DV results in tff DV files, which are hard to work with
> in editing software.
>
> The attached filter can:
>
> Convert field order by either moving all of the lines in the picture
> up by 1 line (bff to tff conversion) or down by 1 line (tff to bff
> conversion). The remaining line, the bottom line in bff to tff
> transforms or the top line in tff to bff transforms, is filled by
> copying the closest line in that field.
>
> Previous to this filter I have used a filter chain like this to do
> bff to tff conversion.
>
> format=yuv422p,crop=720:575:0:1,pad=720:576:0:0:black
>
> but that chain does not fill the remaining line.
>
> --
> Mark
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 5193b66..b24b53f 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -500,6 +500,35 @@ fade=in:0:25, fade=out:975:25
> fade=in:5:20
> @end example
>
> + at section fieldorder
> +
> +Transform the field order of the input video.
> +
> +It requires one parameter: @var{tff}
> +
> + at var{tff} specifies the required field order that the input interlaced
> +video will be transformed to, and it accepts one of the following values:
> +
> + at table @option
> + at item 0
> +output bottom field first
> + at item 1
> +output top field first
> + at end table
> +
> +There is no default.
> +
> +Transformation is done by shifting the picture content up or down
> +by one line, and filling the remaining line with appropriate picture content.
> +This method is consistent with most broadcast field order converters.
> +
> +This filter is very useful when converting to or from PAL DV material,
> +which is bottom field first.
> +
> + at example
> +./ffmpeg -i in.vob -vf "fieldorder=0" out.dv
> + at end example
> +
> @section fifo
>
> Buffer input images and send them when they are requested.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 028aa52..8290b10 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -29,6 +29,7 @@ OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o
> OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
> OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o
> OBJS-$(CONFIG_FADE_FILTER) += vf_fade.o
> +OBJS-$(CONFIG_FIELDORDER_FILTER) += vf_fieldorder.o
> OBJS-$(CONFIG_FIFO_FILTER) += vf_fifo.o
> OBJS-$(CONFIG_FORMAT_FILTER) += vf_format.o
> OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index eb4cb9f..0668efd 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -47,6 +47,7 @@ void avfilter_register_all(void)
> REGISTER_FILTER (DRAWBOX, drawbox, vf);
> REGISTER_FILTER (DRAWTEXT, drawtext, vf);
> REGISTER_FILTER (FADE, fade, vf);
> + REGISTER_FILTER (FIELDORDER, fieldorder, vf);
> REGISTER_FILTER (FIFO, fifo, vf);
> REGISTER_FILTER (FORMAT, format, vf);
> REGISTER_FILTER (FREI0R, frei0r, vf);
> diff --git a/libavfilter/vf_fieldorder.c b/libavfilter/vf_fieldorder.c
> new file mode 100644
> index 0000000..aca0eda
> --- /dev/null
> +++ b/libavfilter/vf_fieldorder.c
> @@ -0,0 +1,203 @@
> +/*
> + * video field order filter
> + * copyright (c) 2011 Mark Himsley
> + * Heavily influenced by vf_pad.c
> + *
> + * 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
> + */
> +
> +/* #define DEBUG */
> +
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +
> +typedef struct
> +{
> + unsigned int dst_tff; ///< output bff/tff
> + int line_step[4]; ///< bytes per pixel per each plane
> + int hsub; ///< chroma subsampling value
> +} FieldOrderContext;
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + FieldOrderContext *fieldorder = ctx->priv;
> +
> + if (!args || !sscanf(args, "%u", &fieldorder->dst_tff) == 1) {
> + av_log(ctx, AV_LOG_ERROR,
> + "Expected 1 argument '#':'%s'\n", args);
> + return AVERROR(EINVAL);
> + }
> + fieldorder->dst_tff = !!fieldorder->dst_tff;
> +
> + av_log(ctx, AV_LOG_INFO, "ttf:%d\n", fieldorder->dst_tff);
> +
> + return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + AVFilterFormats *formats;
> + enum PixelFormat pix_fmt;
> + int ret;
> +
> + /** accept any input pixel format that is not hardware accelerated
> + * and does not have vertically sub-sampled chroma */
> + if (ctx->inputs[0]) {
> + formats = NULL;
> + for (pix_fmt = 0; pix_fmt < PIX_FMT_NB; pix_fmt++)
> + if ( !(av_pix_fmt_descriptors[pix_fmt].flags & PIX_FMT_HWACCEL)
> + && av_pix_fmt_descriptors[pix_fmt].nb_components
> + && !av_pix_fmt_descriptors[pix_fmt].log2_chroma_h
> + && (ret = avfilter_add_format(&formats, pix_fmt)) < 0) {
> + avfilter_formats_unref(&formats);
> + return ret;
> + }
> + avfilter_formats_ref(formats, &ctx->inputs[0]->out_formats);
> + avfilter_formats_ref(formats, &ctx->outputs[0]->in_formats);
> + }
> +
> + return 0;
> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> + AVFilterContext *ctx = inlink->dst;
> + FieldOrderContext *fieldorder = ctx->priv;
> +
> + int is_packed_rgba;
> + int plane, component;
> +
> + /** discover if the pixel format is packed or planar */
> + is_packed_rgba = 1;
> + for (component = 0; component < av_pix_fmt_descriptors[inlink->format].nb_components; component++) {
> + if (av_pix_fmt_descriptors[inlink->format].comp[component].plane != 0) {
> + is_packed_rgba = 0;
> + }
> + }
Nit, technically this is not necessarily packed RGBA, for example
we support some YUV packed formats, e.g. uyvy and uyyvyy.
> +
> + /** discover the bytes per pixel for each plane */
> + if (is_packed_rgba) {
> + fieldorder->line_step[0] = av_get_bits_per_pixel(&av_pix_fmt_descriptors[inlink->format])>>3;
> + } else {
> + for (plane = 0; plane < 4; plane++) {
> + fieldorder->line_step[plane] = 1 + (av_pix_fmt_descriptors[inlink->format].comp->depth_minus1 >> 3 );
> + }
> + }
Check av_image_fill_max_pixsteps() in libavutil/imgutils.h, I suspect
that you can use it in place of the above code.
> + fieldorder->hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
> +
> + return 0;
> +}
> +
> +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
> +{
> + /** cannot do slices because the filter has
> + * to read from pixels outside of the slice */
> +}
Nit++: call it null_draw_slice() { }, this is the convention used in
other similar filters.
> +static void end_frame(AVFilterLink *inlink)
> +{
> + AVFilterContext *ctx = inlink->dst;
> + FieldOrderContext *fieldorder = ctx->priv;
> + AVFilterLink *outlink = ctx->outputs[0];
> +
> + AVFilterBufferRef *inpic = inlink->cur_buf;
> + AVFilterBufferRef *outpic = outlink->out_buf;
> +
> + int plane, line, linesize, linewidth, hsub, h, w;
> + uint8_t *cpy_src, *cpy_dst;
> +
> + h = outpic->video->h;
> + w = outpic->video->w;
> +
> + if (inpic->video->interlaced) {
> + if (inpic->video->top_field_first != fieldorder->dst_tff) {
> + av_dlog(ctx,
> + "picture will move %s one line\n",
> + fieldorder->dst_tff ? "up" : "down");
> + for (plane = 0; plane < 4 && outpic->data[plane]; plane++) {
> + linesize = outpic->linesize[plane];
> + hsub = plane == 1 || plane == 2 ? fieldorder->hsub : 0;
> + linewidth = (w >> hsub) * fieldorder->line_step[plane];
This unfortunately can't work with "weird" packed formats, like UYVY,
UYYVYY etc (packed formats with subsampled components).
You can add a condition for skipping them in query_formats, or simply
enumerate the working formats, whatever you prefer.
> + cpy_src = inpic->data[plane];
> + cpy_dst = outpic->data[plane];
> + if (fieldorder->dst_tff) {
> + /** Move every line up one line, working from
> + * the top to the bottom of the frame.
> + * The original top line is lost.
> + * The new last line is created as a copy of the
> + * penultimate line from that field. */
> + for (line = 0; line < h; line++) {
> + if (1 + line < outpic->video->h) {
> + memcpy(cpy_dst, cpy_src + linesize, linewidth);
> + } else {
> + memcpy(cpy_dst, cpy_src - linesize - linesize, linewidth);
> + }
> + cpy_src += linesize;
> + cpy_dst += linesize;
> + }
> + } else {
> + /** Move every line down one line, working from
> + * the bottom to the top of the frame.
> + * The original bottom line is lost.
> + * The new first line is created as a copy of the
> + * second line from that field. */
> + cpy_src += (h - 1) * linesize;
> + cpy_dst += (h - 1) * linesize;
> + for (line = h - 1; line >= 0 ; line--) {
> + if ( line > 0) {
> + memcpy(cpy_dst, cpy_src - linesize, linewidth);
> + } else {
> + memcpy(cpy_dst, cpy_src + linesize + linesize, linewidth);
> + }
> + cpy_src -= linesize;
> + cpy_dst -= linesize;
> + }
> + }
> + }
> + outpic->video->top_field_first = fieldorder->dst_tff;
> + } else {
> + av_dlog(ctx,
> + "field order already correct\n");
> + }
> + }
Missing copy.
BTW, if !inpic->video->interlaced you can avoid to copy and use the
usual draw_slice() mechanism by adopting the technique used here:
http://thread.gmane.org/gmane.comp.video.ffmpeg.devel/119581/focus=121439
but I'm OK with simply copying the frame here if you believe it looks
too complicate.
--
FFmpeg = Fierce Fancy Miracolous Pitiless Elitarian Game
More information about the ffmpeg-devel
mailing list