[vlc-devel] [PATCH] v4l2: V4L2 video_output module
Francois Cartegnie
fcvlcdev at free.fr
Thu Apr 19 16:35:00 CEST 2012
- vlc_fourcc_GetYUVFallback/vlc_fourcc_GetRGBFallback
Laurent requested to use the dedicated fallback functions.
But I don't see how I could use it as we need a V4L2 chroma
fallback, not a vlc fourcc one. The priority is different.
This code still uses the original fallback tables then.
- validates and fix sizes after selecting a valid chroma
The prior versions were not checking the frame size which
could make the chroma selection fail.
Now only S_FMT after everything has been validated.
Chroma is always validated first as it's better to have no
conversion from the original source.
- Makes use of prepare
Moved mmap copy and IO picture merge steps to Prepare().
- Defactored/Split from the v4l2 access
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 | 432 ++++++++++++++++++++++
modules/video_output/v4l2/v4l2_common.h | 92 +++++
modules/video_output/v4l2/v4l2_output.c | 603 +++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
9 files changed, 1350 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 58149df..5034236 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1830,6 +1830,7 @@ AS_IF([test "$enable_v4l2" != "no"], [
AC_CHECK_HEADERS([linux/videodev2.h sys/videoio.h], [
have_v4l2="yes"
])
+ VLC_ADD_PLUGIN([v4l2_output])
AC_CHECK_DECLS([V4L2_CTRL_TYPE_BITMASK],,, [
#ifdef HAVE_LINUX_VIDEODEV2_H
# include <linux/videodev2.h>
diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am
index 426b60f..af53ed4 100644
--- a/modules/video_output/Modules.am
+++ b/modules/video_output/Modules.am
@@ -132,6 +132,21 @@ libegl_plugin_la_DEPENDENCIES =
EXTRA_LTLIBRARIES += libegl_plugin.la
libvlc_LTLIBRARIES += $(LTLIBegl)
+### 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..e9d963e
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_common.c
@@ -0,0 +1,432 @@
+/*****************************************************************************
+ * 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;
+}
+
+
+/**
+ * Tries to find a device compatible V4L2 chroma and size.
+ *
+ * Chroma has higher priority than size.
+ */
+
+static const uint32_t const p_chroma_fallbacks[] =
+{
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_YUV422P,
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_UYVY,
+ V4L2_PIX_FMT_BGR24,
+ V4L2_PIX_FMT_BGR32
+ // minus mjpeg/jpeg
+};
+
+bool v4l2_negociate_chroma( vlc_object_t *p_obj, int i_fd,
+ 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;
+ /* Test and set Chroma */
+ fmt->fmt.pix.pixelformat = 0;
+ if( i_requested_fourcc )
+ {
+ /* User specified chroma */
+ get_v4l2pixelformat_by_fourcc( &fmt->fmt.pix.pixelformat, i_requested_fourcc );
+
+ /* Try and set user chroma */
+ bool b_error = !IsPixelFormatSupported( p_codecs, ncodec,
+ fmt->fmt.pix.pixelformat );
+ if( !b_error && fmt->fmt.pix.pixelformat )
+ {
+ /* make frame size valid before trying S_FMT */
+ v4l2_negociate_framesize( p_obj, i_fd,
+ fmt->fmt.pix.pixelformat,
+ &fmt->fmt.pix.width,
+ &fmt->fmt.pix.height );
+ /* and 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;
+ b_error = true;
+ }
+ }
+ }
+ if( b_error )
+ {
+ 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;
+ }
+ }
+
+ /* If no user specified chroma, find best */
+ if( !fmt->fmt.pix.pixelformat )
+ {
+ unsigned int i;
+ for( i = 0; i < ARRAY_SIZE( p_chroma_fallbacks ); i++ )
+ {
+ fmt->fmt.pix.pixelformat = p_chroma_fallbacks[i];
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ if( IsPixelFormatSupported( p_codecs, ncodec,
+ fmt->fmt.pix.pixelformat ) )
+ {
+ v4l2_negociate_framesize( p_obj, i_fd,
+ fmt->fmt.pix.pixelformat,
+ &fmt->fmt.pix.width,
+ &fmt->fmt.pix.height );
+ if( v4l2_ioctl( i_fd, VIDIOC_TRY_FMT, fmt ) >= 0 )
+ break;
+ fmt->fmt.pix.field = V4L2_FIELD_ANY;
+ if( v4l2_ioctl( i_fd, VIDIOC_TRY_FMT, fmt ) >= 0 )
+ break;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ }
+ }
+
+ if( i == ARRAY_SIZE( p_chroma_fallbacks ) )
+ {
+ msg_Warn( p_obj, "Could not select any of the default chromas; attempting to open as MPEG encoder card (access)" );
+ return false;
+ }
+ }
+ return true;
+}
+
+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..1135619
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_common.h
@@ -0,0 +1,92 @@
+/*****************************************************************************
+ * 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_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..9818f0a
--- /dev/null
+++ b/modules/video_output/v4l2/v4l2_output.c
@@ -0,0 +1,603 @@
+/*****************************************************************************
+ * 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 "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. Default is I420.")
+#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, NULL )
+ 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, 0 )
+ 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, video_format_t *fmt )
+{
+ 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 )
+ {
+ vlc_fourcc_t i_fourcc;
+ int i_foo;
+ if ( app_profiles[i].i_chroma &&
+ get_fourcc_by_v4l2pixelformat( &i_fourcc,
+ &i_foo,
+ &i_foo,
+ &i_foo,
+ app_profiles[i].i_chroma ) )
+ {
+ fmt->i_chroma = i_fourcc;
+ }
+ else
+ {
+ return false;
+ }
+ if ( app_profiles[i].i_width )
+ {
+ fmt->i_width = fmt->i_visible_width = app_profiles[i].i_width;
+ fmt->i_height = fmt->i_visible_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;
+ }
+
+ if ( apply_profile( psz_profile, & target_fmt ) ) /* sets vd->fmt.i_chroma */
+ msg_Info( p_obj, "using profile %s", psz_profile );
+
+ vlc_fourcc_t i_requested_fourcc = 0;
+ 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 ) /* user defined size. overrides profiles */
+ {
+ target_fmt.i_width = target_fmt.i_visible_width = i_width;
+ target_fmt.i_height = target_fmt.i_visible_height = i_height;
+ }
+ v4l2fmt.fmt.pix.width = target_fmt.i_visible_width;
+ v4l2fmt.fmt.pix.height = target_fmt.i_visible_height;
+ v4l2fmt.fmt.pix.pixelformat = 0; /* autodetect || i_requested_fourcc */
+
+ if ( ! v4l2_negociate_chroma( p_obj, vd->sys->fd,
+ i_requested_fourcc,
+ &v4l2fmt,
+ codecs, ncodecs ) )
+ {
+ msg_Err( p_obj, "probing device for chromas failed, %m" );
+ goto lateerror;
+ }
+
+ if ( v4l2fmt.fmt.pix.pixelformat == 0 )
+ {
+ 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 );
+ }
+
+ /* 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 9173b1a..2c4572c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1144,6 +1144,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