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

Mark Thompson sw at jkqxz.net
Fri Aug 4 14:51:58 EEST 2017


On 02/08/17 08:32, Jorge Ramirez-Ortiz wrote:
> 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
> -
>  # make sure we are running under a compatible shell
>  # try to make this part work with most shells
>  
> @@ -149,6 +148,7 @@ Component options:
>    --disable-pixelutils     disable pixel utils in libavutil
>  
>  Individual component options:
> +  --disable-v4l2_m2m       disable V4L2 mem2mem code [autodetect]
>    --disable-everything     disable all components listed below
>    --disable-encoder=NAME   disable encoder NAME
>    --enable-encoder=NAME    enable encoder NAME
> @@ -1432,6 +1432,7 @@ AVCODEC_COMPONENTS="
>  
>  AVDEVICE_COMPONENTS="
>      indevs
> +    v4l2_m2m
>      outdevs
>  "
>  AVFILTER_COMPONENTS="
> @@ -2269,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"
>  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
>  
>  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
>  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
>  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);

MPEG-2 is never mentioned at all in this configure change, so it will never even be compiled.  It might be best if you only add the decoders you can actually verify.


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

What happens to the decoder if some parameters like resolution change mid-stream?  (Try the stream h264/reinit-large_420_8-to-small_420_8.h264 in the fate samples.)

Also, are the frames returned here actually still used by the decoder internally as reference frames, or is the reference frame copied somewhere else?  (Seems slightly unlikely that they would be copied given the additional memory requirements, but I know discrete desktop GPUs (which admittedly have 9001x the memory bandwidth and power budget) do do this to hide the possible issues.)  I think, though I'm not entirely sure because I haven't run it, that the returned buffers will only have the user reference, so will appear to be writable to following filters - if that ends up writing on reference frames then the decoder will break.

> +
> +#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);
> +    }
> +
> +    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");

The default values of qmin/qmax in options_table.h are set for the MPEG-4 part 2 encoder, and will do nasty things to others.  (Certainly for VP9 (assuming it maps to qindex) it's a very low value, so it probably won't ever get anywhere near the bitrate target for normal video.  H.26[45] and VP8 also, but to a lesser degree.)

Maybe add per-codec default options?

> +
> +    return 0;
> +}
> +
> +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;
> +}
> +
> +#define OFFSET(x) offsetof(V4Lm2mContext, x)
> +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +
> +static const AVOption options[] = {
> +    V4L_M2M_DEFAULT_OPTS, { "num_capture_pool_buffers", "Number of buffers in the capture pool",
> +      OFFSET(capture_pool.num_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS }, { NULL },
> +};
> +
> +#define M2MENC(NAME, LONGNAME, CODEC) \
> +static const AVClass v4l2_m2m_ ## NAME ## _enc_class = {\
> +    .class_name = #NAME "_v4l2_m2m_encoder",\
> +    .item_name  = av_default_item_name,\
> +    .option     = options,\
> +    .version    = LIBAVUTIL_VERSION_INT,\
> +};\
> +\
> +AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \
> +    .name           = #NAME "_v4l2m2m" ,\
> +    .long_name      = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"),\
> +    .type           = AVMEDIA_TYPE_VIDEO,\
> +    .id             = CODEC ,\
> +    .priv_data_size = sizeof(V4Lm2mContext),\
> +    .priv_class     = &v4l2_m2m_ ## NAME ##_enc_class,\
> +    .init           = v4lm2m_encode_init,\
> +    .encode2        = v4lm2m_encode_frame,\
> +    .close          = ff_v4lm2m_codec_end,\
> +    .capabilities   = CODEC_CAP_DELAY,\
> +};
> +
> +#if CONFIG_H263_V4L2M2M_ENCODER
> +M2MENC(h263, "H.263", AV_CODEC_ID_H263);
> +#endif
> +
> +#if CONFIG_H264_V4L2M2M_ENCODER
> +M2MENC(h264, "H.264", AV_CODEC_ID_H264);
> +#endif
> +
> +#if CONFIG_MPEG4_V4L2M2M_ENCODER
> +M2MENC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4);
> +#endif
> +
> +#if CONFIG_VP8_V4L2M2M_ENCODER
> +M2MENC(vp8, "VP8", AV_CODEC_ID_VP8);
> +#endif
> +
> +#if CONFIG_HEVC_V4L2M2M_ENCODER
> +M2MENC(hevc, "HEVC", AV_CODEC_ID_HEVC);
> +#endif
> +


More information about the ffmpeg-devel mailing list