]> 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 e0c9c683677e857860418348492dbce0880b98ff..df5add03ce7e156432c7447dd37518fc7486eab3 100644 (file)
 # include "config.h"
 #endif
 
-#include "v4l2.h"
-
+#include <assert.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <poll.h>
 
+#include <vlc_common.h>
 #include <vlc_access.h>
-#include <vlc_fs.h>
 
-static block_t *AccessRead( access_t * );
-static ssize_t AccessReadStream( access_t *, uint8_t *, size_t );
+#include "v4l2.h"
+
+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, uint32_t);
 
 int AccessOpen( vlc_object_t *obj )
 {
@@ -46,46 +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 (obj, fd, sys, false))
+    if (InitVideo (access, fd, caps))
     {
         v4l2_close (fd);
         goto error;
     }
 
-    sys->i_fd = fd;
-    if( sys->io == IO_METHOD_READ )
-        access->pf_read = AccessReadStream;
-    else
-        access->pf_block = AccessRead;
+    sys->controls = ControlsInit (VLC_OBJECT(access), fd);
     access->pf_seek = NULL;
     access->pf_control = AccessControl;
     return VLC_SUCCESS;
@@ -94,75 +93,169 @@ error:
     return VLC_EGENERIC;
 }
 
+int InitVideo (access_t *access, int fd, uint32_t caps)
+{
+    access_sys_t *sys = access->p_sys;
+
+    if (!(caps & V4L2_CAP_VIDEO_CAPTURE))
+    {
+        msg_Err (access, "not a video capture device");
+        return -1;
+    }
+
+    if (SetupInput (VLC_OBJECT(access), fd))
+        return -1;
+
+    /* 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)
+    {
+        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);
+
+    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->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+            else*/
+                sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+            break;
+        case V4L2_FIELD_INTERLACED_TB:
+            msg_Dbg (access, "Interlacing setting: interleaved top bottom" );
+            sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+            break;
+        case V4L2_FIELD_INTERLACED_BT:
+            msg_Dbg (access, "Interlacing setting: interleaved bottom top" );
+            sys->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+            break;
+        default:
+            break;
+    }
+
+    /* Init I/O method */
+    if (caps & V4L2_CAP_STREAMING)
+    {
+        sys->bufc = 4;
+        sys->bufv = StartMmap (VLC_OBJECT(access), fd, &sys->bufc);
+        if (sys->bufv == NULL)
+            return -1;
+        access->pf_block = MMapBlock;
+    }
+    else if (caps & V4L2_CAP_READWRITE)
+    {
+        sys->blocksize = fmt.fmt.pix.sizeimage;
+        sys->bufv = NULL;
+        access->pf_block = ReadBlock;
+    }
+    else
+    {
+        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;
 
-    /* Wait for data */
-    /* FIXME: kill timeout */
-    if( poll( &fd, 1, 500 ) <= 0 )
+static block_t *MMapBlock (access_t *access)
+{
+    access_sys_t *sys = access->p_sys;
+
+    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;
+    access_sys_t *sys = access->p_sys;
 
-    ufd.fd = sys->i_fd;
-    ufd.events = POLLIN;
-
-    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 )