[FFmpeg-devel] [PATCHv8] libavcodec: v4l2: add support for v4l2 mem2mem codecs

Mark Thompson sw at jkqxz.net
Sun Sep 3 03:27:46 EEST 2017


On 02/09/17 21:11, Jorge Ramirez-Ortiz wrote:
>     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
>     Configure/make scripts have been validated on Ubuntu 10.04 and
>     16.04.
> 
>     Tested decoders:
>            - h264
>            - h263
>            - mpeg4
>            - vp8
>            - vp9
>            - hevc
> 
>     Tested encoders:
>            - h264
>            - h263
>            - mpeg4
> 
>     Tested transcoding (concurrent encoding/decoding)
> 
>     Some of the changes introduced:
>         - v4l2: code cleanup and abstractions added
>         - v4l2: follow the new encode/decode api.
>         - v4l2: fix display size for NV12 output pool.
>         - v4l2: handle EOS.
>         - v4l2: vp8 and mpeg4 decoding and encoding.
>         - v4l2: hevc and vp9 support.
>         - v4l2: generate EOF on dequeue errors.
>         - v4l2: h264_mp4toannexb filtering.
>         - v4l2: fixed make install and fate issues.
>         - v4l2: codecs enabled/disabled depending on pixfmt defined
>         - v4l2: pass timebase/framerate to the context
>         - v4l2: runtime decoder reconfiguration.
>         - v4l2: add more frame information
>         - v4l2: free hardware resources on last reference being released
>         - v4l2 encoding: disable b-frames for upstreaming (patch required)
> 
>     [1] https://lwn.net/Articles/697956/
> 
>     Reviewed-by: Jorge Ramirez <jorge.ramirez-ortiz at linaro.org>
>     Reviewed-by: Alexis Ballier <aballier at gentoo.org>
>     Tested-by: Jorge Ramirez <jorge.ramirez-ortiz at linaro.org>
> ---
>  Changelog                     |   1 +
>  configure                     |  30 +-
>  libavcodec/Makefile           |  16 +
>  libavcodec/allcodecs.c        |   9 +
>  libavcodec/v4l2_buffers.c     | 918 ++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/v4l2_buffers.h     | 236 +++++++++++
>  libavcodec/v4l2_fmt.c         | 184 +++++++++
>  libavcodec/v4l2_fmt.h         |  34 ++
>  libavcodec/v4l2_m2m.c         | 452 +++++++++++++++++++++
>  libavcodec/v4l2_m2m.h         |  70 ++++
>  libavcodec/v4l2_m2m_avcodec.h |  32 ++
>  libavcodec/v4l2_m2m_dec.c     | 213 ++++++++++
>  libavcodec/v4l2_m2m_enc.c     | 340 ++++++++++++++++
>  13 files changed, 2534 insertions(+), 1 deletion(-)
>  create mode 100644 libavcodec/v4l2_buffers.c
>  create mode 100644 libavcodec/v4l2_buffers.h
>  create mode 100644 libavcodec/v4l2_fmt.c
>  create mode 100644 libavcodec/v4l2_fmt.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

git am notes:

.git/rebase-apply/patch:1244: new blank line at EOF.
+
.git/rebase-apply/patch:1676: new blank line at EOF.
+
.git/rebase-apply/patch:2175: new blank line at EOF.
+
.git/rebase-apply/patch:2508: new blank line at EOF.
+


> 
> diff --git a/Changelog b/Changelog
> index 8309417..c6fcda3 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -40,6 +40,7 @@ version <next>:
>    They must always be used by name.
>  - FITS demuxer and decoder
>  - FITS muxer and encoder
> +- V4L2 mem2mem HW accelerated codecs support

They aren't really accelerated - they are just hardware, there is no acceleration.

>  version 3.3:
>  - CrystalHD decoder moved to new decode API
> diff --git a/configure b/configure
> index 4f1c172..d9244e7 100755
> --- a/configure
> +++ b/configure
> @@ -149,6 +149,7 @@ Component options:
>    --disable-pixelutils     disable pixel utils in libavutil
>  
>  Individual component options:
> +  --disable-v4l2_m2m       disable V4L2 mem2mem code [autodetect]

Should be somewhere further down.

>    --disable-everything     disable all components listed below
>    --disable-encoder=NAME   disable encoder NAME
>    --enable-encoder=NAME    enable encoder NAME
> @@ -1433,6 +1434,7 @@ AVCODEC_COMPONENTS="
>  
>  AVDEVICE_COMPONENTS="
>      indevs
> +    v4l2_m2m

Likewise; it's not an avdevice component.

>      outdevs
>  "
>  AVFILTER_COMPONENTS="
> @@ -2271,6 +2273,7 @@ map 'eval ${v}_inline_deps=inline_asm' $ARCH_EXT_LIST_ARM
>  
>  loongson2_deps="mips"
>  loongson3_deps="mips"
> +v4l2_deps_any="linux_videodev2_h"

I don't think this fits in a section with MIPs CPU capabilities.

>  mipsfpu_deps="mips"
>  mipsdsp_deps="mips"
>  mipsdspr2_deps="mips"
> @@ -2743,6 +2746,8 @@ nvenc_deps="cuda"
>  nvenc_deps_any="dlopen LoadLibrary"
>  nvenc_encoder_deps="nvenc"
>  
> +h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m"
> +h263_v4l2m2m_encoder_deps="v4l2_m2m h263_v4l2_m2m"
>  h264_crystalhd_decoder_select="crystalhd h264_mp4toannexb_bsf h264_parser"
>  h264_cuvid_decoder_deps="cuda cuvid"
>  h264_cuvid_decoder_select="h264_mp4toannexb_bsf"
> @@ -2761,6 +2766,8 @@ h264_vda_decoder_deps="vda"
>  h264_vda_decoder_select="h264_decoder"
>  h264_vdpau_decoder_deps="vdpau"
>  h264_vdpau_decoder_select="h264_decoder"
> +h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
> +h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m"
>  hevc_cuvid_decoder_deps="cuda cuvid"
>  hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf"
>  hevc_mediacodec_decoder_deps="mediacodec"
> @@ -2772,12 +2779,15 @@ hevc_qsv_encoder_deps="libmfx"
>  hevc_qsv_encoder_select="hevcparse qsvenc"
>  hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC"
>  hevc_vaapi_encoder_select="vaapi_encode golomb"
> +hevc_v4l2m2m_decoder_deps="v4l2_m2m hevc_v4l2_m2m"
> +hevc_v4l2m2m_encoder_deps="v4l2_m2m hevc_v4l2_m2m"
>  mjpeg_cuvid_decoder_deps="cuda cuvid"
>  mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG"
>  mjpeg_vaapi_encoder_select="vaapi_encode jpegtables"
>  mpeg1_cuvid_decoder_deps="cuda cuvid"
>  mpeg1_vdpau_decoder_deps="vdpau"
>  mpeg1_vdpau_decoder_select="mpeg1video_decoder"
> +mpeg1_v4l2m2m_decoder_deps="v4l2_m2m mpeg1_v4l2_m2m"
>  mpeg2_crystalhd_decoder_select="crystalhd"
>  mpeg2_cuvid_decoder_deps="cuda cuvid"
>  mpeg2_mmal_decoder_deps="mmal"
> @@ -2788,6 +2798,7 @@ mpeg2_qsv_encoder_deps="libmfx"
>  mpeg2_qsv_encoder_select="qsvenc"
>  mpeg2_vaapi_encoder_deps="VAEncPictureParameterBufferMPEG2"
>  mpeg2_vaapi_encoder_select="vaapi_encode"
> +mpeg2_v4l2m2m_decoder_deps="v4l2_m2m mpeg2_v4l2_m2m"
>  mpeg4_crystalhd_decoder_select="crystalhd"
>  mpeg4_cuvid_decoder_deps="cuda cuvid"
>  mpeg4_mediacodec_decoder_deps="mediacodec"
> @@ -2795,6 +2806,8 @@ mpeg4_mmal_decoder_deps="mmal"
>  mpeg4_omx_encoder_deps="omx"
>  mpeg4_vdpau_decoder_deps="vdpau"
>  mpeg4_vdpau_decoder_select="mpeg4_decoder"
> +mpeg4_v4l2m2m_decoder_deps="v4l2_m2m mpeg4_v4l2_m2m"
> +mpeg4_v4l2m2m_encoder_deps="v4l2_m2m mpeg4_v4l2_m2m"
>  mpeg_vdpau_decoder_deps="vdpau"
>  mpeg_vdpau_decoder_select="mpeg2video_decoder"
>  msmpeg4_crystalhd_decoder_select="crystalhd"
> @@ -2805,16 +2818,20 @@ vc1_cuvid_decoder_deps="cuda cuvid"
>  vc1_mmal_decoder_deps="mmal"
>  vc1_vdpau_decoder_deps="vdpau"
>  vc1_vdpau_decoder_select="vc1_decoder"
> +vc1_v4l2m2m_decoder_deps="v4l2_m2m vc1_v4l2_m2m"
>  vp8_cuvid_decoder_deps="cuda cuvid"
>  vp8_mediacodec_decoder_deps="mediacodec"
>  vp8_qsv_decoder_deps="libmfx"
>  vp8_qsv_decoder_select="qsvdec vp8_qsv_hwaccel vp8_parser"
>  vp8_vaapi_encoder_deps="VAEncPictureParameterBufferVP8"
>  vp8_vaapi_encoder_select="vaapi_encode"
> +vp8_v4l2m2m_decoder_deps="v4l2_m2m vp8_v4l2_m2m"
> +vp8_v4l2m2m_encoder_deps="v4l2_m2m vp8_v4l2_m2m"
>  vp9_cuvid_decoder_deps="cuda cuvid"
>  vp9_mediacodec_decoder_deps="mediacodec"
>  vp9_vaapi_encoder_deps="VAEncPictureParameterBufferVP9"
>  vp9_vaapi_encoder_select="vaapi_encode"
> +vp9_v4l2m2m_decoder_deps="v4l2_m2m vp9_v4l2_m2m"
>  wmv3_crystalhd_decoder_select="crystalhd"
>  wmv3_vdpau_decoder_select="vc1_vdpau_decoder"
>  
> @@ -3597,7 +3614,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

[This line has disappeared on rebase.]

>  enable_weak xlib
>  
>  enable_weak cuda cuvid nvenc vda_framework videotoolbox videotoolbox_encoder
> @@ -6066,10 +6083,21 @@ perl -v            > /dev/null 2>&1 && enable perl      || disable perl
>  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 V4L2 codecs available in the API
>  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 | V4L2_BUF_FLAG_LAST;" || disable v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_VC1_ANNEX_G;" && enable vc1_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG1;" && enable mpeg1_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG2;" && enable mpeg2_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG4;" && enable mpeg4_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC;" && enable hevc_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_H263;" && enable h263_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_H264;" && enable h264_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_VP8;" && enable vp8_v4l2_m2m
> +check_code cc linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" && enable vp9_v4l2_m2m

I think these need to appear as named config options somewhere.  It doesn't currently detect anything for me, even with e.g. --enable-v4l2-m2m --enable-encoder=h264_v4l2m2m.

>  
>  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 999632c..6e1de37 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -137,6 +137,7 @@ OBJS-$(CONFIG_VIDEODSP)                += videodsp.o
>  OBJS-$(CONFIG_VP3DSP)                  += vp3dsp.o
>  OBJS-$(CONFIG_VP56DSP)                 += vp56dsp.o
>  OBJS-$(CONFIG_VP8DSP)                  += vp8dsp.o
> +OBJS-$(CONFIG_V4L2_M2M)                += v4l2_m2m.o v4l2_buffers.o v4l2_fmt.o
>  OBJS-$(CONFIG_WMA_FREQS)               += wma_freqs.o
>  OBJS-$(CONFIG_WMV2DSP)                 += wmv2dsp.o
>  
> @@ -323,6 +324,8 @@ OBJS-$(CONFIG_H263_DECODER)            += h263dec.o h263.o ituh263dec.o        \
>                                            intelh263dec.o h263data.o
>  OBJS-$(CONFIG_H263_ENCODER)            += mpeg4videoenc.o mpeg4video.o  \
>                                            h263.o ituh263enc.o flvenc.o h263data.o
> +OBJS-$(CONFIG_H263_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_H263_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_H264_DECODER)            += h264dec.o h264_cabac.o h264_cavlc.o \
>                                            h264_direct.o h264_loopfilter.o  \
>                                            h264_mb.o h264_picture.o \
> @@ -340,6 +343,8 @@ OBJS-$(CONFIG_H264_QSV_DECODER)        += qsvdec_h2645.o
>  OBJS-$(CONFIG_H264_QSV_ENCODER)        += qsvenc_h264.o
>  OBJS-$(CONFIG_H264_VAAPI_ENCODER)      += vaapi_encode_h264.o vaapi_encode_h26x.o
>  OBJS-$(CONFIG_H264_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o
> +OBJS-$(CONFIG_H264_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_H264_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_HAP_DECODER)             += hapdec.o hap.o
>  OBJS-$(CONFIG_HAP_ENCODER)             += hapenc.o hap.o
>  OBJS-$(CONFIG_HEVC_DECODER)            += hevcdec.o hevc_mvs.o \
> @@ -353,6 +358,8 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER)        += qsvdec_h2645.o
>  OBJS-$(CONFIG_HEVC_QSV_ENCODER)        += qsvenc_hevc.o hevc_ps_enc.o       \
>                                            hevc_data.o
>  OBJS-$(CONFIG_HEVC_VAAPI_ENCODER)      += vaapi_encode_h265.o vaapi_encode_h26x.o
> +OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER)    += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER)    += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_HNM4_VIDEO_DECODER)      += hnm4video.o
>  OBJS-$(CONFIG_HQ_HQA_DECODER)          += hq_hqa.o hq_hqadata.o hq_hqadsp.o \
>                                            canopus.o
> @@ -422,6 +429,7 @@ OBJS-$(CONFIG_MPC8_DECODER)            += mpc8.o mpc.o
>  OBJS-$(CONFIG_MPEGVIDEO_DECODER)       += mpeg12dec.o mpeg12.o mpeg12data.o
>  OBJS-$(CONFIG_MPEG1VIDEO_DECODER)      += mpeg12dec.o mpeg12.o mpeg12data.o
>  OBJS-$(CONFIG_MPEG1VIDEO_ENCODER)      += mpeg12enc.o mpeg12.o
> +OBJS-$(CONFIG_MPEG1_V4L2M2M_DECODER)   += v4l2_m2m_dec.o
>  OBJS-$(CONFIG_MPEG2_MMAL_DECODER)      += mmaldec.o
>  OBJS-$(CONFIG_MPEG2_QSV_DECODER)       += qsvdec_other.o
>  OBJS-$(CONFIG_MPEG2_QSV_ENCODER)       += qsvenc_mpeg2.o
> @@ -429,9 +437,12 @@ 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
>  OBJS-$(CONFIG_MPEG2_VAAPI_ENCODER)     += vaapi_encode_mpeg2.o
> +OBJS-$(CONFIG_MPEG2_V4L2M2M_DECODER)   += v4l2_m2m_dec.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
> @@ -605,6 +616,7 @@ OBJS-$(CONFIG_VC1_DECODER)             += vc1dec.o vc1_block.o vc1_loopfilter.o
>  OBJS-$(CONFIG_VC1_CUVID_DECODER)       += cuvid.o
>  OBJS-$(CONFIG_VC1_MMAL_DECODER)        += mmaldec.o
>  OBJS-$(CONFIG_VC1_QSV_DECODER)         += qsvdec_other.o
> +OBJS-$(CONFIG_VC1_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
>  OBJS-$(CONFIG_VC2_ENCODER)             += vc2enc.o vc2enc_dwt.o diractab.o
>  OBJS-$(CONFIG_VCR1_DECODER)            += vcr1.o
>  OBJS-$(CONFIG_VMDAUDIO_DECODER)        += vmdaudio.o
> @@ -614,6 +626,7 @@ OBJS-$(CONFIG_VORBIS_DECODER)          += vorbisdec.o vorbisdsp.o vorbis.o \
>                                            vorbis_data.o
>  OBJS-$(CONFIG_VORBIS_ENCODER)          += vorbisenc.o vorbis.o \
>                                            vorbis_data.o
> +OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o

Stray change?

>  OBJS-$(CONFIG_VP3_DECODER)             += vp3.o
>  OBJS-$(CONFIG_VP5_DECODER)             += vp5.o vp56.o vp56data.o vp56rac.o
>  OBJS-$(CONFIG_VP6_DECODER)             += vp6.o vp56.o vp56data.o \
> @@ -624,6 +637,8 @@ OBJS-$(CONFIG_VP8_CUVID_DECODER)       += cuvid.o
>  OBJS-$(CONFIG_VP8_MEDIACODEC_DECODER)  += mediacodecdec.o
>  OBJS-$(CONFIG_VP8_QSV_DECODER)         += qsvdec_other.o
>  OBJS-$(CONFIG_VP8_VAAPI_ENCODER)       += vaapi_encode_vp8.o
> +OBJS-$(CONFIG_VP8_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
> +OBJS-$(CONFIG_VP8_V4L2M2M_ENCODER)     += v4l2_m2m_enc.o
>  OBJS-$(CONFIG_VP9_DECODER)             += vp9.o vp9data.o vp9dsp.o vp9lpf.o vp9recon.o \
>                                            vp9block.o vp9prob.o vp9mvs.o vp56rac.o \
>                                            vp9dsp_8bpp.o vp9dsp_10bpp.o vp9dsp_12bpp.o
> @@ -631,6 +646,7 @@ OBJS-$(CONFIG_VP9_CUVID_DECODER)       += cuvid.o
>  OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER)  += mediacodecdec.o
>  OBJS-$(CONFIG_VP9_VAAPI_ENCODER)       += vaapi_encode_vp9.o
>  OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
> +OBJS-$(CONFIG_VP9_V4L2M2M_DECODER)     += v4l2_m2m_dec.o
>  OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
>  OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o
>  OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackenc.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index ce0bc7e..eea1ae1 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -208,8 +208,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);
> @@ -220,6 +222,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);

Alignment.

>      REGISTER_DECODER(HNM4_VIDEO,        hnm4_video);
>      REGISTER_DECODER(HQ_HQA,            hq_hqa);
>      REGISTER_DECODER(HQX,               hqx);
> @@ -254,6 +257,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);
> @@ -263,8 +267,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);
> @@ -362,6 +368,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);
> @@ -373,7 +380,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);
> diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c
> new file mode 100644
> index 0000000..c3c0d03
> --- /dev/null
> +++ b/libavcodec/v4l2_buffers.c
> @@ -0,0 +1,918 @@
> +/*
> + * V4L2 buffer 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 <linux/videodev2.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <poll.h>
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/internal.h"
> +#include "v4l2_buffers.h"
> +#include "v4l2_m2m.h"
> +
> +#define USEC_PER_SEC 1000000
> +
> +enum V4L2Buffer_status {
> +    V4L2BUF_AVAILABLE,
> +    V4L2BUF_IN_DRIVER,
> +    V4L2BUF_RET_USER,
> +};
> +
> +/* buffer transform */
> +typedef int (*pkt_to_buf_f)(const AVPacket *, V4L2Buffer *);
> +typedef int (*frm_to_buf_f)(const AVFrame *, V4L2Buffer *);
> +typedef int (*buf_to_pkt_f)(AVPacket *, V4L2Buffer *);
> +typedef int (*buf_to_frm_f)(AVFrame *, V4L2Buffer *);
> +
> +typedef int (*buf_to_bufref_f)(V4L2Buffer *in, int plane, AVBufferRef **buf);
> +typedef int (*bufref_to_buf_f)(V4L2Buffer *out, int plane, const uint8_t* data, int size, AVBufferRef* bref);
> +
> +struct V4L2Buffer_ops {
> +    pkt_to_buf_f pkt_to_buf;
> +    frm_to_buf_f frm_to_buf;
> +    buf_to_pkt_f buf_to_pkt;
> +    buf_to_frm_f buf_to_frm;
> +
> +    bufref_to_buf_f bufref_to_buf;
> +    buf_to_bufref_f buf_to_bufref;
> +};
> +
> +struct V4L2Buffer {
> +    /* each buffer needs to have a reference to its context */
> +    struct V4L2Context *context;
> +
> +    struct V4L2Plane_info {
> +        void * mm_addr;
> +        size_t lengths;
> +    } plane_info[VIDEO_MAX_PLANES];
> +
> +    /* some common buffer operations */
> +    struct V4L2Buffer_ops ops;
> +
> +    /* memcpy to the v4l2_buffer planes array when needed */
> +    struct v4l2_plane planes[VIDEO_MAX_PLANES];
> +    struct v4l2_buffer buf;
> +
> +    int bytesperline[4];

Why 4?  Should this be VIDEO_MAX_PLANES again?

> +    int num_planes;
> +
> +    int flags;
> +    enum V4L2Buffer_status status;
> +
> +};
> ...
I'll read the buffer stuff more carefully later.

> diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
> new file mode 100644
> index 0000000..809e591
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m.c
> @@ -0,0 +1,452 @@
> +/*
> + * V4L mem2mem
> + *
> + * 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 <linux/videodev2.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <unistd.h>
> +#include <dirent.h>
> +#include <fcntl.h>
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/internal.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/pixfmt.h"
> +#include "v4l2_m2m_avcodec.h"
> +#include "v4l2_buffers.h"
> +#include "v4l2_fmt.h"
> +#include "v4l2_m2m.h"
> +
> +static inline int try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfmt)
> +{
> +    struct v4l2_format *fmt = &ctx->format;
> +    uint32_t v4l2_fmt;
> +    int ret;
> +
> +    v4l2_fmt = ff_v4l2_avfmt_to_v4l2fmt(pixfmt);
> +    if (!v4l2_fmt)
> +        return AVERROR(EINVAL);
> +
> +    if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type))
> +          fmt->fmt.pix_mp.pixelformat = v4l2_fmt;
> +    else
> +        fmt->fmt.pix.pixelformat = v4l2_fmt;
> +
> +   fmt->type = ctx->type;
> +
> +    ret = ioctl(ctx->fd, VIDIOC_TRY_FMT, fmt);
> +    if (ret)
> +        return AVERROR(EINVAL);
> +
> +    return 0;
> +}
> +
> +static int query_raw_format(V4L2Context* ctx, int set)
> +{
> +    enum AVPixelFormat pixfmt = ctx->av_pix_fmt;
> +    struct v4l2_fmtdesc fdesc;
> +    int ret;
> +
> +    memset(&fdesc, 0, sizeof(fdesc));
> +    fdesc.type = ctx->type;
> +
> +    if (pixfmt != AV_PIX_FMT_NONE) {
> +        ret = try_raw_format(ctx, pixfmt);
> +        if (ret)
> +            pixfmt = AV_PIX_FMT_NONE;
> +        else
> +            return 0;
> +    }
> +
> +    for (;;) {
> +        ret = ioctl(ctx->fd, VIDIOC_ENUM_FMT, &fdesc);
> +        if (ret)
> +            return AVERROR(EINVAL);
> +
> +        pixfmt = ff_v4l2_v4l2fmt_to_avfmt(fdesc.pixelformat, AV_CODEC_ID_RAWVIDEO);
> +        ret = try_raw_format(ctx, pixfmt);
> +        if (ret){
> +            fdesc.index++;
> +            continue;
> +        }
> +
> +        if (set)
> +            ctx->av_pix_fmt = pixfmt;
> +
> +        return 0;
> +    }
> +
> +    return AVERROR(EINVAL);
> +}
> +
> +static int query_coded_format(V4L2Context* ctx, uint32_t *p)
> +{
> +    struct v4l2_fmtdesc fdesc;
> +    uint32_t v4l2_fmt;
> +    int ret;
> +
> +    v4l2_fmt = ff_v4l2_avcodec_to_v4l2fmt(ctx->av_codec_id);
> +    if (!v4l2_fmt)
> +        return AVERROR(EINVAL);
> +
> +    memset(&fdesc, 0, sizeof(fdesc));
> +    fdesc.type = ctx->type;
> +
> +    for (;;) {
> +        ret = ioctl(ctx->fd, VIDIOC_ENUM_FMT, &fdesc);
> +        if (ret)
> +            return AVERROR(EINVAL);
> +
> +        if (fdesc.pixelformat == v4l2_fmt) {
> +            break;
> +        }
> +
> +        fdesc.index++;
> +    }
> +
> +    *p = v4l2_fmt;
> +
> +    return 0;
> +}
> +
> +static inline int splane_video(struct v4l2_capability *cap)
> +{
> +    if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) & V4L2_CAP_STREAMING)

>From videodev2.h:

#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT		0x00000002  /* Is a video output device */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */

So this is always zero?

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

Similarly.

> +        return 1;
> +
> +    if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
> +        return 1;
> +
> +    return 0;
> +}
> +
> +static int prepare_contexts(V4L2m2mContext* s, void *log_ctx)
> +{
> +    struct v4l2_capability cap;
> +    int ret;
> +
> +    s->capture.log_ctx = s->output.log_ctx = log_ctx;
> +    s->capture.done = s->output.done = 0;
> +    s->capture.fd = s->output.fd = s->fd;
> +    s->capture.name = "v4l2_cap";
> +    s->output.name = "v4l2_out";
> +    atomic_init(&s->refcount, 0);
> +    sem_init(&s->refsync, 0, 0);
> +
> +    memset(&cap, 0, sizeof(cap));
> +    ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap);
> +    if (ret < 0)
> +        return ret;
> +
> +    av_log(log_ctx, AV_LOG_INFO, "driver '%s' on card '%s'\n", cap.driver, cap.card);
> +
> +    if (mplane_video(&cap)) {
> +        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +        return 0;
> +    }
> +
> +    if (splane_video(&cap)) {
> +        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +        return 0;
> +    }
> +
> +    return AVERROR(EINVAL);
> +}
> +
> +static int probe_v4l2_driver(V4L2m2mContext* s, void *log_ctx)
> +{
> +    int ret;
> +
> +    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
> +    if (s->fd < 0)
> +        return AVERROR(errno);
> +
> +    ret = prepare_contexts(s, log_ctx);
> +    if (ret < 0)
> +        goto done;
> +
> +    ret = ff_v4l2_m2m_codec_format_context(&s->output, 0);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_DEBUG, "can't set input format\n");
> +        goto done;
> +    }
> +
> +    ret = ff_v4l2_m2m_codec_format_context(&s->capture, 0);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_DEBUG, "can't to set output format\n");
> +        goto done;
> +    }
> +
> +done:
> +    close(s->fd);
> +    s->fd = 0;
> +
> +    return ret;
> +}
> +
> +static int configure_contexts(V4L2m2mContext* s, void *log_ctx)
> +{
> +    int ret;
> +
> +    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
> +    if (s->fd < 0)
> +        return AVERROR(errno);
> +
> +    ret = prepare_contexts(s, log_ctx);
> +    if (ret < 0)
> +        goto error;
> +
> +    ret = ff_v4l2_m2m_codec_format_context(&s->output, 1);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_ERROR, "can't set input format\n");
> +        goto error;
> +    }
> +
> +    ret = ff_v4l2_m2m_codec_format_context(&s->capture, 1);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_ERROR, "can't to set output format\n");
> +        goto error;
> +    }
> +
> +    ret = ff_v4l2_context_init(&s->output, s->output.lazy_init);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_ERROR, "no output context's buffers\n");
> +        goto error;
> +    }
> +
> +    ret = ff_v4l2_context_init(&s->capture, s->capture.lazy_init);
> +    if (ret) {
> +        av_log(log_ctx, AV_LOG_ERROR, "no capture context's buffers\n");
> +        goto error;
> +    }
> +
> +error:
> +    if (ret) {
> +        close(s->fd);
> +        s->fd = 0;
> +    }
> +
> +    return 0;
> +}
> +
> +static void save_to_context(V4L2Context* ctx, uint32_t v4l2_fmt)
> +{
> +    ctx->format.type = ctx->type;
> +
> +    if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
> +        /* this is to handle the reconfiguration of the capture stream at runtime */
> +        ctx->format.fmt.pix_mp.height = ctx->height;
> +        ctx->format.fmt.pix_mp.width = ctx->width;
> +        if (v4l2_fmt)
> +            ctx->format.fmt.pix_mp.pixelformat = v4l2_fmt;
> +    } else {
> +        ctx->format.fmt.pix.height = ctx->height;
> +        ctx->format.fmt.pix.width = ctx->width;
> +        if (v4l2_fmt)
> +            ctx->format.fmt.pix_mp.pixelformat = v4l2_fmt;
> +    }
> +}
> +
> +int ff_v4l2_m2m_codec_format_context(V4L2Context* ctx, int set)
> +{
> +    uint32_t v4l2_fmt;
> +    int ret;
> +
> +    if  (ctx->av_codec_id == AV_CODEC_ID_RAWVIDEO) {
> +        ret = query_raw_format(ctx, set);
> +        if (ret)
> +            return ret;
> +
> +        save_to_context(ctx, 0);
> +        if (set)
> +            return ioctl(ctx->fd, VIDIOC_S_FMT, &ctx->format);
> +
> +         return ret;
> +    }
> +
> +    ret = query_coded_format(ctx, &v4l2_fmt);
> +    if (ret)
> +        return ret;
> +
> +    save_to_context(ctx, v4l2_fmt);
> +    if (set)
> +        return ioctl(ctx->fd, VIDIOC_S_FMT, &ctx->format);
> +
> +    return ioctl(ctx->fd, VIDIOC_TRY_FMT, &ctx->format);
> +}
> +
> +int ff_v4l2_m2m_codec_end(V4L2m2mContext* s)
> +{
> +    int ret;
> +
> +    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
> +    if (ret)
> +            av_log(s->output.log_ctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
> +
> +    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
> +    if (ret)
> +        av_log(s->capture.log_ctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
> +
> +    ff_v4l2_context_release(&s->output);
> +
> +    if (atomic_load(&s->refcount)) {
> +        av_log(s->capture.log_ctx, AV_LOG_DEBUG, "avpriv_v4l2m2m_end leaving pending buffers \n");
> +
> +        return 0;
> +    }
> +
> +    ff_v4l2_context_release(&s->capture);
> +    sem_destroy(&s->refsync);
> +
> +    /* release the hardware */
> +    close(s->fd);
> +
> +    return 0;
> +}
> +
> +int ff_v4l2_m2m_codec_init(V4L2m2mContext* s, void* log_ctx)
> +{
> +    char *devname_save = s->devname;
> +    int ret = AVERROR(EINVAL);
> +    struct dirent *entry;
> +    char node[PATH_MAX];
> +    DIR *dirp;
> +
> +    if (s->devname && *s->devname)
> +        return configure_contexts(s, log_ctx);
> +
> +    dirp = opendir("/dev");
> +    if (!dirp)
> +        return AVERROR(errno);
> +
> +    for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
> +
> +        if (strncmp(entry->d_name, "video", 5))
> +            continue;
> +
> +        snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
> +
> +        av_log(log_ctx, AV_LOG_DEBUG, "probing device %s\n", node);
> +
> +        s->devname = node;
> +        ret = probe_v4l2_driver(s, log_ctx);
> +        if (!ret)
> +                break;
> +    }
> +
> +    closedir(dirp);
> +
> +    if (!ret) {
> +        av_log(log_ctx, AV_LOG_INFO, "Using device %s\n", node);
> +        ret = configure_contexts(s, log_ctx);
> +    } else {
> +        av_log(log_ctx, AV_LOG_ERROR, "Could not find a valid device\n");
> +    }
> +    s->devname = devname_save;
> +
> +    return ret;
> +}
> +
> +int ff_v4l2_m2m_codec_reinit(V4L2m2mContext* s)
> +{
> +    int ret;
> +
> +    /* 1. reinit in progress */
> +    s->reinit = 1;
> +
> +    av_log(s->avctx, AV_LOG_DEBUG, "reinit context\n");
> +
> +    /* 2. streamoff */
> +    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
> +    if (ret)
> +        av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
> +
> +    /* 3. unmap the capture buffers (v4l2 and ffmpeg):
> +     *    we must wait for all references to be released before being allowed
> +     *    to queue new buffers.
> +     */
> +    av_log(s->avctx, AV_LOG_DEBUG, "capture wait for user to release AVBufferRefs \n");
> +    if (atomic_load(&s->refcount)) {
> +        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
> +    }
> +
> +    ff_v4l2_context_release(&s->capture);
> +
> +    /* 4. query the new format */
> +    ret = ff_v4l2_m2m_codec_format_context(&s->capture, 1);
> +    if (ret) {
> +        av_log(s->avctx, AV_LOG_ERROR, "setting capture format\n");
> +        return ret;
> +    }
> +
> +    /* 5. do lazy initialization */
> +    ret = ff_v4l2_context_init(&s->capture, s->capture.lazy_init);
> +    if (ret) {
> +        av_log(s->avctx, AV_LOG_ERROR, "capture buffers lazy init\n");
> +        return ret;
> +    }
> +
> +    /* 6. update AVCodecContext */
> +    ret = ff_set_dimensions(s->avctx, s->capture.width, s->capture.height);
> +    if (ret < 0)
> +        av_log(s->avctx, AV_LOG_WARNING, "update avcodec height and width\n");
> +
> +    /* 7. complete reinit */
> +    sem_destroy(&s->refsync);
> +    sem_init(&s->refsync, 0, 0);
> +    s->draining = 0;
> +    s->reinit = 0;
> +
> +    return 0;
> +}

Seems like it might be nicer if you just passed the AVCodecContext to all of these functions rather than separate V4L2m2mContext and log context.  (Feel free to ignore that.)

> +int ff_v4l2m2m_codec_end(AVCodecContext *avctx)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +
> +    av_log(avctx, AV_LOG_DEBUG, "Closing context\n");
> +
> +    return ff_v4l2_m2m_codec_end(s);
> +}
> +
> +int ff_v4l2m2m_codec_init(AVCodecContext *avctx)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    s->avctx = avctx;
> +
> +    return ff_v4l2_m2m_codec_init(s, avctx);
> +}

There's also some funny indenting in a few places in this file.

> diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h
> new file mode 100644
> index 0000000..63a4326
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m.h
> @@ -0,0 +1,70 @@
> +/*
> + * 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 <semaphore.h>
> +#include "v4l2_buffers.h"
> +
> +#define container_of(ptr, type, member) ({ \
> +        const __typeof__(((type *)0)->member ) *__mptr = (ptr); \
> +        (type *)((char *)__mptr - offsetof(type,member) );})
> +
> +#define V4L_M2M_DEFAULT_OPTS \
> +    { "device",\
> +      "Path to the device to use",\
> +        OFFSET(devname), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS },\
> +    { "num_output_buffers",\
> +      "Number of buffers in the output context",\
> +        OFFSET(output.num_buffers), AV_OPT_TYPE_INT, { .i64 = 16 }, 6, INT_MAX, FLAGS }
> +
> +typedef struct V4L2m2mContext
> +{
> +    AVClass *class;
> +    int fd;
> +    char *devname;
> +
> +    /* the codec context queues */
> +    V4L2Context capture;
> +    V4L2Context output;
> +
> +    /* refcount of buffers held by the user */
> +    atomic_uint refcount;
> +
> +    /* dynamic stream reconfig */
> +    AVCodecContext *avctx;
> +    sem_t refsync;
> +    int reinit;
> +
> +    /* null frame or packet received */
> +    int draining;
> +} V4L2m2mContext;
> +
> +int ff_v4l2_m2m_codec_init(V4L2m2mContext *ctx, void* log_ctx);
> +int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *ctx);
> +int ff_v4l2_m2m_codec_end(V4L2m2mContext *ctx);
> +int ff_v4l2_m2m_codec_format_context(V4L2Context *ctx, int set);
> +
> +
> +#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..c6ad5d4
> --- /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 "libavcodec/avcodec.h"
> +
> +int ff_v4l2m2m_codec_init(AVCodecContext *avctx);
> +int ff_v4l2m2m_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..205f695
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m_dec.c
> @@ -0,0 +1,213 @@
> +/*
> + * 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 <linux/videodev2.h>
> +#include <sys/ioctl.h>
> +#include "libavutil/pixfmt.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/opt.h"
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/decode.h"
> +#include "v4l2_m2m_avcodec.h"
> +#include "v4l2_buffers.h"
> +#include "v4l2_fmt.h"
> +#include "v4l2_m2m.h"
> +
> +static int try_start(AVCodecContext *avctx)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *const capture = &s->capture;
> +    V4L2Context *const output = &s->output;
> +    struct v4l2_event_subscription sub;
> +    struct v4l2_selection selection;
> +    int ret;
> +
> +    if (output->streamon && capture->streamon)
> +        return 0;
> +
> +    /* 0. subscribe to source change event */
> +    memset(&sub, 0, sizeof(sub));
> +    sub.type = V4L2_EVENT_SOURCE_CHANGE;
> +    ret = ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
> +    if ( ret < 0)
> +        av_log(avctx, AV_LOG_WARNING, "decoding does not support resolution change\n");

How fatal should that be?  What happens if it changes anyway?

> +
> +    /* 1. start the output process */
> +    if (!output->streamon) {
> +        ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output context\n");
> +            return ret;
> +        }
> +    }
> +
> +    /* 2. get the capture format */
> +    capture->format.type = capture->type;
> +    ret = ioctl(capture->fd, VIDIOC_G_FMT, &capture->format);
> +    if (ret) {
> +        av_log(avctx, AV_LOG_ERROR, "VIDIOC_G_FMT ioctl\n");
> +        return ret;
> +    }
> +
> +    /* 2.1 update the AVCodecContext */
> +    avctx->pix_fmt = ff_v4l2_v4l2fmt_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
> +    capture->av_pix_fmt = avctx->pix_fmt;
> +
> +    /* 3. 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 */
> +            capture->height = selection.r.height;
> +            capture->width  = selection.r.width;
> +        }
> +    }

What is this trying to do?  I'm not sure that the coded_width|height values are something you should be using here - in many cases they are larger than the actual output (what does 1920x1080 H.264, which is coded as 1920x1088, do?).  Also, while they are often sensible for the stream if it comes from lavf and a normal file container, they need not be set at all here (0x0).

> +
> +    /* 4. init the capture context now that we have the capture format */
> +    if (!capture->buffers) {
> +        av_log(capture->log_ctx, AV_LOG_DEBUG, "%s requested (%dx%d)\n",
> +            capture->name, capture->format.fmt.pix_mp.width, capture->format.fmt.pix_mp.height);
> +
> +        ret = ff_v4l2_context_init(capture, 0);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_DEBUG, "can't request output buffers\n");
> +            return ret;
> +        }
> +    }
> +
> +    /* 6. start the capture process */
> +    ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
> +    if (ret) {
> +        av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture context\n");
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +static av_cold int v4l2m2m_decode_init(AVCodecContext *avctx)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *capture = &s->capture;
> +    V4L2Context *output = &s->output;
> +
> +    output->height = capture->height = avctx->coded_height;
> +    output->width = capture->width =avctx->coded_width;

Again, I don't think you should be using these.

> +
> +    output->av_codec_id = avctx->codec_id;
> +    output->av_pix_fmt  = AV_PIX_FMT_NONE;
> +
> +    /*
> +     * the buffers associated to this context can not be initialized without
> +     * additional information available in the kernel driver,
> +     * so let's postpone requesting the buffers until we know more about the frames
> +     */
> +    capture->lazy_init = 1;
> +    capture->av_codec_id = AV_CODEC_ID_RAWVIDEO;
> +    capture->av_pix_fmt = avctx->pix_fmt;
> +
> +    return ff_v4l2m2m_codec_init(avctx);
> +}
> +/* in ffmpeg there is a single thread could be queueing/dequeuing buffers so a
> + * timeout is * required when retrieving a frame in case the driver has not received
> + * enough input * to start generating output.
> + *
> + * once decoding starts, the timeout should not be hit.

This seems like it could introduce a significant delay on startup for no good reason.  Can you instead just queue packets until either you run out of input buffers or a nonblocking dequeue succeeds?

(I might need to think more about how the semantics of this work.)

> + */
> +static int v4l2m2m_receive_frame(AVCodecContext *avctx, AVFrame *frame)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *const capture = &s->capture;
> +    V4L2Context *const output = &s->output;
> +    AVPacket avpkt = {0};
> +    int timeout = 50;
> +    int ret;
> +
> +    ret = ff_decode_get_packet(avctx, &avpkt);
> +    if (ret < 0 && ret != AVERROR_EOF)
> +        return ret;
> +
> +    if (s->draining)
> +        goto dequeue;
> +
> +    ret = ff_v4l2_enqueue_packet(output, &avpkt);
> +    if (ret < 0)
> +        return ret;
> +
> +    if (avpkt.size) {
> +        ret = try_start(avctx);
> +        if (ret)
> +            return 0;
> +    }
> +
> +dequeue:
> +    return ff_v4l2_dequeue_frame(capture, frame, timeout);
> +}
> +
> +#define OFFSET(x) offsetof(V4L2m2mContext, x)
> +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
> +
> +static const AVOption options[] = {
> +    V4L_M2M_DEFAULT_OPTS,{ "num_capture_extra_buffers","Number of extra buffers in the capture context",
> +        OFFSET(capture.num_buffers), AV_OPT_TYPE_INT,{.i64 = 6}, 6, 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(V4L2m2mContext),\
> +    .priv_class     = &v4l2_m2m_ ## NAME ## _dec_class,\
> +    .init           = v4l2m2m_decode_init,\
> +    .receive_frame  = v4l2m2m_receive_frame,\
> +    .close          = ff_v4l2m2m_codec_end,\
> +    .bsfs           = bsf_name, \
> +};
> +
> +M2MDEC(h264,  "H.264", AV_CODEC_ID_H264,       "h264_mp4toannexb");
> +M2MDEC(hevc,  "HEVC",  AV_CODEC_ID_HEVC,       "h264_mp4toannexb");

h264_mp4toannexb is for H.264, and will refuse to do anything with H.265:

"[AVBSFContext @ 0x55f4d346c620] Codec 'hevc' (174) is not supported by the bitstream filter 'h264_mp4toannexb'. Supported codecs are: h264 (28)."

(Was this decoder really tested as the commit message claims?)

> +M2MDEC(mpeg1, "MPEG1", AV_CODEC_ID_MPEG1VIDEO, NULL);
> +M2MDEC(mpeg2, "MPEG2", AV_CODEC_ID_MPEG2VIDEO, NULL);
> +M2MDEC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4,      NULL);
> +M2MDEC(h263,  "H.263", AV_CODEC_ID_H263,       NULL);
> +M2MDEC(vc1 ,  "VC1",   AV_CODEC_ID_VC1,        NULL);
> +M2MDEC(vp8,   "VP8",   AV_CODEC_ID_VP8,        NULL);
> +M2MDEC(vp9,   "VP9",   AV_CODEC_ID_VP9,        NULL);
> +
> diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
> new file mode 100644
> index 0000000..47bb160
> --- /dev/null
> +++ b/libavcodec/v4l2_m2m_enc.c
> @@ -0,0 +1,340 @@
> +/*
> + * 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 <linux/videodev2.h>
> +#include <sys/ioctl.h>
> +#include <search.h>
> +#include "libavcodec/avcodec.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/pixfmt.h"
> +#include "libavutil/opt.h"
> +#include "v4l2_m2m_avcodec.h"
> +#include "v4l2_buffers.h"
> +#include "v4l2_m2m.h"
> +
> +#define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
> +#define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
> +
> +static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, unsigned int den)
> +{
> +    struct v4l2_streamparm parm = { 0 };
> +
> +    parm.parm.output.timeperframe.denominator = den;
> +    parm.parm.output.timeperframe.numerator = num;
> +    parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;

What does this do if the output isn't MPLANE?

> +
> +    if (ioctl(s->fd, VIDIOC_S_PARM, &parm) < 0)
> +        av_log(s->avctx, AV_LOG_WARNING, "Failed to set  timeperframe");
> +}
> +
> +static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name)
> +{
> +    struct v4l2_ext_controls ctrls = { 0 };
> +    struct v4l2_ext_control ctrl = { 0 };
> +
> +    /* set ctrls */
> +    ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
> +    ctrls.controls = &ctrl;
> +    ctrls.count = 1;
> +
> +    /* set ctrl*/
> +    ctrl.value = value;
> +    ctrl.id = id ;
> +
> +    if (ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
> +        av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name);
> +    else
> +        av_log(s->avctx, AV_LOG_DEBUG, "Encoder: %s = %d\n", name, value);
> +}
> +
> +static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name)
> +{
> +    struct v4l2_ext_controls ctrls = { 0 };
> +    struct v4l2_ext_control ctrl = { 0 };
> +    int ret;
> +
> +    /* set ctrls */
> +    ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
> +    ctrls.controls = &ctrl;
> +    ctrls.count = 1;
> +
> +    /* set ctrl*/
> +    ctrl.id = id ;
> +
> +    ret = ioctl(s->fd, VIDIOC_G_EXT_CTRLS, &ctrls);
> +    if (ret < 0) {
> +        av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name);
> +        return ret;
> +    }
> +
> +    *value = ctrl.value;
> +
> +    return 0;
> +}
> +
> +static int match_profile(const void *a, const void *b)
> +{
> +    if (*(unsigned int *)a == *(unsigned int *)b)
> +        return 0;
> +
> +    return 1;
> +}
> +
> +static inline unsigned int v4l2_h264_profile_from_ff(int p)
> +{
> +    struct h264_profile  {
> +        unsigned int ffmpeg_val;
> +        unsigned int v4l2_val;
> +    } *val, profile[] = {
> +        { FF_PROFILE_H264_CONSTRAINED_BASELINE, MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE) },
> +        { FF_PROFILE_H264_HIGH_444_PREDICTIVE, MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE) },
> +        { FF_PROFILE_H264_HIGH_422_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA) },
> +        { FF_PROFILE_H264_HIGH_444_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA) },
> +        { FF_PROFILE_H264_HIGH_10_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA) },
> +        { FF_PROFILE_H264_HIGH_422, MPEG_VIDEO(H264_PROFILE_HIGH_422) },
> +        { FF_PROFILE_H264_BASELINE, MPEG_VIDEO(H264_PROFILE_BASELINE) },
> +        { FF_PROFILE_H264_EXTENDED, MPEG_VIDEO(H264_PROFILE_EXTENDED) },
> +        { FF_PROFILE_H264_HIGH_10, MPEG_VIDEO(H264_PROFILE_HIGH_10) },
> +        { FF_PROFILE_H264_MAIN, MPEG_VIDEO(H264_PROFILE_MAIN) },
> +        { FF_PROFILE_H264_HIGH, MPEG_VIDEO(H264_PROFILE_HIGH) },
> +    };
> +    size_t len = sizeof(profile) / sizeof(profile[0]);

FF_ARRAY_LENGTH().

> +
> +    val = lfind(&p, profile, &len, sizeof(profile[0]), match_profile);
> +    if (val)
> +        return val->v4l2_val;
> +
> +    return FF_PROFILE_UNKNOWN;

Returning FF_PROFILE_UNKNOWN here is bit weird because it mixes the namespaces.  I know that's how you handle it below, but still...

> +}
> +
> +static inline int v4l2_mpeg4_profile_from_ff(int p)
> +{
> +    struct mpeg4_profile {
> +        unsigned int ffmpeg_val;
> +        unsigned int v4l2_val;
> +    } *val, profile[] = {
> +        { FF_PROFILE_MPEG4_ADVANCED_CODING, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY) },
> +        { FF_PROFILE_MPEG4_ADVANCED_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_SIMPLE) },
> +        { FF_PROFILE_MPEG4_SIMPLE_SCALABLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE_SCALABLE) },
> +        { FF_PROFILE_MPEG4_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE) },
> +        { FF_PROFILE_MPEG4_CORE, MPEG_VIDEO(MPEG4_PROFILE_CORE) },
> +    };
> +    size_t len = sizeof(profile) / sizeof(profile[0]);

FF_ARRAY_LENGTH().

> +
> +    val = lfind(&p, profile, &len, sizeof(profile[0]), match_profile);
> +    if (val)
> +        return val->v4l2_val;
> +
> +    return FF_PROFILE_UNKNOWN;
> +}
> +
> +static int check_b_frame_support(V4L2m2mContext *s)
> +{
> +    if (s->avctx->max_b_frames)
> +        av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support b-frames yet\n");
> +
> +    v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames");
> +
> +    v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames");
> +    if (s->avctx->max_b_frames == 0)
> +        return 0;
> +
> +    avpriv_report_missing_feature(s->avctx, "DTS/PTS calculation for V4L2 encoding");
> +
> +    return AVERROR_PATCHWELCOME;
> +}
> +
> +static av_cold int v4l2m2m_encode_init(AVCodecContext *avctx)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *capture = &s->capture;
> +    V4L2Context *output = &s->output;
> +    int qmin_cid, qmax_cid, ret, val;
> +    int qmin, qmax;
> +
> +    /* common settings output/capture */
> +    output->height = capture->height = avctx->height;
> +    output->width = capture->width = avctx->width;
> +
> +    /* output context */
> +    output->av_codec_id = AV_CODEC_ID_RAWVIDEO;
> +    output->av_pix_fmt = avctx->pix_fmt;
> +
> +    /* capture context */
> +    capture->av_codec_id = avctx->codec_id;
> +    capture->av_pix_fmt = AV_PIX_FMT_NONE;
> +
> +    ret = ff_v4l2m2m_codec_init(avctx);
> +    if (ret)
> +        return ret;
> +
> +    ret = check_b_frame_support(s);
> +    if (ret)
> +        return ret;
> +
> +    /* set params */
> +    v4l2_set_timeperframe(s, avctx->framerate.num, avctx->framerate.den);

Framerate need not be set if the input is VFR.  What does timeperframe actually get used for here?

> +
> +    /* set ext ctrls */
> +    v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode");
> +    v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate");
> +    v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size");
> +
> +    av_log(avctx, AV_LOG_DEBUG, "Encoder Context: id (%d), profile (%d), frame rate(%d/%d), number b-frames (%d), "
> +          "gop size (%d), bit rate (%ld), qmin (%d), qmax (%d)\n",
> +        avctx->codec_id, avctx->profile, avctx->framerate.num, avctx->framerate.den,
> +        avctx->max_b_frames, avctx->gop_size, avctx->bit_rate, avctx->qmin, avctx->qmax);
> +
> +    switch (avctx->codec_id) {
> +    case AV_CODEC_ID_H264:
> +        val = v4l2_h264_profile_from_ff(avctx->profile);
> +        if (val != FF_PROFILE_UNKNOWN)
> +            v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile");
> +        else
> +            av_log(avctx, AV_LOG_WARNING, "h264 profile unknown)\n");
> +        qmin_cid = MPEG_CID(H264_MIN_QP);
> +        qmax_cid = MPEG_CID(H264_MAX_QP);
> +
> +        qmin = 0;
> +        qmax = 51;
> +        break;
> +    case AV_CODEC_ID_MPEG4:
> +        val = v4l2_mpeg4_profile_from_ff(avctx->profile);
> +        if (val != FF_PROFILE_UNKNOWN)
> +            v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile");
> +        else
> +            av_log(avctx, AV_LOG_WARNING, "mpeg4 profile unknown)\n");
> +        qmin_cid = MPEG_CID(MPEG4_MIN_QP);
> +        qmax_cid = MPEG_CID(MPEG4_MAX_QP);
> +        if (avctx->flags & CODEC_FLAG_QPEL)
> +            v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel");
> +        qmax = 51;
> +        qmin = 0;

MPEG-4 part 2 should have the same ranges as H.263, I think.

> +        break;
> +    case AV_CODEC_ID_H263:
> +        qmin_cid = MPEG_CID(H263_MIN_QP);
> +        qmax_cid = MPEG_CID(H263_MAX_QP);
> +        qmin = 1;
> +        qmax = 31;
> +        break;
> +    case AV_CODEC_ID_VP8:
> +        qmin_cid = MPEG_CID(VPX_MIN_QP);
> +        qmax_cid = MPEG_CID(VPX_MAX_QP);
> +        qmin = 0;
> +        qmax = 127;
> +        break;
> +    case AV_CODEC_ID_VP9:
> +        qmin_cid = MPEG_CID(VPX_MIN_QP);
> +        qmax_cid = MPEG_CID(VPX_MAX_QP);
> +        qmin = 0;
> +        qmax = 255;
> +        break;
> +    default:
> +        return 0;
> +    }
> +
> +    if (qmin != avctx->qmin || qmax != avctx->qmax)
> +        av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax);
> +
> +    v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale");
> +    v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale");
> +
> +    return 0;
> +}
> +
> +static int v4l2m2m_send_frame(AVCodecContext *avctx, const AVFrame *frame)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *const output = &s->output;
> +
> +    return ff_v4l2_enqueue_frame(output, frame);
> +}
> +
> +/* Send and receive frame happen on the same thread, hence the need for a polling timeout */

Maybe this should only block if there isn't any space to queue more input?

> +static int v4l2m2m_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
> +{
> +    V4L2m2mContext *s = avctx->priv_data;
> +    V4L2Context *const capture = &s->capture;
> +    V4L2Context *const output = &s->output;
> +    unsigned int timeout = 50;
> +    int ret;
> +
> +    if (s->draining)
> +        goto dequeue;
> +
> +    if (!output->streamon) {
> +        ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF failed on output context\n");
> +            return ret;
> +        }
> +    }
> +
> +    if (!capture->streamon) {
> +        ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on capture context\n");
> +            return ret;
> +        }
> +    }
> +
> +dequeue:
> +    return ff_v4l2_dequeue_packet(capture, avpkt, timeout);
> +}
> +
> +#define OFFSET(x) offsetof(V4L2m2mContext, x)
> +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +
> +static const AVOption options[] = {
> +    V4L_M2M_DEFAULT_OPTS,
> +    { "num_capture_buffers", "Number of buffers in the capture context",
> +        OFFSET(capture.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(V4L2m2mContext),\
> +    .priv_class     = &v4l2_m2m_ ## NAME ##_enc_class,\
> +    .init           = v4l2m2m_encode_init,\
> +    .send_frame     = v4l2m2m_send_frame,\
> +    .receive_packet = v4l2m2m_receive_packet,\
> +    .close          = ff_v4l2m2m_codec_end,\
> +};
> +
> +M2MENC(mpeg4,"MPEG4", AV_CODEC_ID_MPEG4);
> +M2MENC(h263, "H.263", AV_CODEC_ID_H263);
> +M2MENC(h264, "H.264", AV_CODEC_ID_H264);
> +M2MENC(hevc, "HEVC",  AV_CODEC_ID_HEVC);
> +M2MENC(vp8,  "VP8",   AV_CODEC_ID_VP8);
> 


More information about the ffmpeg-devel mailing list