[FFmpeg-devel,4/4] libavcodec/dev: v4l2: add support for v4l2 mem2mem codecs

Message ID 1500923329-2510-5-git-send-email-jorge.ramirez-ortiz@linaro.org
State New
Headers

Commit Message

Jorge Ramirez-Ortiz July 24, 2017, 7:08 p.m. UTC
From: Alexis Ballier <aballier@gentoo.org>

This patchset enhances Alexis Ballier's original patch and validates
it using Qualcomm's Venus hardware (driver recently landed upstream
[1]).

This has been tested on Qualcomm's DragonBoard 410c and 820c

ffplay tested video decoders:
- h264,
- vp8
- mpeg4

Some of the changes introduced:
- v4l2: some cleanup of the code.
- v4l2: some cleaup before upstreaming.
- v4l2: follow the new decode api.
- v4l2: fix display size for NV12 output pool.
- v4l2: handle EOS.
- v4l2: fix vp8 and mpeg4 decoding.
- v4l2: generate EOF on dequeue errors.
- v4l2: h264_mp4toannexb filtering.

[1] https://lwn.net/Articles/697956/

Reviewed-by: Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
Reviewed-by: Alexis Ballier <aballier@gentoo.org>
Tested-by: Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
---
 Changelog                     |   3 +-
 configure                     |  17 +-
 libavcodec/Makefile           |  15 +-
 libavcodec/allcodecs.c        |   7 +
 libavcodec/v4l2-buffers.c     | 633 ++++++++++++++++++++++++++++++++++++++++++
 libavcodec/v4l2-buffers.h     | 247 ++++++++++++++++
 libavcodec/v4l2-common.c      |  48 ++--
 libavcodec/v4l2-common.h      |   4 +-
 libavcodec/v4l2_m2m.c         | 358 ++++++++++++++++++++++++
 libavcodec/v4l2_m2m.h         |  69 +++++
 libavcodec/v4l2_m2m_avcodec.h |  32 +++
 libavcodec/v4l2_m2m_dec.c     | 244 ++++++++++++++++
 libavcodec/v4l2_m2m_enc.c     | 251 +++++++++++++++++
 13 files changed, 1897 insertions(+), 31 deletions(-)
 create mode 100644 libavcodec/v4l2-buffers.c
 create mode 100644 libavcodec/v4l2-buffers.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
  

Comments

Michael Niedermayer July 26, 2017, 2:04 p.m. UTC | #1
On Mon, Jul 24, 2017 at 09:08:49PM +0200, Jorge Ramirez-Ortiz wrote:
> From: Alexis Ballier <aballier@gentoo.org>
> 
> This patchset enhances Alexis Ballier's original patch and validates
> it using Qualcomm's Venus hardware (driver recently landed upstream
> [1]).
> 
> This has been tested on Qualcomm's DragonBoard 410c and 820c
> 
> ffplay tested video decoders:
> - h264,
> - vp8
> - mpeg4
> 
> Some of the changes introduced:
> - v4l2: some cleanup of the code.
> - v4l2: some cleaup before upstreaming.
> - v4l2: follow the new decode api.
> - v4l2: fix display size for NV12 output pool.
> - v4l2: handle EOS.
> - v4l2: fix vp8 and mpeg4 decoding.
> - v4l2: generate EOF on dequeue errors.
> - v4l2: h264_mp4toannexb filtering.
> 
> [1] https://lwn.net/Articles/697956/

this with the other patches breaks build
CC      libavcodec/v4l2-buffers.o
CC      libavcodec/v4l2_m2m_enc.o
libavcodec/v4l2_m2m_enc.c: In function ‘v4lm2m_encode_init’:
libavcodec/v4l2_m2m_enc.c:150:103: error: ‘V4L2_CID_MPEG_VIDEO_VPX_MIN_QP’ undeclared (first use in this function)
             SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
                                                                                                       ^
libavcodec/v4l2_m2m_enc.c:150:103: note: each undeclared identifier is reported only once for each function it appears in
libavcodec/v4l2_m2m_enc.c:151:103: error: ‘V4L2_CID_MPEG_VIDEO_VPX_MAX_QP’ undeclared (first use in this function)
             SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
                                                                                                       ^
make: *** [libavcodec/v4l2_m2m_enc.o] Error 1
libavcodec/v4l2-buffers.c: In function ‘avpriv_init_v4lbufpool’:
libavcodec/v4l2-buffers.c:276:9: warning: unknown conversion type character ‘y’ in format [-Wformat=]
         av_log(bufs->log_ctx, AV_LOG_ERROR, "%type %i not supported\n", bufs->type);
         ^
libavcodec/v4l2-buffers.c: In function ‘avpkt_to_v4lbuf’:
libavcodec/v4l2-buffers.c:414:22: error: ‘V4L2_BUF_FLAG_LAST’ undeclared (first use in this function)
         out->flags = V4L2_BUF_FLAG_LAST;
                      ^
libavcodec/v4l2-buffers.c:414:22: note: each undeclared identifier is reported only once for each function it appears in
libavcodec/v4l2-buffers.c: In function ‘v4lbuf_to_avpkt’:
libavcodec/v4l2-buffers.c:464:5: warning: ‘av_free_packet’ is deprecated (declared at libavcodec/avcodec.h:4621) [-Wdeprecated-declarations]
     av_free_packet(pkt);
     ^
make: *** [libavcodec/v4l2-buffers.o] Error 1
make: Target `all' not remade because of errors.

[...]
  
Jorge Ramirez-Ortiz July 27, 2017, 6:44 a.m. UTC | #2
On 07/26/2017 04:04 PM, Michael Niedermayer wrote:
> On Mon, Jul 24, 2017 at 09:08:49PM +0200, Jorge Ramirez-Ortiz wrote:
>> From: Alexis Ballier <aballier@gentoo.org>
>>
>> This patchset enhances Alexis Ballier's original patch and validates
>> it using Qualcomm's Venus hardware (driver recently landed upstream
>> [1]).
>>
>> This has been tested on Qualcomm's DragonBoard 410c and 820c
>>
>> ffplay tested video decoders:
>> - h264,
>> - vp8
>> - mpeg4
>>
>> Some of the changes introduced:
>> - v4l2: some cleanup of the code.
>> - v4l2: some cleaup before upstreaming.
>> - v4l2: follow the new decode api.
>> - v4l2: fix display size for NV12 output pool.
>> - v4l2: handle EOS.
>> - v4l2: fix vp8 and mpeg4 decoding.
>> - v4l2: generate EOF on dequeue errors.
>> - v4l2: h264_mp4toannexb filtering.
>>
>> [1] https://lwn.net/Articles/697956/
> this with the other patches breaks build

Hi Michael,

Could you provide some details of your build environment and kernel please?
What is the oldest kernel that you have to build on?

The identifiers that the compiler is moaning about were added to the API 
a few years back (2 and 4) to v4l2-controls.h and videodev2.h)

1) For instance the kernel added support for 
V4L2_CID_MPEG_VIDEO_VPX_MIN_QP in 2013 with commit:

commit 4773ab99aa8bda57de22bf54ddbaa1a941b25fb0
Author: Arun Kumar K <arun.kk@samsung.com>
Date:   Fri Nov 15 02:29:22 2013 -0300

     [media] s5p-mfc: Add QP setting support for vp8 encoder

     Adds v4l2 controls to set MIN, MAX QP values and
     I, P frame QP for vp8 encoder.

     Signed-off-by: Kiran AVND <avnd.kiran@samsung.com>
     Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
     Signed-off-by: Kamil Debski <k.debski@samsung.com>
     Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>


2) Similarly V4L2_BUF_FLAG_LAST was introduced a couple of years ago with

Author: Philipp Zabel <p.zabel@pengutronix.de>
Date:   Mon May 4 07:51:04 2015 -0300

     [media] DocBook media: document codec draining flow

     Document the interaction between VIDIOC_DECODER_CMD 
V4L2_DEC_CMD_STOP and
     VIDIOC_ENCODER_CMD V4L2_ENC_CMD_STOP to start the draining, the 
V4L2_EVENT_EOS
     event signalling all capture buffers are finished and ready to be 
dequeud,
     the new V4L2_BUF_FLAG_LAST buffer flag indicating the last buffer 
being dequeued
     from the capture queue, and the poll and VIDIOC_DQBUF ioctl return 
values once
     the queue is drained.

     Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
     Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
     Signed-off-by: Kamil Debski <k.debski@samsung.com>
     Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>


For users, those identifiers are defined in 
/usr/include/linux/videodev2.h and /usr/include/linux/v4l2-controls.h. 
so they should have been there along with the others; however and since 
this is not the case for you, it must be caused by the build environment 
being not too recent (I have been testing on Ubuntu 16.04)

(btw I have fixed the av log warning on v2)

> CC      libavcodec/v4l2-buffers.o
> CC      libavcodec/v4l2_m2m_enc.o
> libavcodec/v4l2_m2m_enc.c: In function ‘v4lm2m_encode_init’:
> libavcodec/v4l2_m2m_enc.c:150:103: error: ‘V4L2_CID_MPEG_VIDEO_VPX_MIN_QP’ undeclared (first use in this function)
>               SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, avctx->qmin, V4L2_CTRL_CLASS_MPEG, "minimum video quantizer scale");
>                                                                                                         ^
> libavcodec/v4l2_m2m_enc.c:150:103: note: each undeclared identifier is reported only once for each function it appears in
> libavcodec/v4l2_m2m_enc.c:151:103: error: ‘V4L2_CID_MPEG_VIDEO_VPX_MAX_QP’ undeclared (first use in this function)
>               SET_V4L_EXT_CTRL(value, V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, avctx->qmax, V4L2_CTRL_CLASS_MPEG, "maximum video quantizer scale");
>                                                                                                         ^
> make: *** [libavcodec/v4l2_m2m_enc.o] Error 1
> libavcodec/v4l2-buffers.c: In function ‘avpriv_init_v4lbufpool’:
> libavcodec/v4l2-buffers.c:276:9: warning: unknown conversion type character ‘y’ in format [-Wformat=]
>           av_log(bufs->log_ctx, AV_LOG_ERROR, "%type %i not supported\n", bufs->type);
>           ^
> libavcodec/v4l2-buffers.c: In function ‘avpkt_to_v4lbuf’:
> libavcodec/v4l2-buffers.c:414:22: error: ‘V4L2_BUF_FLAG_LAST’ undeclared (first use in this function)
>           out->flags = V4L2_BUF_FLAG_LAST;
>                        ^
> libavcodec/v4l2-buffers.c:414:22: note: each undeclared identifier is reported only once for each function it appears in
> libavcodec/v4l2-buffers.c: In function ‘v4lbuf_to_avpkt’:
> libavcodec/v4l2-buffers.c:464:5: warning: ‘av_free_packet’ is deprecated (declared at libavcodec/avcodec.h:4621) [-Wdeprecated-declarations]
>       av_free_packet(pkt);
>       ^
> make: *** [libavcodec/v4l2-buffers.o] Error 1
> make: Target `all' not remade because of errors.
>
> [...]
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
  
Michael Niedermayer July 27, 2017, 10:45 p.m. UTC | #3
On Thu, Jul 27, 2017 at 08:44:37AM +0200, Jorge Ramirez wrote:
> On 07/26/2017 04:04 PM, Michael Niedermayer wrote:
> >On Mon, Jul 24, 2017 at 09:08:49PM +0200, Jorge Ramirez-Ortiz wrote:
> >>From: Alexis Ballier <aballier@gentoo.org>
> >>
> >>This patchset enhances Alexis Ballier's original patch and validates
> >>it using Qualcomm's Venus hardware (driver recently landed upstream
> >>[1]).
> >>
> >>This has been tested on Qualcomm's DragonBoard 410c and 820c
> >>
> >>ffplay tested video decoders:
> >>- h264,
> >>- vp8
> >>- mpeg4
> >>
> >>Some of the changes introduced:
> >>- v4l2: some cleanup of the code.
> >>- v4l2: some cleaup before upstreaming.
> >>- v4l2: follow the new decode api.
> >>- v4l2: fix display size for NV12 output pool.
> >>- v4l2: handle EOS.
> >>- v4l2: fix vp8 and mpeg4 decoding.
> >>- v4l2: generate EOF on dequeue errors.
> >>- v4l2: h264_mp4toannexb filtering.
> >>
> >>[1] https://lwn.net/Articles/697956/
> >this with the other patches breaks build
> 
> Hi Michael,
> 
> Could you provide some details of your build environment and kernel please?

this should have been a ubuntu 14.04 LTS with 3.13.0-125-generic which
should be the default for ubuntu 14-04


> What is the oldest kernel that you have to build on?

whatever the oldest kernel used by any supported distribution is


[...]
  
Jorge Ramirez-Ortiz July 28, 2017, 9:23 a.m. UTC | #4
On 07/28/2017 12:45 AM, Michael Niedermayer wrote:
> On Thu, Jul 27, 2017 at 08:44:37AM +0200, Jorge Ramirez wrote:
>> On 07/26/2017 04:04 PM, Michael Niedermayer wrote:
>>> On Mon, Jul 24, 2017 at 09:08:49PM +0200, Jorge Ramirez-Ortiz wrote:
>>>> From: Alexis Ballier <aballier@gentoo.org>
>>>>
>>>> This patchset enhances Alexis Ballier's original patch and validates
>>>> it using Qualcomm's Venus hardware (driver recently landed upstream
>>>> [1]).
>>>>
>>>> This has been tested on Qualcomm's DragonBoard 410c and 820c
>>>>
>>>> ffplay tested video decoders:
>>>> - h264,
>>>> - vp8
>>>> - mpeg4
>>>>
>>>> Some of the changes introduced:
>>>> - v4l2: some cleanup of the code.
>>>> - v4l2: some cleaup before upstreaming.
>>>> - v4l2: follow the new decode api.
>>>> - v4l2: fix display size for NV12 output pool.
>>>> - v4l2: handle EOS.
>>>> - v4l2: fix vp8 and mpeg4 decoding.
>>>> - v4l2: generate EOF on dequeue errors.
>>>> - v4l2: h264_mp4toannexb filtering.
>>>>
>>>> [1] https://lwn.net/Articles/697956/
>>> this with the other patches breaks build
>> Hi Michael,
>>
>> Could you provide some details of your build environment and kernel please?
> this should have been a ubuntu 14.04 LTS with 3.13.0-125-generic which
> should be the default for ubuntu 14-04
>
>
>> What is the oldest kernel that you have to build on?
> whatever the oldest kernel used by any supported distribution is

ok will disable the codecs not supported at configure time (will post 
the fix in v2)

>
>
> [...]
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
  

Patch

diff --git a/Changelog b/Changelog
index 187ae79..d31de07 100644
--- a/Changelog
+++ b/Changelog
@@ -29,6 +29,7 @@  version <next>:
 - limiter video filter
 - libvmaf video filter
 - Dolby E decoder and SMPTE 337M demuxer
+- V4L2 mem2mem HW accelerated codecs support
 
 version 3.3:
 - CrystalHD decoder moved to new decode API
@@ -65,7 +66,6 @@  version 3.3:
 - Intel QSV-accelerated VP8 video decoding
 - VAAPI-accelerated deinterlacing
 
-
 version 3.2:
 - libopenmpt demuxer
 - tee protocol
@@ -105,7 +105,6 @@  version 3.2:
 - Changed mapping of rtp MIME type G726 to codec g726le.
 - spec compliant VAAPI/DXVA2 VC-1 decoding of slices in frame-coded images
 
-
 version 3.1:
 - DXVA2-accelerated HEVC Main10 decoding
 - fieldhint filter
diff --git a/configure b/configure
index 0ebc022..9762f32 100755
--- a/configure
+++ b/configure
@@ -10,7 +10,6 @@ 
 # Prevent locale nonsense from breaking basic text processing.
 LC_ALL=C
 export LC_ALL
-
 # make sure we are running under a compatible shell
 # try to make this part work with most shells
 
@@ -149,6 +148,7 @@  Component options:
   --disable-pixelutils     disable pixel utils in libavutil
 
 Individual component options:
+  --disable-v4l2_m2m       disable V4L2 mem2mem code [autodetect]
   --disable-everything     disable all components listed below
   --disable-encoder=NAME   disable encoder NAME
   --enable-encoder=NAME    enable encoder NAME
@@ -1432,6 +1432,7 @@  AVCODEC_COMPONENTS="
 
 AVDEVICE_COMPONENTS="
     indevs
+    v4l2_m2m
     outdevs
 "
 AVFILTER_COMPONENTS="
@@ -2269,10 +2270,12 @@  map 'eval ${v}_inline_deps=inline_asm' $ARCH_EXT_LIST_ARM
 loongson2_deps="mips"
 loongson3_deps="mips"
 v4l2_deps_any="linux_videodev2_h sys_videoio_h"
+v4l2_m2m_select="v4l2"
 mipsfpu_deps="mips"
 mipsdsp_deps="mips"
 mipsdspr2_deps="mips"
 mips32r2_deps="mips"
+vc1_v4l2m2m_decoder_deps="v4l2_m2m"
 mips32r5_deps="mips"
 mips32r6_deps="mips"
 mips64r2_deps="mips"
@@ -2283,6 +2286,8 @@  mmi_deps="mips"
 altivec_deps="ppc"
 dcbzl_deps="ppc"
 ldbrx_deps="ppc"
+vp8_v4l2m2m_decoder_deps="v4l2_m2m"
+vp8_v4l2m2m_encoder_deps="v4l2_m2m"
 ppc4xx_deps="ppc"
 vsx_deps="altivec"
 power8_deps="vsx"
@@ -2436,11 +2441,15 @@  h261_decoder_select="mpegvideo"
 h261_encoder_select="aandcttables mpegvideoenc"
 h263_decoder_select="h263_parser h263dsp 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_decoder_select="h263_decoder"
 h263p_encoder_select="h263_encoder"
 h264_decoder_select="cabac golomb h264chroma h264dsp h264parse h264pred h264qpel videodsp"
 h264_decoder_suggest="error_resilience"
+h264_v4l2m2m_decoder_deps="v4l2_m2m"
+h264_v4l2m2m_encoder_deps="v4l2_m2m"
 hap_decoder_select="snappy texturedsp"
 hap_encoder_deps="libsnappy"
 hap_encoder_select="texturedspenc"
@@ -2481,6 +2490,7 @@  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="mpegvideo"
 mpeg1video_decoder_select="mpegvideo"
 mpeg1video_encoder_select="aandcttables mpegvideoenc h263dsp"
@@ -2488,6 +2498,8 @@  mpeg2video_decoder_select="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"
 msa1_decoder_select="mss34dsp"
 mscc_decoder_select="zlib"
 msmpeg4v1_decoder_select="h263_decoder"
@@ -3590,7 +3602,7 @@  done
 enable_weak audiotoolbox
 
 # Enable hwaccels by default.
-enable_weak d3d11va dxva2 vaapi vda vdpau videotoolbox_hwaccel xvmc
+enable_weak d3d11va dxva2 vaapi v4l2_m2m vda vdpau videotoolbox_hwaccel xvmc
 enable_weak xlib
 
 enable_weak cuda cuvid nvenc vda_framework videotoolbox videotoolbox_encoder
@@ -6059,6 +6071,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 a594b23..12be85b 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -6,6 +6,7 @@  HEADERS = avcodec.h                                                     \
           avfft.h                                                       \
           d3d11va.h                                                     \
           dirac.h                                                       \
+          v4l2.h                                                        \
           dv_profile.h                                                  \
           dxva2.h                                                       \
           jni.h                                                         \
@@ -101,7 +102,8 @@  OBJS-$(CONFIG_LZF)                     += lzf.o
 OBJS-$(CONFIG_MDCT)                    += mdct_fixed.o mdct_float.o mdct_fixed_32.o
 OBJS-$(CONFIG_ME_CMP)                  += me_cmp.o
 OBJS-$(CONFIG_MEDIACODEC)              += mediacodecdec_common.o mediacodec_surface.o mediacodec_wrapper.o mediacodec_sw_buffer.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_MPEG_ER)                 += mpeg_er.o
 OBJS-$(CONFIG_MPEGAUDIO)               += mpegaudio.o
 OBJS-$(CONFIG_MPEGAUDIODSP)            += mpegaudiodsp.o                \
@@ -320,6 +322,8 @@  OBJS-$(CONFIG_H261_ENCODER)            += h261enc.o h261data.o h261.o
 OBJS-$(CONFIG_H263_DECODER)            += h263dec.o h263.o ituh263dec.o        \
                                           mpeg4video.o mpeg4videodec.o flvdec.o\
                                           intelh263dec.o h263data.o
+OBJS-$(CONFIG_H263_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
+OBJS-$(CONFIG_H263_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
 OBJS-$(CONFIG_H263_ENCODER)            += mpeg4videoenc.o mpeg4video.o  \
                                           h263.o ituh263enc.o flvenc.o h263data.o
 OBJS-$(CONFIG_H264_DECODER)            += h264dec.o h264_cabac.o h264_cavlc.o \
@@ -327,6 +331,8 @@  OBJS-$(CONFIG_H264_DECODER)            += h264dec.o h264_cabac.o h264_cavlc.o \
                                           h264_mb.o h264_picture.o \
                                           h264_refs.o h264_sei.o \
                                           h264_slice.o h264data.o
+OBJS-$(CONFIG_H264_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
+OBJS-$(CONFIG_H264_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
 OBJS-$(CONFIG_H264_CUVID_DECODER)      += cuvid.o
 OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_H264_MMAL_DECODER)       += mmaldec.o
@@ -419,11 +425,13 @@  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_MMAL_DECODER)      += mmaldec.o
 OBJS-$(CONFIG_MPEG2_QSV_DECODER)       += qsvdec_other.o
 OBJS-$(CONFIG_MPEG2_QSV_ENCODER)       += qsvenc_mpeg2.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_MPEG2_MEDIACODEC_DECODER) += mediacodecdec.o
@@ -431,6 +439,8 @@  OBJS-$(CONFIG_MPEG2_VAAPI_ENCODER)     += vaapi_encode_mpeg2.o
 OBJS-$(CONFIG_MPEG4_DECODER)           += xvididct.o
 OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_MPEG4_OMX_ENCODER)       += omx.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_MSA1_DECODER)            += mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)            += mscc.o
@@ -503,6 +513,7 @@  OBJS-$(CONFIG_RALF_DECODER)            += ralf.o
 OBJS-$(CONFIG_RAWVIDEO_DECODER)        += rawdec.o
 OBJS-$(CONFIG_RAWVIDEO_ENCODER)        += rawenc.o
 OBJS-$(CONFIG_REALTEXT_DECODER)        += realtextdec.o ass.o
+OBJS-$(CONFIG_VC1_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
 OBJS-$(CONFIG_RL2_DECODER)             += rl2.o
 OBJS-$(CONFIG_ROQ_DECODER)             += roqvideodec.o roqvideo.o
 OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
@@ -518,6 +529,8 @@  OBJS-$(CONFIG_RV30_DECODER)            += rv30.o rv34.o rv30dsp.o
 OBJS-$(CONFIG_RV40_DECODER)            += rv40.o rv34.o rv40dsp.o
 OBJS-$(CONFIG_SAMI_DECODER)            += samidec.o ass.o htmlsubtitles.o
 OBJS-$(CONFIG_S302M_DECODER)           += s302m.o
+OBJS-$(CONFIG_VP8_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
+OBJS-$(CONFIG_VP8_V4L2M2M_ENCODER)     += v4l2_m2m_enc.o
 OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
 OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
 OBJS-$(CONFIG_SCPR_DECODER)            += scpr.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 4712592..12af5bd 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -207,8 +207,10 @@  static void register_all(void)
     REGISTER_ENCDEC (H263,              h263);
     REGISTER_DECODER(H263I,             h263i);
     REGISTER_ENCDEC (H263P,             h263p);
+    REGISTER_ENCDEC (H263_V4L2M2M,      h263_v4l2m2m);
     REGISTER_DECODER(H264,              h264);
     REGISTER_DECODER(H264_CRYSTALHD,    h264_crystalhd);
+    REGISTER_ENCDEC (H264_V4L2M2M,      h264_v4l2m2m);
     REGISTER_DECODER(H264_MEDIACODEC,   h264_mediacodec);
     REGISTER_DECODER(H264_MMAL,         h264_mmal);
     REGISTER_DECODER(H264_QSV,          h264_qsv);
@@ -253,6 +255,7 @@  static void 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_MMAL,        mpeg4_mmal);
 #if FF_API_VDPAU
     REGISTER_DECODER(MPEG4_VDPAU,       mpeg4_vdpau);
@@ -262,8 +265,10 @@  static void register_all(void)
     REGISTER_DECODER(MPEG_VDPAU,        mpeg_vdpau);
     REGISTER_DECODER(MPEG1_VDPAU,       mpeg1_vdpau);
 #endif
+    REGISTER_DECODER(MPEG1_V4L2M2M,     mpeg1_v4l2m2m);
     REGISTER_DECODER(MPEG2_MMAL,        mpeg2_mmal);
     REGISTER_DECODER(MPEG2_CRYSTALHD,   mpeg2_crystalhd);
+    REGISTER_DECODER(MPEG2_V4L2M2M,     mpeg2_v4l2m2m);
     REGISTER_DECODER(MPEG2_QSV,         mpeg2_qsv);
     REGISTER_DECODER(MPEG2_MEDIACODEC,  mpeg2_mediacodec);
     REGISTER_DECODER(MSA1,              msa1);
@@ -361,6 +366,7 @@  static void register_all(void)
     REGISTER_DECODER(VC1IMAGE,          vc1image);
     REGISTER_DECODER(VC1_MMAL,          vc1_mmal);
     REGISTER_DECODER(VC1_QSV,           vc1_qsv);
+    REGISTER_DECODER(VC1_V4L2M2M,       vc1_v4l2m2m);
     REGISTER_ENCODER(VC2,               vc2);
     REGISTER_DECODER(VCR1,              vcr1);
     REGISTER_DECODER(VMDVIDEO,          vmdvideo);
@@ -372,6 +378,7 @@  static void 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(BITPACKED,         bitpacked);
diff --git a/libavcodec/v4l2-buffers.c b/libavcodec/v4l2-buffers.c
new file mode 100644
index 0000000..bc6ef28
--- /dev/null
+++ b/libavcodec/v4l2-buffers.c
@@ -0,0 +1,633 @@ 
+/*
+ * V4L2 buffer{,pool} helper functions.
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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-buffers.h"
+#include "v4l2-common.h"
+
+#if 0
+#define V4L_BUFFER_DEBUG */
+#endif
+
+#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))
+
+enum V4LBuffer_status {
+    V4LBUF_AVAILABLE,
+    V4LBUF_IN_DRIVER,
+    V4LBUF_RET_USER,
+};
+
+struct V4LBuffer {
+    AVBufferRef *bufrefs[VIDEO_MAX_PLANES];
+    struct V4LBufferPool *pool;
+    struct v4l2_plane planes[VIDEO_MAX_PLANES];
+    struct v4l2_buffer buf;
+
+    void * mm_addr[VIDEO_MAX_PLANES];
+    size_t lengths[VIDEO_MAX_PLANES];
+    enum V4LBuffer_status status;
+    int bytesperline[4];
+    int num_planes;
+    int num_lines;
+    int index;
+    int flags;
+    struct timeval timestamp;
+    int ref_cnt;
+};
+
+static int enqueue_v4lbuf(V4LBuffer* avbuf)
+{
+    int ret;
+
+    memset(&avbuf->buf, 0, sizeof(avbuf->buf));
+    avbuf->buf.memory = avbuf->pool->memory;
+    avbuf->buf.type = avbuf->pool->type;
+    avbuf->buf.index = avbuf->index;
+
+    if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        avbuf->buf.length   = avbuf->num_planes;
+        avbuf->buf.m.planes = avbuf->planes;
+    } else {
+        avbuf->buf.bytesused = avbuf->planes[avbuf->index].bytesused;
+        avbuf->buf.m.userptr = avbuf->planes[avbuf->index].m.userptr;
+        avbuf->buf.length    = avbuf->planes[avbuf->index].length;
+    }
+
+    avbuf->buf.flags = avbuf->pool->default_flags | avbuf->flags;
+    avbuf->buf.timestamp = avbuf->timestamp;
+
+    ret = ioctl(avbuf->pool->fd, VIDIOC_QBUF, &avbuf->buf);
+    if (ret < 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, " buffer enqueued on %s\n", avbuf->pool->name);
+#endif
+
+    return 0;
+}
+
+static V4LBuffer* dequeue_v4lbuf(V4LBufferPool *bp)
+{
+    struct v4l2_plane planes[VIDEO_MAX_PLANES];
+    struct v4l2_buffer buf = { 0 };
+    V4LBuffer* avbuf = NULL;
+    struct pollfd pfd;
+    int ret;
+    int i;
+
+    if (bp->num_queued < bp->min_queued_buffers) {
+        return NULL;
+    }
+
+    if (bp->blocking_dequeue) {
+        pfd.fd = bp->fd;
+        switch (bp->type) {
+        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+            pfd.events = POLLIN | POLLERR;
+            break;
+        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+            pfd.events = POLLOUT | POLLERR | POLLWRNORM;
+            break;
+        default:
+            pfd.events = POLLIN | POLLERR | POLLRDNORM;
+        }
+
+        ret = poll(&pfd, 1, bp->blocking_dequeue);
+        if (ret<= 0) {
+            av_log(bp->log_ctx, AV_LOG_WARNING, "%s: timeout (%d ms)\n", bp->name, bp->blocking_dequeue);
+            return NULL;
+        }
+    }
+
+    memset(&buf, 0, sizeof(buf));
+    buf.memory = bp->memory;
+    buf.type = bp->type;
+    if (V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        memset(planes, 0, sizeof(planes));
+        buf.length = VIDEO_MAX_PLANES;
+        buf.m.planes = planes;
+    }
+
+    ret = ioctl(bp->fd, VIDIOC_DQBUF, &buf);
+    if (ret) {
+        if (errno != EAGAIN) {
+            av_log(bp->log_ctx, AV_LOG_DEBUG, "%s: VIDIOC_DQBUF, errno (%d)\n", bp->name, errno);
+            bp->broken = errno;
+        }
+        return NULL;
+    }
+
+    avbuf = &(bp->buffers[buf.index]);
+    if (V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        memcpy(avbuf->planes, planes, sizeof(planes));
+        avbuf->buf.m.planes = avbuf->planes;
+    }
+    avbuf->status = V4LBUF_AVAILABLE;
+    avbuf->pool->num_queued--;
+    avbuf->buf = buf;
+
+    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, "dequeued buffer on %s\n", bp->name);
+#endif
+
+    return avbuf;
+}
+
+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 {
+                avbuf->status = V4LBUF_AVAILABLE;
+            }
+        }
+    }
+}
+
+static inline int init_buffer(V4LBuffer* avbuf)
+{
+    int ret, i;
+
+    avbuf->buf.memory = avbuf->pool->memory;
+    avbuf->buf.type = avbuf->pool->type;
+    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;
+    }
+
+    ret = ioctl(avbuf->pool->fd, VIDIOC_QUERYBUF, &avbuf->buf);
+    if (ret < 0)
+        return AVERROR(errno);
+
+    if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+        avbuf->num_planes = 0;
+        for (;;) {
+            if (avbuf->num_planes < avbuf->buf.length) {
+                if (avbuf->buf.m.planes[avbuf->num_planes].length) {
+                    avbuf->num_planes++;
+                    continue;
+                }
+            }
+            break;
+        }
+    } else {
+        avbuf->num_planes = 1;
+    }
+
+    avbuf->num_lines = avbuf->pool->format.fmt.pix_mp.height;
+    for (i = 0; i < avbuf->num_planes; i++) {
+        if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+            avbuf->bytesperline[i] = avbuf->pool->format.fmt.pix_mp.plane_fmt[i].bytesperline;
+        } else {
+            avbuf->bytesperline[i] = avbuf->pool->format.fmt.pix.bytesperline;
+        }
+
+        switch (avbuf->pool->memory) {
+        case V4L2_MEMORY_MMAP:
+            if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type)) {
+                avbuf->lengths[i] = avbuf->buf.m.planes[i].length;
+                avbuf->mm_addr[i] = mmap(NULL, avbuf->buf.m.planes[i].length,
+                                         PROT_READ | PROT_WRITE, MAP_SHARED,
+                                         avbuf->pool->fd, avbuf->buf.m.planes[i].m.mem_offset);
+            } else {
+                avbuf->lengths[i] = avbuf->buf.length;
+                avbuf->mm_addr[i] = mmap(NULL, avbuf->buf.length,
+                                         PROT_READ | PROT_WRITE, MAP_SHARED,
+                                         avbuf->pool->fd, avbuf->buf.m.offset);
+            }
+            if (avbuf->mm_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, "memory type %i not supported\n", avbuf->pool->memory);
+            return AVERROR_PATCHWELCOME;
+        }
+    }
+    avbuf->status = V4LBUF_AVAILABLE;
+
+    if (!V4L2_TYPE_IS_OUTPUT(avbuf->pool->type)) {
+        if (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, "%type %i not supported\n", bufs->type);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    memset(&req, 0, sizeof(req));
+    req.count = bufs->num_buffers + bufs->min_queued_buffers;
+    req.memory = bufs->memory;
+    req.type = bufs->type;
+
+    ret = ioctl(bufs->fd, VIDIOC_REQBUFS, &req);
+    if (ret< 0)
+        return AVERROR(errno);
+
+    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;
+        ret = init_buffer(avbuf);
+        if (ret < 0) {
+            av_log(bufs->log_ctx, AV_LOG_ERROR, "%s buffer initialization (%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->mm_addr[i] && b->lengths[i]) {
+            munmap(b->mm_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 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)
+        return AVERROR(EINVAL);
+
+    switch (out->pool->memory) {
+    case V4L2_MEMORY_MMAP:
+            memcpy(out->mm_addr[plane], data, FFMIN(size, out->lengths[plane]));
+            break;
+    case V4L2_MEMORY_USERPTR:
+        if (!bref) {
+            av_log(out->pool->log_ctx, AV_LOG_ERROR,
+                   "needs to be set with an AVBufferRef for USERPTR memory type\n");
+            return AVERROR_PATCHWELCOME;
+        }
+
+        if (out->bufrefs[plane]) {
+            av_log(out->pool->log_ctx, AV_LOG_WARNING,
+                   "V4L buffer already had a buffer referenced\n");
+            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,
+               "memory type %i not supported", out->pool->memory);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    out->planes[plane].bytesused = FFMIN(size ? size : 1, 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++) {
+        ret = buf2v4l(out, i, pict->buf[i]->data, pict->buf[i]->size, pict->buf[i]);
+        if (ret)
+            return ret;
+    }
+    set_pts(out, pict->pts);
+
+    return 0;
+}
+
+static int avpkt_to_v4lbuf(const AVPacket *pkt, V4LBuffer *out) {
+    int ret;
+
+    ret = buf2v4l(out, 0, pkt->data, pkt->size, pkt->buf);
+    if (ret)
+        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;
+
+    if (!pkt->size)
+        out->flags = V4L2_BUF_FLAG_LAST;
+
+    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) {
+        return AVERROR(EINVAL);
+    }
+
+    switch (in->pool->memory) {
+    case V4L2_MEMORY_MMAP:
+        *buf = av_buffer_create(in->mm_addr[plane], in->lengths[plane], buffer_callback, in, 0);
+        if (!*buf)
+            return AVERROR(ENOMEM);
+
+        in->status = V4LBUF_RET_USER;
+        in->ref_cnt++;
+        break;
+    case V4L2_MEMORY_USERPTR:
+        if (!in->bufrefs[plane]) {
+            av_log(in->pool->log_ctx, AV_LOG_ERROR, "AVBufferRef not found\n");
+            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, "memory type %i not supported", 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++) {
+        ret = v4l2bufref(avbuf, i, &frame->buf[i]);
+        if (ret)
+            return ret;
+
+        frame->linesize[i] = avbuf->bytesperline[i];
+        frame->data[i] = frame->buf[i]->data;
+
+        if (avbuf->num_planes == 1) {
+            if (avbuf->pool->av_pix_fmt == AV_PIX_FMT_NV12) {
+                frame->linesize[1] = avbuf->bytesperline[0];
+                frame->data[1] = frame->buf[0]->data + avbuf->bytesperline[0] * avbuf->num_lines;
+            }
+        }
+    }
+
+    frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME);
+    frame->format = avbuf->pool->av_pix_fmt;
+    frame->height = avbuf->pool->height;
+    frame->width = avbuf->pool->width;
+    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) {
+            if (p->memory == V4L2_MEMORY_MMAP) {
+                if (p->buffers[i].mm_addr[0] == frame->buf[0]->data)
+                    return &p->buffers[i];
+                continue;
+            }
+            av_log(p->log_ctx, AV_LOG_ERROR, "memory type %i not supported\n", p->memory);
+            return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+V4LBuffer* avpriv_v4lbufpool_getfreebuf(V4LBufferPool *p, const AVFrame *f, const AVPacket* pkt)
+{
+    V4LBuffer* ret;
+    int i;
+
+#ifdef V4L_BUFFER_DEBUG
+    av_log(p->log_ctx, AV_LOG_DEBUG, "Polling for a free buffer on %s\n", p->name);
+#endif
+
+    if (V4L2_TYPE_IS_OUTPUT(p->type)) {
+        for (;;) {
+            if (!dequeue_v4lbuf(p)) {
+                break;
+            }
+        }
+    }
+
+    if (f) {
+        ret = v4lbufpool_get_from_avframe(f, p);
+        if (ret)
+            return ret;
+    }
+
+    for (i = 0; i < p->num_buffers; i++) {
+        if (p->buffers[i].status == V4LBUF_AVAILABLE)
+            return &p->buffers[i];
+    }
+
+    return NULL;
+}
+
+int avpriv_set_stream_status(V4LBufferPool* bp, int cmd)
+{
+    int type = bp->type;
+    int ret;
+
+    ret = ioctl(bp->fd, cmd, &type);
+    if (ret < 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;
+    int ret;
+
+    if (!f && !pkt && !buf) {
+        av_log(bp->log_ctx, AV_LOG_ERROR, "either AVFrame*, AVPacket* or buf must valid\n");
+        return AVERROR_BUG;
+    }
+
+    avbuf = avpriv_v4lbufpool_getfreebuf(bp, f, pkt);
+    if (!avbuf)
+        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, "either AVFrame* or AVPacket* must be valid\n");
+        return AVERROR_BUG;
+    }
+
+    if (!(avbuf = dequeue_v4lbuf(bp))) {
+        if (bp->broken) {
+            return AVERROR_EOF;
+        }
+        return AVERROR(EAGAIN);
+    }
+
+    if (f)
+        return v4lbuf_to_avframe(f, avbuf);
+
+    if (pkt)
+        return v4lbuf_to_avpkt(pkt, avbuf);
+
+    return AVERROR_BUG;
+}
+
+
diff --git a/libavcodec/v4l2-buffers.h b/libavcodec/v4l2-buffers.h
new file mode 100644
index 0000000..ed0362e
--- /dev/null
+++ b/libavcodec/v4l2-buffers.h
@@ -0,0 +1,247 @@ 
+/*
+ * V4L2 buffer{,pool} helper functions.
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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;
+
+struct V4LBufferPool;
+typedef int (*format_f)(struct V4LBufferPool *, int set);
+typedef int (*init_f)(struct V4LBufferPool *);
+
+typedef struct V4LBufferPoolCfg {
+    format_f format;
+    init_f init;
+} V4LBufferPoolCfg;
+
+typedef struct V4LBufferPool {
+    /**
+     * Buffer pool initial configuration.
+     */
+    V4LBufferPoolCfg cfg;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
+     * This is used to work around some encoder bugs.
+     * A demuxer should set this to what is stored in the field used to identify the codec.
+     * If there are multiple such fields in a container then the demuxer should choose the one
+     * which maximizes the information about the used codec.
+     * If the codec tag field in a container is larger than 32 bits then the demuxer should
+     * remap the longer ID to 32 bits with a table or other structure. Alternatively a new
+     * extra_codec_tag + size could be added but for this a clear advantage must be demonstrated
+     * first.
+     * - encoding: Set by user, if not then the default based on codec_id will be used.
+     * - decoding: Set by user, will be converted to uppercase by libavcodec during init.
+     */
+    unsigned int av_codec_tag;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * Pool in unrecoverable error notified by the V4L2 kernel api
+     */
+    int broken;
+
+} 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-common.c b/libavcodec/v4l2-common.c
index 13744fb..f2f98dc 100644
--- a/libavcodec/v4l2-common.c
+++ b/libavcodec/v4l2-common.c
@@ -19,15 +19,14 @@ 
 #include "v4l2-common.h"
 
 const struct v4l_fmt_map avpriv_v4l_fmt_conversion_table[] = {
-    //ff_fmt              codec_id              v4l2_fmt                  pack_flags
-    { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420     , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YVU420     , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_YUV422P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV422P    , FF_V4L_PACK_AVPACKET },
+    /* ff_fmt             codec_id              v4l2_fmt                  pack_flags */
+    { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420     , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
+    { AV_PIX_FMT_YUV422P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV422P    , FF_V4L_PACK_AVPACKET                       },
     { AV_PIX_FMT_YUYV422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUYV       , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
     { AV_PIX_FMT_UYVY422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_UYVY       , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
-    { AV_PIX_FMT_YUV411P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV411P    , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV410     , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YVU410     , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_YUV411P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV411P    , FF_V4L_PACK_AVPACKET                       },
+    { AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV410     , FF_V4L_PACK_AVPACKET                       },
+    { AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YVU410     , FF_V4L_PACK_AVPACKET                       },
     { AV_PIX_FMT_RGB555LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB555     , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
     { AV_PIX_FMT_RGB555BE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB555X    , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
     { AV_PIX_FMT_RGB565LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB565     , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
@@ -40,17 +39,17 @@  const struct v4l_fmt_map avpriv_v4l_fmt_conversion_table[] = {
 #ifdef V4L2_PIX_FMT_Y16
     { AV_PIX_FMT_GRAY16LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_Y16        , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
 #endif
-    { AV_PIX_FMT_NV12,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12       , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MJPEG,    V4L2_PIX_FMT_MJPEG      , FF_V4L_PACK_AVPACKET },
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MJPEG,    V4L2_PIX_FMT_JPEG       , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NV12,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12       , FF_V4L_PACK_AVPACKET                       },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MJPEG,    V4L2_PIX_FMT_MJPEG      , FF_V4L_PACK_AVPACKET                       },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MJPEG,    V4L2_PIX_FMT_JPEG       , FF_V4L_PACK_AVPACKET                       },
 #ifdef V4L2_PIX_FMT_H264
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_H264,     V4L2_PIX_FMT_H264       , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_H264,     V4L2_PIX_FMT_H264       , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG4
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG4,    V4L2_PIX_FMT_MPEG4      , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG4,    V4L2_PIX_FMT_MPEG4      , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_CPIA1
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_CPIA,     V4L2_PIX_FMT_CPIA1      , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_CPIA,     V4L2_PIX_FMT_CPIA1      , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_SRGGB8
     { AV_PIX_FMT_BAYER_BGGR8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SBGGR8 , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
@@ -59,36 +58,36 @@  const struct v4l_fmt_map avpriv_v4l_fmt_conversion_table[] = {
     { AV_PIX_FMT_BAYER_RGGB8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SRGGB8 , FF_V4L_PACK_AVPACKET | FF_V4L_PACK_AVFRAME },
 #endif
 #ifdef V4L2_PIX_FMT_NV12M
-    { AV_PIX_FMT_NV12,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12M      , FF_V4L_PACK_AVFRAME  },
+    { AV_PIX_FMT_NV12,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12M      , FF_V4L_PACK_AVFRAME                        },
 #endif
 #ifdef V4L2_PIX_FMT_NV21M
-    { AV_PIX_FMT_NV21,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV21M      , FF_V4L_PACK_AVFRAME  },
+    { AV_PIX_FMT_NV21,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV21M      , FF_V4L_PACK_AVFRAME                        },
 #endif
 #ifdef V4L2_PIX_FMT_YUV420M
-    { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420M    , FF_V4L_PACK_AVFRAME  },
+    { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420M    , FF_V4L_PACK_AVFRAME                        },
 #endif
 #ifdef V4L2_PIX_FMT_NV16M
-    { AV_PIX_FMT_NV16,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV16M      , FF_V4L_PACK_AVFRAME  },
+    { AV_PIX_FMT_NV16,    AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV16M      , FF_V4L_PACK_AVFRAME                        },
 #endif
 #ifdef V4L2_PIX_FMT_DV
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_DVVIDEO,  V4L2_PIX_FMT_DV         , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_DVVIDEO,  V4L2_PIX_FMT_DV         , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_H263
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_H263,     V4L2_PIX_FMT_H263       , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_H263,     V4L2_PIX_FMT_H263       , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG1
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG1VIDEO, V4L2_PIX_FMT_MPEG1    , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG1VIDEO, V4L2_PIX_FMT_MPEG1    , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG2
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG2VIDEO, V4L2_PIX_FMT_MPEG2    , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_MPEG2VIDEO, V4L2_PIX_FMT_MPEG2    , FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_VC1_ANNEX_G
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_VC1,      V4L2_PIX_FMT_VC1_ANNEX_G, FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_VC1,      V4L2_PIX_FMT_VC1_ANNEX_G, FF_V4L_PACK_AVPACKET                       },
 #endif
 #ifdef V4L2_PIX_FMT_VP8
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_VP8,      V4L2_PIX_FMT_VP8        , FF_V4L_PACK_AVPACKET },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_VP8,      V4L2_PIX_FMT_VP8        , FF_V4L_PACK_AVPACKET                       },
 #endif
-    { AV_PIX_FMT_NONE,    AV_CODEC_ID_NONE,     0                    },
+    { AV_PIX_FMT_NONE,    AV_CODEC_ID_NONE,     0                       , 0                                          },
 };
 
 uint32_t avpriv_v4l_fmt_ff2v4l(enum AVPixelFormat pix_fmt, enum AVCodecID codec_id, int pack_flags)
@@ -101,6 +100,7 @@  uint32_t avpriv_v4l_fmt_ff2v4l(enum AVPixelFormat pix_fmt, enum AVCodecID codec_
             (pix_fmt == AV_PIX_FMT_NONE ||
              avpriv_v4l_fmt_conversion_table[i].ff_fmt == pix_fmt) &&
              (avpriv_v4l_fmt_conversion_table[i].pack_flags & pack_flags)) {
+
             return avpriv_v4l_fmt_conversion_table[i].v4l2_fmt;
         }
     }
diff --git a/libavcodec/v4l2-common.h b/libavcodec/v4l2-common.h
index 1df64f8..346887c 100644
--- a/libavcodec/v4l2-common.h
+++ b/libavcodec/v4l2-common.h
@@ -33,8 +33,8 @@ 
 #include "libavutil/imgutils.h"
 #include "libavutil/log.h"
 #include "libavutil/opt.h"
-#include "avdevice.h"
-#include "timefilter.h"
+#include "libavdevice/avdevice.h"
+#include "libavdevice/timefilter.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/time.h"
diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
new file mode 100644
index 0000000..a27c637
--- /dev/null
+++ b/libavcodec/v4l2_m2m.c
@@ -0,0 +1,358 @@ 
+/*
+ * V4L mem2mem wrapper
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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 <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include "libavutil/imgutils.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
+#include "avcodec.h"
+#include "v4l2_m2m_avcodec.h"
+#include "v4l2-buffers.h"
+#include "v4l2-common.h"
+#include "v4l2_m2m.h"
+
+#define V4L_MAX_STREAM_SIZE (3*1024*1024)
+
+static inline int try_raw_format(V4LBufferPool* bp, enum AVPixelFormat pixfmt)
+{
+    struct v4l2_format *fmt = &bp->format;
+    int ret, i, h;
+
+    fmt->type  = bp->type;
+
+    if (V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixfmt);
+
+        fmt->fmt.pix_mp.pixelformat = avpriv_v4l_fmt_ff2v4l(pixfmt, bp->av_codec_id, FF_V4L_PACK_AVFRAME);
+        if (!fmt->fmt.pix_mp.pixelformat)
+            return AVERROR(EINVAL);
+
+        fmt->fmt.pix_mp.num_planes = av_pix_fmt_count_planes(pixfmt);
+        for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++) {
+            fmt->fmt.pix_mp.plane_fmt[i].bytesperline = av_image_get_linesize(pixfmt, bp->width, i);
+            h = (i == 1 || i == 2) ? FF_CEIL_RSHIFT(bp->height, desc->log2_chroma_h) : bp->height;
+            fmt->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->fmt.pix_mp.plane_fmt[i].bytesperline * h;
+        }
+    } else {
+
+        fmt->fmt.pix.pixelformat  = avpriv_v4l_fmt_ff2v4l(pixfmt, bp->av_codec_id, FF_V4L_PACK_AVFRAME);
+        if (!fmt->fmt.pix.pixelformat)
+            return AVERROR(EINVAL);
+
+        fmt->fmt.pix.bytesperline = av_image_get_linesize(pixfmt, bp->width, 0);
+        fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * bp->height;
+    }
+
+    ret = ioctl(bp->fd, VIDIOC_TRY_FMT, fmt);
+    if (ret)
+        return AVERROR(EINVAL);
+
+    if (V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
+        fmt->fmt.pix_mp.height = bp->height;
+        fmt->fmt.pix_mp.width = bp->width;
+    } else {
+        fmt->fmt.pix.height = bp->height;
+        fmt->fmt.pix.width = bp->width;
+    }
+
+    return 0;
+}
+
+static int set_raw_format(V4LBufferPool* bp, int set)
+{
+    enum AVPixelFormat pixfmt = bp->av_pix_fmt;
+    struct v4l2_format *fmt = &bp->format;
+    struct v4l2_fmtdesc fmtdesc = { 0 };
+    int ret;
+
+    fmtdesc.type = bp->type;
+    if (pixfmt != AV_PIX_FMT_NONE) {
+        ret = try_raw_format(bp, pixfmt);
+        if (ret)
+            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);
+
+        ret = try_raw_format(bp, pixfmt);
+        if (ret)
+            pixfmt = AV_PIX_FMT_NONE;
+
+        if (pixfmt != AV_PIX_FMT_NONE && set) {
+            bp->av_pix_fmt = pixfmt;
+        }
+
+        fmtdesc.index++;
+    }
+
+    if (pixfmt == AV_PIX_FMT_NONE)
+        return AVERROR(EINVAL);
+
+    if (set)
+        return ioctl(bp->fd, VIDIOC_S_FMT, fmt);
+
+    return 0;
+}
+
+static int set_coded_format(V4LBufferPool* bp, int set)
+{
+    struct v4l2_format *fmt = &bp->format;
+    struct v4l2_fmtdesc fdesc;
+    uint32_t v4l2_fmt;
+    int found = 0;
+
+    v4l2_fmt = avpriv_v4l_fmt_ff2v4l(bp->av_pix_fmt, bp->av_codec_id, FF_V4L_PACK_AVPACKET);
+    memset(&fdesc, 0, sizeof(fdesc));
+    fdesc.type = bp->type;
+
+    while (!ioctl(bp->fd, VIDIOC_ENUM_FMT, &fdesc)) {
+        if (v4l2_fmt == fdesc.pixelformat) {
+            found = 1;
+            break;
+        }
+        fdesc.index++;
+    }
+
+    if (!found)
+        return AVERROR(EINVAL);
+
+    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 = v4l2_fmt;
+        if (!fmt->fmt.pix_mp.pixelformat) {
+            av_log(bp->log_ctx, AV_LOG_ERROR, "no V4L codec for id %i\n", bp->av_codec_id);
+            return AVERROR(EINVAL);
+        }
+        fmt->fmt.pix_mp.plane_fmt[0].sizeimage = V4L_MAX_STREAM_SIZE;
+        fmt->fmt.pix_mp.height = bp->height;
+        fmt->fmt.pix_mp.width = bp->width;
+
+    } else {
+        fmt->fmt.pix.pixelformat = v4l2_fmt;
+        if (!fmt->fmt.pix.pixelformat) {
+            av_log(bp->log_ctx, AV_LOG_ERROR, "no V4L codec for id %i\n", bp->av_codec_id);
+            return AVERROR(EINVAL);
+        }
+        fmt->fmt.pix.sizeimage = V4L_MAX_STREAM_SIZE;
+        fmt->fmt.pix.height = bp->height;
+        fmt->fmt.pix.width = bp->width;
+    }
+
+    if (set)
+        return ioctl(bp->fd, VIDIOC_S_FMT, fmt);
+
+    return ioctl(bp->fd, VIDIOC_TRY_FMT, fmt);
+}
+
+int avpriv_set_pool_format(V4LBufferPool* bp, int set)
+{
+    if (bp->av_codec_id == AV_CODEC_ID_RAWVIDEO)
+        return set_raw_format(bp, set);
+
+    return set_coded_format(bp, set);
+}
+
+static inline int splane_video(struct v4l2_capability *cap)
+{
+    if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING))
+        return 1;
+
+    if (cap->capabilities & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING))
+        return 1;
+
+    return 0;
+}
+
+static inline int mplane_video(struct v4l2_capability *cap)
+{
+    if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING))
+        return 1;
+
+    if (cap->capabilities & (V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING))
+        return 1;
+
+    return 0;
+}
+
+static int prepare_pools(V4Lm2mContext* s, void *log_ctx)
+{
+    int ret;
+
+    s->capture_pool.log_ctx = s->output_pool.log_ctx = log_ctx;
+    s->capture_pool.broken = s->output_pool.broken = 0;
+    s->capture_pool.fd = s->output_pool.fd = s->fd;
+    s->capture_pool.name = "capture pool";
+    s->output_pool.name = "output pool";
+
+    memset(&s->cap, 0, sizeof(s->cap));
+    ret = ioctl(s->fd, VIDIOC_QUERYCAP, &s->cap);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (mplane_video(&s->cap)) {
+        s->capture_pool.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+        s->output_pool.type  = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+        return 0;
+    }
+
+    if (splane_video(&s->cap)) {
+        s->capture_pool.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        s->output_pool.type  = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+        return 0;
+    }
+
+    return AVERROR(EINVAL);
+}
+
+static int probe_and_set(V4Lm2mContext* s, void *log_ctx, int set)
+{
+    int fail_log_level = ( set ? AV_LOG_ERROR : AV_LOG_DEBUG);
+    int ret;
+
+    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
+    if (s->fd < 0)
+        return AVERROR(errno);
+
+    ret = prepare_pools(s, log_ctx);
+    if (ret < 0)
+        goto error;
+
+    if (s->output_pool.cfg.format) {
+        ret = s->output_pool.cfg.format(&s->output_pool, set);
+        if (ret) {
+            av_log(log_ctx, fail_log_level, "can't set input format\n");
+            goto error;
+        }
+    }
+
+    if (s->capture_pool.cfg.format) {
+        ret = s->capture_pool.cfg.format(&s->capture_pool, set);
+        if (ret) {
+            av_log(log_ctx, fail_log_level, "can't to set output format\n");
+            goto error;
+        }
+    }
+
+    if (s->output_pool.cfg.init && set) {
+        ret = s->output_pool.cfg.init(&s->output_pool);
+        if (ret) {
+            av_log(log_ctx, fail_log_level, "no output pool's buffers\n");
+            goto error;
+        }
+    }
+
+    if (s->capture_pool.cfg.init && set) {
+        ret = s->capture_pool.cfg.init(&s->capture_pool);
+        if (ret) {
+            av_log(log_ctx, fail_log_level, "no capture pool's buffers\n");
+            goto error;
+        }
+    }
+
+    av_log(log_ctx, AV_LOG_INFO, "using driver '%s' on card '%s'\n", s->cap.driver, s->cap.card);
+
+error:
+    if (!set || ret) {
+        close(s->fd);
+        s->fd = 0;
+    }
+
+    return ret;
+}
+
+int avpriv_v4lm2m_init(V4Lm2mContext* s, void* log_ctx)
+{
+    char *devname_save = s->devname;
+    int ret = AVERROR(EINVAL);
+    char tmpbuf[PATH_MAX];
+    struct dirent *dp;
+    DIR *dirp;
+
+    if (s->devname && *s->devname)
+        return probe_and_set(s, log_ctx, 1);
+
+    if (!(dirp = opendir("/dev")))
+        return AVERROR(errno);
+
+    for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
+
+        if (!strncmp(dp->d_name, "video", sizeof("video") - 1)) {
+            snprintf(tmpbuf, sizeof(tmpbuf) - 1, "/dev/%s", dp->d_name);
+            av_log(log_ctx, AV_LOG_DEBUG, "probing %s\n", tmpbuf);
+
+            s->devname = tmpbuf;
+            ret = probe_and_set(s, log_ctx, 0);
+            if (!ret)
+                break;
+        }
+    }
+    closedir(dirp);
+
+    if (ret) {
+        av_log(log_ctx, AV_LOG_ERROR, "Could not find a valid device\n");
+        s->devname = devname_save;
+
+        return ret;
+    }
+
+    av_log(log_ctx, AV_LOG_INFO, "Using device %s\n", tmpbuf);
+    ret = probe_and_set(s, log_ctx, 1);
+    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..6bfd4c4
--- /dev/null
+++ b/libavcodec/v4l2_m2m.h
@@ -0,0 +1,69 @@ 
+/*
+ * V4L2 mem2mem helper functions
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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-buffers.h"
+#include "v4l2-common.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 }
+
+typedef struct V4Lm2mContext
+{
+    AVClass *class;
+    int fd;
+    char *devname;
+    struct v4l2_capability cap;
+    V4LBufferPool output_pool;
+    V4LBufferPool capture_pool;
+} V4Lm2mContext;
+
+int avpriv_v4lm2m_init(V4Lm2mContext* s, void* log_ctx);
+int avpriv_set_pool_format(V4LBufferPool* bp, int set);
+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..2d0f1b6
--- /dev/null
+++ b/libavcodec/v4l2_m2m_avcodec.h
@@ -0,0 +1,32 @@ 
+/*
+ * V4L2 mem2mem avcodec helper functions
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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..c09ce6a
--- /dev/null
+++ b/libavcodec/v4l2_m2m_dec.c
@@ -0,0 +1,244 @@ 
+/*
+ * V4L2 mem2mem decoders
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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 "v4l2_m2m_avcodec.h"
+#include "v4l2-common.h"
+#include "v4l2-buffers.h"
+#include "v4l2_m2m.h"
+#include "decode.h"
+#include "avcodec.h"
+
+static int try_start(AVCodecContext *avctx)
+{
+    V4Lm2mContext *s = avctx->priv_data;
+    struct v4l2_selection selection;
+    struct v4l2_control ctrl;
+    int ret;
+
+    if (s->output_pool.streamon && s->capture_pool.streamon)
+        return 0;
+
+    /* this will report the size of the frame back (see a4lbuf_to_avframe) */
+    s->capture_pool.height = avctx->coded_height;
+    s->capture_pool.width = avctx->coded_width;
+
+    /* start the output process */
+    if (!s->output_pool.streamon) {
+        ret = avpriv_set_stream_status(&s->output_pool, VIDIOC_STREAMON);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output pool\n");
+            return ret;
+        }
+    }
+
+    /* get the capture format */
+    s->capture_pool.format.type = s->capture_pool.type;
+    ret = ioctl(s->fd, VIDIOC_G_FMT, &s->capture_pool.format);
+    if (ret) {
+        av_log(avctx, AV_LOG_DEBUG, "VIDIOC_G_FMT ioctl\n");
+        return ret;
+    }
+
+    /* store what the decoder gives */
+    avctx->pix_fmt = avpriv_v4l_fmt_v4l2ff(s->capture_pool.format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
+    s->capture_pool.av_pix_fmt = avctx->pix_fmt;
+
+    /* set the crop parameters */
+    selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    selection.r.height = avctx->coded_height;
+    selection.r.width = avctx->coded_width;
+    ret = ioctl(s->fd, VIDIOC_S_SELECTION, &selection);
+    if (!ret) {
+        ret = ioctl(s->fd, VIDIOC_G_SELECTION, &selection);
+        if (ret) {
+            av_log(avctx, AV_LOG_ERROR, "VIDIOC_G_SELECTION ioctl\n");
+        } else {
+            av_log(avctx, AV_LOG_DEBUG, "crop output %dx%d\n", selection.r.width, selection.r.height);
+            /* update the size of the resulting frame */
+            s->capture_pool.height = selection.r.height;
+            s->capture_pool.width  = selection.r.width;
+        }
+    }
+
+    /* get the minimum number of buffers required by capture */
+    memset(&ctrl, 0, sizeof(ctrl));
+    ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
+    ret = ioctl(s->fd, VIDIOC_G_CTRL, &ctrl);
+    if (ret) {
+        s->capture_pool.min_queued_buffers = 6;
+    } else {
+        s->capture_pool.min_queued_buffers = ctrl.value;
+    }
+
+    /* init the capture pool */
+    if (!s->capture_pool.buffers) {
+        ret = avpriv_init_v4lbufpool(&s->capture_pool);
+        if (ret) {
+            av_log(avctx, AV_LOG_DEBUG, "can't request output buffers\n");
+            return ret;
+        }
+    }
+
+    /* start the capture process */
+    ret = avpriv_set_stream_status(&s->capture_pool, VIDIOC_STREAMON);
+    if (ret) {
+        av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture pool\n");
+        return ret;
+    }
+
+
+    return 0;
+}
+
+static av_cold int v4lm2m_decode_init(AVCodecContext *avctx)
+{
+    V4Lm2mContext *s = avctx->priv_data;
+
+    s->output_pool.cfg.format = avpriv_set_pool_format;
+    s->output_pool.cfg.init = avpriv_init_v4lbufpool;
+
+    s->output_pool.av_codec_tag = avctx->codec_tag;
+    s->output_pool.av_codec_id = avctx->codec_id;
+    s->output_pool.av_pix_fmt  = AV_PIX_FMT_NONE;
+    s->output_pool.height = avctx->coded_height;
+    s->output_pool.width = avctx->coded_width;
+    s->output_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    s->capture_pool.cfg.format = avpriv_set_pool_format;
+    s->capture_pool.cfg.init = NULL;
+
+    s->capture_pool.av_codec_tag = 0;
+    s->capture_pool.av_codec_id = AV_CODEC_ID_RAWVIDEO;
+    s->capture_pool.av_pix_fmt = avctx->pix_fmt;
+    s->capture_pool.height = avctx->coded_height;
+    s->capture_pool.width = avctx->coded_width;
+    s->capture_pool.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    return ff_v4lm2m_codec_init(avctx);
+}
+
+static inline void set_dequeue_mode(V4Lm2mContext *s)
+{
+    s->capture_pool.blocking_dequeue = 0;
+
+    if (s->output_pool.num_queued >= s->output_pool.num_buffers - 2)
+        s->capture_pool.blocking_dequeue = 1000;
+}
+
+static int v4lm2m_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    V4Lm2mContext *s = avctx->priv_data;
+    AVPacket avpkt = {0};
+    int ret;
+
+    ret = ff_decode_get_packet(avctx, &avpkt);
+    if (ret < 0 && ret != AVERROR_EOF)
+        return ret;
+
+    if (avctx->extradata && avctx->extradata_size) {
+            ret = avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, NULL,
+                                NULL, avctx->extradata, avctx->extradata_size);
+            if (ret)
+                return ret;
+
+            (void) try_start(avctx);
+    }
+
+    ret = avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, NULL, &avpkt, NULL, 0);
+    if (ret < 0)
+        return ret;
+
+    ret = try_start(avctx);
+    if (ret)
+        return 0;
+
+    set_dequeue_mode(s);
+
+    return avpriv_v4l_dequeue_frame_or_pkt(&s->capture_pool, frame, NULL);
+}
+
+#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 = 6}, 4, INT_MAX, FLAGS},
+        { NULL},
+        };
+
+#define M2MDEC(NAME, LONGNAME, CODEC, bsf_name) \
+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,\
+    .receive_frame  = v4lm2m_receive_frame,\
+    .close          = ff_v4lm2m_codec_end,\
+    .capabilities   = CODEC_CAP_DELAY,\
+    .bsfs           = bsf_name, \
+};
+
+#if CONFIG_H263_V4L2M2M_DECODER
+        M2MDEC(h263 , "H.263" , AV_CODEC_ID_H263, NULL);
+#endif
+
+#if CONFIG_H264_V4L2M2M_DECODER
+        M2MDEC(h264 , "H.264" , AV_CODEC_ID_H264, "h264_mp4toannexb");
+#endif
+
+#if CONFIG_MPEG1_V4L2M2M_DECODER
+        M2MDEC(mpeg1, "MPEG1", AV_CODEC_ID_MPEG1VIDEO, NULL);
+#endif
+
+#if CONFIG_MPEG2_V4L2M2M_DECODER
+        M2MDEC(mpeg2, "MPEG2", AV_CODEC_ID_MPEG2VIDEO, NULL);
+#endif
+
+#if CONFIG_MPEG4_V4L2M2M_DECODER
+        M2MDEC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4, NULL);
+#endif
+
+#if CONFIG_VC1_V4L2M2M_DECODER
+        M2MDEC(vc1  , "VC1"  , AV_CODEC_ID_VC1, NULL);
+#endif
+
+#if CONFIG_VP8_V4L2M2M_DECODER
+        M2MDEC(vp8  , "VP8"  , AV_CODEC_ID_VP8, NULL);
+#endif
+
+
diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
new file mode 100644
index 0000000..899a54f
--- /dev/null
+++ b/libavcodec/v4l2_m2m_enc.c
@@ -0,0 +1,251 @@ 
+/*
+ * V4L2 mem2mem encoders
+ *
+ * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
+ * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
+ *
+ * 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 "v4l2_m2m_avcodec.h"
+#include "v4l2-buffers.h"
+#include "v4l2-common.h"
+#include "v4l2_m2m.h"
+#include "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)
+{
+    V4Lm2mContext *s = avctx->priv_data;
+    int ret, val;
+
+    s->output_pool.cfg.format = avpriv_set_pool_format;
+    s->output_pool.cfg.init = avpriv_init_v4lbufpool;
+    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.default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+    s->capture_pool.cfg.format = avpriv_set_pool_format;
+    s->capture_pool.cfg.init = avpriv_init_v4lbufpool;
+    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.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)
+{
+    V4Lm2mContext *s = avctx->priv_data;
+    int ret;
+
+    if (pict) {
+        ret = avpriv_v4l_enqueue_frame_or_pkt_or_buf(&s->output_pool, pict, NULL, NULL, 0);
+        if (ret < 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;
+
+        ret = ioctl(s->fd, VIDIOC_ENCODER_CMD, &cmd);
+        if (ret)
+            av_log(avctx, AV_LOG_ERROR, "Failed to stop encoder (%s)\n", av_err2str(AVERROR(errno)));
+
+        s->output_pool.streamon = 0;
+    }
+
+    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 (AVERROR(EAGAIN) == ret )
+        return 0;
+
+    if (!ret)
+        *got_packet = 1;
+
+    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