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

Mark Thompson sw at jkqxz.net
Thu Aug 3 02:53:55 EEST 2017


On 02/08/17 08:32, Jorge Ramirez-Ortiz wrote:
> ...
> diff --git a/compat/v4l2/v4l2-common.h b/compat/v4l2/v4l2-common.h
> diff --git a/compat/v4l2/videodev2.h b/compat/v4l2/videodev2.h

These are discussed in other threads.  I don't really have any comment either way.

> diff --git a/configure b/configure
> index ed94de0..650c8fb 100755
> --- a/configure
> +++ b/configure
> @@ -10,7 +10,6 @@
>  # Prevent locale nonsense from breaking basic text processing.
>  LC_ALL=C
>  export LC_ALL
> -

Spurious change?

>  # 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]

s/_/-/ here.

>    --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,11 +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"
> @@ -2284,6 +2286,9 @@ mmi_deps="mips"
>  altivec_deps="ppc"
>  dcbzl_deps="ppc"
>  ldbrx_deps="ppc"
> +vp8_v4l2m2m_decoder_deps="v4l2_m2m"
> +vp8_v4l2m2m_encoder_deps="v4l2_m2m"
> +vp9_v4l2m2m_decoder_deps="v4l2_m2m"

These seem to be placed randomly in the file.  There is a hardware codec section at around line 2800, put all of these declarations there.

>  ppc4xx_deps="ppc"
>  vsx_deps="altivec"
>  power8_deps="vsx"
> @@ -2437,15 +2442,22 @@ 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"
>  hevc_decoder_select="bswapdsp cabac golomb hevcparse videodsp"
> +hevc_encoder_select="hevc_v4l2m2m"
> +hevc_v4l2m2m_decoder_deps="v4l2_m2m"
> +hevc_v4l2m2m_encoder_deps="v4l2_m2m"
>  huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
>  huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp"
>  iac_decoder_select="imc_decoder"
> @@ -2482,6 +2494,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"
> @@ -2489,6 +2502,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"
> @@ -3042,7 +3057,6 @@ qtkit_indev_select="qtkit"
>  sdl2_outdev_deps="sdl2"
>  sndio_indev_deps="sndio"
>  sndio_outdev_deps="sndio"
> -v4l_indev_deps="linux_videodev_h"
>  v4l2_indev_select="v4l2"
>  v4l2_outdev_select="v4l2"
>  vfwcap_indev_deps="vfw32 vfwcap_defines"
> @@ -3592,7 +3606,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
> @@ -6058,12 +6072,10 @@ pod2man --help     > /dev/null 2>&1 && enable pod2man   || disable pod2man
>  rsync --help 2> /dev/null | grep -q 'contimeout' && enable rsync_contimeout || disable rsync_contimeout
>  
>  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_header sys/videoio.h
>  check_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_safe struct_v4l2_frmivalenum_discrete
> +add_cflags -I$source_path/compat/v4l2

Unconditionally?  Sounds useful on Windows.

>  
>  check_lib user32 "windows.h winuser.h" GetShellWindow -luser32
>  check_lib vfw32 "windows.h vfw.h" capCreateCaptureWindow -lvfw32
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 364aec9..f45050e 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -101,7 +101,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_fmt.o v4l2_buffers.o
> +OBJS-$(CONFIG_V4L2_M2M)                += v4l2_m2m.o

Random placing?  These should be in alphabetical order.

>  OBJS-$(CONFIG_MPEG_ER)                 += mpeg_er.o
>  OBJS-$(CONFIG_MPEGAUDIO)               += mpegaudio.o
>  OBJS-$(CONFIG_MPEGAUDIODSP)            += mpegaudiodsp.o                \
> @@ -320,6 +321,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 +330,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
> @@ -346,6 +351,8 @@ OBJS-$(CONFIG_HEVC_DECODER)            += hevcdec.o hevc_mvs.o \
>                                            hevcdsp.o hevc_filter.o hevc_data.o
>  OBJS-$(CONFIG_HEVC_CUVID_DECODER)      += cuvid.o
>  OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o
> +OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_HEVC_NVENC_ENCODER)      += nvenc_hevc.o
>  OBJS-$(CONFIG_NVENC_HEVC_ENCODER)      += nvenc_hevc.o
>  OBJS-$(CONFIG_HEVC_QSV_DECODER)        += qsvdec_h2645.o
> @@ -419,11 +426,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 +440,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 +514,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 +530,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

As should all of the rest of these.

>  OBJS-$(CONFIG_S302M_ENCODER)           += s302menc.o
>  OBJS-$(CONFIG_SANM_DECODER)            += sanm.o
>  OBJS-$(CONFIG_SCPR_DECODER)            += scpr.o
> @@ -628,6 +642,8 @@ OBJS-$(CONFIG_VP9_DECODER)             += vp9.o vp9data.o vp9dsp.o vp9lpf.o vp9r
>                                            vp9dsp_8bpp.o vp9dsp_10bpp.o vp9dsp_12bpp.o
>  OBJS-$(CONFIG_VP9_CUVID_DECODER)       += cuvid.o
>  OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
> +OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_VP9_V4L2M2M_ENCODER)     += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
>  OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
>  OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 4712592..d154ac9 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);
> @@ -219,6 +221,7 @@ static void register_all(void)
>      REGISTER_ENCDEC (HAP,               hap);
>      REGISTER_DECODER(HEVC,              hevc);
>      REGISTER_DECODER(HEVC_QSV,          hevc_qsv);
> +    REGISTER_ENCDEC(HEVC_V4L2M2M,       hevc_v4l2m2m);
>      REGISTER_DECODER(HNM4_VIDEO,        hnm4_video);
>      REGISTER_DECODER(HQ_HQA,            hq_hqa);
>      REGISTER_DECODER(HQX,               hqx);
> @@ -253,6 +256,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 +266,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 +367,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,7 +379,9 @@ 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(VP9_V4L2M2M,       vp9_v4l2m2m);
>      REGISTER_DECODER(VQA,               vqa);
>      REGISTER_DECODER(BITPACKED,         bitpacked);
>      REGISTER_DECODER(WEBP,              webp);

And these.

> diff --git a/libavcodec/v4l2-common.c b/libavcodec/v4l2-common.c
> deleted file mode 100644
> diff --git a/libavcodec/v4l2-common.h b/libavcodec/v4l2-common.h
> deleted file mode 100644
> diff --git a/libavcodec/v4l2_fmt.c b/libavcodec/v4l2_fmt.c
> new file mode 100644
> diff --git a/libavcodec/v4l2_fmt.h b/libavcodec/v4l2_fmt.h
> new file mode 100644

I think it would have made more sense to move these files in a previous patch rather than here.


> diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h
> new file mode 100644
> index 0000000..e3a4173
> --- /dev/null
> +++ b/libavcodec/v4l2_buffers.h
> @@ -0,0 +1,226 @@
> +/*
> + * V4L2 buffer{,pool} helper functions.
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.h"
> +#include "avcodec.h"
> +#include "libavutil/pixfmt.h"
> +#include "libavutil/frame.h"
> +
> +struct V4LBuffer;
> +typedef struct V4LBuffer V4LBuffer;

Everywhere - the API is V4L2, not V4L.  Please use that name consistently.

> +
> +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;
> +
> +    /**
> +     * 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;
> +
> +    /**
> +     * 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;

This structure seems to me to be two pretty much separate things: an AVBufferPool (for storing the buffer structures) and a common context for streaming operations on a V4L2 device.  I think it would be better if they were actually separate, and just use AVBufferPool for the buffer pool part.

> +
> +/**
> + * 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);

avpriv names are fully global.  Where you have to use them, please make them consistently namespaced - e.g. "avpriv_v4l2_foo_bar".

> +
> +/**
> + * 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.
> + * @param[ino] timeout The number of milliseconds to wait for the dequeue.
> + * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error.
> + */
> +int avpriv_v4l_dequeue(V4LBufferPool* bp, AVFrame* f, AVPacket* pkt, unsigned int timeout);
> +
> +/**
> + * Enqueues a buffer to a V4LBufferPool from either an AVFrame, an AVPacket or a raw buffer.
> + * Exactly one of f or pkt  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.
> + * @return 0 in case of success, a negative error otherwise.
> + */
> +int avpriv_v4l_enqueue(V4LBufferPool* bp, const AVFrame* f, const AVPacket* pkt);
> +
> +/**
> + * 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_buffers.c b/libavcodec/v4l2_buffers.c
> new file mode 100644
> index 0000000..4bae6aa
> --- /dev/null
> +++ b/libavcodec/v4l2_buffers.c
> @@ -0,0 +1,614 @@
> +/*
> + * V4L2 buffer{,pool} helper functions.
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.h"
> +
> +#define IS_BP_SUPPORTED(bp) ((bp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) || \
> +                             (bp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)  || \
> +                             (bp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)        || \
> +                             (bp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT))
> +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 inline void set_pts(V4LBuffer *out, int64_t pts)
> +{
> +    if (pts == AV_NOPTS_VALUE) {
> +        out->timestamp.tv_sec  = 0;
> +        out->timestamp.tv_usec = 0;> +    } else {
> +        out->timestamp.tv_sec  = pts / INT64_C(1000000);
> +        out->timestamp.tv_usec = pts % INT64_C(1000000);
> +    }
> +}

This seems to have come from the pts supplied by the user - that should be measured in whatever the time_base is of the relevant component, which is highly unlikely to be microseconds.

Also, zero is a valid timestamp (and common - often the first frame in a file).

> +
> +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 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++;
> +
> +    return 0;
> +}
> +
> +static V4LBuffer* dequeue_v4lbuf(V4LBufferPool *bp, unsigned int timeout)
> +{
> +    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;
> +
> +    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;

What hits this default case?

> +    }
> +
> +    ret = poll(&pfd, 1, timeout);

Can this poll be interrupted?

> +    if (ret <=  0)
> +        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 && errno != EINVAL) {

EINVAL is considered ok?

> +            av_log(bp->log_ctx, AV_LOG_DEBUG, "%s: VIDIOC_DQBUF, errno (%d)\n", bp->name, errno);
> +            bp->broken = errno;

errno can be overwritten by the av_log() call.

> +        }
> +        return NULL;
> +    }
> +
> +    avbuf = &(bp->buffers[buf.index]);
> +    avbuf->status = V4LBUF_AVAILABLE;
> +    avbuf->pool->num_queued--;
> +    avbuf->buf = buf;
> +    if (V4L2_TYPE_IS_MULTIPLANAR(bp->type)) {
> +        memcpy(avbuf->planes, planes, sizeof(planes));
> +        avbuf->buf.m.planes = avbuf->planes;
> +    }
> +
> +    if (!V4L2_TYPE_IS_OUTPUT(avbuf->pool->type))
> +        return avbuf;
> +
> +    for (i = 0; i < avbuf->num_planes; i++) {
> +        if (avbuf->bufrefs[i])
> +            av_buffer_unref(&avbuf->bufrefs[i]);
> +    }
> +
> +    return avbuf;
> +}
> +
> +static void buffer_callback(void *opaque, uint8_t *unused)
> +{
> +    V4LBuffer* avbuf = opaque;
> +
> +    if (--avbuf->ref_cnt > 0)
> +        return;
> +
> +    if (V4LBUF_IN_DRIVER == avbuf->status)
> +        return;
> +
> +    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)
> +                break;
> +            if (avbuf->buf.m.planes[avbuf->num_planes].length)
> +                avbuf->num_planes++;
> +        }
> +    } else
> +        avbuf->num_planes = 1;
> +
> +    avbuf->num_lines = avbuf->pool->format.fmt.pix_mp.height;
> +
> +    for (i = 0; i < avbuf->num_planes; i++) {
> +
> +        avbuf->bytesperline[i] = V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type) ?
> +            avbuf->pool->format.fmt.pix_mp.plane_fmt[i].bytesperline :
> +            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:
> +            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))
> +        return 0;
> +
> +    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)

Log something?

> +        return AVERROR(errno);
> +
> +    bufs->num_buffers = req.count;
> +    bufs->num_queued  = 0;
> +    bufs->buffers = av_mallocz(bufs->num_buffers * sizeof(V4LBuffer));

Can fail.

> +
> +    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)
> +{
> +    int i;
> +
> +    if (!bp->buffers)
> +        return;
> +
> +    for (i = 0; i < bp->num_buffers; i++) {
> +        release_buf(&bp->buffers[i]);
> +    }
> +
> +    av_free(bp->buffers);
> +}
> +
> +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]));

Why would the pitches be the same except by coincidence?

> +            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 avpkt_to_v4lbuf(const AVPacket *pkt, V4LBuffer *out) {
> +    int ret;
> +
> +    ret = buf2v4l(out, 0, pkt->data, pkt->size, pkt->buf);
> +    if (ret)
> +        return ret;
> +
> +    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)
> +{
> +    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_packet_unref(pkt);
> +    if (ret = v4l2bufref(avbuf, 0, &pkt->buf))
> +        return ret;
> +
> +    pkt->size = V4L2_TYPE_IS_MULTIPLANAR(avbuf->pool->type) ? avbuf->buf.m.planes[0].bytesused : avbuf->buf.bytesused;
> +    pkt->data = pkt->buf->data;
> +
> +    if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME)
> +        pkt->flags |= AV_PKT_FLAG_KEY;
> +
> +    if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) {
> +        av_log(avbuf->pool->log_ctx, AV_LOG_ERROR, "V4L2 packet decode error\n");
> +        pkt->flags |= AV_PKT_FLAG_CORRUPT;
> +    }
> +
> +    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) {

Special-casing like this rather inspires doubt that other formats are handled correctly.

> +                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);
> +
> +    if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) {
> +        av_log(avbuf->pool->log_ctx, AV_LOG_ERROR, "V4L2 buffer decode error\n");
> +        frame->decode_error_flags |= FF_DECODE_ERROR_INVALID_BITSTREAM;
> +    }
> +
> +    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]);
Shouldn't this be using pict->data[i] rather than the buffer references (which could point anywhere)?

In any case, pict->buf[i] need not match pict->data[i] - there could be a single buffer covering all frames, for example.

> +        if (ret)
> +            return ret;
> +    }
> +
> +    set_pts(out, pict->pts);
> +
> +    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)
> +            continue;
> +
> +        if (p->memory != V4L2_MEMORY_MMAP) {
> +            av_log(p->log_ctx, AV_LOG_ERROR, "memory type %i not supported\n", p->memory);
> +            return NULL;
> +        }
> +
> +        if (p->buffers[i].mm_addr[0] == frame->buf[0]->data)
> +            return &p->buffers[i];
> +    }
> +
> +    return NULL;
> +}
> +
> +V4LBuffer* avpriv_v4lbufpool_getfreebuf(V4LBufferPool *p, const AVFrame *f, const AVPacket* pkt)
> +{
> +    V4LBuffer* ret;
> +    unsigned int timeout = 0;
> +    int i;
> +
> +    if (V4L2_TYPE_IS_OUTPUT(p->type)) {
> +          do {
> +          } while (dequeue_v4lbuf(p, timeout));
> +    }
> +
> +    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(V4LBufferPool* bp, const AVFrame* f, const AVPacket* pkt)

Just make separate functions.

> +{
> +    V4LBuffer* avbuf;
> +    int ret;
> +
> +    if (!f && !pkt) {
> +        av_log(bp->log_ctx, AV_LOG_ERROR, "either AVFrame* or AVPacket*must valid\n");
> +        return AVERROR_BUG;
> +    }
> +
> +    avbuf = avpriv_v4lbufpool_getfreebuf(bp, f, pkt);
> +    if (!avbuf)
> +        return AVERROR(ENOMEM);
> +
> +    if (pkt) {
> +        ret = avpkt_to_v4lbuf(pkt, avbuf);
> +        if (ret)
> +            return ret;
> +    }
> +
> +    if (f) {
> +        ret = avframe_to_v4lbuf(f, avbuf);
> +        if (ret)
> +            return ret;
> +    }
> +
> +    ret = enqueue_v4lbuf(avbuf);
> +    if (ret)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +int avpriv_v4l_dequeue(V4LBufferPool* bp, AVFrame* f, AVPacket* pkt, unsigned int timeout)

Likewise.

> +{
> +    V4LBuffer* avbuf = NULL;
> +
> +    if (!f && !pkt) {
> +        av_log(bp->log_ctx, AV_LOG_ERROR, "either AVFrame* or AVPacket* must be valid\n");
> +        return AVERROR_BUG;
> +    }
> +
> +    avbuf = dequeue_v4lbuf(bp, timeout);
> +    if (!avbuf) {
> +        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;
> +}

As noted above, I think all of this stuff should be split into the buffer pool parts and the common V4L2 streaming parts.

> diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
> new file mode 100644
> index 0000000..d457473
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m.c
> @@ -0,0 +1,356 @@
> +/*
> + * V4L mem2mem wrapper
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.h"
> +#include "v4l2_m2m.h"
> +
> +#define V4L_MAX_STREAM_SIZE (3*1024*1024)

Please describe where this number comes from.

> +
> +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 = 1;
> +        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)
> +        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)
> +        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, 0);

O_NONBLOCK, given DQBUF is used as if it is?

> +    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;

This probing structure feels very fragile and possibly harmful (opening random devices and poking them).  It might be better to just force the user to supply the right device path?

> +
> +    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..226bc3c
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m.h
> @@ -0,0 +1,69 @@
> +/*
> + * V4L2 mem2mem helper functions
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.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 }

Can we attempt to set this automatically based on the codec?  16 won't be enough for worst-case H.264/H.265.

> +
> +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);

Needs more namespace.

> +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 at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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..2cc59c5
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m_dec.c
> @@ -0,0 +1,229 @@
> +/*
> + * V4L2 mem2mem decoders
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.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;
> +    V4LBufferPool *const cap_pool = &s->capture_pool;
> +    V4LBufferPool *const out_pool = &s->output_pool;
> +    struct v4l2_selection selection;
> +    struct v4l2_control ctrl;
> +    int ret;
> +
> +    if (out_pool->streamon && cap_pool->streamon)
> +        return 0;
> +
> +    /* this will report the size of the frame back (see a4lbuf_to_avframe) */
> +    cap_pool->height = avctx->coded_height;
> +    cap_pool->width = avctx->coded_width;

Does coded_height/width need to be set to anything sensible here?  That won't be true in general.

> +
> +    /* start the output process */
> +    if (!out_pool->streamon) {
> +        ret = avpriv_set_stream_status(out_pool, VIDIOC_STREAMON);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output pool\n");
> +            return ret;
> +        }
> +    }
> +
> +    /* get the capture format */
> +    cap_pool->format.type = cap_pool->type;
> +    ret = ioctl(s->fd, VIDIOC_G_FMT, &cap_pool->format);
> +    if (ret) {
> +        av_log(avctx, AV_LOG_ERROR, "VIDIOC_G_FMT ioctl\n");
> +        return ret;
> +    }
> +
> +    /* store what the decoder gives */
> +    avctx->pix_fmt = avpriv_v4l_fmt_v4l2ff(cap_pool->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
> +    cap_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 */
> +            cap_pool->height = selection.r.height;
> +            cap_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) {
> +        cap_pool->min_queued_buffers = ctrl.value;
> +    }
> +
> +    /* init the capture pool */
> +    if (!cap_pool->buffers) {
> +        ret = avpriv_init_v4lbufpool(cap_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(cap_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;
> +    V4LBufferPool *const cap_pool = &s->capture_pool;
> +    V4LBufferPool *const out_pool = &s->output_pool;
> +
> +    out_pool->default_flags = cap_pool->default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +    out_pool->cfg.format = cap_pool->cfg.format = avpriv_set_pool_format;
> +    out_pool->height = cap_pool->height = avctx->coded_height;
> +    out_pool->width = cap_pool->width =avctx->coded_width;
> +
> +    out_pool->cfg.init = avpriv_init_v4lbufpool;
> +    out_pool->av_codec_id = avctx->codec_id;
> +    out_pool->av_pix_fmt  = AV_PIX_FMT_NONE;
> +
> +    cap_pool->cfg.init = NULL;
> +    cap_pool->av_codec_id = AV_CODEC_ID_RAWVIDEO;
> +    cap_pool->av_pix_fmt = avctx->pix_fmt;
> +    cap_pool->min_queued_buffers = 6;
> +
> +    return ff_v4lm2m_codec_init(avctx);
> +}
> +
> +static int v4lm2m_receive_frame(AVCodecContext *avctx, AVFrame *frame)
> +{
> +    V4Lm2mContext *s = avctx->priv_data;
> +    V4LBufferPool *const cap_pool = &s->capture_pool;
> +    V4LBufferPool *const out_pool = &s->output_pool;
> +    unsigned int timeout = 1000;
> +    AVPacket avpkt = {0};
> +    int ret;
> +
> +    ret = ff_decode_get_packet(avctx, &avpkt);
> +    if (ret < 0 && ret != AVERROR_EOF)
> +        return ret;
> +
> +    ret = avpriv_v4l_enqueue(out_pool, NULL, &avpkt);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = try_start(avctx);
> +    if (ret)
> +        return 0;
> +
> +    return avpriv_v4l_dequeue(cap_pool, frame, NULL, timeout);

I feel like if the stream has a lot of delay then you will end up needing to wait for the timeout to get the required EAGAIN, which at 1s is very large.  (Though I think you won't in common cases because of the min_queued_buffers constraint in dequeue.)

> +}
> +
> +#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
> +
> +#if CONFIG_HEVC_V4L2M2M_DECODER
> +        M2MDEC(hevc , "HEVC", AV_CODEC_ID_HEVC, "hevc_mp4toannexb");
> +#endif
> +
> +#if CONFIG_VP9_V4L2M2M_DECODER
> +        M2MDEC(vp9, "VP9", AV_CODEC_ID_VP9, NULL);
> +#endif
> diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
> new file mode 100644
> index 0000000..bc32e33
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m_enc.c
> @@ -0,0 +1,270 @@
> +/*
> + * V4L2 mem2mem encoders
> + *
> + * Copyright (C) 2017 Alexis Ballier <aballier at gentoo.org>
> + * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz at 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_fmt.h"
> +#include "v4l2_m2m.h"
> +#include "avcodec.h"
> +
> +#define STR(s) AV_TOSTRING(s)
> +#define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
> +#define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
> +
> +#define SET_V4L_EXT_CTRL(TYPE, ID, VALUE, NAME)                     \
> +{                                                                   \
> +    struct v4l2_ext_control ctrl = { 0 };                           \
> +    struct v4l2_ext_controls ctrls = { 0 };                         \
> +    ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;                        \
> +    ctrls.controls = &ctrl;                                         \
> +    ctrl.TYPE = VALUE ;                                             \
> +    ctrl.id = ID ;                                                  \
> +    ctrls.count = 1;                                                \
> +                                                                    \
> +    if ((ret = ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls)) < 0)       \
> +        av_log(avctx, AV_LOG_WARNING, "Failed to set " NAME "%s\n", STR(ID));  \
> +}
> +
> +static inline int v4l_h264_profile_from_ff(int p)
> +{
> +    switch(p) {
> +    case FF_PROFILE_H264_CONSTRAINED_BASELINE:
> +        return MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE);
> +    case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE);
> +    case FF_PROFILE_H264_HIGH_422_INTRA:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA);
> +    case FF_PROFILE_H264_HIGH_444_INTRA:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA);
> +    case FF_PROFILE_H264_HIGH_10_INTRA:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA);
> +    case FF_PROFILE_H264_HIGH_422:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_422);
> +    case FF_PROFILE_H264_BASELINE:
> +        return MPEG_VIDEO(H264_PROFILE_BASELINE);
> +    case FF_PROFILE_H264_EXTENDED:
> +        return MPEG_VIDEO(H264_PROFILE_EXTENDED);> +    case FF_PROFILE_H264_HIGH_10:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH_10);
> +    case FF_PROFILE_H264_MAIN:
> +        return MPEG_VIDEO(H264_PROFILE_MAIN);
> +    case FF_PROFILE_H264_HIGH:
> +        return MPEG_VIDEO(H264_PROFILE_HIGH);

Out of curiosity, how many of these actually have any support at all on real devices?

> +    }
> +
> +    return -1;
> +}
> +
> +static inline int v4l_mpeg4_profile_from_ff(int p)
> +{
> +    switch(p) {
> +    case FF_PROFILE_MPEG4_ADVANCED_CODING:
> +        return MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY);
> +    case FF_PROFILE_MPEG4_ADVANCED_SIMPLE:
> +        return MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_SIMPLE);
> +    case FF_PROFILE_MPEG4_SIMPLE_SCALABLE:
> +        return MPEG_VIDEO(MPEG4_PROFILE_SIMPLE_SCALABLE);
> +    case FF_PROFILE_MPEG4_SIMPLE:
> +        return MPEG_VIDEO(MPEG4_PROFILE_SIMPLE);
> +    case FF_PROFILE_MPEG4_CORE:
> +        return MPEG_VIDEO(MPEG4_PROFILE_CORE);
> +    }
> +
> +    return -1;
> +}
> +
> +static av_cold int v4lm2m_encode_init(AVCodecContext *avctx)
> +{
> +    V4Lm2mContext *s = avctx->priv_data;
> +    V4LBufferPool *const cap_pool = &s->capture_pool;
> +    V4LBufferPool *const out_pool = &s->output_pool;
> +    int qmin, qmax, ret, val;
> +
> +    out_pool->default_flags = cap_pool->default_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +    out_pool->cfg.format = cap_pool->cfg.format = avpriv_set_pool_format;
> +    out_pool->cfg.init = cap_pool->cfg.init = avpriv_init_v4lbufpool;
> +    out_pool->height = cap_pool->height = avctx->height;
> +    out_pool->width = cap_pool->width = avctx->width;
> +
> +    /* out pool */
> +    out_pool->av_codec_id = AV_CODEC_ID_RAWVIDEO;
> +    out_pool->av_pix_fmt = avctx->pix_fmt;
> +
> +    /* cap pool */
> +    cap_pool->av_codec_id = avctx->codec_id;
> +    cap_pool->av_pix_fmt = AV_PIX_FMT_NONE;
> +    cap_pool->min_queued_buffers = 1;
> +
> +    if (ret = ff_v4lm2m_codec_init(avctx))
> +        return ret;
> +
> +    SET_V4L_EXT_CTRL(value, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_JOINED_WITH_1ST_FRAME), "header mode");
> +
> +    SET_V4L_EXT_CTRL(value, MPEG_CID(B_FRAMES), avctx->max_b_frames,  "number of B-frames");
> +    SET_V4L_EXT_CTRL(value, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size");
> +    SET_V4L_EXT_CTRL(value, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate");
> +
> +    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, MPEG_CID(H264_PROFILE), val, "h264 profile");
> +        }
> +        qmin = MPEG_CID(H264_MIN_QP);
> +        qmax = MPEG_CID(H264_MAX_QP);
> +        break;
> +    case AV_CODEC_ID_MPEG4:
> +        val = v4l_mpeg4_profile_from_ff(avctx->profile);
> +        if (val >= 0) {
> +            SET_V4L_EXT_CTRL(value, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile");
> +        }
> +        qmin = MPEG_CID(MPEG4_MIN_QP);
> +        qmax = MPEG_CID(MPEG4_MAX_QP);
> +        if (avctx->flags & CODEC_FLAG_QPEL) {
> +            SET_V4L_EXT_CTRL(value, MPEG_CID(MPEG4_QPEL), 1, "qpel");
> +        }
> +        break;
> +    case AV_CODEC_ID_H263:
> +        qmin = MPEG_CID(H263_MIN_QP);
> +        qmax = MPEG_CID(H263_MAX_QP);
> +        break;
> +    case AV_CODEC_ID_VP8:
> +    case AV_CODEC_ID_VP9:
> +        qmin = MPEG_CID(VPX_MIN_QP);
> +        qmax = MPEG_CID(VPX_MAX_QP);
> +        break;
> +    default:
> +        return 0;
> +    }
> +
> +    SET_V4L_EXT_CTRL(value, qmin, avctx->qmin, "minimum video quantizer scale");
> +    SET_V4L_EXT_CTRL(value, qmax, avctx->qmax, "maximum video quantizer scale");
> +
> +    return 0;
> +}

This doesn't set extradata - you need to extract the codec global headers (such as H.264 SPS and PPS) at init time to be able to write correct files for some codecs (such as H.264) with muxers requiring global headers (such as MP4).  It kindof works without it, but the files created will not conform and will not be usable on some players.

> +
> +static int v4lm2m_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
> +                              const AVFrame *pict, int *got_packet)
> +{
> +    V4Lm2mContext *s = avctx->priv_data;
> +    V4LBufferPool *const cap_pool = &s->capture_pool;
> +    V4LBufferPool *const out_pool = &s->output_pool;
> +    unsigned int timeout = 1000;
> +    int ret;
> +
> +    /* end of transfer */
> +    if (!pict) {
> +        if (out_pool->streamon) {
> +            ret = avpriv_set_stream_status(out_pool, VIDIOC_STREAMOFF);
> +            if (ret) {
> +                av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF failed on output pool\n");
> +                return ret;
> +            }
> +        }
> +        goto dequeue;
> +    }
> +
> +    ret = avpriv_v4l_enqueue(out_pool, pict, NULL);
> +    if (ret < 0)
> +        return ret;
> +
> +    if (!out_pool->streamon) {
> +        ret = avpriv_set_stream_status(out_pool, VIDIOC_STREAMON);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output pool\n");
> +            return ret;
> +        }
> +    }
> +    if (!cap_pool->streamon) {
> +        ret = avpriv_set_stream_status(cap_pool, VIDIOC_STREAMON);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on capture pool\n");
> +            return ret;
> +        }
> +    }
> +
> +dequeue:
> +
> +    ret = avpriv_v4l_dequeue(cap_pool, NULL, pkt, timeout);
> +    if (ret == AVERROR(EAGAIN))
> +        return 0;
> +    if (!ret)
> +        *got_packet = 1;
> +
> +    return ret;
> +}

Does this actually work for a long stream?  You won't necessarily return a packet for every frame, but always need to enqueue a frame to be able to attempt to dequeue a packet.

It might be better to use the send/receive API here as well.

> +
> +#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 },

4 feels very low for a default - more encode delay than that is quite common with B-frames.

> +};
> +
> +#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
> +
> +#if CONFIG_HEVC_V4L2M2M_ENCODER
> +M2MENC(hevc, "HEVC", AV_CODEC_ID_HEVC);
> +#endif
> +

There is quite a bit more to think about here, I'll look at it further later.

- Mark


More information about the ffmpeg-devel mailing list