[FFmpeg-devel] [PATCH] avfilter: add mergeplanes

Nicolas George george at nsup.org
Mon Sep 30 11:06:52 CEST 2013


Le nonidi 9 vendémiaire, an CCXXII, Paul B Mahol a écrit :
> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> ---
>  doc/filters.texi             |  20 ++++
>  libavfilter/Makefile         |   1 +
>  libavfilter/allfilters.c     |   1 +
>  libavfilter/vf_mergeplanes.c | 227 +++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 249 insertions(+)
>  create mode 100644 libavfilter/vf_mergeplanes.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index bd39495..9d8c7cc 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -5248,6 +5248,26 @@ lutyuv=y='bitand(val, 128+64+32)'
>  @end example
>  @end itemize
>  
> + at section mergeplanes
> +
> +Merge color channel components from several video streams.

Good idea.

> +
> +This filter accepts the following options:
> + at table @option
> + at item inputs
> +Set the number of inputs. Default is 2.
> + at end table

I have concerns about usability: why does 2 mean YUV+A instead of GRAY+A?
And if 3 means Y+U+V, how do you express R+G+B? Maybe something like that
would be more appropriate:

	mode=y_u_v
	mode=yuv_a

Or, possibly using additive features:

	mode=y+u+v
	mode=yuv+a

(note that in that case, yuv must be a different value from y+u+v; I suppose
each of y, u, v, yuv, r, g, b, rgb, alpha need its own bit)

Support for more formats can come later, of course, but the user interface
need to be ready to avoid compatibility concerns.

> +
> + at subsection Examples
> +
> + at itemize
> + at item
> +Merge four gray video streams into single video stream:
> + at example
> +mergeplanes=4
> + at end example
> + at end itemize
> +
>  @section mcdeint
>  
>  Apply motion-compensation deinterlacing.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index b2d3587..0200e29 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -157,6 +157,7 @@ OBJS-$(CONFIG_LUT_FILTER)                    += vf_lut.o
>  OBJS-$(CONFIG_LUTRGB_FILTER)                 += vf_lut.o
>  OBJS-$(CONFIG_LUTYUV_FILTER)                 += vf_lut.o
>  OBJS-$(CONFIG_MCDEINT_FILTER)                += vf_mcdeint.o
> +OBJS-$(CONFIG_MERGEPLANES_FILTER)            += vf_mergeplanes.o
>  OBJS-$(CONFIG_MP_FILTER)                     += vf_mp.o
>  OBJS-$(CONFIG_MPDECIMATE_FILTER)             += vf_mpdecimate.o
>  OBJS-$(CONFIG_NEGATE_FILTER)                 += vf_lut.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index ed11d67..8f33281 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -153,6 +153,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(LUTRGB,         lutrgb,         vf);
>      REGISTER_FILTER(LUTYUV,         lutyuv,         vf);
>      REGISTER_FILTER(MCDEINT,        mcdeint,        vf);
> +    REGISTER_FILTER(MERGEPLANES,    mergeplanes,    vf);
>      REGISTER_FILTER(MP,             mp,             vf);
>      REGISTER_FILTER(MPDECIMATE,     mpdecimate,     vf);
>      REGISTER_FILTER(NEGATE,         negate,         vf);
> diff --git a/libavfilter/vf_mergeplanes.c b/libavfilter/vf_mergeplanes.c
> new file mode 100644
> index 0000000..31729cf
> --- /dev/null
> +++ b/libavfilter/vf_mergeplanes.c
> @@ -0,0 +1,227 @@
> +/*
> + * 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
> + */
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +#include "drawutils.h"
> +#include "internal.h"
> +#include "framesync.h"
> +
> +typedef struct MergePlanesContext {
> +    const AVClass *class;
> +    unsigned nb_inputs;
> +
> +    int nb_planes[4];
> +    int planewidth[4][4];
> +    int planeheight[4][4];
> +
> +    FFFrameSync fs;
> +    FFFrameSyncIn fsi[3]; /* must be immediately after fs */
> +} MergePlanesContext;
> +
> +#define OFFSET(x) offsetof(MergePlanesContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +static const AVOption mergeplanes_options[] = {
> +    { "inputs", "Number of input streams.", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, 4, FLAGS},
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(mergeplanes);
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    MergePlanesContext *s = inlink->dst->priv;
> +    return ff_framesync_filter_frame(&s->fs, inlink, in);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    MergePlanesContext *s = ctx->priv;
> +    AVFilterFormats *in_formats = NULL;
> +    AVFilterFormats *out_formats = NULL;
> +    int i;
> +
> +    ff_add_format(&in_formats, AV_PIX_FMT_GRAY8);
> +    switch (s->nb_inputs) {
> +    case 4:
> +        ff_add_format(&out_formats, AV_PIX_FMT_YUVA444P);
> +        for (i = 0; i < s->nb_inputs; i++)
> +            ff_formats_ref(in_formats, &ctx->inputs[i]->out_formats);
> +        break;
> +    case 3:
> +        ff_add_format(&out_formats, AV_PIX_FMT_YUV444P);
> +        ff_formats_ref(in_formats, &ctx->inputs[0]->out_formats);
> +        ff_formats_ref(in_formats, &ctx->inputs[1]->out_formats);
> +        ff_formats_ref(in_formats, &ctx->inputs[2]->out_formats);
> +        break;
> +    case 2:
> +        ff_formats_ref(in_formats, &ctx->inputs[1]->out_formats);
> +        in_formats = NULL;
> +        ff_add_format(&in_formats,  AV_PIX_FMT_YUV444P);
> +        ff_formats_ref(in_formats, &ctx->inputs[0]->out_formats);
> +        ff_add_format(&out_formats, AV_PIX_FMT_YUVA444P);
> +        break;
> +    }
> +
> +    ff_formats_ref(out_formats, &ctx->outputs[0]->in_formats);
> +
> +    return 0;
> +}
> +
> +static int request_frame(AVFilterLink *outlink)
> +{
> +    MergePlanesContext *s = outlink->src->priv;
> +    return ff_framesync_request_frame(&s->fs, outlink);
> +}
> +
> +static void merge_planes(AVFilterContext *ctx, AVFrame *in[4], AVFrame *out)
> +{
> +    MergePlanesContext *s = ctx->priv;
> +    int i, p, j = 0;
> +
> +    for (i = 0; i < s->nb_inputs; i++) {
> +        for (p = 0; p < s->nb_planes[i]; p++, j++) {
> +            av_image_copy_plane(out->data[j], out->linesize[j],
> +                                in[i]->data[p], in[i]->linesize[p],
> +                                s->planewidth[i][p], s->planeheight[i][p]);
> +        }
> +    }
> +}
> +
> +static int process_frame(FFFrameSync *fs)
> +{
> +    AVFilterContext *ctx = fs->parent;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    MergePlanesContext *s = fs->opaque;
> +    AVFrame *in[4] = { NULL };
> +    AVFrame *out;
> +    int i, ret = 0;
> +
> +    av_assert0(s->nb_inputs <= 4);
> +    for (i = 0; i < s->nb_inputs; i++) {

> +        if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], s->nb_inputs - (i + 1))) < 0)

I wonder about the last argument: "s->nb_inputs - (i + 1)". It looks like
you are using the input frames read-only, therefore, 0 should be just fine.

> +            return ret;
> +    }
> +
> +    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> +    if (!out)
> +        return AVERROR(ENOMEM);
> +

> +    out->pts = av_rescale_q(in[0]->pts, s->fs.time_base, outlink->time_base);

The correct timestamp is in s->fs.pts; in[0]->pts may be wrong if the same
frame is duplicated to sync with the other streams.

> +    merge_planes(ctx, in, out);
> +    ret = ff_filter_frame(outlink, out);
> +
> +    return ret;
> +}
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    MergePlanesContext *s = ctx->priv;
> +    FFFrameSyncIn *in = s->fs.in;
> +    int i, ret;
> +
> +    ff_framesync_init(&s->fs, ctx, s->nb_inputs);
> +    s->fs.opaque = s;
> +    s->fs.on_event = process_frame;
> +
> +    for (i = 0; i < s->nb_inputs; i++) {
> +        AVFilterLink *inlink = ctx->inputs[i];
> +        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> +
> +        if ((ret = av_image_fill_linesizes(s->planewidth[i], inlink->format, inlink->w)) < 0)
> +            return ret;
> +
> +        s->planeheight[i][1] =
> +        s->planeheight[i][2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
> +        s->planeheight[i][0] =
> +        s->planeheight[i][3] = inlink->h;
> +        s->nb_planes[i] = av_pix_fmt_count_planes(inlink->format);

I do not understand the logic here: unless I am missing something, there
should be a check that all input streams have compatible sizes (i.e., since
only 444 is supported, all the same size) (and pixel aspect ratio too).

> +
> +        in[i].time_base = inlink->time_base;
> +        in[i].sync   = 1;
> +        in[i].before = EXT_STOP;
> +        in[i].after  = EXT_STOP;
> +    }
> +
> +    return ff_framesync_configure(&s->fs);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    MergePlanesContext *s = ctx->priv;
> +    int i;
> +
> +    ff_framesync_uninit(&s->fs);
> +
> +    for (i = 0; i < s->nb_inputs; i++) {
> +        av_freep(&ctx->input_pads[i].name);
> +    }
> +}
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    MergePlanesContext *s = ctx->priv;
> +    int i, ret;
> +
> +    for (i = 0; i < s->nb_inputs; i++) {
> +        AVFilterPad pad = { 0 };
> +
> +        pad.type = AVMEDIA_TYPE_VIDEO;
> +        pad.name = av_asprintf("in%d", i);
> +        if (!pad.name)
> +            return AVERROR(ENOMEM);
> +        pad.filter_frame = filter_frame;
> +
> +        if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0){
> +            av_freep(&pad.name);
> +            return ret;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static const AVFilterPad mergeplanes_outputs[] = {
> +    {
> +        .name          = "default",
> +        .type          = AVMEDIA_TYPE_VIDEO,
> +        .config_props  = config_output,
> +        .request_frame = request_frame,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter avfilter_vf_mergeplanes = {
> +    .name          = "mergeplanes",
> +    .description   = NULL_IF_CONFIG_SMALL("Merge planes."),
> +    .priv_size     = sizeof(MergePlanesContext),
> +    .priv_class    = &mergeplanes_class,
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +    .inputs        = NULL,
> +    .outputs       = mergeplanes_outputs,
> +    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
> +};

Regards,

-- 
  Nicolas George
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130930/02a105ac/attachment.asc>


More information about the ffmpeg-devel mailing list