[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