[vlc-devel] [PATCH 2/2] v4l2: move more definitions to v4l2_common and factorize for modulators
Francois Cartegnie
fcvlcdev at free.fr
Mon Mar 12 22:24:39 CET 2012
---
modules/access/v4l2/v4l2_common.c | 413 ++++++++++++++++++++++++++++++++++++-
modules/access/v4l2/v4l2_common.h | 63 ++++++
modules/access/v4l2/video.c | 355 +++----------------------------
3 files changed, 509 insertions(+), 322 deletions(-)
diff --git a/modules/access/v4l2/v4l2_common.c b/modules/access/v4l2/v4l2_common.c
index 68d7ea6..eac281a 100644
--- a/modules/access/v4l2/v4l2_common.c
+++ b/modules/access/v4l2/v4l2_common.c
@@ -23,12 +23,12 @@
#endif
#include "v4l2_common.h"
-#include "v4l2.h"
#include <vlc_fourcc.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <assert.h>
static const struct
{
@@ -76,6 +76,37 @@ static const struct
{ 0, 0, 0, 0, 0 }
};
+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,
+};
+
+/**
+ * List of V4L2 chromas were confident enough to use as fallbacks if the
+ * user hasn't provided a --v4l2-chroma value.
+ *
+ * Try YUV chromas first, then RGB little endian and MJPEG as last resort.
+ */
+static const uint32_t 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, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG };
+
+
bool get_v4l2pixelformat_by_fourcc( unsigned int *found,
vlc_fourcc_t i_requested_fourcc )
{
@@ -108,3 +139,383 @@ bool get_fourcc_by_v4l2pixelformat( vlc_fourcc_t *i_fourcc,
return false;
}
+/* 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 Capture, (%c) Video Output, (%c) Audio,"
+ " (%c) Tuner, (%c) Modulator, (%c) Radio",
+ ( cap->capabilities & V4L2_CAP_VIDEO_CAPTURE ? 'X':' '),
+ ( cap->capabilities & V4L2_CAP_VIDEO_OUTPUT ? 'X':' '),
+ ( cap->capabilities & V4L2_CAP_AUDIO ? 'X':' '),
+ ( cap->capabilities & V4L2_CAP_TUNER ? 'X':' '),
+ ( cap->capabilities & V4L2_CAP_MODULATOR ? 'X':' '),
+ ( cap->capabilities & V4L2_CAP_RADIO ? '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 0x%"PRIx64": %m", std );
+ return false;
+ }
+ msg_Dbg( p_obj, "standard set to 0x%"PRIx64":", 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_type,
+ int i_fd, unsigned *index )
+{
+ int i_ioctl;
+ struct v4l2_input input;
+ struct v4l2_input output;
+
+ switch( i_type )
+ {
+ default:
+ case V4L2_CAP_VIDEO_CAPTURE:
+ input.index = 0;
+ while( v4l2_ioctl( i_fd, VIDIOC_ENUMINPUT, input ) >= 0 )
+ {
+ msg_Dbg( p_obj, "video input %u (%s) has type: %s %c",
+ input.index, input.name,
+ input.type == V4L2_INPUT_TYPE_TUNER
+ ? "Tuner adapter" : "External analog input",
+ input.index == *index ? '*' : ' ' );
+ input.index++;
+ }
+ i_ioctl = VIDIOC_S_INPUT;
+ break;
+
+ case V4L2_CAP_VIDEO_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++;
+ }
+ i_ioctl = VIDIOC_S_OUTPUT;
+ break;
+ }
+
+ /* Select input/output */
+ if( v4l2_ioctl( i_fd, i_ioctl, index ) < 0 )
+ {
+ msg_Err( p_obj, "cannot set %s %u: %m",
+ (i_type == V4L2_CAP_VIDEO_CAPTURE) ? "input" : "output",
+ *index );
+ return false;
+ }
+ msg_Dbg( p_obj, "%s set to %u",
+ (i_type == V4L2_CAP_VIDEO_CAPTURE) ? "input" : "output",
+ *index );
+
+ return true;
+}
+
+/* List and set tuner/modulator caps */
+bool v4l2_set_tuner_freq( vlc_object_t *p_obj, int i_type, int i_fd,
+ uint32_t idx, uint32_t freq, int32_t audmode )
+{
+ struct v4l2_tuner tuner;
+ struct v4l2_modulator modulator;
+ enum v4l2_tuner_type tuner_type = V4L2_TUNER_RADIO;
+
+ switch( i_type )
+ {
+ case V4L2_CAP_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++;
+ }
+ break;
+ default:
+ case V4L2_CAP_TUNER:
+ tuner.index = 0;
+ while( v4l2_ioctl( i_fd, VIDIOC_G_TUNER, &tuner ) >= 0 )
+ {
+ if( tuner.index == idx )
+ tuner_type = tuner.type;
+
+ const char *unit =
+ (tuner.capability & V4L2_TUNER_CAP_LOW) ? "Hz" : "kHz";
+ msg_Dbg( p_obj, "tuner %u (%s) has type: %s, "
+ "frequency range: %.1f %s -> %.1f %s", tuner.index,
+ tuner.name,
+ tuner.type == V4L2_TUNER_RADIO ? "Radio" : "Analog TV",
+ tuner.rangelow * 62.5, unit,
+ tuner.rangehigh * 62.5, unit );
+
+ struct v4l2_frequency frequency = { .tuner = tuner.index };
+ if( v4l2_ioctl( i_fd, VIDIOC_G_FREQUENCY, &frequency ) < 0 )
+ {
+ msg_Err( p_obj, "cannot get tuner frequency: %m" );
+ return false;
+ }
+ msg_Dbg( p_obj, "tuner %u (%s) frequency: %.1f %s", tuner.index,
+ tuner.name, frequency.frequency * 62.5, unit );
+ tuner.index++;
+ }
+ }
+
+ /* Tune the tuner/modulator */
+ if( freq != (uint32_t)-1 )
+ {
+ struct v4l2_frequency frequency = {
+ .tuner = idx,
+ .type = tuner_type, /* will be ignored by modulators */
+ .frequency = freq / 62.5,
+ };
+ memset( frequency.reserved, 0, 8 * sizeof(__u32) ); /* req'd by api */
+
+ if( v4l2_ioctl( i_fd, VIDIOC_S_FREQUENCY, &frequency ) < 0 )
+ {
+ msg_Err( p_obj, "cannot set %s frequency: %m",
+ (i_type == V4L2_CAP_MODULATOR) ? "modulator" : "tuner" );
+ return false;
+ }
+ msg_Dbg( p_obj, "%s frequency set",
+ (i_type == V4L2_CAP_MODULATOR) ? "modulator" : "tuner" );
+ }
+
+ /* Set the preferred audio (tuner) or audio (modulator) mode */
+ if( audmode >= 0 )
+ {
+ switch ( i_type )
+ {
+ case V4L2_CAP_MODULATOR:
+ modulator.index = idx;
+ modulator.txsubchans = audmode; /* V4L2_TUNER_SUB_* */
+ memset( modulator.reserved, 0, 4 * sizeof(__u32) );
+
+ if( v4l2_ioctl( i_fd, VIDIOC_S_MODULATOR, &modulator ) < 0 )
+ {
+ msg_Err( p_obj, "cannot set modulator audio mode: %m" );
+ return false;
+ }
+
+ break;
+
+ case V4L2_CAP_TUNER:
+ default:
+ tuner.index = idx;
+ tuner.audmode = audmode; /* V4L2_TUNER_MODE_* */
+
+ if( v4l2_ioctl( i_fd, VIDIOC_S_TUNER, &tuner ) < 0 )
+ {
+ msg_Err( p_obj, "cannot set tuner audio mode: %m" );
+ return false;
+ }
+
+ }
+ msg_Dbg( p_obj, "%s audio mode set",
+ (i_type == V4L2_CAP_MODULATOR) ? "modulator" : "tuner" );
+ }
+
+ return true;
+}
+
+bool v4l2_probe_device_chromas( vlc_object_t *p_obj, int i_fd,
+ uint32_t type, struct v4l2_fmtdesc **p_codecs,
+ uint32_t *ncodec )
+{
+ /* Probe for available chromas */
+ struct v4l2_fmtdesc *codecs, codec = {
+ .index = 0,
+ .type = ( type == V4L2_CAP_VIDEO_OUTPUT )
+ ? V4L2_BUF_TYPE_VIDEO_OUTPUT
+ : V4L2_BUF_TYPE_VIDEO_CAPTURE
+ };
+
+ 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 = type;
+
+ if( 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;
+}
+
+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 )
+{
+ /* 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 )
+ {
+ if( v4l2_ioctl( i_fd, VIDIOC_S_FMT, fmt ) < 0 )
+ {
+ fmt->fmt.pix.field = V4L2_FIELD_ANY;
+ if( v4l2_ioctl( i_fd, VIDIOC_S_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;
+ }
+ }
+
+ /* If no user specified chroma, find best */
+ /* This also decides if MPEG encoder card or not */
+ 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];
+ if( IsPixelFormatSupported( p_codecs, ncodec,
+ fmt->fmt.pix.pixelformat ) )
+ {
+ if( v4l2_ioctl( i_fd, VIDIOC_S_FMT, fmt ) >= 0 )
+ break;
+ fmt->fmt.pix.field = V4L2_FIELD_ANY;
+ if( v4l2_ioctl( i_fd, VIDIOC_S_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;
+}
diff --git a/modules/access/v4l2/v4l2_common.h b/modules/access/v4l2/v4l2_common.h
index 870c818..f8238b9 100644
--- a/modules/access/v4l2/v4l2_common.h
+++ b/modules/access/v4l2/v4l2_common.h
@@ -20,8 +20,51 @@
#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",
+};
+
/**
* Matchs a V4L2 fourcc with VLC's one.
* @return true if successfully matched.
@@ -37,4 +80,24 @@ bool get_fourcc_by_v4l2pixelformat( vlc_fourcc_t *i_fourcc,
bool get_v4l2pixelformat_by_fourcc( unsigned int *found,
vlc_fourcc_t i_requested_fourcc );
+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_type,
+ 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_type, 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,
+ uint32_t type, 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 );
#endif // V4L2_COMMON_H
diff --git a/modules/access/v4l2/video.c b/modules/access/v4l2/video.c
index f493664..b33ae39 100644
--- a/modules/access/v4l2/video.c
+++ b/modules/access/v4l2/video.c
@@ -37,9 +37,9 @@
# include "config.h"
#endif
-#include "v4l2.h"
-#include "v4l2_input.h"
#include "v4l2_common.h"
+#include "v4l2_input.h"
+
#include <vlc_plugin.h>
#include <vlc_fs.h>
#include <vlc_demux.h>
@@ -215,65 +215,6 @@ static const int tristate_vlc[] = { -1, 0, 1 };
static const char *const tristate_user[] = {
N_("Unspecified"), N_("Off"), N_("On") };
-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,
-};
-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",
-};
-
static const int i_tuner_audio_modes_list[] = {
-1, V4L2_TUNER_MODE_MONO, V4L2_TUNER_MODE_STEREO,
V4L2_TUNER_MODE_LANG1, V4L2_TUNER_MODE_LANG2,
@@ -459,17 +400,6 @@ vlc_module_end ()
static block_t* ProcessVideoFrame( vlc_object_t *p_demux, uint8_t *p_frame, size_t );
/**
- * List of V4L2 chromas were confident enough to use as fallbacks if the
- * user hasn't provided a --v4l2-chroma value.
- *
- * Try YUV chromas first, then RGB little endian and MJPEG as last resort.
- */
-static const uint32_t 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, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG };
-
-/**
* Parses a V4L2 MRL into VLC object variables.
*/
void ParseMRL( vlc_object_t *obj, const char *mrl )
@@ -718,20 +648,6 @@ static int InitUserP( vlc_object_t *p_demux, demux_sys_t *p_sys, int i_fd, unsig
return 0;
}
-/**
- * \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 int InitVideo( vlc_object_t *p_obj, int i_fd, demux_sys_t *p_sys,
bool b_demux );
@@ -782,101 +698,42 @@ static int InitVideo( vlc_object_t *p_obj, int i_fd, demux_sys_t *p_sys,
/* Get device capabilites */
struct v4l2_capability cap;
- if( v4l2_ioctl( i_fd, VIDIOC_QUERYCAP, &cap ) < 0 )
+ if ( ! v4l2_enumerate_capabilities( p_obj, i_fd, &cap ) )
{
- msg_Err( p_obj, "cannot get video capabilities: %m" );
return -1;
}
- 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 Capture, (%c) Audio, (%c) Tuner, (%c) Radio",
- ( cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ? 'X':' '),
- ( cap.capabilities & V4L2_CAP_AUDIO ? 'X':' '),
- ( cap.capabilities & V4L2_CAP_TUNER ? 'X':' '),
- ( cap.capabilities & V4L2_CAP_RADIO ? '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':' ' ) );
-
if( cap.capabilities & V4L2_CAP_STREAMING )
p_sys->io = IO_METHOD_MMAP;
- else if( cap.capabilities & V4L2_CAP_READWRITE )
- p_sys->io = IO_METHOD_READ;
+ else if( cap.capabilities & V4L2_CAP_READWRITE ) /* Q: do we really need this ? */
+ p_sys->io = IO_METHOD_READ; /* IO support is mandatory for v4l2 devices */
else
{
msg_Err( p_obj, "no supported I/O method" );
return -1;
}
- /* 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 */
+ /* Select input */
if( cap.capabilities & V4L2_CAP_VIDEO_CAPTURE )
{
- struct v4l2_input input;
+ /* 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"input" );
-
- input.index = 0;
- while( v4l2_ioctl( i_fd, VIDIOC_ENUMINPUT, &input ) >= 0 )
+ if ( ! v4l2_enumerate_and_set_video_io( p_obj,
+ V4L2_CAP_VIDEO_CAPTURE, i_fd, &index ) )
{
- msg_Dbg( p_obj, "video input %u (%s) has type: %s %c",
- input.index, input.name,
- input.type == V4L2_INPUT_TYPE_TUNER
- ? "Tuner adapter" : "External analog input",
- input.index == index ? '*' : ' ' );
- input.index++;
- }
-
- /* Select input */
- if( v4l2_ioctl( i_fd, VIDIOC_S_INPUT, &index ) < 0 )
- {
- msg_Err( p_obj, "cannot set input %u: %m", index );
return -1;
}
- msg_Dbg( p_obj, "input set to %u", index );
}
/* Select standard */
bool bottom_first;
const char *stdname = var_InheritString( p_obj, CFG_PREFIX"standard" );
- if( stdname != NULL )
+ if ( ! v4l2_select_standard( p_obj, i_fd, stdname, &bottom_first ) )
{
- 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 0x%"PRIx64": %m", std );
- return -1;
- }
- msg_Dbg( p_obj, "standard set to 0x%"PRIx64":", std );
- bottom_first = std == V4L2_STD_NTSC;
+ return -1;
}
- else
- bottom_first = false;
/* Set audio input */
if( cap.capabilities & V4L2_CAP_AUDIO )
@@ -911,126 +768,29 @@ static int InitVideo( vlc_object_t *p_obj, int i_fd, demux_sys_t *p_sys,
}
}
- /* List tuner caps */
+ /* List & set tuner caps */
if( cap.capabilities & V4L2_CAP_TUNER )
{
- struct v4l2_tuner tuner;
uint32_t idx = var_CreateGetInteger( p_obj, CFG_PREFIX"tuner" );
- enum v4l2_tuner_type type = V4L2_TUNER_RADIO;
-
- tuner.index = 0;
- while( v4l2_ioctl( i_fd, VIDIOC_G_TUNER, &tuner ) >= 0 )
- {
- if( tuner.index == idx )
- type = tuner.type;
-
- const char *unit =
- (tuner.capability & V4L2_TUNER_CAP_LOW) ? "Hz" : "kHz";
- msg_Dbg( p_obj, "tuner %u (%s) has type: %s, "
- "frequency range: %.1f %s -> %.1f %s", tuner.index,
- tuner.name,
- tuner.type == V4L2_TUNER_RADIO ? "Radio" : "Analog TV",
- tuner.rangelow * 62.5, unit,
- tuner.rangehigh * 62.5, unit );
-
- struct v4l2_frequency frequency = { .tuner = tuner.index };
- if( v4l2_ioctl( i_fd, VIDIOC_G_FREQUENCY, &frequency ) < 0 )
- {
- msg_Err( p_obj, "cannot get tuner frequency: %m" );
- return -1;
- }
- msg_Dbg( p_obj, "tuner %u (%s) frequency: %.1f %s", tuner.index,
- tuner.name, frequency.frequency * 62.5, unit );
- tuner.index++;
- }
-
- /* Tune the tuner */
- uint32_t freq = var_InheritInteger( p_obj,
- CFG_PREFIX"tuner-frequency" );
- if( freq != (uint32_t)-1 )
+ uint32_t freq = var_InheritInteger( p_obj, CFG_PREFIX"tuner-frequency" );
+ int32_t audmode = var_InheritInteger( p_obj, CFG_PREFIX"tuner-audio-mode" );
+ if ( ! v4l2_set_tuner_freq( p_obj, V4L2_CAP_TUNER, i_fd, idx, freq, audmode ) )
{
- struct v4l2_frequency frequency = {
- .tuner = idx,
- .type = type,
- .frequency = freq / 62.5,
- };
-
- if( v4l2_ioctl( i_fd, VIDIOC_S_FREQUENCY, &frequency ) < 0 )
- {
- msg_Err( p_obj, "cannot set tuner frequency: %m" );
- return -1;
- }
- msg_Dbg( p_obj, "tuner frequency set" );
- }
-
- /* Set the tuner audio mode */
- int32_t audmode = var_InheritInteger( p_obj,
- CFG_PREFIX"tuner-audio-mode" );
- if( audmode >= 0 )
- {
- struct v4l2_tuner tuner = {
- .index = idx,
- .audmode = audmode,
- };
-
- if( v4l2_ioctl( i_fd, VIDIOC_S_TUNER, &tuner ) < 0 )
- {
- msg_Err( p_obj, "cannot set tuner audio mode: %m" );
- return -1;
- }
- msg_Dbg( p_obj, "tuner audio mode set" );
+ return -1;
}
}
/* Probe for available chromas */
struct v4l2_fmtdesc *codecs = NULL;
- uint_fast32_t ncodec = 0;
+ uint32_t ncodec = 0;
if( cap.capabilities & V4L2_CAP_VIDEO_CAPTURE )
{
- struct v4l2_fmtdesc codec = {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- };
-
- while( v4l2_ioctl( i_fd, VIDIOC_ENUM_FMT, &codec ) >= 0 )
- codec.index = ++ncodec;
-
- codecs = malloc( ncodec * sizeof( *codecs ) );
- if( unlikely(codecs == NULL) )
- ncodec = 0;
-
- for( uint_fast32_t i = 0; i < ncodec; i++ )
+ if ( ! v4l2_probe_device_chromas( p_obj, i_fd,
+ V4L2_CAP_VIDEO_CAPTURE,
+ &codecs,
+ &ncodec ) )
{
- codecs[i].index = i;
- codecs[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if( v4l2_ioctl( i_fd, VIDIOC_ENUM_FMT, &codecs[i] ) < 0 )
- {
- msg_Err( p_obj, "cannot get codec description: %m" );
- goto error;
- }
-
- /* only print if vlc supports the format */
- char fourcc_v4l2[5];
- memset( fourcc_v4l2, 0, sizeof( fourcc_v4l2 ) );
- vlc_fourcc_to_char( codecs[i].pixelformat, fourcc_v4l2 );
-
- bool b_codec_supported = false;
- vlc_fourcc_t i_fourcc;
- int i_rmask, i_gmask, i_bmask;
- b_codec_supported = get_fourcc_by_v4l2pixelformat( &i_fourcc,
- &i_rmask, &i_gmask, &i_bmask, codecs[i].pixelformat );
- if( b_codec_supported )
- {
- char fourcc[5];
- memset( fourcc, 0, sizeof( fourcc ) );
- vlc_fourcc_to_char( i_fourcc, fourcc );
- msg_Dbg( p_obj, "device supports chroma %4.4s [%s, %s]",
- fourcc, codecs[i].description, fourcc_v4l2 );
- } else {
- msg_Dbg( p_obj, "device codec %4.4s (%s) not supported",
- fourcc_v4l2, codecs[i].description );
- }
+ goto error;
}
}
@@ -1099,68 +859,21 @@ static int InitVideo( vlc_object_t *p_obj, int i_fd, demux_sys_t *p_sys,
float f_fps;
if (b_demux)
{
- char *reqchroma = var_InheritString( p_obj, CFG_PREFIX"chroma" );
+ char *psz_reqchroma = var_InheritString( p_obj, CFG_PREFIX"chroma" );
+ vlc_fourcc_t i_requested_fourcc = 0;
- /* Test and set Chroma */
- fmt.fmt.pix.pixelformat = 0;
- if( reqchroma != NULL )
- {
- /* User specified chroma */
- const vlc_fourcc_t i_requested_fourcc =
- vlc_fourcc_GetCodecFromString( VIDEO_ES, reqchroma );
-
- get_v4l2pixelformat_by_fourcc( &fmt.fmt.pix.pixelformat, i_requested_fourcc );
-
- /* Try and set user chroma */
- bool b_error = !IsPixelFormatSupported( codecs, ncodec,
- fmt.fmt.pix.pixelformat );
- if( !b_error && fmt.fmt.pix.pixelformat )
- {
- if( v4l2_ioctl( i_fd, VIDIOC_S_FMT, &fmt ) < 0 )
- {
- fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if( v4l2_ioctl( i_fd, VIDIOC_S_FMT, &fmt ) < 0 )
- {
- fmt.fmt.pix.field = V4L2_FIELD_NONE;
- b_error = true;
- }
- }
- }
- if( b_error )
- {
- msg_Warn( p_obj, "requested chroma %s not supported. "
- " Trying default.", reqchroma );
- fmt.fmt.pix.pixelformat = 0;
- }
- free( reqchroma );
- }
+ if ( psz_reqchroma != NULL )
+ i_requested_fourcc =
+ vlc_fourcc_GetCodecFromString( VIDEO_ES, psz_reqchroma );
- /* If no user specified chroma, find best */
- /* This also decides if MPEG encoder card or not */
- if( !fmt.fmt.pix.pixelformat )
+ if ( ! v4l2_negociate_chroma( p_obj, i_fd, i_requested_fourcc, &fmt,
+ codecs, ncodec ) )
{
- unsigned int i;
- for( i = 0; i < ARRAY_SIZE( p_chroma_fallbacks ); i++ )
- {
- fmt.fmt.pix.pixelformat = p_chroma_fallbacks[i];
- if( IsPixelFormatSupported( codecs, ncodec,
- fmt.fmt.pix.pixelformat ) )
- {
- if( v4l2_ioctl( i_fd, VIDIOC_S_FMT, &fmt ) >= 0 )
- break;
- fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if( v4l2_ioctl( i_fd, VIDIOC_S_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)" );
- goto error;
- }
+ goto error;
}
+ free( psz_reqchroma );
+
if( width < 0 || height < 0 )
{
f_fps = var_InheritFloat( p_obj, CFG_PREFIX"fps" );
--
1.7.6
More information about the vlc-devel
mailing list