[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