[FFmpeg-devel] [PATCH 6/6] Add V4L2 m2m filter support. Very useful for HW accelerated format conversion & scaling.
Alexis Ballier
aballier at gentoo.org
Thu Nov 20 17:51:57 CET 2014
---
Changelog | 2 +-
configure | 1 +
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_v4l2_m2m.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 254 insertions(+), 1 deletion(-)
create mode 100644 libavfilter/vf_v4l2_m2m.c
diff --git a/Changelog b/Changelog
index 5910ef6..7b2d2a7 100644
--- a/Changelog
+++ b/Changelog
@@ -15,7 +15,7 @@ version <next>:
- ffserver supports codec private options
- creating DASH compatible fragmented MP4, MPEG-DASH segmenting muxer
- WebP muxer with animated WebP support
-- V4L2 mem2mem HW accelerated codecs support
+- V4L2 mem2mem HW accelerated codecs and filters support
version 2.4:
diff --git a/configure b/configure
index 68a64e2..5c6c909 100755
--- a/configure
+++ b/configure
@@ -2620,6 +2620,7 @@ stereo3d_filter_deps="gpl"
subtitles_filter_deps="avformat avcodec libass"
super2xsai_filter_deps="gpl"
tinterlace_filter_deps="gpl"
+v4l2_m2m_filter_deps="v4l2_m2m"
vidstabdetect_filter_deps="libvidstab"
vidstabtransform_filter_deps="libvidstab"
pixfmts_super2xsai_test_deps="super2xsai_filter"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 2c56e38..6d523be 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -193,6 +193,7 @@ OBJS-$(CONFIG_TINTERLACE_FILTER) += vf_tinterlace.o
OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o
OBJS-$(CONFIG_TRIM_FILTER) += trim.o
OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o
+OBJS-$(CONFIG_V4L2_M2M_FILTER) += vf_v4l2_m2m.o
OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o
OBJS-$(CONFIG_VIDSTABDETECT_FILTER) += vidstabutils.o vf_vidstabdetect.o
OBJS-$(CONFIG_VIDSTABTRANSFORM_FILTER) += vidstabutils.o vf_vidstabtransform.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 2352d44..f2e4e02 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -208,6 +208,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(TRANSPOSE, transpose, vf);
REGISTER_FILTER(TRIM, trim, vf);
REGISTER_FILTER(UNSHARP, unsharp, vf);
+ REGISTER_FILTER(V4L2_M2M, v4l2_m2m, vf);
REGISTER_FILTER(VFLIP, vflip, vf);
REGISTER_FILTER(VIDSTABDETECT, vidstabdetect, vf);
REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
diff --git a/libavfilter/vf_v4l2_m2m.c b/libavfilter/vf_v4l2_m2m.c
new file mode 100644
index 0000000..293162a
--- /dev/null
+++ b/libavfilter/vf_v4l2_m2m.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2014 Alexis Ballier
+ *
+ * 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
+ */
+
+/*
+ * Missing features:
+ * - get_buffer
+ * - effects (hflip, vflip, etc.)
+ */
+#include <sys/ioctl.h>
+
+#include "libavcodec/avcodec.h"
+#include "libavcodec/v4l2_m2m.h"
+#include "libavcodec/v4l2-common.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+
+static av_cold void uninit(AVFilterContext *ctx) {
+ V4Lm2mContext *s = ctx->priv;
+ avpriv_v4lm2m_end(s);
+}
+
+static av_cold int init(AVFilterContext *ctx) {
+ V4Lm2mContext *s = ctx->priv;
+
+ s->output_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ s->capture_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+ // For probing raw2raw device
+ s->output_pool.av_codec_id = AV_CODEC_ID_RAWVIDEO;
+ s->output_pool.av_pix_fmt = AV_PIX_FMT_NONE;
+ s->output_pool_needs_format = 1;
+
+ // We must have 1:1 input:output mapping, so we wait for the device to process data.
+ s->capture_pool.blocking_dequeue = 100;
+
+ // For probing raw2raw device
+ s->capture_pool.av_codec_id = AV_CODEC_ID_RAWVIDEO;
+ s->capture_pool.av_pix_fmt = AV_PIX_FMT_NONE;
+ s->capture_pool_needs_format = 1;
+
+ return avpriv_v4lm2m_init(s, ctx);
+}
+
+static int query_formats_local(AVFilterFormats **fmts, V4Lm2mContext *s, V4LBufferPool* bp) {
+ int ret;
+ AVFilterFormats *formats = NULL;
+ struct v4l2_fmtdesc fmtdesc = { 0 };
+ enum AVPixelFormat pixfmt;
+
+ fmtdesc.type = bp->type;
+
+ while(!ioctl(s->fd, VIDIOC_ENUM_FMT, &fmtdesc)) {
+ pixfmt = avpriv_v4l_fmt_v4l2ff(fmtdesc.pixelformat, AV_CODEC_ID_RAWVIDEO);
+ if(AV_PIX_FMT_NONE != pixfmt) {
+ /* Avoid duplicates, lavfi really does not like it */
+ if(formats && formats->nb_formats) {
+ int i;
+ for(i=0; i<formats->nb_formats; i++) {
+ if(formats->formats[i] == pixfmt)
+ break;
+ }
+ if(i<formats->nb_formats) {
+ goto ignore_format;
+ }
+ }
+
+ if((ret = ff_add_format(&formats, pixfmt)) < 0) {
+ ff_formats_unref(&formats);
+ return ret;
+ }
+ }
+ignore_format:
+ fmtdesc.index++;
+ }
+
+ ff_formats_ref(formats, fmts);
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx) {
+ int ret;
+ V4Lm2mContext *s = ctx->priv;
+ if(ctx->inputs[0] && (ret = query_formats_local(&(ctx->inputs[0]->out_formats), s, &s->output_pool)) < 0) {
+ return ret;
+ }
+ if(ctx->inputs[0] && (ret = query_formats_local(&(ctx->outputs[0]->in_formats), s, &s->capture_pool)) < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static inline int config_pool(V4LBufferPool *bp) {
+ int ret;
+ av_log(bp->log_ctx, AV_LOG_DEBUG, "Setting pixelformat %s on %s\n", av_get_pix_fmt_name(bp->av_pix_fmt), bp->name);
+ if((ret = avpriv_set_pool_format(bp, 1)) < 0)
+ return ret;
+ return avpriv_init_v4lbufpool(bp);
+}
+
+static int config_input(AVFilterLink *link) {
+ AVFilterContext *ctx = link->dst;
+ V4Lm2mContext *s = ctx->priv;
+
+ s->output_pool.width = link->w;
+ s->output_pool.height = link->h;
+ s->output_pool.av_pix_fmt = link->format;
+
+ return config_pool(&s->output_pool);
+}
+
+static int config_output(AVFilterLink *link) {
+ AVFilterContext *ctx = link->src;
+ V4Lm2mContext *s = ctx->priv;
+
+ if(!s->capture_pool.width)
+ s->capture_pool.width = ctx->inputs[0]->w;
+
+ link->w = s->capture_pool.width;
+
+ if(!s->capture_pool.height)
+ s->capture_pool.height = ctx->inputs[0]->h;
+
+ link->h = s->capture_pool.height;
+
+ s->capture_pool.av_pix_fmt = link->format;
+
+ return config_pool(&s->capture_pool);
+}
+
+static int filter_frame(AVFilterLink *link, AVFrame *pict) {
+ AVFilterContext *ctx = link->dst;
+ V4Lm2mContext *s = ctx->priv;
+ AVFilterLink *outlink = link->dst->outputs[0];
+ AVFrame *out = NULL;
+ int ret;
+
+ if(pict) {
+ if((ret = avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, pict, NULL, NULL, 0)) < 0)
+ return ret;
+
+ if(!s->output_pool.streamon && (ret = avpriv_set_stream_status(&s->output_pool, VIDIOC_STREAMON))) {
+ av_log(ctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output pool\n");
+ return ret;
+ }
+ if(!s->capture_pool.streamon && (ret = avpriv_set_stream_status(&s->capture_pool, VIDIOC_STREAMON))) {
+ av_log(ctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on capture pool\n");
+ return ret;
+ }
+ } else {
+ return 0;
+ }
+
+ av_frame_free(&pict);
+ out = av_frame_alloc();
+
+ if(!out) {
+ return AVERROR(ENOMEM);
+ }
+
+ if(ret = avpriv_v4l_dequeue_frame_or_pkt(&(s->capture_pool), out, NULL)) {
+ return ret;
+ }
+
+ return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(V4Lm2mContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption m2m_options[] = {
+ V4L_M2M_DEFAULT_OPTS,
+ { "width",
+ "Output video width",
+ OFFSET(capture_pool.width),
+ AV_OPT_TYPE_INT,
+ {.i64 = 0 },
+ 0, INT_MAX, FLAGS },
+ { "height",
+ "Output video height",
+ OFFSET(capture_pool.height),
+ AV_OPT_TYPE_INT,
+ {.i64 = 0 },
+ 0, INT_MAX, FLAGS },
+ { "num_capture_pool_buffers",
+ "Number of buffers in the capture pool",
+ OFFSET(capture_pool.num_buffers),
+ AV_OPT_TYPE_INT,
+ {.i64 = 16 },
+ 4, INT_MAX, FLAGS },
+ { NULL }
+};
+
+static const AVClass v4l2_m2m_class = {
+ .class_name = "v4l2_m2m_filter",
+ .item_name = av_default_item_name,
+ .option = m2m_options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_FILTER,
+};
+
+static const AVFilterPad avfilter_vf_v4l2_m2m_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = filter_frame,
+ .config_props = config_input,
+ },
+ { NULL }
+};
+
+static const AVFilterPad avfilter_vf_v4l2_m2m_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+ { NULL }
+};
+
+AVFilter ff_vf_v4l2_m2m = {
+ .name = "v4l2_m2m",
+ .description = NULL_IF_CONFIG_SMALL("V4L2 M2M filter: Scale the input video size and convert the image format."),
+ .init = init,
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .priv_size = sizeof(V4Lm2mContext),
+ .priv_class = &v4l2_m2m_class,
+ .inputs = avfilter_vf_v4l2_m2m_inputs,
+ .outputs = avfilter_vf_v4l2_m2m_outputs,
+};
--
2.1.3
More information about the ffmpeg-devel
mailing list