]> git.sesse.net Git - vlc/blobdiff - modules/access/v4l2/access.c
macosx: add new common window class in order to deduplicate code
[vlc] / modules / access / v4l2 / access.c
index 2f7788dd8aef78e356aae592c46d7f90b3a5ef9e..df5add03ce7e156432c7447dd37518fc7486eab3 100644 (file)
 
 #include <assert.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <poll.h>
 
 #include <vlc_common.h>
 #include <vlc_access.h>
-#include <vlc_fs.h>
 
 #include "v4l2.h"
 
-static block_t *AccessRead( access_t * );
-static ssize_t AccessReadStream( access_t *, uint8_t *, size_t );
+struct access_sys_t
+{
+    int fd;
+    uint32_t block_flags;
+    union
+    {
+        uint32_t bufc;
+        uint32_t blocksize;
+    };
+    struct buffer_t *bufv;
+    vlc_v4l2_ctrl_t *controls;
+};
+
+static block_t *MMapBlock (access_t *);
+static block_t *ReadBlock (access_t *);
 static int AccessControl( access_t *, int, va_list );
-static int InitVideo(access_t *, int);
+static int InitVideo(access_t *, int, uint32_t);
 
 int AccessOpen( vlc_object_t *obj )
 {
@@ -49,42 +60,31 @@ int AccessOpen( vlc_object_t *obj )
 
     access_InitFields( access );
 
-    demux_sys_t *sys = calloc( 1, sizeof( demux_sys_t ));
+    access_sys_t *sys = calloc (1, sizeof (*sys));
     if( unlikely(sys == NULL) )
         return VLC_ENOMEM;
-    access->p_sys = (access_sys_t *)sys;
+    access->p_sys = sys;
 
     ParseMRL( obj, access->psz_location );
 
     char *path = var_InheritString (obj, CFG_PREFIX"dev");
     if (unlikely(path == NULL))
         goto error; /* probably OOM */
-    msg_Dbg (obj, "opening device '%s'", path);
 
-    int rawfd = vlc_open (path, O_RDWR);
-    if (rawfd == -1)
-    {
-        msg_Err (obj, "cannot open device '%s': %m", path);
-        free (path);
-        goto error;
-    }
+    uint32_t caps;
+    int fd = OpenDevice (obj, path, &caps);
     free (path);
-
-    int fd = v4l2_fd_open (rawfd, 0);
     if (fd == -1)
-    {
-        msg_Warn (obj, "cannot initialize user-space library: %m");
-        /* fallback to direct kernel mode anyway */
-        fd = rawfd;
-    }
+        goto error;
+    sys->fd = fd;
 
-    if (InitVideo (access, fd))
+    if (InitVideo (access, fd, caps))
     {
         v4l2_close (fd);
         goto error;
     }
 
-    sys->i_fd = fd;
+    sys->controls = ControlsInit (VLC_OBJECT(access), fd);
     access->pf_seek = NULL;
     access->pf_control = AccessControl;
     return VLC_SUCCESS;
@@ -93,35 +93,11 @@ error:
     return VLC_EGENERIC;
 }
 
-int InitVideo (access_t *access, int fd)
+int InitVideo (access_t *access, int fd, uint32_t caps)
 {
-    demux_sys_t *sys = (demux_sys_t *)access->p_sys;
-    enum v4l2_buf_type buf_type;
+    access_sys_t *sys = access->p_sys;
 
-    /* Get device capabilites */
-    struct v4l2_capability cap;
-    if (v4l2_ioctl (fd, VIDIOC_QUERYCAP, &cap) < 0)
-    {
-        msg_Err (access, "cannot get device capabilities: %m");
-        return -1;
-    }
-
-    msg_Dbg (access, "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 (access, "the device has the capabilities: 0x%08X",
-             cap.capabilities);
-    msg_Dbg (access, " (%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 (access, " (%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_VIDEO_CAPTURE))
+    if (!(caps & V4L2_CAP_VIDEO_CAPTURE))
     {
         msg_Err (access, "not a video capture device");
         return -1;
@@ -130,152 +106,156 @@ int InitVideo (access_t *access, int fd)
     if (SetupInput (VLC_OBJECT(access), fd))
         return -1;
 
-    sys->controls = ControlsInit (VLC_OBJECT(access), fd);
-
-    /* Try and find default resolution if not specified */
-    struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
-    if (v4l2_ioctl (fd, VIDIOC_G_FMT, &fmt) < 0)
+    /* NOTE: The V4L access_demux expects a VLC FOURCC as "chroma". It is used to set the
+     * es_format_t structure correctly. However, the V4L access (*here*) has no use for a
+     * VLC FOURCC and expects a V4L2 format directly instead. That is confusing :-( */
+    uint32_t pixfmt = 0;
+    char *fmtstr = var_InheritString (access, CFG_PREFIX"chroma");
+    if (fmtstr != NULL && strlen (fmtstr) <= 4)
     {
-        msg_Err (access, "cannot get default format: %m");
-        return -1;
+        memcpy (&pixfmt, fmtstr, strlen (fmtstr));
+        free (fmtstr);
+    }
+    else
+    /* Use the default^Wprevious format if none specified */
+    {
+        struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
+        if (v4l2_ioctl (fd, VIDIOC_G_FMT, &fmt) < 0)
+        {
+            msg_Err (access, "cannot get default format: %m");
+            return -1;
+        }
+        pixfmt = fmt.fmt.pix.pixelformat;
     }
+    msg_Dbg (access, "selected format %4.4s", (const char *)&pixfmt);
 
-    /* Print extra info */
-    msg_Dbg (access, "%d bytes maximum for complete image",
-             fmt.fmt.pix.sizeimage );
+    struct v4l2_format fmt;
+    struct v4l2_streamparm parm;
+    if (SetupFormat (access, fd, pixfmt, &fmt, &parm))
+        return -1;
+
+    msg_Dbg (access, "%"PRIu32" bytes for complete image", fmt.fmt.pix.sizeimage);
     /* Check interlacing */
     switch (fmt.fmt.pix.field)
     {
         case V4L2_FIELD_INTERLACED:
             msg_Dbg (access, "Interlacing setting: interleaved");
             /*if (NTSC)
-                sys->i_block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+                sys->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
             else*/
-                sys->i_block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+                sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
             break;
         case V4L2_FIELD_INTERLACED_TB:
             msg_Dbg (access, "Interlacing setting: interleaved top bottom" );
-            sys->i_block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+            sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
             break;
         case V4L2_FIELD_INTERLACED_BT:
             msg_Dbg (access, "Interlacing setting: interleaved bottom top" );
-            sys->i_block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+            sys->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
             break;
         default:
             break;
     }
 
     /* Init I/O method */
-    if (cap.capabilities & V4L2_CAP_STREAMING)
+    if (caps & V4L2_CAP_STREAMING)
     {
-        if (InitMmap (VLC_OBJECT(access), sys, fd))
-            return -1;
-        for (unsigned int i = 0; i < sys->i_nbuffers; i++)
-        {
-            struct v4l2_buffer buf = {
-                .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                .memory = V4L2_MEMORY_MMAP,
-                .index = i,
-            };
-
-            if (v4l2_ioctl (fd, VIDIOC_QBUF, &buf) < 0)
-            {
-                msg_Err (access, "cannot queue buffer: %m");
-                return -1;
-            }
-        }
-
-        buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-        if (v4l2_ioctl (fd, VIDIOC_STREAMON, &buf_type) < 0)
-        {
-            msg_Err (access, "cannot start streaming: %m" );
+        sys->bufc = 4;
+        sys->bufv = StartMmap (VLC_OBJECT(access), fd, &sys->bufc);
+        if (sys->bufv == NULL)
             return -1;
-        }
-
-        access->pf_block = AccessRead;
+        access->pf_block = MMapBlock;
     }
-    else if (cap.capabilities & V4L2_CAP_READWRITE)
+    else if (caps & V4L2_CAP_READWRITE)
     {
         sys->blocksize = fmt.fmt.pix.sizeimage;
-        access->pf_read = AccessReadStream;
+        sys->bufv = NULL;
+        access->pf_block = ReadBlock;
     }
     else
     {
-        msg_Err (access, "no supported I/O method");
+        msg_Err (access, "no supported capture method");
         return -1;
     }
+
     return 0;
 }
 
 void AccessClose( vlc_object_t *obj )
 {
     access_t *access = (access_t *)obj;
-    demux_sys_t *sys = (demux_sys_t *)access->p_sys;
+    access_sys_t *sys = access->p_sys;
 
+    if (sys->bufv != NULL)
+        StopMmap (sys->fd, sys->bufv, sys->bufc);
     ControlsDeinit( obj, sys->controls );
-    v4l2_close( sys->i_fd );
+    v4l2_close (sys->fd);
     free( sys );
 }
 
-static block_t *AccessRead( access_t *access )
+/* Wait for data */
+static int AccessPoll (access_t *access)
 {
-    demux_sys_t *sys = (demux_sys_t *)access->p_sys;
+    access_sys_t *sys = access->p_sys;
+    struct pollfd ufd;
+
+    ufd.fd = sys->fd;
+    ufd.events = POLLIN;
+
+    switch (poll (&ufd, 1, 500))
+    {
+        case -1:
+            if (errno == EINTR)
+        case 0:
+            /* FIXME: kill this case (arbitrary timeout) */
+                return -1;
+            msg_Err (access, "poll error: %m");
+            access->info.b_eof = true;
+            return -1;
+    }
+    return 0;
+}
+
 
-    struct pollfd fd;
-    fd.fd = sys->i_fd;
-    fd.events = POLLIN|POLLPRI;
-    fd.revents = 0;
+static block_t *MMapBlock (access_t *access)
+{
+    access_sys_t *sys = access->p_sys;
 
-    /* Wait for data */
-    /* FIXME: kill timeout */
-    if( poll( &fd, 1, 500 ) <= 0 )
+    if (AccessPoll (access))
         return NULL;
 
-    block_t *block = GrabVideo( VLC_OBJECT(access), sys );
+    block_t *block = GrabVideo (VLC_OBJECT(access), sys->fd, sys->bufv);
     if( block != NULL )
     {
         block->i_pts = block->i_dts = mdate();
-        block->i_flags |= sys->i_block_flags;
+        block->i_flags |= sys->block_flags;
     }
     return block;
 }
 
-static ssize_t AccessReadStream( access_t *access, uint8_t *buf, size_t len )
+static block_t *ReadBlock (access_t *access)
 {
-    demux_sys_t *sys = (demux_sys_t *)access->p_sys;
-    struct pollfd ufd;
-    int i_ret;
-
-    ufd.fd = sys->i_fd;
-    ufd.events = POLLIN;
+    access_sys_t *sys = access->p_sys;
 
-    if( access->info.b_eof )
-        return 0;
-
-    /* FIXME: kill timeout and vlc_object_alive() */
-    do
-    {
-        if( !vlc_object_alive(access) )
-            return 0;
+    if (AccessPoll (access))
+        return NULL;
 
-        ufd.revents = 0;
-    }
-    while( ( i_ret = poll( &ufd, 1, 500 ) ) == 0 );
+    block_t *block = block_Alloc (sys->blocksize);
+    if (unlikely(block == NULL))
+        return NULL;
 
-    if( i_ret < 0 )
+    ssize_t val = v4l2_read (sys->fd, block->p_buffer, block->i_buffer);
+    if (val < 0)
     {
-        if( errno != EINTR )
-            msg_Err( access, "poll error: %m" );
-        return -1;
-    }
-
-    i_ret = v4l2_read( sys->i_fd, buf, len );
-    if( i_ret == 0 )
+        block_Release (block);
+        msg_Err (access, "cannot read buffer: %m");
         access->info.b_eof = true;
-    else if( i_ret > 0 )
-        access->info.i_pos += i_ret;
+        return NULL;
+    }
 
-    return i_ret;
+    block->i_buffer = val;
+    access->info.i_pos += val;
+    return block;
 }
 
 static int AccessControl( access_t *access, int query, va_list args )