[FFmpeg-devel] [PATCH 5/6] Add support for V4L2 mem2mem codecs.

Alexis Ballier aballier at gentoo.org
Thu Nov 20 17:51:56 CET 2014


Default buffer settings should work with all drivers (mmaped memory from the driver) but would imply useless memcpy()'s if used in the standard way.
libavcodec/v4l2.h is now installed to allow applications to get the proper buffers to avoid these copies.

This has been tested on MFC5, on a Samsung exynos 4412 SOC (odroid u3).
---
 Changelog                     |   1 +
 configure                     |  17 +-
 libavcodec/Makefile           |  15 +-
 libavcodec/allcodecs.c        |   7 +
 libavcodec/v4l2-buffers.c     | 725 ++++++++++++++++++++++++++++++++++++++++++
 libavcodec/v4l2-buffers.h     | 230 ++++++++++++++
 libavcodec/v4l2.h             |  66 ++++
 libavcodec/v4l2_m2m.c         | 277 ++++++++++++++++
 libavcodec/v4l2_m2m.h         |  86 +++++
 libavcodec/v4l2_m2m_avcodec.h |  30 ++
 libavcodec/v4l2_m2m_dec.c     | 227 +++++++++++++
 libavcodec/v4l2_m2m_enc.c     | 232 ++++++++++++++
 12 files changed, 1911 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/v4l2-buffers.c
 create mode 100644 libavcodec/v4l2-buffers.h
 create mode 100644 libavcodec/v4l2.h
 create mode 100644 libavcodec/v4l2_m2m.c
 create mode 100644 libavcodec/v4l2_m2m.h
 create mode 100644 libavcodec/v4l2_m2m_avcodec.h
 create mode 100644 libavcodec/v4l2_m2m_dec.c
 create mode 100644 libavcodec/v4l2_m2m_enc.c

diff --git a/Changelog b/Changelog
index 5f38aea..5910ef6 100644
--- a/Changelog
+++ b/Changelog
@@ -15,6 +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
 
 
 version 2.4:
diff --git a/configure b/configure
index 81a6acf..68a64e2 100755
--- a/configure
+++ b/configure
@@ -150,6 +150,7 @@ Component options:
 
 Hardware accelerators:
   --disable-dxva2          disable DXVA2 code [autodetect]
+  --disable-v4l2_m2m       disable V4L2 mem2mem code [autodetect]
   --disable-vaapi          disable VAAPI code [autodetect]
   --disable-vda            disable VDA code [autodetect]
   --disable-vdpau          disable VDPAU code [autodetect]
@@ -1423,6 +1424,7 @@ FEATURE_LIST="
 
 HWACCEL_LIST="
     dxva2
+    v4l2_m2m
     vaapi
     vda
     vdpau
@@ -2053,6 +2055,7 @@ mpegaudiodsp_select="dct"
 mpegvideo_select="blockdsp h264chroma hpeldsp idctdsp me_cmp videodsp"
 mpegvideoenc_select="me_cmp mpegvideo pixblockdsp qpeldsp"
 v4l2_deps_any="linux_videodev2_h sys_videoio_h"
+v4l2_m2m_select="v4l2"
 
 # decoders / encoders
 aac_decoder_select="mdct sinewin"
@@ -2121,10 +2124,14 @@ h261_decoder_select="mpeg_er mpegvideo"
 h261_encoder_select="aandcttables mpegvideoenc"
 h263_decoder_select="error_resilience h263_parser h263dsp mpeg_er mpegvideo qpeldsp"
 h263_encoder_select="aandcttables h263dsp mpegvideoenc"
+h263_v4l2m2m_decoder_deps="v4l2_m2m"
+h263_v4l2m2m_encoder_deps="v4l2_m2m"
 h263i_decoder_select="h263_decoder"
 h263p_encoder_select="h263_encoder"
 h264_decoder_select="cabac golomb h264chroma h264dsp h264pred h264qpel startcode videodsp"
 h264_decoder_suggest="error_resilience"
+h264_v4l2m2m_decoder_deps="v4l2_m2m"
+h264_v4l2m2m_encoder_deps="v4l2_m2m"
 hevc_decoder_select="bswapdsp cabac golomb videodsp"
 huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
 huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llviddsp"
@@ -2160,13 +2167,17 @@ mpc7_decoder_select="bswapdsp mpegaudiodsp"
 mpc8_decoder_select="mpegaudiodsp"
 mpeg_xvmc_decoder_deps="X11_extensions_XvMClib_h"
 mpeg_xvmc_decoder_select="mpeg2video_decoder"
+mpeg1_v4l2m2m_decoder_deps="v4l2_m2m"
 mpegvideo_decoder_select="error_resilience mpeg_er mpegvideo"
 mpeg1video_decoder_select="error_resilience mpeg_er mpegvideo"
 mpeg1video_encoder_select="aandcttables mpegvideoenc h263dsp"
+mpeg2_v4l2m2m_decoder_deps="v4l2_m2m"
 mpeg2video_decoder_select="error_resilience mpeg_er mpegvideo"
 mpeg2video_encoder_select="aandcttables mpegvideoenc h263dsp"
 mpeg4_decoder_select="h263_decoder mpeg4video_parser"
 mpeg4_encoder_select="h263_encoder"
+mpeg4_v4l2m2m_decoder_deps="v4l2_m2m"
+mpeg4_v4l2m2m_encoder_deps="v4l2_m2m"
 msmpeg4v1_decoder_select="h263_decoder"
 msmpeg4v2_decoder_select="h263_decoder"
 msmpeg4v2_encoder_select="h263_encoder"
@@ -2222,6 +2233,7 @@ utvideo_decoder_select="bswapdsp"
 utvideo_encoder_select="bswapdsp huffman huffyuvencdsp"
 vble_decoder_select="huffyuvdsp"
 vc1_decoder_select="blockdsp error_resilience h263_decoder h264chroma h264qpel intrax8 mpeg_er qpeldsp startcode"
+vc1_v4l2m2m_decoder_deps="v4l2_m2m"
 vc1image_decoder_select="vc1_decoder"
 vorbis_decoder_select="mdct"
 vorbis_encoder_select="mdct"
@@ -2232,6 +2244,8 @@ vp6a_decoder_select="vp6_decoder"
 vp6f_decoder_select="vp6_decoder"
 vp7_decoder_select="h264pred videodsp"
 vp8_decoder_select="h264pred videodsp"
+vp8_v4l2m2m_decoder_deps="v4l2_m2m"
+vp8_v4l2m2m_encoder_deps="v4l2_m2m"
 vp9_decoder_select="videodsp vp9_parser"
 webp_decoder_select="vp8_decoder"
 wmalossless_decoder_select="llauddsp"
@@ -2727,7 +2741,7 @@ sws_max_filter_size_default=256
 set_default sws_max_filter_size
 
 # Enable hwaccels by default.
-enable dxva2 vaapi vda vdpau xvmc
+enable dxva2 v4l2_m2m vaapi vda vdpau xvmc
 enable xlib
 
 # build settings
@@ -4983,6 +4997,7 @@ check_header linux/fb.h
 check_header linux/videodev.h
 check_header linux/videodev2.h
 check_code cc linux/videodev2.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_safe struct_v4l2_frmivalenum_discrete
+check_code cc linux/videodev2.h "int i = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M;" || disable v4l2_m2m
 
 check_header sys/videoio.h
 check_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_safe struct_v4l2_frmivalenum_discrete
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a134fa6..76c76c1 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -7,6 +7,7 @@ HEADERS = avcodec.h                                                     \
           dv_profile.h                                                  \
           dxva2.h                                                       \
           old_codec_ids.h                                               \
+          v4l2.h                                                        \
           vaapi.h                                                       \
           vda.h                                                         \
           vdpau.h                                                       \
@@ -95,7 +96,8 @@ OBJS-$(CONFIG_SHARED)                  += log2_tab.o
 OBJS-$(CONFIG_SINEWIN)                 += sinewin.o
 OBJS-$(CONFIG_STARTCODE)               += startcode.o
 OBJS-$(CONFIG_TPELDSP)                 += tpeldsp.o
-OBJS-$(CONFIG_V4L2)                    += v4l2-common.o
+OBJS-$(CONFIG_V4L2)                    += v4l2-common.o v4l2-buffers.o
+OBJS-$(CONFIG_V4L2_M2M)                += v4l2_m2m.o
 OBJS-$(CONFIG_VIDEODSP)                += videodsp.o
 OBJS-$(CONFIG_VP3DSP)                  += vp3dsp.o
 OBJS-$(CONFIG_WMA_FREQS)               += wma_freqs.o
@@ -255,10 +257,14 @@ OBJS-$(CONFIG_H263_DECODER)            += h263dec.o h263.o ituh263dec.o        \
                                           intelh263dec.o
 OBJS-$(CONFIG_H263_ENCODER)            += mpeg4videoenc.o mpeg4video.o  \
                                           h263.o ituh263enc.o flvenc.o
+OBJS-$(CONFIG_H263_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
+OBJS-$(CONFIG_H263_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
 OBJS-$(CONFIG_H264_DECODER)            += h264.o h264_cabac.o h264_cavlc.o \
                                           h264_direct.o h264_loopfilter.o  \
                                           h264_mb.o h264_picture.o h264_ps.o \
                                           h264_refs.o h264_sei.o h264_slice.o
+OBJS-$(CONFIG_H264_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
+OBJS-$(CONFIG_H264_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
 OBJS-$(CONFIG_H264_VDA_DECODER)        += vda_h264_dec.o
 OBJS-$(CONFIG_HEVC_DECODER)            += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \
                                           hevc_cabac.o hevc_refs.o hevcpred.o    \
@@ -322,11 +328,15 @@ OBJS-$(CONFIG_MP3ON4FLOAT_DECODER)     += mpegaudiodec_float.o mpeg4audio.o
 OBJS-$(CONFIG_MPC7_DECODER)            += mpc7.o mpc.o
 OBJS-$(CONFIG_MPC8_DECODER)            += mpc8.o mpc.o
 OBJS-$(CONFIG_MPEGVIDEO_DECODER)       += mpeg12dec.o mpeg12.o mpeg12data.o
+OBJS-$(CONFIG_MPEG1_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG1VIDEO_DECODER)      += mpeg12dec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_MPEG1VIDEO_ENCODER)      += mpeg12enc.o mpeg12.o
+OBJS-$(CONFIG_MPEG2_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
 OBJS-$(CONFIG_MPEG2VIDEO_DECODER)      += mpeg12dec.o mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_MPEG2VIDEO_ENCODER)      += mpeg12enc.o mpeg12.o
 OBJS-$(CONFIG_MPEG4_DECODER)           += xvididct.o
+OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
+OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER)   += v4l2_m2m_enc.o
 OBJS-$(CONFIG_MPL2_DECODER)            += mpl2dec.o ass.o
 OBJS-$(CONFIG_MSMPEG4V1_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
 OBJS-$(CONFIG_MSMPEG4V2_DECODER)       += msmpeg4dec.o msmpeg4.o msmpeg4data.o
@@ -482,6 +492,7 @@ OBJS-$(CONFIG_VC1_DECODER)             += vc1dec.o vc1_block.o vc1_loopfilter.o
                                           vc1dsp.o \
                                           msmpeg4dec.o msmpeg4.o msmpeg4data.o \
                                           wmv2dsp.o
+OBJS-$(CONFIG_VC1_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_VCR1_DECODER)            += vcr1.o
 OBJS-$(CONFIG_VMDAUDIO_DECODER)        += vmdaudio.o
 OBJS-$(CONFIG_VMDVIDEO_DECODER)        += vmdvideo.o
@@ -497,6 +508,8 @@ OBJS-$(CONFIG_VP6_DECODER)             += vp6.o vp56.o vp56data.o vp56dsp.o \
                                           vp6dsp.o vp56rac.o
 OBJS-$(CONFIG_VP7_DECODER)             += vp8.o vp8dsp.o vp56rac.o
 OBJS-$(CONFIG_VP8_DECODER)             += vp8.o vp8dsp.o vp56rac.o
+OBJS-$(CONFIG_VP8_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
+OBJS-$(CONFIG_VP8_V4L2M2M_ENCODER)     += v4l2_m2m_enc.o
 OBJS-$(CONFIG_VP9_DECODER)             += vp9.o vp9dsp.o vp56rac.o
 OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index d08abd8..a8ab72e 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -164,10 +164,12 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC (GIF,               gif);
     REGISTER_ENCDEC (H261,              h261);
     REGISTER_ENCDEC (H263,              h263);
+    REGISTER_ENCDEC (H263_V4L2M2M,      h263_v4l2m2m);
     REGISTER_DECODER(H263I,             h263i);
     REGISTER_ENCDEC (H263P,             h263p);
     REGISTER_DECODER(H264,              h264);
     REGISTER_DECODER(H264_CRYSTALHD,    h264_crystalhd);
+    REGISTER_ENCDEC (H264_V4L2M2M,      h264_v4l2m2m);
     REGISTER_DECODER(H264_VDA,          h264_vda);
     REGISTER_DECODER(H264_VDPAU,        h264_vdpau);
     REGISTER_DECODER(HEVC,              hevc);
@@ -202,11 +204,14 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC (MPEG2VIDEO,        mpeg2video);
     REGISTER_ENCDEC (MPEG4,             mpeg4);
     REGISTER_DECODER(MPEG4_CRYSTALHD,   mpeg4_crystalhd);
+    REGISTER_ENCDEC (MPEG4_V4L2M2M,     mpeg4_v4l2m2m);
     REGISTER_DECODER(MPEG4_VDPAU,       mpeg4_vdpau);
     REGISTER_DECODER(MPEGVIDEO,         mpegvideo);
     REGISTER_DECODER(MPEG_VDPAU,        mpeg_vdpau);
     REGISTER_DECODER(MPEG1_VDPAU,       mpeg1_vdpau);
+    REGISTER_DECODER(MPEG1_V4L2M2M,     mpeg1_v4l2m2m);
     REGISTER_DECODER(MPEG2_CRYSTALHD,   mpeg2_crystalhd);
+    REGISTER_DECODER(MPEG2_V4L2M2M,     mpeg2_v4l2m2m);
     REGISTER_DECODER(MSA1,              msa1);
     REGISTER_DECODER(MSMPEG4_CRYSTALHD, msmpeg4_crystalhd);
     REGISTER_DECODER(MSMPEG4V1,         msmpeg4v1);
@@ -284,6 +289,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(VBLE,              vble);
     REGISTER_DECODER(VC1,               vc1);
     REGISTER_DECODER(VC1_CRYSTALHD,     vc1_crystalhd);
+    REGISTER_DECODER(VC1_V4L2M2M,       vc1_v4l2m2m);
     REGISTER_DECODER(VC1_VDPAU,         vc1_vdpau);
     REGISTER_DECODER(VC1IMAGE,          vc1image);
     REGISTER_DECODER(VCR1,              vcr1);
@@ -296,6 +302,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(VP6F,              vp6f);
     REGISTER_DECODER(VP7,               vp7);
     REGISTER_DECODER(VP8,               vp8);
+    REGISTER_ENCDEC (VP8_V4L2M2M,       vp8_v4l2m2m);
     REGISTER_DECODER(VP9,               vp9);
     REGISTER_DECODER(VQA,               vqa);
     REGISTER_DECODER(WEBP,              webp);
diff --git a/libavcodec/v4l2-buffers.c b/libavcodec/v4l2-buffers.c
new file mode 100644
index 0000000..7bcb1f9
--- /dev/null
+++ b/libavcodec/v4l2-buffers.c
@@ -0,0 +1,725 @@
+/*
+ * V4L2 buffer{,pool} helper functions.
+ * 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
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "avcodec.h"
+#include "internal.h"
+#include "v4l2.h"
+#include "v4l2-buffers.h"
+#include "v4l2-common.h"
+
+#define IS_BP_SUPPORTED(bp) ((bp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) || (bp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || (bp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || (bp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT))
+
+// Define it if you want more AV_LOG_DEBUG output
+// #define V4L_BUFFER_DEBUG
+
+/*
+ * Missing features:
+ *  - USERPTR support is a bit rough
+ *  - DMABUF ?
+ */
+
+enum V4LBuffer_status {
+    V4LBUF_AVAILABLE,
+    V4LBUF_IN_DRIVER,
+    V4LBUF_RET_USER,
+};
+
+struct V4LBuffer {
+    struct V4LBufferPool *pool;
+    int index;
+    int num_planes;
+    int linesize[4];
+    size_t lengths[VIDEO_MAX_PLANES];
+    void* mmap_addr[VIDEO_MAX_PLANES];
+    AVBufferRef *bufrefs[VIDEO_MAX_PLANES];
+    enum V4LBuffer_status status;
+    int flags;
+    struct v4l2_plane planes[VIDEO_MAX_PLANES];
+    struct v4l2_buffer buf;
+    struct timeval timestamp;
+    int ref_cnt;
+    V4LBuffer *piped;
+};
+
+static void buffer_callback(void *opaque, uint8_t *unused);
+
+static int enqueue_v4lbuf(V4LBuffer* avbuf) {
+    int ret;
+    memset(&avbuf->buf, 0, sizeof(avbuf->buf));
+
+    avbuf->buf.type = avbuf->pool->type;
+    avbuf->buf.memory = avbuf->pool->memory;
+    avbuf->buf.index = avbuf->index;
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        avbuf->buf.m.planes = avbuf->planes;
+        avbuf->buf.length   = avbuf->num_planes;
+    } else {
+        avbuf->buf.length    = avbuf->planes[avbuf->index].length;
+        avbuf->buf.bytesused = avbuf->planes[avbuf->index].bytesused;
+        avbuf->buf.m.userptr = avbuf->planes[avbuf->index].m.userptr;
+    }
+
+    avbuf->buf.timestamp = avbuf->timestamp;
+    avbuf->buf.flags     = avbuf->pool->default_flags | avbuf->flags;
+
+    if((ret = avbuf->pool->ioctl_f(avbuf->pool->fd, VIDIOC_QBUF, &avbuf->buf)) < 0)
+        return AVERROR(errno);
+    avbuf->status = V4LBUF_IN_DRIVER;
+    avbuf->pool->num_queued++;
+
+#ifdef V4L_BUFFER_DEBUG
+    av_log(avbuf->pool->log_ctx, AV_LOG_DEBUG, "Successfuly enqueued a buffer on %s\n", avbuf->pool->name);
+#endif
+
+    return 0;
+}
+
+static V4LBuffer* dequeue_v4lbuf(V4LBufferPool *bp) {
+    int ret;
+    struct v4l2_plane planes[VIDEO_MAX_PLANES];
+    struct v4l2_buffer buf = { 0 };
+    V4LBuffer* avbuf = NULL;
+    int i;
+
+    if(bp->num_queued <= bp->min_queued_buffers) {
+        if(!V4L2_TYPE_IS_OUTPUT(bp->type))
+            av_log(bp->log_ctx, AV_LOG_WARNING, "Trying to dequeue on %s with %i buffers queued and requiring %i buffers\n", bp->name, bp->num_queued, bp->min_queued_buffers);
+        return NULL;
+    }
+
+    memset(&buf, 0, sizeof(buf));
+    buf.type = bp->type;
+    buf.memory = bp->memory;
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        memset(planes, 0, sizeof(planes));
+        buf.m.planes = planes;
+        buf.length = VIDEO_MAX_PLANES;
+    }
+
+    if(bp->blocking_dequeue) {
+        struct pollfd pfd = { .fd = bp->fd, .events = POLLIN | POLLERR };
+        av_log(bp->log_ctx, AV_LOG_DEBUG, "Waiting for event for %i msec before dequeuing on %s\n", bp->blocking_dequeue, bp->name);
+        if((ret = poll(&pfd, 1, bp->blocking_dequeue)) <= 0) {
+            av_log(bp->log_ctx, AV_LOG_WARNING, "No event occurred while waiting\n");
+            return NULL;
+        }
+    }
+
+    ret = bp->ioctl_f(bp->fd, VIDIOC_DQBUF, &buf);
+    if(ret)
+        return NULL;
+
+    avbuf = &(bp->buffers[buf.index]);
+    avbuf->status = V4LBUF_AVAILABLE;
+    avbuf->buf = buf;
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        memcpy(avbuf->planes, planes, sizeof(planes));
+        avbuf->buf.m.planes = avbuf->planes;
+    }
+    avbuf->pool->num_queued--;
+
+    /*
+     * unref buffers if any for output pools.
+     * capture pools will send it back to user and thus we need to keep the ref for now.
+     */
+    if(V4L2_TYPE_IS_OUTPUT(avbuf->pool->type)) {
+        for(i=0; i<avbuf->num_planes; i++) {
+            if(avbuf->bufrefs[i]) {
+                av_buffer_unref(&avbuf->bufrefs[i]);
+            }
+        }
+    }
+
+#ifdef V4L_BUFFER_DEBUG
+    av_log(bp->log_ctx, AV_LOG_DEBUG, "Successfuly dequeued a buffer on %s\n", bp->name);
+#endif
+
+    return avbuf;
+}
+
+static inline int init_buffer(V4LBuffer* avbuf) {
+    int ret, i;
+
+    avbuf->buf.type = avbuf->pool->type;
+    avbuf->buf.memory = avbuf->pool->memory;
+    avbuf->buf.index = avbuf->index;
+    if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        avbuf->buf.length = VIDEO_MAX_PLANES;
+        avbuf->buf.m.planes = avbuf->planes;
+    }
+
+    if((ret= avbuf->pool->ioctl_f(avbuf->pool->fd, VIDIOC_QUERYBUF, &avbuf->buf)) < 0)
+        return AVERROR(errno);
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        for(avbuf->num_planes=0; avbuf->num_planes < avbuf->buf.length && avbuf->buf.m.planes[avbuf->num_planes].length; avbuf->num_planes++);
+    } else avbuf->num_planes = 1;
+
+    for(i=0; i < avbuf->num_planes; i++) {
+        if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+            avbuf->linesize[i] = avbuf->pool->format.fmt.pix_mp.plane_fmt[i].bytesperline;
+        } else
+            avbuf->linesize[i] = avbuf->pool->format.fmt.pix.bytesperline;
+
+        switch(avbuf->pool->memory) {
+            case V4L2_MEMORY_MMAP:
+                if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+                    avbuf->mmap_addr[i] = avbuf->pool->mmap_f(NULL, avbuf->buf.m.planes[i].length,
+                            PROT_READ | PROT_WRITE, MAP_SHARED,
+                            avbuf->pool->fd, avbuf->buf.m.planes[i].m.mem_offset);
+                    avbuf->lengths[i] = avbuf->buf.m.planes[i].length;
+                } else {
+                    avbuf->mmap_addr[i] = avbuf->pool->mmap_f(NULL, avbuf->buf.length,
+                            PROT_READ | PROT_WRITE, MAP_SHARED,
+                            avbuf->pool->fd, avbuf->buf.m.offset);
+                    avbuf->lengths[i] = avbuf->buf.length;
+                }
+
+                if(avbuf->mmap_addr[i] == MAP_FAILED)
+                    return AVERROR(ENOMEM);
+
+                break;
+            case V4L2_MEMORY_USERPTR:
+                /* Nothing to do */
+                break;
+            default:
+                av_log(avbuf->pool->log_ctx, AV_LOG_ERROR, "%s: Sorry, memory type %i is not yet supported\n", __func__, avbuf->pool->memory);
+                return AVERROR_PATCHWELCOME;
+        }
+    }
+    avbuf->status = V4LBUF_AVAILABLE;
+
+    av_log(avbuf->pool->log_ctx, AV_LOG_DEBUG, "Successfully created %i plane(s) for buffer %i in %s\n", avbuf->num_planes, avbuf->buf.index, avbuf->pool->name);
+
+    /* feed capture buffers; can't enqueue userptr buffers now */
+    if(!V4L2_TYPE_IS_OUTPUT(avbuf->pool->type) && (avbuf->pool->memory != V4L2_MEMORY_USERPTR))
+        return enqueue_v4lbuf(avbuf);
+
+    return 0;
+}
+
+int avpriv_init_v4lbufpool(V4LBufferPool* bufs) {
+    struct v4l2_requestbuffers req;
+    int ret, i;
+
+    if(!IS_BP_SUPPORTED(bufs)) {
+        av_log(bufs->log_ctx, AV_LOG_ERROR, "%s: Sorry, type %i is not supported\n", __func__, bufs->type);
+        return AVERROR_PATCHWELCOME;
+    }
+
+#define SET_WRAPPER(F) if(!bufs->F ## _f) bufs->F ## _f = F
+    SET_WRAPPER(open);
+    SET_WRAPPER(close);
+    SET_WRAPPER(dup);
+    SET_WRAPPER(ioctl);
+    SET_WRAPPER(read);
+    SET_WRAPPER(mmap);
+    SET_WRAPPER(munmap);
+#undef SET_WRAPPER
+
+    memset(&req, 0, sizeof(req));
+    req.count = bufs->num_buffers + bufs->min_queued_buffers;
+    req.type = bufs->type;
+    req.memory = bufs->memory;
+
+    if((ret = bufs->ioctl_f(bufs->fd, VIDIOC_REQBUFS, &req)) < 0)
+        return AVERROR(errno);
+
+    if(bufs->num_buffers > req.count)
+        av_log(bufs->log_ctx, AV_LOG_WARNING, "Requested %i buffers but driver gave us only %i for %s\n", bufs->num_buffers, req.count, bufs->name);
+
+    bufs->num_buffers = req.count;
+    bufs->num_queued  = 0;
+    bufs->buffers = av_mallocz(bufs->num_buffers * sizeof(V4LBuffer));
+
+    for(i=0; i < req.count; i++) {
+        V4LBuffer *avbuf = &bufs->buffers[i];
+
+        avbuf->pool = bufs;
+        avbuf->index = i;
+        if((ret = init_buffer(avbuf)) < 0) {
+            av_log(bufs->log_ctx, AV_LOG_ERROR, "Buffer initialization for %s failed (%s)\n", bufs->name, av_err2str(ret));
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static void release_buf(V4LBuffer* b) {
+    int i;
+    for(i=0; i < b->num_planes; i++)
+        if(b->mmap_addr[i] && b->lengths[i])
+            b->pool->munmap_f(b->mmap_addr[i], b->lengths[i]);
+}
+
+void avpriv_release_buffer_pool(V4LBufferPool* bp) {
+    if(bp->buffers) {
+        int i;
+        for(i=0; i < bp->num_buffers; i++)
+            release_buf(&bp->buffers[i]);
+        av_free(bp->buffers);
+    }
+}
+
+static int avbuf_to_avbuf(V4LBuffer* ibuf, V4LBuffer* obuf) {
+    int i;
+    if( ibuf->pool->memory != V4L2_MEMORY_MMAP    ||
+        obuf->pool->memory != V4L2_MEMORY_USERPTR ||
+        ibuf->num_planes   != obuf->num_planes ) {
+        av_log(ibuf->pool->log_ctx, AV_LOG_ERROR, "Buffers do not match:\n");
+        av_log(ibuf->pool->log_ctx, AV_LOG_ERROR, "  Memory: %i vs %i.\n", ibuf->pool->memory, obuf->pool->memory);
+        av_log(ibuf->pool->log_ctx, AV_LOG_ERROR, "  Planes: %i vs %i.\n", ibuf->num_planes, obuf->num_planes);
+        return AVERROR(EINVAL);
+    }
+
+    memcpy(obuf->planes , ibuf->planes,  sizeof(obuf->planes ));
+    memcpy(obuf->lengths, ibuf->lengths, sizeof(obuf->lengths));
+
+    for(i=0; i<ibuf->num_planes; i++) {
+        if(obuf->bufrefs[i])
+            av_buffer_unref(&obuf->bufrefs[i]);
+        if(ibuf->bufrefs[i])
+            av_buffer_unref(&ibuf->bufrefs[i]);
+
+        /*
+         * We keep two refs here:
+         * - One in obuf, which belongs to a capture pool. The ref will be "returned" to user when dequeueing it.
+         *   It will be released when the user frees the frame/packet.
+         * - One in ibuf, which belongs to an output pool. The ref will be released when it'll be dequeued from the output pool.
+         *   i.e. when the buffer is not used by the driver anymore.
+         */
+        ibuf->bufrefs[i] = av_buffer_create(ibuf->mmap_addr[i], ibuf->lengths[i], buffer_callback, ibuf, 0);
+        if(!ibuf->bufrefs[i]) {
+            return AVERROR(ENOMEM);
+        }
+
+        obuf->bufrefs[i] = av_buffer_ref(ibuf->bufrefs[i]);
+        if(!obuf->bufrefs[i]) {
+            return AVERROR(ENOMEM);
+        }
+
+        ibuf->ref_cnt++;
+        obuf->planes[i].m.userptr = (unsigned long)ibuf->mmap_addr[i];
+    }
+    ibuf->status = V4LBUF_RET_USER;
+    return 0;
+}
+
+static inline void set_pts(V4LBuffer *out, int64_t pts) {
+    out->timestamp.tv_sec  = pts / INT64_C(1000000);
+    out->timestamp.tv_usec = pts % INT64_C(1000000);
+}
+
+static inline uint64_t get_pts(V4LBuffer *avbuf) {
+    if(avbuf->buf.timestamp.tv_sec || avbuf->buf.timestamp.tv_usec)
+        return avbuf->buf.timestamp.tv_sec * INT64_C(1000000) + avbuf->buf.timestamp.tv_usec;
+    return AV_NOPTS_VALUE;
+}
+
+static int buf2v4l(V4LBuffer *out, int plane, const uint8_t* data, int size, AVBufferRef* bref) {
+    if(plane >= out->num_planes) {
+        av_log(out->pool->log_ctx, AV_LOG_ERROR, "Trying to feed the %ith plane of a V4L buffer with only %i planes\n", plane+1, out->num_planes);
+        return AVERROR(EINVAL);
+    }
+
+    switch(out->pool->memory) {
+        case V4L2_MEMORY_MMAP:
+            if(out->mmap_addr[plane] != data) {
+                if(!out->pool->copy_warn) {
+                    av_log(out->pool->log_ctx, AV_LOG_WARNING, "Performing useless memcpy() on %s because buffers do not match\n", out->pool->name);
+                    av_log(out->pool->log_ctx, AV_LOG_WARNING, "This could be avoided by using av_v4l_buffer_pool_get_buffer*() or av_v4l_buffer_pool_make_pipe()\n");
+                    out->pool->copy_warn = 1;
+                }
+                memcpy(out->mmap_addr[plane], data, FFMIN(size, out->lengths[plane]));
+            }
+            break;
+        case V4L2_MEMORY_USERPTR:
+            if(!bref) {
+                av_log(out->pool->log_ctx, AV_LOG_ERROR, "%s needs to be fed with an AVBufferRef for USERPTR memory type\n", __func__);
+                return AVERROR_PATCHWELCOME;
+            }
+
+            if(out->bufrefs[plane]) {
+                av_log(out->pool->log_ctx, AV_LOG_WARNING, "%s: V4L buffer already had a buffer referenced\n", __func__);
+                av_buffer_unref(&out->bufrefs[plane]);
+            }
+
+            out->bufrefs[plane] = av_buffer_ref(bref);
+
+            if(!out->bufrefs[plane])
+                return AVERROR(ENOMEM);
+
+            out->planes[plane].m.userptr = (unsigned long)out->bufrefs[plane]->data;
+            out->lengths[plane]          = out->bufrefs[plane]->size;
+            break;
+        default:
+            av_log(out->pool->log_ctx, AV_LOG_ERROR, "%s: Sorry, memory type %i not supported", __func__, out->pool->memory);
+            return AVERROR_PATCHWELCOME;
+    }
+
+    out->planes[plane].bytesused = FFMIN(size, out->lengths[plane]);
+    out->planes[plane].length    = out->lengths[plane];
+
+    return 0;
+}
+
+static int avframe_to_v4lbuf(const AVFrame *pict, V4LBuffer* out) {
+    int i, ret;
+    for(i=0; i<out->num_planes; i++) {
+        if(ret = buf2v4l(out, i, pict->buf[i]->data, pict->buf[i]->size, pict->buf[i]))
+            return ret;
+    }
+    set_pts(out, pict->pts);
+    return 0;
+}
+
+static int avpkt_to_v4lbuf(const AVPacket *pkt, V4LBuffer *out) {
+    int ret;
+    if(ret = buf2v4l(out, 0, pkt->data, pkt->size, pkt->buf))
+        return ret;
+
+    if(pkt->pts != AV_NOPTS_VALUE)
+        set_pts(out, pkt->pts);
+
+    if(pkt->flags & AV_PKT_FLAG_KEY)
+        out->flags = V4L2_BUF_FLAG_KEYFRAME;
+
+    return 0;
+}
+
+static inline int v4l2bufref(V4LBuffer *in, int plane, AVBufferRef **buf) {
+
+#ifdef V4L_BUFFER_DEBUG
+    av_log(in->pool->log_ctx, AV_LOG_DEBUG, "Making an avbuffer from V4L buffer %i[%i] on %s (%i,%i)\n", in->index, plane, in->pool->name, in->pool->type, in->pool->memory);
+#endif
+
+    if(plane >= in->num_planes) {
+        av_log(in->pool->log_ctx, AV_LOG_ERROR, "Trying to use the %ith plane of a V4L buffer with only %i planes\n", plane+1, in->num_planes);
+        return AVERROR(EINVAL);
+    }
+
+    switch(in->pool->memory) {
+        case V4L2_MEMORY_MMAP:
+            *buf = av_buffer_create(in->mmap_addr[plane], in->lengths[plane], buffer_callback, in, 0);
+            if(!*buf)
+                return AVERROR(ENOMEM);
+            in->ref_cnt++;
+            in->status = V4LBUF_RET_USER;
+            break;
+        case V4L2_MEMORY_USERPTR:
+            if(!in->bufrefs[plane]) {
+                av_log(in->pool->log_ctx, AV_LOG_ERROR, "Calling %s with a USERPTR buffer but no AVBufferRef has been found...\n", __func__);
+                return AVERROR(EINVAL);
+            }
+            *buf = av_buffer_ref(in->bufrefs[plane]);
+            if(!*buf)
+                return AVERROR(ENOMEM);
+            av_buffer_unref(&in->bufrefs[plane]);
+            in->status = V4LBUF_AVAILABLE;
+            break;
+        default:
+            av_log(in->pool->log_ctx, AV_LOG_ERROR, "%s: Sorry, memory type %i not supported", __func__, in->pool->memory);
+            return AVERROR_PATCHWELCOME;
+    }
+    return 0;
+}
+
+static int v4lbuf_to_avpkt(AVPacket *pkt, V4LBuffer *avbuf) {
+    int ret;
+    av_free_packet(pkt);
+    if(ret = v4l2bufref(avbuf, 0, &pkt->buf))
+        return ret;
+
+    pkt->data = pkt->buf->data;
+    if(V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        pkt->size = avbuf->buf.m.planes[0].bytesused;
+    } else
+        pkt->size = avbuf->buf.bytesused;
+
+    if(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME)
+        pkt->flags |= AV_PKT_FLAG_KEY;
+
+    pkt->pts = get_pts(avbuf);
+    return 0;
+}
+
+static int v4lbuf_to_avframe(AVFrame *frame, V4LBuffer *avbuf) {
+    int i, ret;
+    av_frame_unref(frame);
+    for(i=0; i<avbuf->num_planes; i++) {
+        if(ret = v4l2bufref(avbuf, i, &frame->buf[i]))
+            return ret;
+        frame->data[i]     = frame->buf[i]->data;
+        frame->linesize[i] = avbuf->linesize[i];
+    }
+
+    frame->format    = avbuf->pool->av_pix_fmt;
+    frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME);
+    frame->width     = avbuf->pool->width;
+    frame->height    = avbuf->pool->height;
+    frame->pts       = get_pts(avbuf);
+    return 0;
+}
+
+static V4LBuffer* v4lbufpool_get_from_avframe(const AVFrame* frame, V4LBufferPool *p) {
+    int i;
+    for(i=0; i<p->num_buffers; i++) {
+        if(V4LBUF_RET_USER == p->buffers[i].status) {
+            switch(p->memory) {
+                case V4L2_MEMORY_MMAP:
+                    if(p->buffers[i].mmap_addr[0] == frame->buf[0]->data)
+                        return &(p->buffers[i]);
+                    break;
+                default:
+                    av_log(p->log_ctx, AV_LOG_ERROR, "%s: Sorry, memory type %i not supported\n", __func__, p->memory);
+                    return NULL;
+            }
+        }
+    }
+    return NULL;
+}
+
+V4LBuffer* avpriv_v4lbufpool_getfreebuf(V4LBufferPool *p, const AVFrame *f, const AVPacket* pkt) {
+    int i;
+    V4LBuffer* ret = NULL;
+
+#ifdef V4L_BUFFER_DEBUG
+    av_log(p->log_ctx, AV_LOG_DEBUG, "Polling for a free buffer on %s\n", p->name);
+#endif
+
+    /* For input pools, we prefer to dequeue available buffers first. */
+    if(V4L2_TYPE_IS_OUTPUT(p->type))
+        while(dequeue_v4lbuf(p));
+
+    if(f && (ret = v4lbufpool_get_from_avframe(f,p)))
+        return ret;
+
+    if(!ret)
+        for(i=0; i<p->num_buffers; i++)
+            if(p->buffers[i].status == V4LBUF_AVAILABLE)
+                return &(p->buffers[i]);
+    return ret;
+}
+
+int avpriv_set_stream_status(V4LBufferPool* bp, int cmd) {
+    int ret;
+    int type = bp->type;
+    if((ret = bp->ioctl_f(bp->fd, cmd, &type)) < 0)
+        return AVERROR(errno);
+    bp->streamon = (cmd == VIDIOC_STREAMON);
+    return 0;
+}
+
+int avpriv_v4l_enqueue_frame_or_pkt_or_buf(V4LBufferPool* bp, const AVFrame* f, const AVPacket* pkt, const uint8_t* buf, int buf_size) {
+    V4LBuffer* avbuf = NULL;
+    int ret;
+
+    if(!f && !pkt && !buf) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "%s: At least one of AVFrame*, AVPacket* or buf must be non NULL!\n", __func__);
+        return AVERROR_BUG;
+    }
+
+    if(!(avbuf = avpriv_v4lbufpool_getfreebuf(bp, f, pkt))) {
+        if(bp->memory == V4L2_MEMORY_MMAP)
+            av_log(bp->log_ctx, AV_LOG_ERROR, "No free input buffer found\n");
+        return AVERROR(ENOMEM);
+    }
+
+    if(f && (ret = avframe_to_v4lbuf(f, avbuf)))
+        return ret;
+
+    if(pkt && (ret = avpkt_to_v4lbuf(pkt, avbuf)))
+        return ret;
+
+    if(buf && (ret = buf2v4l(avbuf, 0, buf, buf_size, NULL)))
+        return ret;
+
+    if(ret = enqueue_v4lbuf(avbuf))
+        return ret;
+
+    return 0;
+}
+
+int avpriv_v4l_dequeue_frame_or_pkt(V4LBufferPool* bp, AVFrame* f, AVPacket* pkt) {
+    V4LBuffer* avbuf = NULL;
+
+    if((!f && !pkt) || (f && pkt)) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "%s: Exactly one of AVFrame* or AVPacket* must be non NULL!\n", __func__);
+        return AVERROR_BUG;
+    }
+
+    if(!(avbuf = dequeue_v4lbuf(bp)))
+        return AVERROR(EAGAIN);
+
+    if(f)
+        return v4lbuf_to_avframe(f, avbuf);
+
+    if(pkt)
+        return v4lbuf_to_avpkt(pkt, avbuf);
+
+    /* Impossible to get here */
+    return AVERROR_BUG;
+}
+
+static inline int get_buffer_sanity_check(V4LBufferPool* bp, const char* func) {
+    if(bp->memory != V4L2_MEMORY_MMAP) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense with memory type %i\n", func, bp->memory);
+        return AVERROR(EINVAL);
+    }
+
+    if(!V4L2_TYPE_IS_OUTPUT(bp->type)) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense on capture pools.\n", func);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+int av_v4l_buffer_pool_get_buffer_frame(void* priv_data, AVFrame* frame) {
+    V4LBufferPool* bp = priv_data;
+    V4LBuffer* avbuf = NULL;
+    int ret;
+
+    if(!frame)
+        return AVERROR(EINVAL);
+
+    if(ret = get_buffer_sanity_check(bp, __func__))
+        return ret;
+
+    if(bp->av_codec_id != AV_CODEC_ID_RAWVIDEO || bp->av_pix_fmt == AV_PIX_FMT_NONE) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense on encoded streams.\n", __func__);
+        return AVERROR(EINVAL);
+    }
+
+    if(!(avbuf = avpriv_v4lbufpool_getfreebuf(bp, NULL, NULL)))
+        return AVERROR(ENOMEM);
+
+    if(ret = v4lbuf_to_avframe(frame, avbuf)) {
+        av_frame_unref(frame);
+        return ret;
+    }
+
+    return 0;
+}
+
+int av_v4l_buffer_pool_get_buffer_packet(void* priv_data, AVPacket* pkt) {
+    V4LBufferPool* bp = priv_data;
+    V4LBuffer* avbuf = NULL;
+    int ret;
+
+    if(!pkt)
+        return AVERROR(EINVAL);
+
+    if(ret = get_buffer_sanity_check(bp, __func__))
+        return ret;
+
+    if(bp->av_codec_id == AV_CODEC_ID_RAWVIDEO || bp->av_pix_fmt != AV_PIX_FMT_NONE) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense on raw streams.\n", __func__);
+        return AVERROR(EINVAL);
+    }
+
+    if(!(avbuf = avpriv_v4lbufpool_getfreebuf(bp, NULL, NULL)))
+        return AVERROR(ENOMEM);
+
+    if(ret = v4lbuf_to_avpkt(pkt, avbuf)) {
+        av_free_packet(pkt);
+        return ret;
+    }
+
+    return 0;
+}
+
+int av_v4l_buffer_pool_make_pipe(void* src, void* dst) {
+    int ret;
+    V4LBuffer *ibuf = NULL, *obuf = NULL;
+    V4LBufferPool *ibp = src;
+    V4LBufferPool *obp = dst;
+
+    /* Sanity checks */
+    if(ibp->memory != V4L2_MEMORY_MMAP) {
+        av_log(ibp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense with memory type %i for src\n", __func__, ibp->memory);
+        return AVERROR(EINVAL);
+    }
+
+    if(!V4L2_TYPE_IS_OUTPUT(ibp->type)) {
+        av_log(ibp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense withg src a capture pool.\n", __func__);
+        return AVERROR(EINVAL);
+    }
+
+    if(obp->memory != V4L2_MEMORY_USERPTR) {
+        av_log(obp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense with memory type %i for dst\n", __func__, obp->memory);
+        return AVERROR(EINVAL);
+    }
+
+    if(V4L2_TYPE_IS_OUTPUT(obp->type)) {
+        av_log(obp->log_ctx, AV_LOG_ERROR, "Calling %s does not make sense with dst an output pool.\n", __func__);
+        return AVERROR(EINVAL);
+    }
+
+    /* Check for optimal settings */
+    if(ibp->num_buffers != obp->num_buffers)
+        av_log(ibp->log_ctx, AV_LOG_WARNING,
+            "%s cannot perfectly match buffer pool sizes, please check your settings for optimal results.\n"
+            "src has %i buffers, while dst has %i\n", __func__, ibp->num_buffers, obp->num_buffers);
+
+    while( (ibuf = avpriv_v4lbufpool_getfreebuf(ibp, NULL, NULL)) &&
+           (obuf = avpriv_v4lbufpool_getfreebuf(obp, NULL, NULL))) {
+        if(ret = avbuf_to_avbuf(ibuf, obuf)) {
+            av_log(ibp->log_ctx, AV_LOG_ERROR, "Failed to convert buffers\n");
+            return ret;
+        }
+
+        if(ret = enqueue_v4lbuf(obuf)) {
+            av_log(obp->log_ctx, AV_LOG_ERROR, "Failed to enqueue buffer\n");
+            return ret;
+        }
+
+        ibuf->piped = obuf;
+    }
+
+    return 0;
+}
+
+static void buffer_callback(void *opaque, uint8_t *unused) {
+    V4LBuffer* avbuf = opaque;
+    if(--avbuf->ref_cnt<=0) {
+        if(V4LBUF_IN_DRIVER != avbuf->status) {
+            if(!V4L2_TYPE_IS_OUTPUT(avbuf->pool->type)) {
+                enqueue_v4lbuf(avbuf);
+            } else if(avbuf->piped) {
+                if(avbuf_to_avbuf(avbuf, avbuf->piped) || enqueue_v4lbuf(avbuf->piped))
+                    av_log(avbuf->pool->log_ctx, AV_LOG_FATAL, "Something went wrong when sending back buffer to its pipe...\n");
+            } else {
+                avbuf->status = V4LBUF_AVAILABLE;
+            }
+        }
+    }
+}
diff --git a/libavcodec/v4l2-buffers.h b/libavcodec/v4l2-buffers.h
new file mode 100644
index 0000000..abf2816
--- /dev/null
+++ b/libavcodec/v4l2-buffers.h
@@ -0,0 +1,230 @@
+/*
+ * V4L2 buffer{,pool} helper functions.
+ * 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
+ */
+
+#ifndef AVCODEC_V4L2_BUFFERS_H
+#define AVCODEC_V4L2_BUFFERS_H
+
+#include "v4l2-common.h"
+#include "avcodec.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/frame.h"
+
+struct V4LBuffer;
+typedef struct V4LBuffer V4LBuffer;
+
+typedef struct V4LBufferPool {
+    /**
+     * Log context (for av_log()). Can be NULL.
+     */
+    void *log_ctx;
+
+    /**
+     * Pool's name. Must be set before calling avpriv_init_v4lbufpool().
+     */
+    const char* name;
+
+    /**
+     * File descriptor obtained from opening the associated device.
+     * Must be set before calling avpriv_init_v4lbufpool().
+     * Readonly after init.
+     */
+    int fd;
+
+    /**
+     * Wrapper for their corresponding standard functions.
+     * If unset before calling avpriv_init_v4lbufpool() these will be set to the OS/libc functions.
+     * If the bufferpool should be used through libv4l2, these must be set to the libv4l2 wrappers
+     * before calling avpriv_init_v4lbufpool().
+     */
+    int (*open_f)(const char *file, int oflag, ...);
+    int (*close_f)(int fd);
+    int (*dup_f)(int fd);
+    int (*ioctl_f)(int fd, unsigned long int request, ...);
+    ssize_t (*read_f)(int fd, void *buffer, size_t n);
+    void *(*mmap_f)(void *start, size_t length, int prot, int flags, int fd, int64_t offset);
+    int (*munmap_f)(void *_start, size_t length);
+
+    /**
+     * Type of this buffer pool.
+     * See V4L2_BUF_TYPE_VIDEO_* in videodev2.h
+     * Must be set before calling avpriv_init_v4lbufpool().
+     * Readonly after init.
+     */
+    enum v4l2_buf_type type;
+
+    /**
+     * Memory type this buffer pool uses.
+     * See V4L2_MEMORY_* in videodev2.h
+     * Must be set before calling avpriv_init_v4lbufpool().
+     * Readonly after init.
+     */
+    enum v4l2_memory memory;
+
+    /**
+     * AVPixelFormat corresponding to this buffer pool.
+     * AV_PIX_FMT_NONE means this is an encoded stream.
+     */
+    enum AVPixelFormat av_pix_fmt;
+
+    /**
+     * AVCodecID corresponding to this buffer pool.
+     * AV_CODEC_ID_RAWVIDEO means this is a raw stream and av_pix_fmt must be set to a valid value.
+     */
+    enum AVCodecID     av_codec_id;
+
+    /**
+     * Format returned by the driver after initializing the buffer pool.
+     * Must be set before calling avpriv_init_v4lbufpool().
+     * avpriv_set_pool_format() can set it.
+     * Readonly after init.
+     */
+    struct v4l2_format format;
+
+    /**
+     * Width and height of the frames it produces (in case of a capture pool, e.g. when decoding)
+     * or accepts (in case of an output pool, e.g. when encoding).
+     *
+     * For output pools, this must must be set before calling avpriv_init_v4lbufpool().
+     * For capture pools, it will be set after having received the information from the driver.
+     */
+    int width, height;
+
+    /**
+     * Default flags to set on buffers to enqueue.
+     * See V4L2_BUF_FLAG_*.
+     */
+    int default_flags;
+
+    /**
+     * Used internally to warn about possibly useless memcpy()'s.
+     * Can be set to 1 to ignore the warning.
+     */
+    int copy_warn;
+
+    /**
+     * Whether the stream has been started (VIDIOC_STREAMON has been sent).
+     */
+    int streamon;
+
+    /**
+     * Number of queued buffers.
+     */
+    int num_queued;
+
+    /**
+     * Time (in ms) we can wait for a buffer before considering it a failure.
+     */
+    int blocking_dequeue;
+
+    /**
+     * Minimum number of buffers that must be kept queued in this queue.
+     *
+     * E.g. for decoders, the drivers might have such requirements to produce proper output.
+     */
+    int min_queued_buffers;
+
+    /**
+     * The actual number of buffers.
+     *
+     * Before calling avpriv_init_v4lbufpool() this is the number of buffers we would like to have available.
+     * avpriv_init_v4lbufpool() asks for (min_buffers + num_buffers) and sets this value to the actual number
+     * of buffers the driver gave us.
+     * Readonly after init.
+     */
+    int num_buffers;
+
+    /**
+     * Opaque pointers to the actual buffers representations.
+     * After initialization, it is an array of size num_buffers.
+     */
+    V4LBuffer *buffers;
+} V4LBufferPool;
+
+/**
+ * Initializes a V4LBufferPool.
+ *
+ * @param[in] bp A pointer to a V4LBufferPool. See V4LBufferPool description for required variables.
+ * @return 0 in case of success, a negative value representing the error otherwise.
+ */
+int avpriv_init_v4lbufpool(V4LBufferPool* bp);
+
+/**
+ * Releases a V4LBufferPool.
+ *
+ * @param[in] bp A pointer to a V4LBufferPool.
+ *               The caller is reponsible for freeing it.
+ *               It must not be used after calling this function.
+ */
+void avpriv_release_buffer_pool(V4LBufferPool* bp);
+
+/**
+ * Sets the status of a V4LBufferPool.
+ *
+ * @param[in] bp A pointer to a V4LBufferPool.
+ * @param[in] cmd The status to set (VIDIOC_STREAMON or VIDIOC_STREAMOFF).
+ *                Warning: If VIDIOC_STREAMOFF is sent to a buffer pool that still has some frames buffered,
+ *                those frames will be dropped.
+ * @return 0 in case of success, a negative value representing the error otherwise.
+ */
+int avpriv_set_stream_status(V4LBufferPool* bp, int cmd);
+
+/**
+ * Dequeues a buffer from a V4LBufferPool to either an AVFrame or an AVPacket.
+ *
+ * Exactly one of f or pkt must be non NULL.
+ * @param[in] bp The V4LBufferPool to dequeue from.
+ * @param[inout] f The AVFrame to dequeue to.
+ * @param[inout] pkt The AVPacket to dequeue to.
+ * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error.
+ */
+int avpriv_v4l_dequeue_frame_or_pkt(V4LBufferPool* bp, AVFrame* f, AVPacket* pkt);
+
+/**
+ * Enqueues a buffer to a V4LBufferPool from either an AVFrame, an AVPacket or a raw buffer.
+ * Exactly one of f, pkt or buf must be non NULL.
+ *
+ * @param[in] bp The V4LBufferPool to enqueue to.
+ * @param[in] f A pointer to an AVFrame to enqueue.
+ * @param[in] pkt A pointer to an AVPacket to enqueue.
+ * @param[in] buf A pointer to a buffer to enqueue.
+ * @param[in] buf_size The size of the buffer pointed by buf.
+ * @return 0 in case of success, a negative error otherwise.
+ */
+int avpriv_v4l_enqueue_frame_or_pkt_or_buf(V4LBufferPool* bp, const AVFrame* f, const AVPacket* pkt, const uint8_t* buf, int buf_size);
+
+/**
+ * Gets a free V4LBuffer from a V4LBufferPool.
+ *
+ * If no matching buffer is found (see below), it tries to dequeue a buffer first
+ * in order to minimize the size of the V4L queue.e
+ *
+ * @param[in] p Pointer to a V4LBufferPool where to get the buffer from.
+ * @param[in] f A pointer to an existing AVFrame:
+ *              If the AVFrame's buffers match a V4LBuffer, this V4LBuffer will be returned.
+ *              Can be NULL.
+ * @param[in] pkt A pointer to an existing AVPacket:
+ *                If the AVPacket's buffers match a V4LBuffer, this V4LBuffer will be returned.
+ *                Can be NULL.
+ * @return A pointer to the V4LBuffer or NULL in case of error.
+ */
+V4LBuffer* avpriv_v4lbufpool_getfreebuf(V4LBufferPool *p, const AVFrame *f, const AVPacket* pkt);
+
+#endif // AVCODEC_V4L2_BUFFERS_H
diff --git a/libavcodec/v4l2.h b/libavcodec/v4l2.h
new file mode 100644
index 0000000..8771b4f
--- /dev/null
+++ b/libavcodec/v4l2.h
@@ -0,0 +1,66 @@
+/*
+ * V4L2 bufferpools helper functions
+ * 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
+ */
+
+#ifndef AVCODEC_V4L2_H
+#define AVCODEC_V4L2_H
+
+#include <libavutil/frame.h>
+#include <libavcodec/avcodec.h>
+
+/**
+ * Allocates an AVFrame's buffers for feeding a V4L M2M device whose buffers
+ * are mmaped in device memory.
+ *
+ * @param[in] priv_data A pointer to an output pool.
+ * @param[in] frame A pre-allocated AVFrame. It will be unref'ed.
+ * @return 0 in case of success, a negative value indicating the error otherwise.
+ */
+int av_v4l_buffer_pool_get_buffer_frame(void* priv_data, AVFrame* frame);
+
+/**
+ * Allocates an AVPacket's buffers for feeding a V4L M2M device whose buffers
+ * are mmaped in device memory.
+ *
+ * @param[in] priv_data A pointer to an output pool.
+ * @param[in] frame A pre-allocated AVPacket. Its buffers will be unref'ed.
+ * @return 0 in case of success, a negative value indicating the error otherwise.
+ */
+int av_v4l_buffer_pool_get_buffer_packet(void* priv_data, AVPacket* pkt);
+
+/**
+ * Makes a "pipe" between src and dst buffer pools.
+ *
+ * @param[in] src A mmap'ed output buffer pool.
+ * @param[in] dst A userptr capture buffer pool.
+ *
+ * src and dst should be configured to have the same number of buffers
+ * for optimal performance.
+ *
+ * Buffers from src will be queued to dst, so that dst will write its results there.
+ * When buffers from dst get fed back to src after processing them, they will be
+ * queued without copy in src (an output queue).
+ * When buffers are no longer used by src, they will be automatically queued back to dst.
+ *
+ * @return 0 in case of success, a negative value indicating the error otherwise.
+ */
+int av_v4l_buffer_pool_make_pipe(void* src, void* dst);
+
+#endif // AVCODEC_V4L2_H
diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
new file mode 100644
index 0000000..772f1c2
--- /dev/null
+++ b/libavcodec/v4l2_m2m.c
@@ -0,0 +1,277 @@
+/*
+ * V4L mem2mem wrapper
+ * 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
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <dirent.h>
+
+
+#include "libavutil/imgutils.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
+#include "avcodec.h"
+#include "v4l2-buffers.h"
+#include "v4l2-common.h"
+#include "v4l2_m2m.h"
+#include "v4l2_m2m_avcodec.h"
+#include "v4l2.h"
+
+#define V4L_MAX_STREAM_SIZE (2*1024*1024)
+
+static const char opool_name[] =  "output pool";
+static const char cpool_name[] = "capture pool";
+
+static inline int try_raw_format(V4LBufferPool* bp, enum AVPixelFormat pixfmt) {
+    struct v4l2_format *fmt = &bp->format;
+    int i;
+
+    fmt->type  = bp->type;
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixfmt);
+
+        fmt->fmt.pix_mp.width  = bp->width;
+        fmt->fmt.pix_mp.height = bp->height;
+
+        fmt->fmt.pix_mp.num_planes = av_pix_fmt_count_planes(pixfmt);
+
+        for(i=0; i<fmt->fmt.pix_mp.num_planes; i++) {
+            uint32_t h;
+            fmt->fmt.pix_mp.plane_fmt[i].bytesperline = av_image_get_linesize(pixfmt, bp->width, i);
+
+            if (i == 1 || i == 2) {
+                h = FF_CEIL_RSHIFT(bp->height, desc->log2_chroma_h);
+            } else
+                h = bp->height;
+
+            fmt->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->fmt.pix_mp.plane_fmt[i].bytesperline * h;
+        }
+
+        fmt->fmt.pix_mp.pixelformat = avpriv_v4l_fmt_ff2v4l(pixfmt, bp->av_codec_id, FF_V4L_PACK_AVFRAME);
+        if(fmt->fmt.pix_mp.pixelformat && !ioctl(bp->fd, VIDIOC_TRY_FMT, fmt))
+            return 0;
+    } else {
+        fmt->fmt.pix.width  = bp->width;
+        fmt->fmt.pix.height = bp->height;
+
+        fmt->fmt.pix.bytesperline = av_image_get_linesize(pixfmt, bp->width, 0);
+        fmt->fmt.pix.sizeimage    = fmt->fmt.pix.bytesperline * bp->height;
+        fmt->fmt.pix.pixelformat  = avpriv_v4l_fmt_ff2v4l(pixfmt, bp->av_codec_id, FF_V4L_PACK_AVFRAME);
+
+        if(fmt->fmt.pix.pixelformat && !ioctl(bp->fd, VIDIOC_TRY_FMT, fmt))
+            return 0;
+    }
+
+    return AVERROR(EINVAL);
+}
+
+static int set_raw_format(V4LBufferPool* bp, int set) {
+    struct v4l2_format *fmt = &bp->format;
+    enum AVPixelFormat pixfmt = bp->av_pix_fmt;
+    struct v4l2_fmtdesc fmtdesc = { 0 };
+
+    fmtdesc.type = bp->type;
+
+    if(AV_PIX_FMT_NONE != pixfmt) {
+        if(try_raw_format(bp, pixfmt)) {
+            if(set)
+                av_log(bp->log_ctx, AV_LOG_INFO, "Suggested pixel format %s is not accepted on %s, will guess one.\n", av_get_pix_fmt_name(pixfmt), bp->name);
+            pixfmt = AV_PIX_FMT_NONE;
+        }
+    }
+
+    while(AV_PIX_FMT_NONE == pixfmt && !ioctl(bp->fd, VIDIOC_ENUM_FMT, &fmtdesc)) {
+        pixfmt = avpriv_v4l_fmt_v4l2ff(fmtdesc.pixelformat, AV_CODEC_ID_RAWVIDEO);
+        if(try_raw_format(bp, pixfmt))
+            pixfmt = AV_PIX_FMT_NONE;
+        if(AV_PIX_FMT_NONE != pixfmt && set) {
+            av_log(bp->log_ctx, AV_LOG_INFO, "Pixelformat %s is accepted on %s, using it.\n", av_get_pix_fmt_name(pixfmt), bp->name);
+            bp->av_pix_fmt = pixfmt;
+        }
+        fmtdesc.index++;
+    }
+
+    if(AV_PIX_FMT_NONE == pixfmt)
+        return AVERROR(EINVAL);
+
+    return (set ? ioctl(bp->fd, VIDIOC_S_FMT, fmt) : 0);
+}
+
+static int set_coded_format(V4LBufferPool* bp, int set) {
+    struct v4l2_format *fmt = &bp->format;
+
+    fmt->type = bp->type;
+
+    if(V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        fmt->fmt.pix_mp.num_planes = VIDEO_MAX_PLANES;
+        fmt->fmt.pix_mp.pixelformat = avpriv_v4l_fmt_ff2v4l(bp->av_pix_fmt, bp->av_codec_id, FF_V4L_PACK_AVPACKET);
+        if(!fmt->fmt.pix_mp.pixelformat) {
+            av_log(bp->log_ctx, AV_LOG_ERROR, "No matching V4L Codec ID found for %i\n", bp->av_codec_id);
+            return AVERROR(EINVAL);
+        }
+        fmt->fmt.pix_mp.plane_fmt[0].sizeimage = V4L_MAX_STREAM_SIZE;
+    } else {
+        fmt->fmt.pix.pixelformat = avpriv_v4l_fmt_ff2v4l(bp->av_pix_fmt, bp->av_codec_id, FF_V4L_PACK_AVPACKET);
+        if(!fmt->fmt.pix.pixelformat) {
+            av_log(bp->log_ctx, AV_LOG_ERROR, "No matching V4L Codec ID found for %i\n", bp->av_codec_id);
+            return AVERROR(EINVAL);
+        }
+        fmt->fmt.pix.sizeimage = V4L_MAX_STREAM_SIZE;
+    }
+    return (set ? ioctl(bp->fd, VIDIOC_S_FMT, fmt) : ioctl(bp->fd, VIDIOC_TRY_FMT, fmt));
+}
+
+int avpriv_set_pool_format(V4LBufferPool* bp, int set) {
+    if(AV_CODEC_ID_RAWVIDEO == bp->av_codec_id)
+        return set_raw_format(bp, set);
+   return set_coded_format(bp, set);
+}
+
+static int probe_and_set(V4Lm2mContext* s, void *log_ctx, int set) {
+    int ret;
+
+    int fail_log_level = ( set ? AV_LOG_ERROR : AV_LOG_DEBUG);
+
+    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
+
+    if(s->fd < 0)
+        return AVERROR(errno);
+
+    s->capture_pool.fd      = s->fd;
+    s->capture_pool.log_ctx = log_ctx;
+    s->capture_pool.name    = cpool_name;
+    s->output_pool.fd       = s->fd;
+    s->output_pool.log_ctx  = log_ctx;
+    s->output_pool.name     = opool_name;
+
+    if((ret = ioctl(s->fd, VIDIOC_QUERYCAP, &(s->cap))) < 0)
+        goto fail;
+
+#define CHECK_CAPS(s, ncap) ((s->cap.capabilities & (ncap)) == (ncap))
+    if(CHECK_CAPS(s, V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING) || CHECK_CAPS(s, V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING)) {
+        s->capture_pool.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+        s->output_pool.type  = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+    } else if(CHECK_CAPS(s, V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING) || CHECK_CAPS(s, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING)) {
+        s->capture_pool.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        s->output_pool.type  = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+    } else {
+        av_log(log_ctx, fail_log_level, "Sorry, driver '%s' on card '%s' is not a V4L mem2mem device\n", s->cap.driver, s->cap.card);
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+#undef CHECK_CAPS
+
+    if(s->output_pool_needs_format && (ret = avpriv_set_pool_format(&s->output_pool, set))) {
+        av_log(log_ctx, fail_log_level, "Failed to set input format\n");
+        goto fail;
+    }
+
+    if(s->capture_pool_needs_format && (ret = avpriv_set_pool_format(&s->capture_pool, set))) {
+        av_log(log_ctx, fail_log_level, "Failed to set output format\n");
+        goto fail;
+    }
+
+    if(s->output_pool_needs_init && set && (ret = avpriv_init_v4lbufpool(&(s->output_pool)))) {
+        av_log(log_ctx, fail_log_level, "Failed to request output pool's buffers\n");
+        goto fail;
+    }
+
+    if(s->capture_pool_needs_init && set && (ret = avpriv_init_v4lbufpool(&(s->capture_pool)))) {
+        av_log(log_ctx, fail_log_level, "Failed to request capture pool's buffers\n");
+        goto fail;
+    }
+
+fail:
+    if(!set || ret) {
+        close(s->fd);
+        s->fd = 0;
+    }
+    return ret;
+}
+
+int avpriv_v4lm2m_init(V4Lm2mContext* s, void* log_ctx) {
+    int ret = AVERROR(EINVAL);
+
+    if(s->devname && *s->devname) {
+        return probe_and_set(s, log_ctx, 1);
+    } else {
+        DIR *dirp;
+        struct dirent *dp;
+        const char pref[] = "video";
+        char *devname_save = s->devname;
+        char tmpbuf[PATH_MAX];
+
+        av_log(log_ctx, AV_LOG_INFO, "Device path not set, probing /dev/video*\n");
+
+        if(!(dirp = opendir("/dev")))
+            return AVERROR(errno);
+
+        for(dp = readdir(dirp); dp; dp = readdir(dirp)) {
+            if(!strncmp(dp->d_name, pref, sizeof(pref)-1)) {
+                errno = 0;
+                snprintf(tmpbuf, sizeof(tmpbuf)-1, "/dev/%s", dp->d_name);
+                av_log(log_ctx, AV_LOG_DEBUG, "Probing %s\n", tmpbuf);
+                s->devname = tmpbuf;
+                if(!(ret = probe_and_set(s, log_ctx, 0)))
+                    break;
+            }
+        }
+
+        closedir(dirp);
+
+        if(!ret) {
+            av_log(log_ctx, AV_LOG_INFO, "Using device %s\n", tmpbuf);
+            ret = probe_and_set(s, log_ctx, 1);
+        } else {
+            av_log(log_ctx, AV_LOG_ERROR, "Could not find a valid device\n");
+        }
+
+        s->devname = devname_save;
+    }
+
+    return ret;
+}
+
+
+int ff_v4lm2m_codec_init(AVCodecContext *avctx) {
+    V4Lm2mContext *s = avctx->priv_data;
+    return avpriv_v4lm2m_init(s, avctx);
+}
+
+int avpriv_v4lm2m_end(V4Lm2mContext* s) {
+    avpriv_release_buffer_pool(&s->output_pool);
+    avpriv_release_buffer_pool(&s->capture_pool);
+
+    avpriv_set_stream_status(&s->output_pool, VIDIOC_STREAMOFF);
+    avpriv_set_stream_status(&s->capture_pool, VIDIOC_STREAMOFF);
+
+    close(s->fd);
+    return 0;
+}
+
+int ff_v4lm2m_codec_end(AVCodecContext *avctx) {
+    V4Lm2mContext *s = avctx->priv_data;
+
+    av_log(avctx, AV_LOG_DEBUG, "Closing context\n");
+    return avpriv_v4lm2m_end(s);
+}
diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h
new file mode 100644
index 0000000..5b0db3c
--- /dev/null
+++ b/libavcodec/v4l2_m2m.h
@@ -0,0 +1,86 @@
+/*
+ * V4L2 mem2mem helper functions
+ * 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
+ */
+
+#ifndef AVCODEC_V4L2_M2M_H
+#define AVCODEC_V4L2_M2M_H
+
+#include "v4l2-common.h"
+#include "v4l2-buffers.h"
+
+#define V4L_M2M_DEFAULT_OPTS \
+    { "device",\
+        "Path to the device to use",\
+        OFFSET(devname),\
+        AV_OPT_TYPE_STRING,\
+        {.str = NULL }, 0, 0, FLAGS },\
+    { "input_memory",\
+        "Input memory model: See V4L2_MEMORY_* in videodev2.h. This depends on the HW but default should work with most but would imply useless memcpy()'s if used improperly.",\
+        OFFSET(output_pool.memory),\
+        AV_OPT_TYPE_INT,\
+        {.i64 = V4L2_MEMORY_MMAP},\
+        0, INT_MAX, FLAGS },\
+    { "output_memory",\
+        "Output memory model: See V4L2_MEMORY_* in videodev2.h. This depends on the HW but default should work with most.",\
+        OFFSET(capture_pool.memory),\
+        AV_OPT_TYPE_INT,\
+        {.i64 = V4L2_MEMORY_MMAP},\
+        0, INT_MAX, FLAGS },\
+    { "num_output_pool_buffers",\
+        "Number of buffers in the output pool",\
+        OFFSET(output_pool.num_buffers),\
+        AV_OPT_TYPE_INT,\
+        { .i64 = 16 },\
+        4, INT_MAX, FLAGS },\
+    { "output_pool_offset", \
+        "Offset of the output buffer pool in the private context.",\
+        OFFSET(output_pool_offset_storage),\
+        AV_OPT_TYPE_INT64,\
+        {.i64 = OFFSET(output_pool) },\
+        INT_MIN, INT_MAX, FLAGS },\
+    { "capture_pool_offset", \
+        "Offset of the capture buffer pool in the private context.",\
+        OFFSET(capture_pool_offset_storage),\
+        AV_OPT_TYPE_INT64,\
+        {.i64 = OFFSET(capture_pool) },\
+        INT_MIN, INT_MAX, FLAGS }
+
+typedef struct V4Lm2mContext {
+    AVClass        *class;
+    int fd;
+    char *devname;
+    struct v4l2_capability cap;
+
+    int64_t output_pool_offset_storage;
+    int output_pool_needs_format;
+    int output_pool_needs_init;
+    V4LBufferPool  output_pool;
+
+    int64_t capture_pool_offset_storage;
+    int capture_pool_needs_format;
+    int capture_pool_needs_init;
+    V4LBufferPool capture_pool;
+} V4Lm2mContext;
+
+int avpriv_set_pool_format(V4LBufferPool* bp, int set);
+int avpriv_v4lm2m_init(V4Lm2mContext* s, void* log_ctx);
+int avpriv_v4lm2m_end(V4Lm2mContext* ctx);
+
+#endif // AVCODEC_V4L2_M2M_H
diff --git a/libavcodec/v4l2_m2m_avcodec.h b/libavcodec/v4l2_m2m_avcodec.h
new file mode 100644
index 0000000..c145713
--- /dev/null
+++ b/libavcodec/v4l2_m2m_avcodec.h
@@ -0,0 +1,30 @@
+/*
+ * V4L2 mem2mem avcodec helper functions
+ * 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
+ */
+
+#ifndef AVCODEC_V4L2_M2M_AVCODEC_H
+#define AVCODEC_V4L2_M2M_AVCODEC_H
+
+#include "avcodec.h"
+
+int ff_v4lm2m_codec_init(AVCodecContext *avctx);
+int ff_v4lm2m_codec_end(AVCodecContext *avctx);
+
+#endif
diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
new file mode 100644
index 0000000..2eef728
--- /dev/null
+++ b/libavcodec/v4l2_m2m_dec.c
@@ -0,0 +1,227 @@
+/*
+ * V4L2 mem2mem decoders
+ * 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:
+ *   - seek (flush)
+ *   - resolution change
+ */
+
+#include <sys/ioctl.h>
+
+#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
+#include "avcodec.h"
+#include "v4l2-buffers.h"
+#include "v4l2_m2m.h"
+#include "v4l2_m2m_avcodec.h"
+#include "v4l2-common.h"
+
+
+#include "libavutil/opt.h"
+
+static int try_start(AVCodecContext *avctx) {
+    struct v4l2_control ctrl;
+    struct v4l2_crop crop;
+    int ret;
+    V4Lm2mContext *s = avctx->priv_data;
+
+    if(!s->output_pool.streamon && (ret = avpriv_set_stream_status(&s->output_pool, VIDIOC_STREAMON) < 0)) {
+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on input pool\n");
+        return ret;
+    }
+
+    s->capture_pool.format.type = s->capture_pool.type;
+    if(ret = ioctl(s->fd, VIDIOC_G_FMT, &s->capture_pool.format)) {
+        av_log(avctx, AV_LOG_WARNING, "Failed to get output format\n");
+        return ret;
+    }
+
+    crop.type = s->capture_pool.type;
+    if(ret = ioctl(s->fd, VIDIOC_G_CROP, &crop)) {
+        av_log(avctx, AV_LOG_WARNING, "Failed to get cropping information\n");
+        return ret;
+    }
+
+    s->capture_pool.width      = avctx->width   = s->capture_pool.format.fmt.pix_mp.width;
+    s->capture_pool.height     = avctx->height  = s->capture_pool.format.fmt.pix_mp.height;
+    s->capture_pool.av_pix_fmt = avctx->pix_fmt = avpriv_v4l_fmt_v4l2ff(s->capture_pool.format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
+
+    avctx->coded_width  = crop.c.width;
+    avctx->coded_height = crop.c.height;
+
+    ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
+    if(ret = ioctl(s->fd, VIDIOC_G_CTRL, &ctrl)) {
+        av_log(avctx, AV_LOG_WARNING, "Failed to get minimum input packets for decoding\n");
+        return ret;
+    }
+
+    s->capture_pool.min_queued_buffers = ctrl.value;
+
+    /* Init capture pool after starting output pool */
+    if(!s->capture_pool.buffers && (ret = avpriv_init_v4lbufpool(&(s->capture_pool)))) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to request output buffers\n");
+        return ret;
+    }
+
+    if(ret = avpriv_set_stream_status(&s->capture_pool, VIDIOC_STREAMON)) {
+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output pool\n");
+        return ret;
+    }
+
+    return 0;
+}
+
+static av_cold int v4lm2m_decode_init(AVCodecContext *avctx) {
+    int ret;
+    V4Lm2mContext *s = avctx->priv_data;
+
+    s->output_pool.av_pix_fmt  = AV_PIX_FMT_NONE;
+    s->output_pool.av_codec_id = avctx->codec_id;
+    s->output_pool_needs_format = 1;
+    s->output_pool_needs_init   = 1;
+    s->output_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    s->capture_pool.av_pix_fmt = avctx->pix_fmt;
+    s->capture_pool.av_codec_id = AV_CODEC_ID_RAWVIDEO;
+    s->capture_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    if(ret = ff_v4lm2m_codec_init(avctx))
+        return ret;
+
+    if( avctx->extradata &&
+        avctx->extradata_size &&
+        !avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, NULL, NULL, avctx->extradata, avctx->extradata_size))
+        try_start(avctx);
+
+    return 0;
+}
+
+
+static int v4lm2m_decode_frame(AVCodecContext *avctx, void *data,
+                             int *got_frame, AVPacket *avpkt) {
+    int ret;
+    V4Lm2mContext *s = avctx->priv_data;
+    AVFrame *frame = data;
+    *got_frame = 0;
+
+    if( avpkt->size &&
+        !s->capture_pool.streamon &&
+        avctx->extradata &&
+        avctx->extradata_size &&
+        !avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, NULL, NULL, avctx->extradata, avctx->extradata_size))
+        try_start(avctx);
+
+    /* Send regular packet, or one empty packet to notify EOS */
+    if(avpkt->size || s->output_pool.streamon)
+        if((ret = avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, NULL, avpkt, NULL, 0)) < 0)
+            return ret;
+
+    if(!s->capture_pool.streamon)
+        try_start(avctx);
+
+    /* Got EOS. We already sent the empty packet to the driver to notify EOS. */
+    if(!avpkt->size && s->output_pool.streamon)
+        s->output_pool.streamon = 0;
+
+    if(!s->capture_pool.streamon)
+        return avpkt->size;
+
+    /* Need to wait for decoder before enqueuing more if output pool is almost full or on EOS */
+    if(s->output_pool.num_queued >= s->output_pool.num_buffers - 2 || !avpkt->size) {
+        s->capture_pool.blocking_dequeue = 1000;
+    } else {
+        s->capture_pool.blocking_dequeue = 0;
+    }
+
+    ret = avpriv_v4l_dequeue_frame_or_pkt(&(s->capture_pool), frame, NULL);
+
+    if(!ret) {
+        *got_frame = 1;
+    } else if(AVERROR(EAGAIN) == ret && !s->capture_pool.blocking_dequeue) {
+        ret = 0;
+    }
+
+    return (ret < 0 ? ret : avpkt->size);
+}
+
+#define OFFSET(x) offsetof(V4Lm2mContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+    V4L_M2M_DEFAULT_OPTS,
+    { "num_capture_pool_extra_buffers",
+        "Number of extra buffers in the capture pool",
+        OFFSET(capture_pool.num_buffers),
+        AV_OPT_TYPE_INT,
+        {.i64 = 4 },
+        4, INT_MAX, FLAGS },
+    { NULL },
+};
+
+#define M2MDEC(NAME, LONGNAME, CODEC) \
+static const AVClass v4l2_m2m_ ## NAME ## _dec_class = {\
+    .class_name = #NAME "_v4l2_m2m_decoder",\
+    .item_name  = av_default_item_name,\
+    .option     = options,\
+    .version    = LIBAVUTIL_VERSION_INT,\
+};\
+\
+AVCodec ff_ ## NAME ## _v4l2m2m_decoder = { \
+    .name           = #NAME "_v4l2m2m" ,\
+    .long_name      = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " decoder wrapper"),\
+    .type           = AVMEDIA_TYPE_VIDEO,\
+    .id             = CODEC ,\
+    .priv_data_size = sizeof(V4Lm2mContext),\
+    .priv_class     = &v4l2_m2m_ ## NAME ## _dec_class,\
+    .init           = v4lm2m_decode_init,\
+    .decode         = v4lm2m_decode_frame,\
+    .close          = ff_v4lm2m_codec_end,\
+    .capabilities   = CODEC_CAP_DELAY,\
+};
+
+#if CONFIG_H263_V4L2M2M_DECODER
+M2MDEC(h263 , "H.263" , AV_CODEC_ID_H263 );
+#endif
+
+#if CONFIG_H264_V4L2M2M_DECODER
+M2MDEC(h264 , "H.264" , AV_CODEC_ID_H264 );
+#endif
+
+#if CONFIG_MPEG1_V4L2M2M_DECODER
+M2MDEC(mpeg1, "MPEG1", AV_CODEC_ID_MPEG1VIDEO);
+#endif
+
+#if CONFIG_MPEG2_V4L2M2M_DECODER
+M2MDEC(mpeg2, "MPEG2", AV_CODEC_ID_MPEG2VIDEO);
+#endif
+
+#if CONFIG_MPEG4_V4L2M2M_DECODER
+M2MDEC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4);
+#endif
+
+#if CONFIG_VC1_V4L2M2M_DECODER
+M2MDEC(vc1  , "VC1"  , AV_CODEC_ID_VC1  );
+#endif
+
+#if CONFIG_VP8_V4L2M2M_DECODER
+M2MDEC(vp8  , "VP8"  , AV_CODEC_ID_VP8  );
+#endif
diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
new file mode 100644
index 0000000..c70f3c4
--- /dev/null
+++ b/libavcodec/v4l2_m2m_enc.c
@@ -0,0 +1,232 @@
+/*
+ * V4L2 mem2mem encoders
+ * 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
+ */
+
+#include <sys/ioctl.h>
+
+#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "avcodec.h"
+#include "v4l2-buffers.h"
+#include "v4l2-common.h"
+#include "v4l2_m2m.h"
+#include "v4l2_m2m_avcodec.h"
+
+
+#define SET_V4L_EXT_CTRL(TYPE, ID, VALUE, CLASS, NAME) \
+{ \
+    struct v4l2_ext_control ctrl = { 0 };\
+    struct v4l2_ext_controls ctrls = { 0 };\
+    ctrl.id = ID ;\
+    ctrl.TYPE = VALUE ;\
+    ctrls.ctrl_class = CLASS ;\
+    ctrls.count = 1;\
+    ctrls.controls = &ctrl;\
+\
+    if( (ret = ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls)) < 0)\
+        av_log(avctx, AV_LOG_WARNING, "Failed to set " NAME "\n");\
+}
+
+static inline int v4l_h264_profile_from_ff(int p) {
+    switch(p) {
+        case FF_PROFILE_H264_BASELINE            : return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
+        case FF_PROFILE_H264_CONSTRAINED_BASELINE: return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
+        case FF_PROFILE_H264_MAIN                : return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
+        case FF_PROFILE_H264_EXTENDED            : return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED;
+        case FF_PROFILE_H264_HIGH                : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
+        case FF_PROFILE_H264_HIGH_10             : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10;
+        case FF_PROFILE_H264_HIGH_10_INTRA       : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA;
+        case FF_PROFILE_H264_HIGH_422            : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422;
+        case FF_PROFILE_H264_HIGH_422_INTRA      : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA;
+        case FF_PROFILE_H264_HIGH_444_PREDICTIVE : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE;
+        case FF_PROFILE_H264_HIGH_444_INTRA      : return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA;
+    }
+    return -1;
+}
+
+static inline int v4l_mpeg4_profile_from_ff(int p) {
+    switch(p) {
+        case FF_PROFILE_MPEG4_SIMPLE         : return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE;
+        case FF_PROFILE_MPEG4_ADVANCED_SIMPLE: return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE;
+        case FF_PROFILE_MPEG4_CORE           : return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE;
+        case FF_PROFILE_MPEG4_SIMPLE_SCALABLE: return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE;
+        case FF_PROFILE_MPEG4_ADVANCED_CODING: return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY;
+    }
+    return -1;
+}
+
+static av_cold int v4lm2m_encode_init(AVCodecContext *avctx) {
+    int ret, val;
+    V4Lm2mContext *s = avctx->priv_data;
+
+    s->output_pool.av_pix_fmt = avctx->pix_fmt;
+    s->output_pool.width = avctx->width;
+    s->output_pool.height = avctx->height;
+    s->output_pool.av_codec_id = AV_CODEC_ID_RAWVIDEO;
+    s->output_pool_needs_format = 1;
+    s->output_pool_needs_init = 1;
+    s->output_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+
+    s->capture_pool.min_queued_buffers = 1;
+    s->capture_pool.av_pix_fmt  = AV_PIX_FMT_NONE;
+    s->capture_pool.av_codec_id = avctx->codec_id;
+    s->capture_pool_needs_format = 1;
+    s->capture_pool_needs_init = 1;
+    s->capture_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    if( (ret = ff_v4lm2m_codec_init(avctx)) )
+        return ret;
+
+    SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_GOP_SIZE, avctx->gop_size, V4L2_CTRL_CLASS_MPEG, "gop size");
+    SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_BITRATE , avctx->bit_rate, V4L2_CTRL_CLASS_MPEG, "bit rate");
+    SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_HEADER_MODE, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, V4L2_CTRL_CLASS_MPEG, "header mode");
+    SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_B_FRAMES, avctx->max_b_frames, V4L2_CTRL_CLASS_MPEG, "number of B-frames");
+
+    switch(avctx->codec_id) {
+        case AV_CODEC_ID_H264:
+            val = v4l_h264_profile_from_ff(avctx->profile);
+            if(val >= 0)
+                 SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_H264_PROFILE, val, V4L2_CTRL_CLASS_MPEG, "h264 profile");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_H264_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
+            break;
+        case AV_CODEC_ID_MPEG4:
+            val = v4l_mpeg4_profile_from_ff(avctx->profile);
+            if(val >= 0)
+                 SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, val, V4L2_CTRL_CLASS_MPEG, "mpeg4 profile");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
+            if(avctx->flags & CODEC_FLAG_QPEL)
+                SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_MPEG4_QPEL, 1, V4L2_CTRL_CLASS_MPEG, "qpel");
+            break;
+        case AV_CODEC_ID_H263:
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_H263_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_H263_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
+            break;
+        case AV_CODEC_ID_VP8:
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
+            SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
+            break;
+    }
+    return 0;
+}
+
+
+static int v4lm2m_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                              const AVFrame *pict, int *got_packet) {
+    int ret;
+    V4Lm2mContext *s = avctx->priv_data;
+
+    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(avctx, 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(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on capture pool\n");
+            return ret;
+        }
+    } else if(s->output_pool.streamon) {
+        /* last frame, stop encoder */
+        struct v4l2_encoder_cmd cmd;
+        memset(&cmd, 0, sizeof(cmd));
+        cmd.cmd = V4L2_ENC_CMD_STOP;
+        if((ret = ioctl(s->fd, VIDIOC_ENCODER_CMD, &cmd)))
+            av_log(avctx, AV_LOG_ERROR, "Failed to stop encoder (%s)\n", av_err2str(AVERROR(errno)));
+        /* Wait for event on output queue. 100ms seems enough. Might need tuning. */
+        s->capture_pool.blocking_dequeue = 100;
+        /* set streamon to 0, while not entirely true, it will close soon anyway, so that we don't send the command twice */
+        s->output_pool.streamon = 0;
+    }
+
+    /* Need to wait for encoder before enqueuing more if output pool is almost full or on EOS */
+    if(s->output_pool.num_queued >= s->output_pool.num_buffers - 2 || !pict) {
+        s->capture_pool.blocking_dequeue = 100;
+    } else {
+        s->capture_pool.blocking_dequeue = 0;
+    }
+
+    ret = avpriv_v4l_dequeue_frame_or_pkt(&(s->capture_pool), NULL, pkt);
+
+    if(!ret) {
+        *got_packet = 1;
+    } else if(AVERROR(EAGAIN) == ret) {
+        return 0;
+    }
+
+    return ret;
+}
+
+
+#define OFFSET(x) offsetof(V4Lm2mContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+
+static const AVOption options[] = {
+    V4L_M2M_DEFAULT_OPTS,
+    { "num_capture_pool_buffers",
+        "Number of buffers in the capture pool",
+        OFFSET(capture_pool.num_buffers),
+        AV_OPT_TYPE_INT,
+        {.i64 = 4 },
+        4, INT_MAX, FLAGS },
+    { NULL },
+};
+
+#define M2MENC(NAME, LONGNAME, CODEC) \
+static const AVClass v4l2_m2m_ ## NAME ## _enc_class = {\
+    .class_name = #NAME "_v4l2_m2m_encoder",\
+    .item_name  = av_default_item_name,\
+    .option     = options,\
+    .version    = LIBAVUTIL_VERSION_INT,\
+};\
+\
+AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \
+    .name           = #NAME "_v4l2m2m" ,\
+    .long_name      = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"),\
+    .type           = AVMEDIA_TYPE_VIDEO,\
+    .id             = CODEC ,\
+    .priv_data_size = sizeof(V4Lm2mContext),\
+    .priv_class     = &v4l2_m2m_ ## NAME ##_enc_class,\
+    .init           = v4lm2m_encode_init,\
+    .encode2        = v4lm2m_encode_frame,\
+    .close          = ff_v4lm2m_codec_end,\
+    .capabilities   = CODEC_CAP_DELAY,\
+};
+
+#if CONFIG_H263_V4L2M2M_ENCODER
+M2MENC(h263 , "H.263" , AV_CODEC_ID_H263 );
+#endif
+
+#if CONFIG_H264_V4L2M2M_ENCODER
+M2MENC(h264 , "H.264" , AV_CODEC_ID_H264 );
+#endif
+
+#if CONFIG_MPEG4_V4L2M2M_ENCODER
+M2MENC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4);
+#endif
+
+#if CONFIG_VP8_V4L2M2M_ENCODER
+M2MENC(vp8  , "VP8"  , AV_CODEC_ID_VP8  );
+#endif
-- 
2.1.3



More information about the ffmpeg-devel mailing list