From: Paul B Mahol Date: Mon, 7 Dec 2015 15:45:52 +0000 (+0100) Subject: avfilter: add swaprect filter X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=f5c3f85eb25cb720fca300841771ce26911f872f;p=ffmpeg avfilter: add swaprect filter Signed-off-by: Paul B Mahol --- diff --git a/Changelog b/Changelog index 1256f1b2510..1bea64d4e1b 100644 --- a/Changelog +++ b/Changelog @@ -65,6 +65,7 @@ version : - significant performance improvements in Windows Television (WTV) demuxer - nnedi deinterlacer - streamselect video and astreamselect audio filter +- swaprect filter version 2.8: diff --git a/doc/filters.texi b/doc/filters.texi index 3751f82e380..6b57c0cd285 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -11473,6 +11473,60 @@ Interpolate) pixel art scaling algorithm. Useful for enlarging pixel art images without reducing sharpness. +@section swaprect + +Swap two rectangular objects in video. + +This filter accepts the following options: + +@table @option +@item w +Set object width. + +@item h +Set object height. + +@item x1 +Set 1st rect x coordinate. + +@item y1 +Set 1st rect y coordinate. + +@item x2 +Set 2nd rect x coordinate. + +@item y2 +Set 2nd rect y coordinate. + +All expressions are evaluated once for each frame. +@end table + +The all options are expressions containing the following constants: + +@table @option +@item w +@item h +The input width and height. + +@item a +same as @var{w} / @var{h} + +@item sar +input sample aspect ratio + +@item dar +input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar} + +@item n +The number of the input frame, starting from 0. + +@item t +The timestamp expressed in seconds. It's NAN if the input timestamp is unknown. + +@item pos +the position in the file of the input frame, NAN if unknown +@end table + @section swapuv Swap U & V plane. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index e62a9ff5eb5..a1268e237f6 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -241,6 +241,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o OBJS-$(CONFIG_STREAMSELECT_FILTER) += f_streamselect.o OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o +OBJS-$(CONFIG_SWAPRECT_FILTER) += vf_swaprect.o OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o OBJS-$(CONFIG_TBLEND_FILTER) += vf_blend.o dualinput.o framesync.o OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index ee59eebee77..489da258f8f 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -261,6 +261,7 @@ void avfilter_register_all(void) REGISTER_FILTER(STREAMSELECT, streamselect, vf); REGISTER_FILTER(SUBTITLES, subtitles, vf); REGISTER_FILTER(SUPER2XSAI, super2xsai, vf); + REGISTER_FILTER(SWAPRECT, swaprect, vf); REGISTER_FILTER(SWAPUV, swapuv, vf); REGISTER_FILTER(TBLEND, tblend, vf); REGISTER_FILTER(TELECINE, telecine, vf); diff --git a/libavfilter/version.h b/libavfilter/version.h index 15aa7ea6ec9..d75d5881989 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 6 -#define LIBAVFILTER_VERSION_MINOR 29 +#define LIBAVFILTER_VERSION_MINOR 30 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_swaprect.c b/libavfilter/vf_swaprect.c new file mode 100644 index 00000000000..340175927ff --- /dev/null +++ b/libavfilter/vf_swaprect.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2015 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/avstring.h" +#include "libavutil/eval.h" +#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 SwapRectContext { + const AVClass *class; + char *w, *h; + char *x1, *y1; + char *x2, *y2; + + int nb_planes; + int pixsteps[4]; + + const AVPixFmtDescriptor *desc; + uint8_t *temp; +} SwapRectContext; + +#define OFFSET(x) offsetof(SwapRectContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +static const AVOption swaprect_options[] = { + { "w", "set rect width", OFFSET(w), AV_OPT_TYPE_STRING, {.str="w/2"}, 0, 0, .flags = FLAGS }, + { "h", "set rect height", OFFSET(h), AV_OPT_TYPE_STRING, {.str="h/2"}, 0, 0, .flags = FLAGS }, + { "x1", "set 1st rect x top left coordinate", OFFSET(x1), AV_OPT_TYPE_STRING, {.str="w/2"}, 0, 0, .flags = FLAGS }, + { "y1", "set 1st rect y top left coordinate", OFFSET(y1), AV_OPT_TYPE_STRING, {.str="h/2"}, 0, 0, .flags = FLAGS }, + { "x2", "set 2nd rect x top left coordinate", OFFSET(x2), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, .flags = FLAGS }, + { "y2", "set 2nd rect y top left coordinate", OFFSET(y2), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(swaprect); + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *pix_fmts = NULL; + int fmt, ret; + + for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!(desc->flags & AV_PIX_FMT_FLAG_PAL || + desc->flags & AV_PIX_FMT_FLAG_HWACCEL || + desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) && + (ret = ff_add_format(&pix_fmts, fmt)) < 0) + return ret; + } + + return ff_set_common_formats(ctx, pix_fmts); +} + +static const char *const var_names[] = { "w", "h", "a", "n", "t", "pos", "sar", "dar", NULL }; +enum { VAR_W, VAR_H, VAR_A, VAR_N, VAR_T, VAR_POS, VAR_SAR, VAR_DAR, VAR_VARS_NB }; + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + SwapRectContext *s = ctx->priv; + double var_values[VAR_VARS_NB]; + int x1[4], y1[4]; + int x2[4], y2[4]; + int aw[4], ah[4]; + int lw[4], lh[4]; + int pw[4], ph[4]; + double dw, dh; + double dx1, dy1; + double dx2, dy2; + int y, p, w, h, ret; + + var_values[VAR_W] = inlink->w; + var_values[VAR_H] = inlink->h; + var_values[VAR_A] = (float) inlink->w / inlink->h; + var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; + var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; + var_values[VAR_N] = inlink->frame_count; + var_values[VAR_T] = in->pts == AV_NOPTS_VALUE ? NAN : in->pts * av_q2d(inlink->time_base); + var_values[VAR_POS] = av_frame_get_pkt_pos(in) == -1 ? NAN : av_frame_get_pkt_pos(in); + + ret = av_expr_parse_and_eval(&dw, s->w, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + ret = av_expr_parse_and_eval(&dh, s->h, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + ret = av_expr_parse_and_eval(&dx1, s->x1, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + ret = av_expr_parse_and_eval(&dy1, s->y1, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + ret = av_expr_parse_and_eval(&dx2, s->x2, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + ret = av_expr_parse_and_eval(&dy2, s->y2, + var_names, &var_values[0], + NULL, NULL, NULL, NULL, + 0, 0, ctx); + if (ret < 0) + return ret; + + w = dw; h = dh; x1[0] = dx1; y1[0] = dy1; x2[0] = dx2; y2[0] = dy2; + + x1[0] = av_clip(x1[0], 0, inlink->w - 1); + y1[0] = av_clip(y1[0], 0, inlink->w - 1); + + x2[0] = av_clip(x2[0], 0, inlink->w - 1); + y2[0] = av_clip(y2[0], 0, inlink->w - 1); + + ah[1] = ah[2] = FF_CEIL_RSHIFT(h, s->desc->log2_chroma_h); + ah[0] = ah[3] = h; + aw[1] = aw[2] = FF_CEIL_RSHIFT(w, s->desc->log2_chroma_w); + aw[0] = aw[3] = w; + + w = FFMIN3(w, inlink->w - x1[0], inlink->w - x2[0]); + h = FFMIN3(h, inlink->h - y1[0], inlink->h - y2[0]); + + ph[1] = ph[2] = FF_CEIL_RSHIFT(h, s->desc->log2_chroma_h); + ph[0] = ph[3] = h; + pw[1] = pw[2] = FF_CEIL_RSHIFT(w, s->desc->log2_chroma_w); + pw[0] = pw[3] = w; + + lh[1] = lh[2] = FF_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + lh[0] = lh[3] = inlink->h; + lw[1] = lw[2] = FF_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w); + lw[0] = lw[3] = inlink->w; + + x1[1] = x1[2] = FF_CEIL_RSHIFT(x1[0], s->desc->log2_chroma_w); + x1[0] = x1[3] = x1[0]; + y1[1] = y1[2] = FF_CEIL_RSHIFT(y1[0], s->desc->log2_chroma_h); + y1[0] = y1[3] = y1[0]; + + x2[1] = x2[2] = FF_CEIL_RSHIFT(x2[0], s->desc->log2_chroma_w); + x2[0] = x2[3] = x2[0]; + y2[1] = y2[2] = FF_CEIL_RSHIFT(y2[0], s->desc->log2_chroma_h); + y2[0] = y2[3] = y2[0]; + + for (p = 0; p < s->nb_planes; p++) { + if (ph[p] == ah[p] && pw[p] == aw[p]) { + uint8_t *src = in->data[p] + y1[p] * in->linesize[p] + x1[p] * s->pixsteps[p]; + uint8_t *dst = in->data[p] + y2[p] * in->linesize[p] + x2[p] * s->pixsteps[p]; + + for (y = 0; y < ph[p]; y++) { + memcpy(s->temp, src, pw[p] * s->pixsteps[p]); + memmove(src, dst, pw[p] * s->pixsteps[p]); + memcpy(dst, s->temp, pw[p] * s->pixsteps[p]); + src += in->linesize[p]; + dst += in->linesize[p]; + } + } + } + + return ff_filter_frame(outlink, in); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + SwapRectContext *s = ctx->priv; + + if (!s->w || !s->h || + !s->x1 || !s->y1 || + !s->x2 || !s->y2) + return AVERROR(EINVAL); + + s->desc = av_pix_fmt_desc_get(inlink->format); + av_image_fill_max_pixsteps(s->pixsteps, NULL, s->desc); + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + s->temp = av_malloc_array(inlink->w, s->pixsteps[0]); + if (!s->temp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SwapRectContext *s = ctx->priv; + av_freep(&s->temp); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + .needs_writable = 1, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_swaprect = { + .name = "swaprect", + .description = NULL_IF_CONFIG_SMALL("Swap 2 rectangular objects in video."), + .priv_size = sizeof(SwapRectContext), + .priv_class = &swaprect_class, + .query_formats = query_formats, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, +};