# 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 )
{
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;
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 )