]> git.sesse.net Git - vlc/blobdiff - modules/access/v4l2/demux.c
BluRay instead of Blu-Ray. Re-using short messages for long ones
[vlc] / modules / access / v4l2 / demux.c
index 7a43ba97af1a883da2e9aede03735a959f5f7d7d..9d9b2ffcca4e87887ca892f8beb875b8f963071a 100644 (file)
@@ -1,21 +1,21 @@
 /*****************************************************************************
  * demux.c : V4L2 raw video demux module for vlc
  *****************************************************************************
- * Copyright (C) 2002-2011 the VideoLAN team
+ * Copyright (C) 2002-2011 VLC authors and VideoLAN
  *
  * Authors: Benjamin Pracht <bigben at videolan dot org>
  *          Richard Hosking <richard at hovis dot net>
  *          Antoine Cellerier <dionoea at videolan d.t org>
  *          Dennis Lou <dlou99 at yahoo dot com>
  *
- * 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
+ * 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
+ * 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
 #include <math.h>
 #include <errno.h>
 #include <assert.h>
-#include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#ifndef MAP_ANONYMOUS
+# define MAP_ANONYMOUS MAP_ANON
+#endif
 #include <poll.h>
 
 #include <vlc_common.h>
 #include <vlc_demux.h>
-#include <vlc_fs.h>
 
 #include "v4l2.h"
 
@@ -56,54 +57,52 @@ struct demux_sys_t
 
     es_out_id_t *es;
     vlc_v4l2_ctrl_t *controls;
+    mtime_t start;
+
+#ifdef ZVBI_COMPILED
+    vlc_v4l2_vbi_t *vbi;
+#endif
 };
 
-static void *StreamThread (void *);
+static void *UserPtrThread (void *);
+static void *MmapThread (void *);
 static void *ReadThread (void *);
 static int DemuxControl( demux_t *, int, va_list );
-static int InitVideo (demux_t *, int);
+static int InitVideo (demux_t *, int fd, uint32_t caps);
 
 int DemuxOpen( vlc_object_t *obj )
 {
     demux_t *demux = (demux_t *)obj;
 
-    demux_sys_t *sys = calloc( 1, sizeof( demux_sys_t ) );
-    if( unlikely(sys == NULL) )
+    demux_sys_t *sys = malloc (sizeof (*sys));
+    if (unlikely(sys == NULL))
         return VLC_ENOMEM;
     demux->p_sys = sys;
+#ifdef ZVBI_COMPILED
+    sys->vbi = NULL;
+#endif
 
     ParseMRL( obj, demux->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 (demux, fd))
+    if (InitVideo (demux, fd, caps))
     {
         v4l2_close (fd);
         goto error;
     }
 
-    sys->fd = fd;
     sys->controls = ControlsInit (VLC_OBJECT(demux), fd);
+    sys->start = mdate ();
     demux->pf_demux = NULL;
     demux->pf_control = DemuxControl;
     demux->info.i_update = 0;
@@ -198,7 +197,6 @@ static const vlc_v4l2_fmt_t v4l2_fmts[] =
 
     /* Compressed data types */
     { V4L2_PIX_FMT_JPEG,   VLC_CODEC_MJPG, 0, 0, 0 },
-#ifdef V4L2_PIX_FMT_H264
     { V4L2_PIX_FMT_H264,   VLC_CODEC_H264, 0, 0, 0 },
     /* FIXME: fill p_extra for avc1... */
 //  { V4L2_PIX_FMT_H264_NO_SC, VLC_FOURCC('a','v','c','1'), 0, 0, 0 }
@@ -209,7 +207,6 @@ static const vlc_v4l2_fmt_t v4l2_fmts[] =
     { V4L2_PIX_FMT_MPEG1,  VLC_CODEC_MPGV, 0, 0, 0 },
     { V4L2_PIX_FMT_VC1_ANNEX_G, VLC_CODEC_VC1, 0, 0, 0 },
     { V4L2_PIX_FMT_VC1_ANNEX_L, VLC_CODEC_VC1, 0, 0, 0 },
-#endif
     //V4L2_PIX_FMT_MPEG -> use access
 
     /* Reserved formats */
@@ -271,44 +268,18 @@ static void GetAR (int fd, unsigned *restrict num, unsigned *restrict den)
     *den = cropcap.pixelaspect.denominator;
 }
 
-static int InitVideo (demux_t *demux, int fd)
+static int InitVideo (demux_t *demux, int fd, uint32_t caps)
 {
     demux_sys_t *sys = demux->p_sys;
+    v4l2_std_id std;
 
-    /* Get device capabilites */
-    struct v4l2_capability cap;
-    if (v4l2_ioctl (fd, VIDIOC_QUERYCAP, &cap) < 0)
-    {
-        msg_Err (demux, "cannot get device capabilities: %m");
-        return -1;
-    }
-
-    msg_Dbg (demux, "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);
-
-    uint32_t caps;
-#ifdef V4L2_CAP_DEVICE_CAPS
-    if (cap.capabilities & V4L2_CAP_DEVICE_CAPS)
-    {
-        msg_Dbg (demux, " with capabilities 0x%08"PRIX32" "
-                 "(overall 0x%08"PRIX32")", cap.device_caps, cap.capabilities);
-        caps = cap.device_caps;
-    }
-    else
-#endif
-    {
-        msg_Dbg (demux, " with unknown capabilities  "
-                 "(overall 0x%08"PRIX32")", cap.capabilities);
-        caps = cap.capabilities;
-    }
     if (!(caps & V4L2_CAP_VIDEO_CAPTURE))
     {
         msg_Err (demux, "not a video capture device");
         return -1;
     }
 
-    if (SetupInput (VLC_OBJECT(demux), fd))
+    if (SetupInput (VLC_OBJECT(demux), fd, &std))
         return -1;
 
     /* Picture format negotiation */
@@ -371,6 +342,7 @@ static int InitVideo (demux_t *demux, int fd)
     msg_Dbg (demux, "%d bytes maximum for complete image",
              fmt.fmt.pix.sizeimage);
     /* Check interlacing */
+    sys->block_flags = 0;
     switch (fmt.fmt.pix.field)
     {
         case V4L2_FIELD_NONE:
@@ -438,26 +410,63 @@ static int InitVideo (demux_t *demux, int fd)
     void *(*entry) (void *);
     if (caps & V4L2_CAP_STREAMING)
     {
-        sys->bufc = 4;
-        sys->bufv = StartMmap (VLC_OBJECT(demux), fd, &sys->bufc);
-        if (sys->bufv == NULL)
-            return -1;
-        entry = StreamThread;
+        if (0 /* BROKEN */ && StartUserPtr (VLC_OBJECT(demux), fd) == 0)
+        {
+            /* In principles, mmap() will pad the length to a multiple of the
+             * page size, so there is no need to care. Nevertheless with the
+             * page size, block->i_size can be set optimally. */
+            const long pagemask = sysconf (_SC_PAGE_SIZE) - 1;
+
+            sys->blocksize = (fmt.fmt.pix.sizeimage + pagemask) & ~pagemask;
+            sys->bufv = NULL;
+            entry = UserPtrThread;
+            msg_Dbg (demux, "streaming with %"PRIu32"-bytes user buffers",
+                     sys->blocksize);
+        }
+        else /* fall back to memory map */
+        {
+            sys->bufc = 4;
+            sys->bufv = StartMmap (VLC_OBJECT(demux), fd, &sys->bufc);
+            if (sys->bufv == NULL)
+                return -1;
+            entry = MmapThread;
+            msg_Dbg (demux, "streaming with %"PRIu32" memory-mapped buffers",
+                     sys->bufc);
+        }
     }
     else if (caps & V4L2_CAP_READWRITE)
     {
-        sys->bufv = NULL;
         sys->blocksize = fmt.fmt.pix.sizeimage;
+        sys->bufv = NULL;
         entry = ReadThread;
+        msg_Dbg (demux, "reading %"PRIu32" bytes at a time", sys->blocksize);
     }
     else
     {
-        msg_Err (demux, "no supported I/O method");
+        msg_Err (demux, "no supported capture method");
         return -1;
     }
 
+#ifdef ZVBI_COMPILED
+    if (std & V4L2_STD_NTSC_M)
+    {
+        char *vbi_path = var_InheritString (demux, CFG_PREFIX"vbidev");
+        if (vbi_path != NULL)
+            sys->vbi = OpenVBI (demux, vbi_path);
+        free(vbi_path);
+    }
+#endif
+
     if (vlc_clone (&sys->thread, entry, demux, VLC_THREAD_PRIORITY_INPUT))
+    {
+#ifdef ZVBI_COMPILED
+        if (sys->vbi != NULL)
+            CloseVBI (sys->vbi);
+#endif
+        if (sys->bufv != NULL)
+            StopMmap (sys->fd, sys->bufv, sys->bufc);
         return -1;
+    }
     return 0;
 }
 
@@ -472,39 +481,146 @@ void DemuxClose( vlc_object_t *obj )
         StopMmap (sys->fd, sys->bufv, sys->bufc);
     ControlsDeinit( obj, sys->controls );
     v4l2_close (sys->fd);
+
+#ifdef ZVBI_COMPILED
+    if (sys->vbi != NULL)
+        CloseVBI (sys->vbi);
+#endif
+
     free( sys );
 }
 
-static void *StreamThread (void *data)
+/** Allocates and queue a user buffer using mmap(). */
+static block_t *UserPtrQueue (vlc_object_t *obj, int fd, size_t length)
+{
+    void *ptr = mmap (NULL, length, PROT_READ | PROT_WRITE,
+                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    if (ptr == MAP_FAILED)
+    {
+        msg_Err (obj, "cannot allocate %zu-bytes buffer: %m", length);
+        return NULL;
+    }
+
+    block_t *block = block_mmap_Alloc (ptr, length);
+    if (unlikely(block == NULL))
+    {
+        munmap (ptr, length);
+        return NULL;
+    }
+
+    struct v4l2_buffer buf = {
+        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+        .memory = V4L2_MEMORY_USERPTR,
+        .m = {
+            .userptr = (uintptr_t)ptr,
+        },
+        .length = length,
+    };
+
+    if (v4l2_ioctl (fd, VIDIOC_QBUF, &buf) < 0)
+    {
+        msg_Err (obj, "cannot queue buffer: %m");
+        block_Release (block);
+        return NULL;
+    }
+    return block;
+}
+
+static void *UserPtrThread (void *data)
+{
+    demux_t *demux = data;
+    demux_sys_t *sys = demux->p_sys;
+    int fd = sys->fd;
+    struct pollfd ufd[2];
+    nfds_t numfds = 1;
+
+    ufd[0].fd = fd;
+    ufd[0].events = POLLIN;
+
+    int canc = vlc_savecancel ();
+    for (;;)
+    {
+        struct v4l2_buffer buf = {
+            .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+            .memory = V4L2_MEMORY_USERPTR,
+        };
+        block_t *block = UserPtrQueue (VLC_OBJECT(demux), fd, sys->blocksize);
+        if (block == NULL)
+            break;
+
+        /* Wait for data */
+        vlc_restorecancel (canc);
+        block_cleanup_push (block);
+        while (poll (ufd, numfds, -1) == -1)
+           if (errno != EINTR)
+               msg_Err (demux, "poll error: %m");
+        vlc_cleanup_pop ();
+        canc = vlc_savecancel ();
+
+        if (v4l2_ioctl (fd, VIDIOC_DQBUF, &buf) < 0)
+        {
+            msg_Err (demux, "cannot dequeue buffer: %m");
+            block_Release (block);
+            continue;
+        }
+
+        assert (block->p_buffer == (void *)buf.m.userptr);
+        block->i_buffer = buf.length;
+        block->i_pts = block->i_dts = GetBufferPTS (&buf);
+        block->i_flags |= sys->block_flags;
+        es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
+        es_out_Send (demux->out, sys->es, block);
+    }
+    vlc_restorecancel (canc); /* <- hmm, this is purely cosmetic */
+    return NULL;
+}
+
+static void *MmapThread (void *data)
 {
     demux_t *demux = data;
     demux_sys_t *sys = demux->p_sys;
     int fd = sys->fd;
-    struct pollfd ufd[1];
+    struct pollfd ufd[2];
+    nfds_t numfds = 1;
 
     ufd[0].fd = fd;
-    ufd[0].events = POLLIN | POLLPRI;
+    ufd[0].events = POLLIN;
+
+#ifdef ZVBI_COMPILED
+    if (sys->vbi != NULL)
+    {
+        ufd[1].fd = GetFdVBI (sys->vbi);
+        ufd[1].events = POLLIN;
+        numfds++;
+    }
+#endif
 
     for (;;)
     {
         /* Wait for data */
-        if (poll (ufd, 1, -1) == -1)
+        if (poll (ufd, numfds, -1) == -1)
         {
            if (errno != EINTR)
                msg_Err (demux, "poll error: %m");
            continue;
         }
 
-        int canc = vlc_savecancel ();
-        block_t *block = GrabVideo (VLC_OBJECT(demux), fd, sys->bufv);
-        if (block != NULL)
+        if( ufd[0].revents )
         {
-            block->i_pts = block->i_dts = mdate ();
-            block->i_flags |= sys->block_flags;
-            es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
-            es_out_Send (demux->out, sys->es, block);
+            int canc = vlc_savecancel ();
+            block_t *block = GrabVideo (VLC_OBJECT(demux), fd, sys->bufv);
+            if (block != NULL)
+            {
+                block->i_flags |= sys->block_flags;
+                es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
+                es_out_Send (demux->out, sys->es, block);
+            }
+            vlc_restorecancel (canc);
         }
-        vlc_restorecancel (canc);
+#ifdef ZVBI_COMPILED
+        if (sys->vbi != NULL && ufd[1].revents)
+            GrabVBI (demux, sys->vbi);
+#endif
     }
 
     assert (0);
@@ -515,48 +631,67 @@ static void *ReadThread (void *data)
     demux_t *demux = data;
     demux_sys_t *sys = demux->p_sys;
     int fd = sys->fd;
-    struct pollfd ufd[1];
+    struct pollfd ufd[2];
+    nfds_t numfds = 1;
 
     ufd[0].fd = fd;
-    ufd[0].events = POLLIN | POLLPRI;
+    ufd[0].events = POLLIN;
+
+#ifdef ZVBI_COMPILED
+    if (sys->vbi != NULL)
+    {
+        ufd[1].fd = GetFdVBI (sys->vbi);
+        ufd[1].events = POLLIN;
+        numfds++;
+    }
+#endif
 
     for (;;)
     {
         /* Wait for data */
-        if (poll (ufd, 1, -1) == -1)
+        if (poll (ufd, numfds, -1) == -1)
         {
            if (errno != EINTR)
                msg_Err (demux, "poll error: %m");
            continue;
         }
 
-        block_t *block = block_Alloc (sys->blocksize);
-        if (unlikely(block == NULL))
+        if( ufd[0].revents )
         {
-            msg_Err (demux, "read error: %m");
-            v4l2_read (fd, NULL, 0); /* discard frame */
-            continue;
-        }
-        block->i_pts = block->i_dts = mdate ();
-        block->i_flags |= sys->block_flags;
+            block_t *block = block_Alloc (sys->blocksize);
+            if (unlikely(block == NULL))
+            {
+                msg_Err (demux, "read error: %m");
+                v4l2_read (fd, NULL, 0); /* discard frame */
+                continue;
+            }
+            block->i_pts = block->i_dts = mdate ();
+            block->i_flags |= sys->block_flags;
 
-        int canc = vlc_savecancel ();
-        ssize_t val = v4l2_read (fd, block->p_buffer, block->i_buffer);
-        if (val != -1)
-        {
-            block->i_buffer = val;
-            es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
-            es_out_Send (demux->out, sys->es, block);
+            int canc = vlc_savecancel ();
+            ssize_t val = v4l2_read (fd, block->p_buffer, block->i_buffer);
+            if (val != -1)
+            {
+                block->i_buffer = val;
+                es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
+                es_out_Send (demux->out, sys->es, block);
+            }
+            else
+                block_Release (block);
+            vlc_restorecancel (canc);
         }
-        else
-            block_Release (block);
-        vlc_restorecancel (canc);
+#ifdef ZVBI_COMPILED
+        if (sys->vbi != NULL && ufd[1].revents)
+            GrabVBI (demux, sys->vbi);
+#endif
     }
     assert (0);
 }
 
 static int DemuxControl( demux_t *demux, int query, va_list args )
 {
+    demux_sys_t *sys = demux->p_sys;
+
     switch( query )
     {
         /* Special for access_demux */
@@ -572,7 +707,7 @@ static int DemuxControl( demux_t *demux, int query, va_list args )
             return VLC_SUCCESS;
 
         case DEMUX_GET_TIME:
-            *va_arg( args, int64_t * ) = mdate();
+            *va_arg (args, int64_t *) = mdate() - sys->start;
             return VLC_SUCCESS;
 
         /* TODO implement others */