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