/*****************************************************************************
* 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"
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;
/* 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 }
{ 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 */
*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 */
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:
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;
}
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);
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 */
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 */