/*****************************************************************************
* video.c : Video4Linux2 input module for vlc
*****************************************************************************
- * Copyright (C) 2002-2009 the VideoLAN team
+ * Copyright (C) 2002-2009 VLC authors and VideoLAN
* Copyright (C) 2011-2012 RĂ©mi Denis-Courmont
*
* Authors: Benjamin Pracht <bigben at videolan dot org>
* 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 "config.h"
#endif
+#include <assert.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
-#include <poll.h>
#include <vlc_common.h>
#include <vlc_block.h>
#include "v4l2.h"
static int SetupStandard (vlc_object_t *obj, int fd,
- const struct v4l2_input *restrict input)
+ const struct v4l2_input *restrict input,
+ v4l2_std_id *restrict std)
{
-#ifdef V4L2_IN_CAP_STD
if (!(input->capabilities & V4L2_IN_CAP_STD))
{
msg_Dbg (obj, "no video standard selection");
+ *std = V4L2_STD_UNKNOWN;
return 0;
}
-#else
- (void) input;
- msg_Dbg (obj, "video standard selection unknown");
-#endif
- v4l2_std_id std = var_InheritStandard (obj, CFG_PREFIX"standard");
- if (std == V4L2_STD_UNKNOWN)
+
+ *std = var_InheritStandard (obj, CFG_PREFIX"standard");
+ if (*std == V4L2_STD_UNKNOWN)
{
msg_Warn (obj, "video standard not set");
+
+ /* Grab the currently selected standard */
+ if (v4l2_ioctl (fd, VIDIOC_G_STD, std) < 0)
+ msg_Err (obj, "cannot get video standard");
return 0;
}
- if (v4l2_ioctl (fd, VIDIOC_S_STD, &std) < 0)
+ if (v4l2_ioctl (fd, VIDIOC_S_STD, std) < 0)
{
- msg_Err (obj, "cannot set video standard 0x%"PRIx64": %m", std);
+ msg_Err (obj, "cannot set video standard 0x%"PRIx64": %s",
+ (uint64_t)*std, vlc_strerror_c(errno));
return -1;
}
- msg_Dbg (obj, "video standard set to 0x%"PRIx64":", std);
+ msg_Dbg (obj, "video standard set to 0x%"PRIx64":", (uint64_t)*std);
return 0;
}
if (v4l2_ioctl (fd, VIDIOC_ENUMAUDIO, &enumaudio) < 0)
{
- msg_Err (obj, "cannot get audio input %"PRIu32" properties: %m", idx);
+ msg_Err (obj, "cannot get audio input %"PRIu32" properties: %s", idx,
+ vlc_strerror_c(errno));
return -1;
}
if (v4l2_ioctl (fd, VIDIOC_S_AUDIO, &audio) < 0)
{
- msg_Err (obj, "cannot select audio input %"PRIu32": %m", idx);
+ msg_Err (obj, "cannot select audio input %"PRIu32": %s", idx,
+ vlc_strerror_c(errno));
return -1;
}
msg_Dbg (obj, "selected audio input %"PRIu32, idx);
if (v4l2_ioctl (fd, VIDIOC_G_TUNER, &tuner) < 0)
{
- msg_Err (obj, "cannot get tuner %"PRIu32" properties: %m", idx);
+ msg_Err (obj, "cannot get tuner %"PRIu32" properties: %s", idx,
+ vlc_strerror_c(errno));
return -1;
}
if (v4l2_ioctl (fd, VIDIOC_S_TUNER, &tuner) < 0)
{
- msg_Err (obj, "cannot set tuner %"PRIu32" audio mode: %m", idx);
+ msg_Err (obj, "cannot set tuner %"PRIu32" audio mode: %s", idx,
+ vlc_strerror_c(errno));
return -1;
}
msg_Dbg (obj, "tuner %"PRIu32" audio mode %u set", idx, tuner.audmode);
struct v4l2_frequency frequency = {
.tuner = idx,
.type = tuner.type,
- .frequency = freq * 125 / 2
+ .frequency = freq * 2 / 125,
};
if (v4l2_ioctl (fd, VIDIOC_S_FREQUENCY, &frequency) < 0)
{
msg_Err (obj, "cannot tune tuner %"PRIu32
- " to frequency %u %sHz: %m", idx, freq, mult);
+ " to frequency %u %sHz: %s", idx, freq, mult,
+ vlc_strerror_c(errno));
return -1;
}
msg_Dbg (obj, "tuner %"PRIu32" tuned to frequency %"PRIu32" %sHz",
* In practice, it does not. */
if (v4l2_ioctl (fd, VIDIOC_CROPCAP, &cropcap) < 0)
{
- msg_Dbg (obj, "cannot get cropping properties: %m");
+ msg_Dbg (obj, "cannot get cropping properties: %s",
+ vlc_strerror_c(errno));
return 0;
}
if (v4l2_ioctl (fd, VIDIOC_S_CROP, &crop) < 0)
{
- msg_Warn (obj, "cannot reset cropping limits: %m");
+ msg_Warn (obj, "cannot reset cropping limits: %s",
+ vlc_strerror_c(errno));
return -1;
}
return 0;
}
-int SetupInput (vlc_object_t *obj, int fd)
+int SetupInput (vlc_object_t *obj, int fd, v4l2_std_id *std)
{
struct v4l2_input input;
input.index = var_InheritInteger (obj, CFG_PREFIX"input");
if (v4l2_ioctl (fd, VIDIOC_ENUMINPUT, &input) < 0)
{
- msg_Err (obj, "invalid video input %"PRIu32": %m", input.index);
+ msg_Err (obj, "invalid video input %"PRIu32": %s", input.index,
+ vlc_strerror_c(errno));
return -1;
}
/* Select input */
if (v4l2_ioctl (fd, VIDIOC_S_INPUT, &input.index) < 0)
{
- msg_Err (obj, "cannot select input %"PRIu32": %m", input.index);
+ msg_Err (obj, "cannot select input %"PRIu32": %s", input.index,
+ vlc_strerror_c(errno));
return -1;
}
msg_Dbg (obj, "selected input %"PRIu32, input.index);
- SetupStandard (obj, fd, &input);
+ SetupStandard (obj, fd, &input, std);
switch (input.type)
{
}
static const struct v4l2_fract infinity = { 1, 0 };
+static const struct v4l2_fract zero = { 0, 1 };
/**
- * Finds the highest frame rate possible of a certain V4L2 format.
+ * Finds the highest frame rate up to a specific limit possible with a certain
+ * V4L2 format.
* @param fmt V4L2 capture format [IN]
+ * @param min_it minimum frame internal [IN]
* @param it V4L2 frame interval [OUT]
* @return 0 on success, -1 on error.
*/
static int FindMaxRate (vlc_object_t *obj, int fd,
const struct v4l2_format *restrict fmt,
+ const struct v4l2_fract *restrict min_it,
struct v4l2_fract *restrict it)
{
struct v4l2_frmivalenum fie = {
if (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMEINTERVALS, &fie) < 0)
{
- msg_Dbg (obj, " unknown frame intervals: %m");
+ msg_Dbg (obj, " unknown frame intervals: %s", vlc_strerror_c(errno));
/* Frame intervals cannot be enumerated. Set the format and then
* get the streaming parameters to figure out the default frame
* interval. This is not necessarily the maximum though. */
*it = infinity;
do
{
- if (fcmp (&fie.discrete, it) < 0)
+ if (fcmp (&fie.discrete, min_it) >= 0
+ && fcmp (&fie.discrete, it) < 0)
*it = fie.discrete;
fie.index++;
}
case V4L2_FRMIVAL_TYPE_STEPWISE:
case V4L2_FRMIVAL_TYPE_CONTINUOUS:
msg_Dbg (obj, " frame intervals from %"PRIu32"/%"PRIu32
- "to %"PRIu32"/%"PRIu32" supported",
+ " to %"PRIu32"/%"PRIu32" supported",
fie.stepwise.min.numerator, fie.stepwise.min.denominator,
fie.stepwise.max.numerator, fie.stepwise.max.denominator);
if (fie.type == V4L2_FRMIVAL_TYPE_STEPWISE)
msg_Dbg (obj, " with %"PRIu32"/%"PRIu32" step",
fie.stepwise.step.numerator,
fie.stepwise.step.denominator);
- *it = fie.stepwise.min;
+
+ if (fcmp (&fie.stepwise.max, min_it) < 0)
+ {
+ *it = infinity;
+ return -1;
+ }
+
+ if (fcmp (&fie.stepwise.min, min_it) >= 0)
+ {
+ *it = fie.stepwise.min;
+ break;
+ }
+
+ if (fie.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
+ {
+ *it = *min_it;
+ break;
+ }
+
+ it->numerator *= fie.stepwise.step.denominator;
+ it->denominator *= fie.stepwise.step.denominator;
+ while (fcmp (it, min_it) < 0)
+ it->numerator += fie.stepwise.step.numerator;
break;
}
return 0;
if (v4l2_ioctl (fd, VIDIOC_G_FMT, fmt) < 0)
{
- msg_Err (obj, "cannot get default format: %m");
+ msg_Err (obj, "cannot get default format: %s", vlc_strerror_c(errno));
return -1;
}
fmt->fmt.pix.pixelformat = fourcc;
struct v4l2_frmsizeenum fse = {
.pixel_format = fourcc,
};
- struct v4l2_fract best_it = infinity;
+ struct v4l2_fract best_it = infinity, min_it;
uint64_t best_area = 0;
+ if (var_InheritURational(obj, &min_it.denominator, &min_it.numerator,
+ CFG_PREFIX"fps") == VLC_SUCCESS)
+ msg_Dbg (obj, " requested frame internal: %"PRIu32"/%"PRIu32,
+ min_it.numerator, min_it.denominator);
+ else
+ min_it = zero;
+
uint32_t width = var_InheritInteger (obj, CFG_PREFIX"width");
uint32_t height = var_InheritInteger (obj, CFG_PREFIX"height");
if (width > 0 && height > 0)
fmt->fmt.pix.height = height;
msg_Dbg (obj, " requested frame size: %"PRIu32"x%"PRIu32,
width, height);
- FindMaxRate (obj, fd, fmt, &best_it);
+ FindMaxRate (obj, fd, fmt, &min_it, &best_it);
}
else
if (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &fse) < 0)
{
/* Fallback to current format, try to maximize frame rate */
- msg_Dbg (obj, " unknown frame sizes: %m");
+ msg_Dbg (obj, " unknown frame sizes: %s", vlc_strerror_c(errno));
msg_Dbg (obj, " current frame size: %"PRIu32"x%"PRIu32,
fmt->fmt.pix.width, fmt->fmt.pix.height);
- FindMaxRate (obj, fd, fmt, &best_it);
+ FindMaxRate (obj, fd, fmt, &min_it, &best_it);
}
else
switch (fse.type)
msg_Dbg (obj, " frame size %"PRIu32"x%"PRIu32,
fse.discrete.width, fse.discrete.height);
- FindMaxRate (obj, fd, fmt, &cur_it);
+ FindMaxRate (obj, fd, fmt, &min_it, &cur_it);
int64_t c = fcmp (&cur_it, &best_it);
uint64_t area = fse.discrete.width * fse.discrete.height;
{
struct v4l2_fract cur_it;
- FindMaxRate (obj, fd, fmt, &cur_it);
+ FindMaxRate (obj, fd, fmt, &min_it, &cur_it);
int64_t c = fcmp (&cur_it, &best_it);
uint64_t area = width * height;
/* Set the final format */
if (v4l2_ioctl (fd, VIDIOC_S_FMT, fmt) < 0)
{
- msg_Err (obj, "cannot set format: %m");
+ msg_Err (obj, "cannot set format: %s", vlc_strerror_c(errno));
return -1;
}
/* Now that the final format is set, fetch and override parameters */
if (v4l2_ioctl (fd, VIDIOC_G_PARM, parm) < 0)
{
- msg_Err (obj, "cannot get streaming parameters: %m");
+ msg_Err (obj, "cannot get streaming parameters: %s",
+ vlc_strerror_c(errno));
memset (parm, 0, sizeof (*parm));
parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
if (best_it.denominator != 0)
parm->parm.capture.timeperframe = best_it;
if (v4l2_ioctl (fd, VIDIOC_S_PARM, parm) < 0)
- msg_Warn (obj, "cannot set streaming parameters: %m");
+ msg_Warn (obj, "cannot set streaming parameters: %s",
+ vlc_strerror_c(errno));
ResetCrop (obj, fd); /* crop depends on frame size */
return 0;
}
+mtime_t GetBufferPTS (const struct v4l2_buffer *buf)
+{
+ mtime_t pts;
+
+ switch (buf->flags & V4L2_BUF_FLAG_TIMESTAMP_MASK)
+ {
+ case V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC:
+ pts = (buf->timestamp.tv_sec * CLOCK_FREQ)
+ + buf->timestamp.tv_usec;
+ static_assert (CLOCK_FREQ == 1000000, "Clock unit mismatch");
+ break;
+ case V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN:
+ default:
+ pts = mdate ();
+ break;
+ }
+ return pts;
+}
/*****************************************************************************
* GrabVideo: Grab a video frame
/* Could ignore EIO, see spec. */
/* fall through */
default:
- msg_Err (demux, "dequeue error: %m");
+ msg_Err (demux, "dequeue error: %s", vlc_strerror_c(errno));
return NULL;
}
}
block_t *block = block_Alloc (buf.bytesused);
if (unlikely(block == NULL))
return NULL;
+ block->i_pts = block->i_dts = GetBufferPTS (&buf);
memcpy (block->p_buffer, bufv[buf.index].start, buf.bytesused);
/* Unlock */
if (v4l2_ioctl (fd, VIDIOC_QBUF, &buf) < 0)
{
- msg_Err (demux, "queue error: %m");
+ msg_Err (demux, "queue error: %s", vlc_strerror_c(errno));
block_Release (block);
return NULL;
}
if (v4l2_ioctl (fd, VIDIOC_REQBUFS, &reqbuf) < 0)
{
- msg_Dbg (obj, "cannot reserve user buffers: %m");
+ msg_Dbg (obj, "cannot reserve user buffers: %s",
+ vlc_strerror_c(errno));
return -1;
}
if (v4l2_ioctl (fd, VIDIOC_STREAMON, &reqbuf.type) < 0)
{
- msg_Err (obj, "cannot start streaming: %m");
+ msg_Err (obj, "cannot start streaming: %s", vlc_strerror_c(errno));
return -1;
}
return 0;
if (v4l2_ioctl (fd, VIDIOC_REQBUFS, &req) < 0)
{
- msg_Err (obj, "cannot allocate buffers: %m" );
+ msg_Err (obj, "cannot allocate buffers: %s", vlc_strerror_c(errno));
return NULL;
}
if (v4l2_ioctl (fd, VIDIOC_QUERYBUF, &buf) < 0)
{
- msg_Err (obj, "cannot query buffer %"PRIu32": %m", bufc);
+ msg_Err (obj, "cannot query buffer %"PRIu32": %s", bufc,
+ vlc_strerror_c(errno));
goto error;
}
MAP_SHARED, fd, buf.m.offset);
if (bufv[bufc].start == MAP_FAILED)
{
- msg_Err (obj, "cannot map buffer %"PRIu32": %m", bufc);
+ msg_Err (obj, "cannot map buffer %"PRIu32": %s", bufc,
+ vlc_strerror_c(errno));
goto error;
}
bufv[bufc].length = buf.length;
/* Some drivers refuse to queue buffers before they are mapped. Bug? */
if (v4l2_ioctl (fd, VIDIOC_QBUF, &buf) < 0)
{
- msg_Err (obj, "cannot queue buffer %"PRIu32": %m", bufc);
+ msg_Err (obj, "cannot queue buffer %"PRIu32": %s", bufc,
+ vlc_strerror_c(errno));
goto error;
}
}
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (v4l2_ioctl (fd, VIDIOC_STREAMON, &type) < 0)
{
- msg_Err (obj, "cannot start streaming: %m");
+ msg_Err (obj, "cannot start streaming: %s", vlc_strerror_c(errno));
goto error;
}
*n = bufc;