[vlc-devel] [PATCH] v4l2: V4L2 video_output module

Francois Cartegnie fcvlcdev at free.fr
Fri Nov 16 15:19:39 CET 2012


Still that v4l2 output patch. Minor requested changes.
Lingering on a branch since few months.

Francois

---
 configure.ac                            |    1 +
 modules/video_output/Modules.am         |   15 +
 modules/video_output/v4l2/v4l2.h        |   62 ++++
 modules/video_output/v4l2/v4l2_chroma.c |  103 ++++++
 modules/video_output/v4l2/v4l2_chroma.h |   41 +++
 modules/video_output/v4l2/v4l2_common.c |  423 ++++++++++++++++++++++
 modules/video_output/v4l2/v4l2_common.h |   93 +++++
 modules/video_output/v4l2/v4l2_output.c |  593 +++++++++++++++++++++++++++++++
 po/POTFILES.in                          |    1 +
 9 files changed, 1332 insertions(+), 0 deletions(-)
 create mode 100644 modules/video_output/v4l2/v4l2.h
 create mode 100644 modules/video_output/v4l2/v4l2_chroma.c
 create mode 100644 modules/video_output/v4l2/v4l2_chroma.h
 create mode 100644 modules/video_output/v4l2/v4l2_common.c
 create mode 100644 modules/video_output/v4l2/v4l2_common.h
 create mode 100644 modules/video_output/v4l2/v4l2_output.c

diff --git a/configure.ac b/configure.ac
index d2acd93..1ef958f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1729,6 +1729,7 @@ AS_IF([test "$enable_v4l2" != "no"], [
   AC_CHECK_HEADERS([linux/videodev2.h sys/videoio.h], [
     have_v4l2="yes"
   ])
+  VLC_ADD_PLUGIN([v4l2_output])
 ])
 AM_CONDITIONAL(HAVE_V4L2, [test "${have_v4l2}" != "no"])
 
diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am
index d8a631c..81058d6 100644
--- a/modules/video_output/Modules.am
+++ b/modules/video_output/Modules.am
@@ -170,6 +170,21 @@ endif
 EXTRA_LTLIBRARIES += libcaca_plugin.la
 libvlc_LTLIBRARIES += $(LTLIBcaca)
 
+### V4L2 output ###
+libv4l2_output_plugin_la_SOURCES = \
+	v4l2/v4l2_output.c \
+	v4l2/v4l2_common.c \
+	v4l2/v4l2_common.h \
+	v4l2/v4l2_chroma.c \
+	v4l2/v4l2_chroma.h \
+	v4l2/v4l2.h
+libv4l2_output_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBV4L2_CFLAGS)
+libv4l2_output_plugin_la_LIBADD = $(AM_LIBADD) $(LIBV4L2_LIBS) $(LIBM)
+libv4l2_output_plugin_la_DEPENDENCIES =
+if HAVE_V4L2
+libvlc_LTLIBRARIES += libv4l2_output_plugin.la
+endif
+
 ### Common ###
 libvdummy_plugin_la_SOURCES = vdummy.c
 libvdummy_plugin_la_CFLAGS = $(AM_CFLAGS)
diff --git a/modules/video_output/v4l2/v4l2.h b/modules/video_output/v4l2/v4l2.h
new file mode 100644
index 0000000..097fb08
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ * v4l2.h : Video4Linux2 headers and fixes
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef V4L2_V4L2_H
+#define V4L2_V4L2_H
+
+#if defined(HAVE_LINUX_VIDEODEV2_H)
+#   include <linux/videodev2.h>
+#elif defined(HAVE_SYS_VIDEOIO_H)
+#   include <sys/videoio.h>
+#else
+#   error "No Video4Linux2 headers found."
+#endif
+
+/* Hacks to compile with old headers */
+#ifndef V4L2_CTRL_FLAG_VOLATILE /* 3.2 */
+# warning Please update Video4Linux2 headers!
+# define V4L2_CTRL_FLAG_VOLATILE 0x0080
+# define V4L2_CID_POWER_LINE_FREQUENCY_AUTO 3
+# define V4L2_STD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G)
+# define V4L2_STD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H)
+# define V4L2_STD_L (V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC)
+# define V4L2_STD_BG (V4L2_STD_B|V4L2_STD_G)
+# define V4L2_STD_MTS (V4L2_STD_NTSC_M|V4L2_STD_PAL_M|V4L2_STD_PAL_N|\
+                       V4L2_STD_PAL_Nc)
+#endif
+
+#ifdef HAVE_LIBV4L2
+#   include <libv4l2.h>
+#else
+#   define v4l2_close close
+#   define v4l2_dup dup
+#   define v4l2_ioctl ioctl
+#   define v4l2_read read
+#   define v4l2_write write
+#   define v4l2_mmap mmap
+#   define v4l2_munmap munmap
+#endif
+
+struct buffer_t
+{
+    void *  start;
+    size_t  length;
+};
+
+#endif // V4L2_V4L2_H
diff --git a/modules/video_output/v4l2/v4l2_chroma.c b/modules/video_output/v4l2/v4l2_chroma.c
new file mode 100644
index 0000000..61e9b8b
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_chroma.c
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * v4l2_chroma.c : V4L2 chromas for vlc
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "v4l2_chroma.h"
+
+static const struct
+{
+    unsigned int i_v4l2;
+    vlc_fourcc_t i_fourcc;
+    int i_rmask;
+    int i_gmask;
+    int i_bmask;
+} v4l2chroma_to_fourcc[] =
+{
+    /* Raw data types */
+    { V4L2_PIX_FMT_GREY,    VLC_CODEC_GREY, 0, 0, 0 },
+    { V4L2_PIX_FMT_HI240,   VLC_FOURCC('I','2','4','0'), 0, 0, 0 },
+    { V4L2_PIX_FMT_RGB555,  VLC_CODEC_RGB15, 0x001f,0x03e0,0x7c00 },
+    { V4L2_PIX_FMT_RGB565,  VLC_CODEC_RGB16, 0x001f,0x07e0,0xf800 },
+    /* Won't work since we don't know how to handle such gmask values
+     * correctly
+    { V4L2_PIX_FMT_RGB555X, VLC_CODEC_RGB15, 0x007c,0xe003,0x1f00 },
+    { V4L2_PIX_FMT_RGB565X, VLC_CODEC_RGB16, 0x00f8,0xe007,0x1f00 },
+    */
+    { V4L2_PIX_FMT_BGR24,   VLC_CODEC_RGB24, 0xff0000,0xff00,0xff },
+    { V4L2_PIX_FMT_RGB24,   VLC_CODEC_RGB24, 0xff,0xff00,0xff0000 },
+    { V4L2_PIX_FMT_BGR32,   VLC_CODEC_RGB32, 0xff0000,0xff00,0xff },
+    { V4L2_PIX_FMT_RGB32,   VLC_CODEC_RGB32, 0xff,0xff00,0xff0000 },
+    { V4L2_PIX_FMT_YUYV,    VLC_CODEC_YUYV, 0, 0, 0 },
+    { V4L2_PIX_FMT_UYVY,    VLC_CODEC_UYVY, 0, 0, 0 },
+    { V4L2_PIX_FMT_Y41P,    VLC_FOURCC('I','4','1','N'), 0, 0, 0 },
+    { V4L2_PIX_FMT_YUV422P, VLC_CODEC_I422, 0, 0, 0 },
+    { V4L2_PIX_FMT_YVU420,  VLC_CODEC_YV12, 0, 0, 0 },
+    { V4L2_PIX_FMT_YUV411P, VLC_CODEC_I411, 0, 0, 0 },
+    { V4L2_PIX_FMT_YUV410,  VLC_CODEC_I410, 0, 0, 0 },
+
+    /* Raw data types, not in V4L2 spec but still in videodev2.h and supported
+     * by VLC */
+    { V4L2_PIX_FMT_YUV420,  VLC_CODEC_I420, 0, 0, 0 },
+    /* FIXME { V4L2_PIX_FMT_RGB444,  VLC_CODEC_RGB32 }, */
+
+    /* Compressed data types */
+    { V4L2_PIX_FMT_MJPEG,   VLC_CODEC_MJPG, 0, 0, 0 },
+    { V4L2_PIX_FMT_JPEG,    VLC_CODEC_JPEG, 0, 0, 0 },
+#if 0
+    { V4L2_PIX_FMT_DV,      VLC_FOURCC('?','?','?','?') },
+    { V4L2_PIX_FMT_MPEG,    VLC_FOURCC('?','?','?','?') },
+#endif
+    { 0, 0, 0, 0, 0 }
+};
+
+bool get_v4l2pixelformat_by_fourcc( unsigned int *found,
+                                    vlc_fourcc_t i_requested_fourcc )
+{
+    for( int i = 0; v4l2chroma_to_fourcc[i].i_v4l2 != 0; i++ )
+    {
+        if( v4l2chroma_to_fourcc[i].i_fourcc == i_requested_fourcc )
+        {
+            *found = v4l2chroma_to_fourcc[i].i_v4l2;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool get_fourcc_by_v4l2pixelformat( vlc_fourcc_t *i_fourcc,
+                                    int *i_rmask, int *i_gmask, int *i_bmask,
+                                    unsigned int pixelformat )
+{
+    for( int i = 0; v4l2chroma_to_fourcc[i].i_fourcc != 0; i++ )
+    {
+        if( v4l2chroma_to_fourcc[i].i_v4l2 == pixelformat )
+        {
+            *i_fourcc = v4l2chroma_to_fourcc[i].i_fourcc;
+            *i_rmask = v4l2chroma_to_fourcc[i].i_rmask;
+            *i_gmask = v4l2chroma_to_fourcc[i].i_gmask;
+            *i_bmask = v4l2chroma_to_fourcc[i].i_bmask;
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/modules/video_output/v4l2/v4l2_chroma.h b/modules/video_output/v4l2/v4l2_chroma.h
new file mode 100644
index 0000000..8c05326
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_chroma.h
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * v4l2_chroma.h : V4L2 chromas for vlc
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef V4L2_CHROMA_H
+#define V4L2_CHROMA_H
+
+#include "v4l2.h"
+#include <vlc_fourcc.h>
+
+/**
+ * Matchs a V4L2 fourcc with VLC's one.
+ * @return true if successfully matched.
+ **/
+bool get_fourcc_by_v4l2pixelformat( vlc_fourcc_t *i_fourcc,
+                                    int *i_rmask, int *i_gmask, int *i_bmask,
+                                    unsigned int pixelformat );
+
+/**
+ * Matchs a VLC fourcc with V4L2's one.
+ * @return true if successfully matched.
+ **/
+bool get_v4l2pixelformat_by_fourcc( unsigned int *found,
+                                    vlc_fourcc_t i_requested_fourcc );
+
+#endif // V4L2_CHROMA_H
diff --git a/modules/video_output/v4l2/v4l2_common.c b/modules/video_output/v4l2/v4l2_common.c
new file mode 100644
index 0000000..8c4e6ce
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_common.c
@@ -0,0 +1,423 @@
+/*****************************************************************************
+ * v4l2_common.c : V4L2 shared data & functions for vlc
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "v4l2_common.h"
+#include "v4l2_chroma.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <assert.h>
+
+
+static const v4l2_std_id standards_v4l2[] = { V4L2_STD_UNKNOWN, V4L2_STD_ALL,
+    V4L2_STD_PAL,     V4L2_STD_PAL_BG,   V4L2_STD_PAL_DK,
+    V4L2_STD_NTSC,
+    V4L2_STD_SECAM,   V4L2_STD_SECAM_DK,
+    V4L2_STD_MTS,     V4L2_STD_525_60,  V4L2_STD_625_50,
+    V4L2_STD_ATSC,
+
+    V4L2_STD_B,       V4L2_STD_G,        V4L2_STD_H,        V4L2_STD_L,
+    V4L2_STD_GH,      V4L2_STD_DK,       V4L2_STD_BG,       V4L2_STD_MN,
+
+    V4L2_STD_PAL_B,   V4L2_STD_PAL_B1,   V4L2_STD_PAL_G,    V4L2_STD_PAL_H,
+    V4L2_STD_PAL_I,   V4L2_STD_PAL_D,    V4L2_STD_PAL_D1,   V4L2_STD_PAL_K,
+    V4L2_STD_PAL_M,   V4L2_STD_PAL_N,    V4L2_STD_PAL_Nc,   V4L2_STD_PAL_60,
+    V4L2_STD_NTSC_M,  V4L2_STD_NTSC_M_JP,V4L2_STD_NTSC_443, V4L2_STD_NTSC_M_KR,
+    V4L2_STD_SECAM_B, V4L2_STD_SECAM_D,  V4L2_STD_SECAM_G,  V4L2_STD_SECAM_H,
+    V4L2_STD_SECAM_K, V4L2_STD_SECAM_K1, V4L2_STD_SECAM_L,  V4L2_STD_SECAM_LC,
+    V4L2_STD_ATSC_8_VSB, V4L2_STD_ATSC_16_VSB,
+};
+
+/* enumerate capabilities */
+bool v4l2_enumerate_capabilities( vlc_object_t *p_obj, int i_fd,
+                                  struct v4l2_capability *cap )
+{
+    if( v4l2_ioctl( i_fd, VIDIOC_QUERYCAP, cap ) < 0 )
+    {
+        msg_Err( p_obj, "cannot get video capabilities: %m" );
+        return false;
+    }
+
+    msg_Dbg( p_obj, "device %s using driver %s (version %u.%u.%u) on %s",
+             cap->card, cap->driver, (cap->version >> 16) & 0xFF,
+             (cap->version >> 8) & 0xFF, cap->version & 0xFF, cap->bus_info );
+    msg_Dbg( p_obj, "the device has the capabilities: 0x%08X",
+             cap->capabilities );
+    msg_Dbg( p_obj, " (%c) Video Output, (%c) Modulator",
+             ( cap->capabilities & V4L2_CAP_VIDEO_OUTPUT  ? 'X':' '),
+             ( cap->capabilities & V4L2_CAP_MODULATOR  ? 'X':' ') );
+    msg_Dbg( p_obj, " (%c) Read/Write, (%c) Streaming, (%c) Asynchronous",
+             ( cap->capabilities & V4L2_CAP_READWRITE ? 'X':' ' ),
+             ( cap->capabilities & V4L2_CAP_STREAMING ? 'X':' ' ),
+             ( cap->capabilities & V4L2_CAP_ASYNCIO ? 'X':' ' ) );
+    return true;
+}
+
+
+/* Select standard */
+bool v4l2_select_standard( vlc_object_t *p_obj, int i_fd,
+                           const char *stdname, bool *b_bottom_first )
+{
+    if( stdname != NULL )
+    {
+        v4l2_std_id std = strtoull( stdname, NULL, 0 );
+        if( std == 0 )
+        {
+            const size_t n = sizeof(standards_vlc) / sizeof(*standards_vlc);
+
+            static_assert(sizeof(standards_vlc) / sizeof(*standards_vlc)
+                         == sizeof (standards_v4l2) / sizeof (*standards_v4l2),
+                          "Inconsistent standards tables");
+            static_assert(sizeof(standards_vlc) / sizeof(*standards_vlc)
+                         == sizeof (standards_user) / sizeof (*standards_user),
+                          "Inconsistent standards tables");
+
+            for( size_t i = 0; i < n; i++ )
+                if( strcasecmp( stdname, standards_vlc[i] ) == 0 )
+                {
+                    std = standards_v4l2[i];
+                    break;
+                }
+        }
+
+        if( v4l2_ioctl( i_fd, VIDIOC_S_STD, &std ) < 0
+            || v4l2_ioctl( i_fd, VIDIOC_G_STD, &std ) < 0 )
+        {
+            msg_Err( p_obj, "cannot set standard %016llX: %m", std );
+            return false;
+        }
+        msg_Dbg( p_obj, "standard set to %016llX:", std );
+        *b_bottom_first = std == V4L2_STD_NTSC;
+    }
+    else
+        *b_bottom_first = false;
+    return true;
+}
+
+bool v4l2_enumerate_and_set_video_io( vlc_object_t *p_obj,
+                                      int i_fd, unsigned index )
+{
+    struct v4l2_output output;
+
+    output.index = 0;
+    while( v4l2_ioctl( i_fd, VIDIOC_ENUMOUTPUT, &output ) >= 0 )
+    {
+        msg_Dbg( p_obj, "video output %u (%s) has type: %s %c",
+                 output.index, output.name,
+                 output.type == V4L2_OUTPUT_TYPE_MODULATOR
+                 ? "Modulator" : "External analog output",
+                 output.index == index ? '*' : ' ' );
+        output.index++;
+    }
+
+    /* Select input/output */
+    if( v4l2_ioctl( i_fd, VIDIOC_S_OUTPUT, &index ) < 0 )
+    {
+        msg_Err( p_obj, "cannot set output %u: %m", index );
+        return false;
+    }
+    msg_Dbg( p_obj, "output set to %u", index );
+
+    return true;
+}
+
+/* List and set tuner/modulator caps */
+bool v4l2_set_tuner_freq( vlc_object_t *p_obj, int i_fd,
+                          uint32_t idx, uint32_t freq, int32_t audmode )
+{
+    struct v4l2_modulator modulator;
+    modulator.index = 0;
+
+    while( v4l2_ioctl( i_fd, VIDIOC_G_MODULATOR, &modulator ) >= 0 )
+    {
+        const char *unit =
+                (modulator.capability & V4L2_TUNER_CAP_LOW) ? "Hz" : "kHz";
+        msg_Dbg( p_obj, "modulator %u (%s), "
+                 "frequency range: %.1f %s -> %.1f %s", modulator.index,
+                 modulator.name,
+                 modulator.rangelow * 62.5, unit,
+                 modulator.rangehigh * 62.5, unit );
+
+        struct v4l2_frequency frequency = { .tuner = modulator.index };
+        if( v4l2_ioctl( i_fd, VIDIOC_G_FREQUENCY, &frequency ) < 0 )
+        {
+            msg_Err( p_obj, "cannot get modulator frequency: %m" );
+            return false;
+        }
+        msg_Dbg( p_obj, "modulator %u (%s) frequency: %.1f %s", modulator.index,
+                 modulator.name, frequency.frequency * 62.5, unit );
+        modulator.index++;
+    }
+
+
+    /* Tune the modulator */
+    if( freq != (uint32_t)-1 )
+    {
+        struct v4l2_frequency frequency = {
+            .tuner = idx,
+            .frequency = freq / 62.5,
+        };
+        memset( frequency.reserved, 0, 8 * sizeof(uint32_t) ); /* req'd by api */
+
+        if( v4l2_ioctl( i_fd, VIDIOC_S_FREQUENCY, &frequency ) < 0 )
+        {
+            msg_Err( p_obj, "cannot set modulator frequency: %m" );
+            return false;
+        }
+        msg_Dbg( p_obj, "modulator frequency set" );
+    }
+
+    /* Set the preferred audio (modulator) mode */
+    if( audmode >= 0 )
+    {
+        modulator.index = idx;
+        modulator.txsubchans = audmode; /* V4L2_TUNER_SUB_* */
+        memset( modulator.reserved, 0, 4 * sizeof(uint32_t) );
+
+        if( v4l2_ioctl( i_fd, VIDIOC_S_MODULATOR, &modulator ) < 0 )
+        {
+            msg_Err( p_obj, "cannot set modulator audio mode: %m" );
+            return false;
+        }
+
+        msg_Dbg( p_obj, "modulator audio mode set" );
+    }
+
+    return true;
+}
+
+bool v4l2_probe_device_chromas( vlc_object_t *p_obj, int i_fd,
+                                struct v4l2_fmtdesc **p_codecs,
+                                uint32_t *ncodec )
+{
+    /* Probe for available chromas */
+    struct v4l2_fmtdesc *codecs, codec = {
+        .index = 0,
+        .type = V4L2_BUF_TYPE_VIDEO_OUTPUT
+    };
+
+    while( v4l2_ioctl( i_fd, VIDIOC_ENUM_FMT, &codec ) >= 0 )
+    {
+        codec.index = ++*ncodec;
+    }
+
+    *p_codecs = malloc( *ncodec * sizeof( struct v4l2_fmtdesc ) );
+    if( unlikely(*p_codecs == NULL) )
+        *ncodec = 0;
+    codecs = *p_codecs;
+
+    for( uint32_t i = 0; i < *ncodec; i++ )
+    {
+        codecs[i].index = i;
+        codecs[i].type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+        if( v4l2_ioctl( i_fd, VIDIOC_ENUM_FMT, &codecs[i] ) < 0 )
+        {
+            msg_Err( p_obj, "cannot get codec description: %m" );
+            return false;
+        }
+
+        /* only print if vlc supports the format */
+        char fourcc_v4l2[4];
+        vlc_fourcc_to_char( codecs[i].pixelformat, fourcc_v4l2 );
+
+        vlc_fourcc_t i_fourcc;
+        int i_foo;
+
+        if( get_fourcc_by_v4l2pixelformat( &i_fourcc,
+                                           &i_foo,
+                                           &i_foo,
+                                           &i_foo,
+                                           codecs[i].pixelformat ) )
+        {
+            char fourcc[4];
+            vlc_fourcc_to_char( i_fourcc, fourcc );
+            msg_Dbg( p_obj, "device supports chroma %4.4s [%s, %4.4s]",
+                     fourcc, codecs[i].description, fourcc_v4l2 );
+        }
+        else
+        {
+            msg_Dbg( p_obj, "device codec %4.4s (%s) not supported",
+                     fourcc_v4l2, codecs[i].description );
+        }
+    }
+    return true;
+}
+
+/**
+ * \return true if the specified V4L2 pixel format is
+ * in the array of supported formats returned by the driver
+ */
+static bool IsPixelFormatSupported( struct v4l2_fmtdesc *codecs, size_t n,
+                                    unsigned int i_pixelformat )
+{
+    for( size_t i = 0; i < n; i++ )
+        if( codecs[i].pixelformat == i_pixelformat )
+            return true;
+    return false;
+}
+
+static bool is_accepted_chroma_and_size( vlc_object_t *p_obj, int i_fd,
+                                         struct v4l2_format *fmt )
+{
+    /* validates or finds a valid size for that chroma */
+    v4l2_negociate_framesize( p_obj, i_fd,
+                              fmt->fmt.pix.pixelformat,
+                              &fmt->fmt.pix.width,
+                              &fmt->fmt.pix.height );
+    /* try chroma+size */
+    if( v4l2_ioctl( i_fd, VIDIOC_TRY_FMT, fmt ) < 0 )
+    {
+        fmt->fmt.pix.field = V4L2_FIELD_ANY;
+        if( v4l2_ioctl( i_fd, VIDIOC_TRY_FMT, fmt ) < 0 )
+        {
+            fmt->fmt.pix.field = V4L2_FIELD_NONE;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool v4l2_negociate_chroma( vlc_object_t *p_obj, int i_fd,
+                            vlc_fourcc_t i_source_fourcc,
+                            vlc_fourcc_t i_requested_fourcc,
+                            struct v4l2_format *fmt,
+                            struct v4l2_fmtdesc *p_codecs, uint32_t ncodec )
+{
+    unsigned int w = fmt->fmt.pix.width;
+    unsigned int h = fmt->fmt.pix.height;
+    vlc_fourcc_t *pi_base_chroma;
+    /* Test and set Chroma */
+    fmt->fmt.pix.pixelformat = 0;
+
+    if( i_requested_fourcc )
+        pi_base_chroma = &i_requested_fourcc;
+    else /* Try current source chroma before doing fallback */
+        pi_base_chroma = &i_source_fourcc;
+
+    get_v4l2pixelformat_by_fourcc( &fmt->fmt.pix.pixelformat, *pi_base_chroma );
+
+     /* Try and set user chroma */
+    if( fmt->fmt.pix.pixelformat )
+    {
+        /* make frame size valid before trying S_FMT */
+        if ( IsPixelFormatSupported( p_codecs, ncodec, fmt->fmt.pix.pixelformat )
+            && is_accepted_chroma_and_size( p_obj, i_fd, fmt ) )
+            {
+            return true;
+        }
+        else
+        {
+            msg_Warn( p_obj, "requested chroma not supported. "
+                      " Trying default." );
+            fmt->fmt.pix.pixelformat = 0;
+            fmt->fmt.pix.width = w;
+            fmt->fmt.pix.height = h;
+        }
+    }
+
+    /* need to use fallback to get an acceptable chroma */
+    if( !fmt->fmt.pix.pixelformat )
+    {
+        unsigned int i = 0;
+        const vlc_fourcc_t *p_fallback_table;
+
+        if ( vlc_fourcc_IsYUV( *pi_base_chroma ) )
+            p_fallback_table = vlc_fourcc_GetYUVFallback( *pi_base_chroma );
+        else
+            p_fallback_table = vlc_fourcc_GetRGBFallback( *pi_base_chroma );
+
+        while( p_fallback_table[i] )
+        {
+            if ( get_v4l2pixelformat_by_fourcc( &fmt->fmt.pix.pixelformat, p_fallback_table[i] ) )
+            {
+                fmt->fmt.pix.width = w;
+                fmt->fmt.pix.height = h;
+                if( IsPixelFormatSupported( p_codecs, ncodec, fmt->fmt.pix.pixelformat )
+                    && is_accepted_chroma_and_size( p_obj, i_fd, fmt ) )
+                    return true;
+            }
+            i++;
+        }
+
+    }
+
+    msg_Warn( p_obj, "Could not select any of the default chromas" );
+    return false;
+}
+
+bool v4l2_negociate_framesize( vlc_object_t *p_obj, int i_fd,
+                               uint32_t pixel_format,
+                               unsigned int *width, unsigned int *height )
+{
+    struct v4l2_frmsizeenum sizesenum;
+    unsigned int h, w;
+
+    sizesenum.pixel_format = pixel_format;
+    sizesenum.index = 0;
+    if( v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMESIZES, &sizesenum ) < 0 )
+    {
+        msg_Err( p_obj, "cannot get sizes: %m" );
+        return false;
+    }
+
+    switch( sizesenum.type )
+    {
+    case V4L2_FRMSIZE_TYPE_DISCRETE:
+        w = sizesenum.discrete.width;
+        h = sizesenum.discrete.height;
+        while( ++sizesenum.index &&
+               v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMESIZES, &sizesenum ) == 0 )
+        {
+            if ( *width == sizesenum.discrete.width && *height == sizesenum.discrete.height )
+            {
+                /* same as requested is in list */
+                return true;
+            }
+        }
+        /* otherwise pick first valid value */
+        *width = w;
+        *height = h;
+        break;
+    case V4L2_FRMSIZE_TYPE_STEPWISE:
+        if ( *width < sizesenum.stepwise.min_width ||
+             *width > sizesenum.stepwise.max_width ||
+             *height < sizesenum.stepwise.min_height ||
+             *height > sizesenum.stepwise.max_height ||
+             *width % sizesenum.stepwise.step_width ||
+             *height % sizesenum.stepwise.step_height
+             )
+        {
+            /* requested values are out of range */
+            *width = sizesenum.stepwise.min_width;
+            *height = sizesenum.stepwise.min_height;
+        }
+        break;
+    case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+        /* any size is valid */
+        break;
+    }
+
+    return true;
+}
diff --git a/modules/video_output/v4l2/v4l2_common.h b/modules/video_output/v4l2/v4l2_common.h
new file mode 100644
index 0000000..08c0568
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_common.h
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * v4l2_common.h : V4L2 shared data & functions for vlc
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef V4L2_COMMON_H
+#define V4L2_COMMON_H
+
+#include "v4l2.h"
+#include <vlc_common.h>
+
+static const char *const standards_vlc[] = { "", "ALL",
+    /* Pseudo standards */
+    "PAL", "PAL_BG", "PAL_DK",
+    "NTSC",
+    "SECAM", "SECAM_DK",
+    "MTS", "525_60", "625_50",
+    "ATSC",
+
+    /* Chroma-agnostic ITU standards (PAL/NTSC or PAL/SECAM) */
+    "B",              "G",               "H",               "L",
+    "GH",             "DK",              "BG",              "MN",
+
+    /* Individual standards */
+    "PAL_B",          "PAL_B1",          "PAL_G",           "PAL_H",
+    "PAL_I",          "PAL_D",           "PAL_D1",          "PAL_K",
+    "PAL_M",          "PAL_N",           "PAL_Nc",          "PAL_60",
+    "NTSC_M",         "NTSC_M_JP",       "NTSC_443",        "NTSC_M_KR",
+    "SECAM_B",        "SECAM_D",         "SECAM_G",         "SECAM_H",
+    "SECAM_K",        "SECAM_K1",        "SECAM_L",         "SECAM_LC",
+    "ATSC_8_VSB",     "ATSC_16_VSB",
+};
+
+static const char *const standards_user[] = { N_("Undefined"), N_("All"),
+    "PAL",            "PAL B/G",         "PAL D/K",
+    "NTSC",
+    "SECAM",          "SECAM D/K",
+    N_("Multichannel television sound (MTS)"),
+    N_("525 lines / 60 Hz"), N_("625 lines / 50 Hz"),
+    "ATSC",
+
+    "PAL/SECAM B",    "PAL/SECAM G",     "PAL/SECAM H",     "PAL/SECAM L",
+    "PAL/SECAM G/H",  "PAL/SECAM D/K",   "PAL/SECAM B/G",   "PAL/NTSC M/N",
+
+    "PAL B",          "PAL B1",          "PAL G",           "PAL H",
+    "PAL I",          "PAL D",           "PAL D1",          "PAL K",
+    "PAL M",          "PAL N",           N_("PAL N Argentina"), "PAL 60",
+    "NTSC M",        N_("NTSC M Japan"), "NTSC 443",  N_("NTSC M South Korea"),
+    "SECAM B",        "SECAM D",         "SECAM G",         "SECAM H",
+    "SECAM K",        "SECAM K1",        "SECAM L",         "SECAM L/C",
+    "ATSC 8-VSB",     "ATSC 16-VSB",
+};
+
+bool v4l2_select_standard( vlc_object_t *p_obj, int i_fd,
+                           const char *stdname, bool *b_bottom_first );
+
+bool v4l2_enumerate_and_set_video_io( vlc_object_t *p_obj,
+                                      int i_fd, unsigned index );
+
+bool v4l2_enumerate_capabilities( vlc_object_t *p_obj, int i_fd,
+                                 struct v4l2_capability *cap );
+
+bool v4l2_set_tuner_freq( vlc_object_t *p_obj, int i_fd,
+                          uint32_t idx, uint32_t freq, int32_t audmode );
+
+bool v4l2_probe_device_chromas( vlc_object_t *p_obj, int i_fd,
+                                struct v4l2_fmtdesc **p_codecs,
+                                uint32_t *ncodec );
+
+bool v4l2_negociate_chroma( vlc_object_t *p_obj, int i_fd,
+                            vlc_fourcc_t i_source_fourcc,
+                            vlc_fourcc_t i_requested_fourcc,
+                            struct v4l2_format *fmt,
+                            struct v4l2_fmtdesc *p_codecs, uint32_t ncodec );
+
+bool v4l2_negociate_framesize( vlc_object_t *p_obj, int i_fd, uint32_t pixel_format,
+                               unsigned int *width, unsigned int *height );
+
+#endif // V4L2_COMMON_H
diff --git a/modules/video_output/v4l2/v4l2_output.c b/modules/video_output/v4l2/v4l2_output.c
new file mode 100644
index 0000000..b074efa
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_output.c
@@ -0,0 +1,593 @@
+/*****************************************************************************
+ * v4l2_output.c : V4L2 output module for vlc
+ *****************************************************************************
+ * Copyright (C) 2012 the VideoLAN team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_fs.h>
+#include <vlc_vout_display.h>
+#include <vlc_fourcc.h>
+#include "v4l2_common.h"
+#include "v4l2_chroma.h"
+
+#define DEVICE_TEXT N_("Device")
+#define DEVICE_LONGTEXT N_("V4L2 output device")
+#define PROFILE_TEXT N_("Loopback application profile")
+#define PROFILE_LONGTEXT N_("Profile (chroma + size) for special apps using v4l2loopback")
+#define CHROMA_TEXT N_("Chroma used")
+#define CHROMA_LONGTEXT N_("Force use of a specific chroma for output.")
+#define STANDARD_TEXT N_( "Standard" )
+#define STANDARD_LONGTEXT N_( "Video standard (Default, SECAM, PAL, or NTSC)." )
+#define OUTPUT_TEXT N_( "Output id" )
+#define OUTPUT_LONGTEXT N_( "Output of the card to use (see debug)." )
+#define MODULATOR_TEXT N_("Modulator id")
+#define MODULATOR_LONGTEXT N_( "Modulator id (see debug output)." )
+#define MODULATOR_FREQUENCY_TEXT N_("Frequency")
+#define MODULATOR_FREQUENCY_LONGTEXT N_( "Tuner frequency in Hz or kHz (see debug output)" )
+#define WIDTH_TEXT N_( "Width" )
+#define HEIGHT_TEXT N_( "Height" )
+#define SIZE_LONGTEXT N_( \
+    "The specified pixel resolution is forced " \
+    "(if both width and height are strictly positive)." )
+
+#define CFG_PREFIX "v4l2_output_"
+
+struct vout_display_sys_t {
+    picture_pool_t      *pool;
+    int fd;
+    bool b_streaming;
+    struct buffer_t *buffers;
+    int i_buffers;
+    int i_target_buffer;
+    bool b_buffer_ready;
+    unsigned int i_buffer_datasize;
+};
+
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+static picture_pool_t *Pool   (vout_display_t *, unsigned);
+static void           PrepareBufferRW(vout_display_t *, picture_t *, subpicture_t *);
+static void           PrepareBufferMMAP(vout_display_t *, picture_t *, subpicture_t *);
+static void           PictureDisplayRW(vout_display_t *, picture_t *, subpicture_t *);
+static void           PictureDisplayMMAP(vout_display_t *, picture_t *, subpicture_t *);
+static int            Control(vout_display_t *, int, va_list);
+
+static const char * const psz_profiles_texts[] = { N_("None"), N_("Skype"), N_("Flash Plugin") };
+static const char * const psz_profiles_values[] = { NULL, "skype", "flashplugin" };
+
+/*
+ * Module descriptor
+ */
+vlc_module_begin ()
+        set_shortname (N_("V4L2 Output"))
+        set_description (N_("V4L2 Output"))
+        set_category (CAT_VIDEO)
+        set_subcategory (SUBCAT_VIDEO_VOUT)
+        set_capability ("vout display", 0)
+        set_callbacks (Open, Close)
+
+        set_section( N_( "Video output" ), NULL )
+        add_string(CFG_PREFIX "dev", "/dev/video0", DEVICE_TEXT, DEVICE_LONGTEXT, false)
+            change_safe()
+        add_string( CFG_PREFIX "standard", "", STANDARD_TEXT, STANDARD_LONGTEXT, false )
+            change_string_list( standards_vlc, standards_user )
+            change_safe()
+        add_integer( CFG_PREFIX "output", 0, OUTPUT_TEXT, OUTPUT_LONGTEXT, true )
+            change_integer_range( 0, 0xFFFFFFFE )
+            change_safe()
+        add_string(CFG_PREFIX "profile", NULL, PROFILE_TEXT, PROFILE_LONGTEXT, true)
+            change_string_list( psz_profiles_values, psz_profiles_texts )
+        add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
+        add_integer( CFG_PREFIX "width", 0, WIDTH_TEXT, SIZE_LONGTEXT, false )
+            change_integer_range( 0, VOUT_MAX_WIDTH )
+            change_safe()
+        add_integer( CFG_PREFIX "height", 0, HEIGHT_TEXT, SIZE_LONGTEXT, false )
+            change_integer_range( 0, VOUT_MAX_WIDTH )
+            change_safe()
+        set_section( N_( "Modulator" ), NULL )
+        add_integer( CFG_PREFIX "modulator", 0, MODULATOR_TEXT, MODULATOR_LONGTEXT,
+                     true )
+            change_integer_range( 0, 0xFFFFFFFE )
+            change_safe()
+        add_integer( CFG_PREFIX "modulator-frequency", -1, MODULATOR_FREQUENCY_TEXT,
+                     MODULATOR_FREQUENCY_LONGTEXT, true )
+            change_integer_range( -1, 0xFFFFFFFE )
+            change_safe()
+vlc_module_end ()
+
+/* applications profiles for v4l2loopback output */
+static struct {
+    const char const * psz_name;
+    const int i_width;
+    const int i_height;
+    const int i_chroma;
+} const app_profiles[] = {
+    /* apps requested profiles */
+    { "skype", 640, 480, V4L2_PIX_FMT_YUV420 },
+    { "flashplugin", 0, 0, V4L2_PIX_FMT_RGB32 },
+    { NULL, 0, 0, 0 }
+};
+
+static bool toggle_stream( vout_display_t *vd, int i_request )
+{
+    struct v4l2_requestbuffers reqbuf;
+    reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+    reqbuf.memory = V4L2_MEMORY_MMAP;
+    reqbuf.count = vd->sys->i_buffers;
+    return v4l2_ioctl( vd->sys->fd, i_request, & reqbuf.type );
+}
+
+static void free_buffers( vout_display_sys_t *sys )
+{
+    for( int i=0; i<sys->i_buffers; i++ )
+        munmap( sys->buffers[ i ].start, sys->buffers[ i ].length );
+    free( sys->buffers );
+}
+
+static bool write_frames( picture_t *picture, struct buffer_t *buffer, unsigned int *offset )
+{
+    *offset = 0;
+    for (int i = 0; i < picture->i_planes; i++)
+    {
+        const plane_t *plane = &picture->p[i];
+        for( int y = 0; y < plane->i_visible_lines; y++)
+        {
+            if ( unlikely( *offset + plane->i_visible_pitch > buffer->length ) )
+                return false;
+            else
+                memcpy( ((char *)buffer->start) + *offset,
+                        &plane->p_pixels[y*plane->i_pitch],
+                        plane->i_visible_pitch );
+            *offset += plane->i_visible_pitch;
+        }
+    }
+    return true;
+}
+
+static bool apply_profile( char *psz_profile, vlc_fourcc_t *i_requested_fourcc,
+                           unsigned int *i_width, unsigned int *i_height  )
+{
+    if ( !psz_profile )
+        return false;
+
+    for (int i=0; app_profiles[i].psz_name != NULL ; i++)
+    {
+        if ( strcmp( app_profiles[i].psz_name, psz_profile ) == 0 )
+        {
+            if ( app_profiles[i].i_chroma )
+                *i_requested_fourcc = app_profiles[i].i_chroma;
+
+            if ( app_profiles[i].i_width )
+            {
+                *i_width = app_profiles[i].i_width;
+                *i_height = app_profiles[i].i_height;
+            }
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static int Open (vlc_object_t *p_obj)
+{
+    vout_display_t *vd = (vout_display_t *)p_obj;
+    video_format_t target_fmt = vd->fmt;
+    void (*p_display_method)(vout_display_t *, picture_t *, subpicture_t *);
+    void (*p_prepare_method)(vout_display_t *, picture_t *, subpicture_t *);
+    struct v4l2_capability vid_caps;
+    struct v4l2_format v4l2fmt;
+    char *psz_video_device = var_InheritString(vd, CFG_PREFIX "dev");
+    char *psz_profile = var_InheritString(vd, CFG_PREFIX "profile");
+    char *psz_reqchroma = var_InheritString( vd, CFG_PREFIX "chroma" );
+    uint32_t i_width = var_InheritInteger(vd, CFG_PREFIX "width");
+    uint32_t i_height = var_InheritInteger(vd, CFG_PREFIX "height");
+
+    bool b_streamable;
+    struct v4l2_fmtdesc *codecs = NULL;
+    uint32_t ncodecs = 0;
+
+    vd->sys = calloc( 1, sizeof(vout_display_sys_t) );
+    if ( !vd->sys ) goto earlyerror;
+
+    vd->sys->fd = vlc_open( psz_video_device, O_RDWR|O_NONBLOCK );
+    if (vd->sys->fd < 0)
+    {
+        msg_Err( p_obj, "cannot open v4l2 output device (%s): %m", psz_video_device );
+        free(vd->sys);
+        goto earlyerror;
+    }
+
+    /* Get device capabilites */
+    if ( ! v4l2_enumerate_capabilities( p_obj, vd->sys->fd, &vid_caps ) )
+    {
+        goto error;
+    }
+
+    /* special driver handling */
+    if ( !strncmp( (char*)vid_caps.driver, "v4l2 loopback", 16 )
+        && ( vid_caps.version < 0x00000500 ) )
+    {
+        /* some apps won't work on v4l2oopback < 0.5 (skype) */
+        msg_Warn( p_obj, "Please update your V4L2 loopback driver" );
+    }
+
+    if ( ( vid_caps.capabilities & V4L2_CAP_VIDEO_OUTPUT ) == 0 )
+    {
+        msg_Err( p_obj, "device (%s) is not an output device", psz_video_device );
+        goto error;
+    }
+
+    if ( ( vid_caps.capabilities & (V4L2_CAP_READWRITE|V4L2_CAP_STREAMING) ) == 0 )
+    {
+        msg_Err( p_obj, "no supported I/O method" );
+        goto error;
+    }
+
+    b_streamable = vid_caps.capabilities & V4L2_CAP_STREAMING;
+#if 0
+    b_streamable = false; // force RW method
+#endif
+
+    /* First, do changes that might affect the chromas & sizes */
+
+    /* set video output */
+    if ( vid_caps.capabilities & V4L2_CAP_VIDEO_OUTPUT )
+    {
+        /* Now, enumerate all the video inputs. This is useless at the moment
+           since we have no way to present that info to the user except with
+           debug messages */
+        unsigned index = var_InheritInteger( p_obj, CFG_PREFIX"output" );
+        if ( ! v4l2_enumerate_and_set_video_io( p_obj, vd->sys->fd, index ) )
+        {
+            goto error;
+        }
+    }
+
+    /* Select standard */
+    bool bottom_first;
+    const char *stdname = var_InheritString( p_obj, CFG_PREFIX"standard" );
+    if ( ! v4l2_select_standard( p_obj,  vd->sys->fd, stdname, &bottom_first ) )
+    {
+        goto error;
+    }
+
+    /* List & set modulator caps */
+    if( vid_caps.capabilities & V4L2_CAP_MODULATOR )
+    {
+        uint32_t idx = var_CreateGetInteger( p_obj, CFG_PREFIX"modulator" );
+        uint32_t freq = var_InheritInteger( p_obj, CFG_PREFIX"modulator-frequency" );
+        if ( ! v4l2_set_tuner_freq( p_obj, vd->sys->fd, idx, freq, -1 ) )
+        {
+            goto error;
+        }
+    }
+
+    /* Load driver defaults */
+    v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+    if ( v4l2_ioctl(vd->sys->fd, VIDIOC_G_FMT, &v4l2fmt) < 0 )
+    {
+        msg_Err( p_obj, "cannot control v4l2 format: %m" );
+        goto error;
+    }
+
+    /* Now probe for available chromas */
+    if ( ! v4l2_probe_device_chromas( p_obj, vd->sys->fd,
+                                      &codecs,
+                                      &ncodecs ) )
+    {
+        goto lateerror;
+    }
+
+    vlc_fourcc_t i_requested_fourcc = 0;
+    if ( apply_profile( psz_profile, &i_requested_fourcc, &i_width, &i_height ) )
+        msg_Info( p_obj, "using profile %s", psz_profile );
+
+    if( psz_reqchroma != NULL ) /* can override profile */
+        i_requested_fourcc = vlc_fourcc_GetCodecFromString( VIDEO_ES, psz_reqchroma );
+
+    /* now trying chroma with a given size */
+    if ( i_width * i_height > 0 )
+    {
+        v4l2fmt.fmt.pix.width = i_width;
+        v4l2fmt.fmt.pix.height = i_height;
+    } else {
+        v4l2fmt.fmt.pix.width = vd->fmt.i_visible_width;
+        v4l2fmt.fmt.pix.height = vd->fmt.i_visible_height;
+    }
+
+    v4l2fmt.fmt.pix.pixelformat = 0; /* autodetect || i_requested_fourcc */
+    if ( ! v4l2_negociate_chroma( p_obj, vd->sys->fd,
+                                  vd->fmt.i_chroma,
+                                  i_requested_fourcc,
+                                  &v4l2fmt,
+                                  codecs, ncodecs ) )
+    {
+        msg_Warn( p_obj, "No valid V4L2 chroma found" );
+        goto lateerror;
+    }
+    else
+    {
+        int foo;
+        /* ensure to have it configured on display's side */
+        get_fourcc_by_v4l2pixelformat( & target_fmt.i_chroma, &foo, &foo, &foo,
+                                       v4l2fmt.fmt.pix.pixelformat );
+    }
+
+    msg_Dbg( p_obj, "req'd chroma %s made %s change to %s",
+             vlc_fourcc_GetDescription( VIDEO_ES, i_requested_fourcc ),
+             vlc_fourcc_GetDescription( VIDEO_ES, vd->fmt.i_chroma ),
+             vlc_fourcc_GetDescription( VIDEO_ES, target_fmt.i_chroma ) );
+
+
+        /* Apply negociated changes to device */
+    if( v4l2_ioctl( vd->sys->fd, VIDIOC_S_FMT, & v4l2fmt ) < 0 )
+    {
+        msg_Err( p_obj, "Can't apply negociated format to device, %m" );
+        goto lateerror;
+    }
+
+    /* backport valid sizes to vout */
+    if ( target_fmt.i_visible_width != v4l2fmt.fmt.pix.width ||
+         target_fmt.i_visible_height != v4l2fmt.fmt.pix.height )
+        msg_Dbg( p_obj, "size changed to w%d.h%d", v4l2fmt.fmt.pix.width, v4l2fmt.fmt.pix.height );
+
+    target_fmt.i_width = target_fmt.i_visible_width = v4l2fmt.fmt.pix.width;
+    target_fmt.i_height = target_fmt.i_visible_height = v4l2fmt.fmt.pix.height;
+
+    struct v4l2_requestbuffers reqbuf;
+    memset (&reqbuf, 0, sizeof (reqbuf));
+    reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+    reqbuf.memory = V4L2_MEMORY_MMAP;
+    reqbuf.count = 8;
+
+    if ( !b_streamable || ( v4l2_ioctl(vd->sys->fd, VIDIOC_REQBUFS, &reqbuf) < 0 ) )
+    {
+        if ( !b_streamable || errno == EINVAL )
+        {
+            msg_Dbg( p_obj, "Video capturing or mmap-streaming is not supported" );
+            p_display_method = PictureDisplayRW;
+            p_prepare_method = PrepareBufferRW;
+            vd->sys->buffers = calloc( 1, sizeof( struct buffer_t ) );
+            if ( !vd->sys->buffers )
+            {
+                msg_Err( p_obj, "can't allocate buffer");
+                goto lateerror;
+            }
+            vd->sys->i_buffers = 1;
+            vd->sys->buffers[0].length = v4l2fmt.fmt.pix.sizeimage;
+            vd->sys->buffers[0].start = calloc( v4l2fmt.fmt.pix.sizeimage, sizeof(uint32_t) );
+            if ( !vd->sys->buffers[0].start ) goto error;
+        }
+        else
+        {
+            msg_Err( p_obj, "ioctl error with reqbufs: %m");
+            goto lateerror;
+        }
+    }
+    else
+    {
+
+        if ( reqbuf.count < 2 ) /* should at least have an in and out queue */
+        {
+            msg_Err( p_obj, "Not enough buffer memory");
+            free( vd->sys );
+            return VLC_EGENERIC;
+        }
+
+        p_display_method = PictureDisplayMMAP;
+        p_prepare_method = PrepareBufferMMAP;
+
+        vd->sys->buffers = calloc( reqbuf.count, sizeof( struct buffer_t ) );
+        if ( !vd->sys->buffers )
+        {
+            msg_Err( p_obj, "can't allocate mmap buffers");
+            goto lateerror;
+        }
+
+        for( unsigned int i=0; i<reqbuf.count; i++ )
+        {
+            struct v4l2_buffer buf;
+            buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+            buf.memory = V4L2_MEMORY_MMAP;
+            buf.index = i;
+
+            if ( v4l2_ioctl (vd->sys->fd, VIDIOC_QUERYBUF, & buf ) < 0 )
+            {
+                msg_Err( p_obj, "Error in VIDIOC_QUERYBUF: %m");
+                goto lateerror;
+            }
+
+            vd->sys->buffers[ i ].start = v4l2_mmap( NULL, buf.length,
+                                                PROT_READ | PROT_WRITE,
+                                                MAP_SHARED,
+                                                vd->sys->fd, buf.m.offset );
+            if ( vd->sys->buffers[ i ].start == MAP_FAILED )
+            {
+                msg_Err( p_obj, "Can't mmap buffer");
+                goto lateerror;
+            }
+            vd->sys->buffers[ i ].length = buf.length;
+            vd->sys->i_buffers++;
+        }
+
+    }
+
+    vd->pool    = Pool;
+    vd->prepare = p_prepare_method;
+    vd->control = Control;
+    vd->manage  = NULL;
+    vd->display = p_display_method;
+    video_format_FixRgb(&target_fmt);
+    vd->fmt = target_fmt;
+    vout_display_SendEventFullscreen(vd, false);
+    return VLC_SUCCESS;
+
+lateerror:
+    free( codecs );
+error:
+    close( vd->sys->fd );
+    free_buffers( vd->sys );
+    free( vd->sys );
+earlyerror:
+    free( psz_video_device );
+    free( psz_profile );
+    free( psz_reqchroma );
+    return VLC_EGENERIC;
+}
+
+static void Close (vlc_object_t *obj)
+{
+    vout_display_t *vd = (vout_display_t *)obj;
+
+    if ( vd->sys->b_streaming )
+        toggle_stream( vd, VIDIOC_STREAMOFF );
+
+    if (vd->sys->pool)
+        picture_pool_Delete(vd->sys->pool);
+
+    close( vd->sys->fd );
+    if ( vd->display == PictureDisplayRW )
+        free( vd->sys->buffers[ 0 ].start ); /* free local buffer */
+    free_buffers( vd->sys );
+    free( vd->sys );
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (!sys->pool)
+        sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
+
+    return sys->pool;
+}
+
+static void PictureDisplayRW(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
+{
+    VLC_UNUSED(subpicture);
+    assert( vd->sys->i_buffers );
+    vout_display_sys_t *p_sys = vd->sys;
+    struct buffer_t *buffer = &vd->sys->buffers[0];
+    unsigned int i_offset = p_sys->i_buffer_datasize;
+
+    if ( i_offset != v4l2_write( p_sys->fd, buffer->start, i_offset ) )
+        msg_Warn( vd, "can't fully write to device");
+
+    picture_Release(picture);
+}
+
+static void PictureDisplayMMAP(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
+{
+    VLC_UNUSED(subpicture);
+    vout_display_sys_t *p_sys = vd->sys;
+    struct v4l2_buffer freebuf;
+    if ( p_sys->b_buffer_ready )
+    {
+        struct timeval tv;
+        gettimeofday (&tv, NULL);
+        freebuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+        freebuf.memory = V4L2_MEMORY_MMAP;
+        freebuf.index = p_sys->i_target_buffer;
+        freebuf.bytesused = p_sys->i_buffer_datasize;
+        freebuf.timestamp = tv;
+        freebuf.flags = 0;
+        freebuf.reserved = 0;
+        freebuf.field = V4L2_FIELD_NONE;
+        if ( v4l2_ioctl( p_sys->fd, VIDIOC_QBUF, &freebuf ) < 0 )
+            msg_Err( vd, "VIDIOC_QBUF: %m");
+        /* we should test for V4L2_BUF_FLAG_QUEUED flag here */
+        /* but this won't work with the v4l2loopback driver which changes */
+        /* flag immediately to DONE and returns it in the ioctl */
+
+        if ( !p_sys->b_streaming )
+        {
+            if ( toggle_stream( vd, VIDIOC_STREAMON ) < 0 )
+                msg_Warn( vd, "can't VIDIOC_STREAMON");
+            else
+                p_sys->b_streaming = true;
+        }
+    }
+
+    picture_Release(picture);
+}
+
+static void PrepareBufferRW( vout_display_t *p_vd, picture_t *p, subpicture_t *s )
+{
+    VLC_UNUSED(p);
+    VLC_UNUSED(s);
+    vout_display_sys_t *p_sys = p_vd->sys;
+    assert( p_sys->i_buffers );
+
+    struct buffer_t *buffer = & p_sys->buffers[0];
+    /* we need to merge into memory to make only 1 write */
+    p_sys->i_buffer_datasize = 0;
+    write_frames( p, buffer, & p_sys->i_buffer_datasize );
+}
+
+static void PrepareBufferMMAP( vout_display_t *p_vd, picture_t *p, subpicture_t *s )
+{
+    VLC_UNUSED(s);
+    vout_display_sys_t *p_sys = p_vd->sys;
+
+    struct v4l2_buffer freebuf;
+
+    freebuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+    freebuf.memory = V4L2_MEMORY_MMAP;
+    freebuf.flags = 0;
+
+    /* Query the driver for a free buffer */
+    if ( v4l2_ioctl( p_sys->fd, VIDIOC_DQBUF, &freebuf ) < 0 )
+    {
+        p_sys->b_buffer_ready = false;
+        msg_Warn( p_vd, "device buffer not ready at VIDIOC_DQBUF: %m");
+    } else {
+        struct buffer_t *buffer = &p_sys->buffers[ freebuf.index ];
+        p_sys->i_buffer_datasize = 0;
+        write_frames( p, buffer, & p_sys->i_buffer_datasize );
+
+        p_sys->b_buffer_ready = true;
+        p_sys->i_target_buffer = freebuf.index;
+    }
+}
+
+static int Control(vout_display_t *vd, int query, va_list args)
+{
+    VLC_UNUSED(vd);
+    VLC_UNUSED(args);
+    switch (query)
+    {
+    default:
+        return VLC_EGENERIC;
+    }
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9dee7c0..70e5496 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1129,6 +1129,7 @@ modules/video_output/xcb/window.c
 modules/video_output/xcb/x11.c
 modules/video_output/xcb/xvideo.c
 modules/video_output/yuv.c
+modules/video_output/v4l2/v4l2_output.c
 modules/visualization/goom.c
 modules/visualization/projectm.cpp
 modules/visualization/visual/effects.c
-- 
1.7.6




More information about the vlc-devel mailing list