From 599b81ca9a8e04a27ddad94af462171d16063167 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sun, 23 Feb 2014 15:38:13 +0100 Subject: [PATCH] lavfi: add shuffleplanes filter --- Changelog | 1 + doc/filters.texi | 30 ++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/version.h | 2 +- libavfilter/vf_shuffleplanes.c | 172 +++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_shuffleplanes.c diff --git a/Changelog b/Changelog index 427a617664..279c0d8d24 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version : - compand audio filter +- shuffleplanes filter version 10: diff --git a/doc/filters.texi b/doc/filters.texi index 68a657ae76..8d46cd1fb4 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2453,6 +2453,36 @@ Adler-32 checksum of each plane of the input frame, expressed in the form "[@var{c0} @var{c1} @var{c2} @var{c3}]" @end table +@section shuffleplanes + +Reorder and/or duplicate video planes. + +This filter accepts the following options: + +@table @option + +@item map0 +The index of the input plane to be used as the first output plane. + +@item map1 +The index of the input plane to be used as the second output plane. + +@item map2 +The index of the input plane to be used as the third output plane. + +@item map3 +The index of the input plane to be used as the fourth output plane. + +@end table + +The first plane has the index 0. The default is to keep the input unchanged. + +E.g. +@example +avconv -i INPUT -vf shuffleplanes=0:2:1:3 OUTPUT +@end example +swaps the second and third planes of the input. + @section split Split input video into several identical outputs. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 23dbd1d1c1..5312c838ae 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -78,6 +78,7 @@ OBJS-$(CONFIG_SETPTS_FILTER) += setpts.o OBJS-$(CONFIG_SETSAR_FILTER) += vf_aspect.o OBJS-$(CONFIG_SETTB_FILTER) += vf_settb.o OBJS-$(CONFIG_SHOWINFO_FILTER) += vf_showinfo.o +OBJS-$(CONFIG_SHUFFLEPLANES_FILTER) += vf_shuffleplanes.o OBJS-$(CONFIG_SPLIT_FILTER) += split.o OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o OBJS-$(CONFIG_TRIM_FILTER) += trim.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index e47a22e6d6..ddca153fab 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -98,6 +98,7 @@ void avfilter_register_all(void) REGISTER_FILTER(SETSAR, setsar, vf); REGISTER_FILTER(SETTB, settb, vf); REGISTER_FILTER(SHOWINFO, showinfo, vf); + REGISTER_FILTER(SHUFFLEPLANES, shuffleplanes, vf); REGISTER_FILTER(SPLIT, split, vf); REGISTER_FILTER(TRANSPOSE, transpose, vf); REGISTER_FILTER(TRIM, trim, vf); diff --git a/libavfilter/version.h b/libavfilter/version.h index 6996f1c11b..1fd0eb6a38 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 4 -#define LIBAVFILTER_VERSION_MINOR 2 +#define LIBAVFILTER_VERSION_MINOR 3 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_shuffleplanes.c b/libavfilter/vf_shuffleplanes.c new file mode 100644 index 0000000000..8422d2f84e --- /dev/null +++ b/libavfilter/vf_shuffleplanes.c @@ -0,0 +1,172 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; 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/common.h" +#include "libavutil/internal.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" + +#include "avfilter.h" +#include "internal.h" +#include "video.h" + +typedef struct ShufflePlanesContext { + const AVClass *class; + + /* number of planes in the selected pixel format */ + int planes; + + /* mapping indices */ + uint8_t map[4]; + + /* set to 1 if some plane is used more than once, so we need to make a copy */ + int copy; +} ShufflePlanesContext; + +static av_cold int shuffleplanes_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ShufflePlanesContext *s = ctx->priv; + const AVPixFmtDescriptor *desc; + int used[4] = { 0 }; + int i; + + s->copy = 0; + s->planes = av_pix_fmt_count_planes(inlink->format); + desc = av_pix_fmt_desc_get(inlink->format); + + for (i = 0; i < s->planes; i++) { + if (s->map[i] >= s->planes) { + av_log(ctx, AV_LOG_ERROR, + "Non-existing input plane #%d mapped to output plane #%d.\n", + s->map[i], i); + return AVERROR(EINVAL); + } + + if ((desc->log2_chroma_h || desc->log2_chroma_w) && + (i == 1 || i == 2) != (s->map[i] == 1 || s->map[i] == 2)) { + av_log(ctx, AV_LOG_ERROR, + "Cannot map between a subsampled chroma plane and a luma " + "or alpha plane.\n"); + return AVERROR(EINVAL); + } + + if ((desc->flags & AV_PIX_FMT_FLAG_PAL || + desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) && + (i == 1) != (s->map[i] == 1)) { + av_log(ctx, AV_LOG_ERROR, + "Cannot map between a palette plane and a data plane.\n"); + return AVERROR(EINVAL); + } + if (used[s->map[i]]) + s->copy = 1; + used[s->map[i]]++; + } + + return 0; +} + +static int shuffleplanes_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + ShufflePlanesContext *s = ctx->priv; + uint8_t *shuffled_data[4] = { NULL }; + int shuffled_linesize[4] = { 0 }; + int i, ret; + + for (i = 0; i < s->planes; i++) { + shuffled_data[i] = frame->data[s->map[i]]; + shuffled_linesize[i] = frame->linesize[s->map[i]]; + } + memcpy(frame->data, shuffled_data, sizeof(shuffled_data)); + memcpy(frame->linesize, shuffled_linesize, sizeof(shuffled_linesize)); + + if (s->copy) { + AVFrame *copy = ff_get_video_buffer(ctx->outputs[0], frame->width, frame->height); + + if (!copy) { + ret = AVERROR(ENOMEM); + goto fail; + } + + av_frame_copy(copy, frame); + + ret = av_frame_copy_props(copy, frame); + if (ret < 0) { + av_frame_free(©); + goto fail; + } + + av_frame_free(&frame); + frame = copy; + } + + return ff_filter_frame(ctx->outputs[0], frame); +fail: + av_frame_free(&frame); + return ret; +} + +#define OFFSET(x) offsetof(ShufflePlanesContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM +static const AVOption shuffleplanes_options[] = { + { "map0", "Index of the input plane to be used as the first output plane ", OFFSET(map[0]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 4, FLAGS }, + { "map1", "Index of the input plane to be used as the second output plane ", OFFSET(map[1]), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 4, FLAGS }, + { "map2", "Index of the input plane to be used as the third output plane ", OFFSET(map[2]), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, 4, FLAGS }, + { "map3", "Index of the input plane to be used as the fourth output plane ", OFFSET(map[3]), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, 4, FLAGS }, + { NULL }, +}; + +static const AVClass shuffleplanes_class = { + .class_name = "shuffleplanes", + .item_name = av_default_item_name, + .option = shuffleplanes_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVFilterPad shuffleplanes_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = shuffleplanes_config_input, + .filter_frame = shuffleplanes_filter_frame, + .get_video_buffer = ff_null_get_video_buffer, + }, + { NULL }, +}; + +static const AVFilterPad shuffleplanes_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL }, +}; + +AVFilter ff_vf_shuffleplanes = { + .name = "shuffleplanes", + .description = NULL_IF_CONFIG_SMALL("Shuffle video planes"), + + .priv_size = sizeof(ShufflePlanesContext), + .priv_class = &shuffleplanes_class, + + .inputs = shuffleplanes_inputs, + .outputs = shuffleplanes_outputs, +};