Commit 514216d8 authored by Giorgio Vazzana's avatar Giorgio Vazzana Committed by Stefano Sabatini

lavd/v4l2: read the correct time per frame from devices that support a standard

Generally speaking, there are two types of v4l2 devices [1]:

1) devices that support a standard, like PAL or NTFS (tv cards, for example). For
this class of devices the framerate is fixed by the standard (for example PAL uses
25 fps) and the v4l2 driver cannot usually negotiate a different framerate (unless
it can skip frames on the driver side, to save I/O bandwidth).

2) devices for which the notion of standard does not make sense (webcams, for example).
For these devices it is usually possibile to request a desidered framerate.

In either case, the desidered frame rate can be requested when the VIDIOC_G_PARM
ioctl returns the V4L2_CAP_TIMEPERFRAME flag in the capability field.

Currently the code does not check for V4L2_CAP_TIMEPERFRAME and supports only the
second category of devices, returning a time per frame of 0/0 for devices in the
first group that do not permit to negotiate the framerate.

This patch adds support to read the correct framerate in all cases.

[1] http://linuxtv.org/downloads/v4l-dvb-apis/standard.htmlSigned-off-by: 's avatarStefano Sabatini <stefasab@gmail.com>
parent 402ea625
......@@ -683,12 +683,10 @@ static int v4l2_set_parameters(AVFormatContext *s1)
struct video_data *s = s1->priv_data;
struct v4l2_standard standard = { 0 };
struct v4l2_streamparm streamparm = { 0 };
struct v4l2_fract *tpf = &streamparm.parm.capture.timeperframe;
struct v4l2_fract *tpf;
AVRational framerate_q = { 0 };
int i, ret;
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (s->framerate &&
(ret = av_parse_video_rate(&framerate_q, s->framerate)) < 0) {
av_log(s1, AV_LOG_ERROR, "Could not parse framerate '%s'.\n",
......@@ -697,57 +695,87 @@ static int v4l2_set_parameters(AVFormatContext *s1)
}
if (s->standard) {
av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set standard: %s\n",
s->standard);
/* set tv standard */
for(i=0;;i++) {
if (s->std_id) {
av_log(s1, AV_LOG_DEBUG, "Setting standard: %s\n", s->standard);
/* set tv standard */
for (i = 0; ; i++) {
standard.index = i;
ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
if (ret < 0 || !av_strcasecmp(standard.name, s->standard))
break;
}
if (ret < 0) {
ret = errno;
av_log(s1, AV_LOG_ERROR, "Unknown or unsupported standard '%s'\n", s->standard);
return AVERROR(ret);
}
if (v4l2_ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
ret = errno;
av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_S_STD): %s\n", strerror(errno));
return AVERROR(ret);
}
} else {
av_log(s1, AV_LOG_WARNING,
"This device does not support any standard\n");
}
}
/* get standard */
if (v4l2_ioctl(s->fd, VIDIOC_G_STD, &s->std_id) == 0) {
tpf = &standard.frameperiod;
for (i = 0; ; i++) {
standard.index = i;
ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
if (ret < 0 || !av_strcasecmp(standard.name, s->standard))
if (ret < 0) {
ret = errno;
av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_ENUMSTD): %s\n", strerror(errno));
return AVERROR(ret);
}
if (standard.id == s->std_id) {
av_log(s1, AV_LOG_DEBUG,
"Current standard: %s, id: %"PRIu64", frameperiod: %d/%d\n",
standard.name, (uint64_t)standard.id, tpf->numerator, tpf->denominator);
break;
}
}
if (ret < 0) {
av_log(s1, AV_LOG_ERROR, "Unknown standard '%s'\n", s->standard);
return ret;
}
} else {
tpf = &streamparm.parm.capture.timeperframe;
}
av_log(s1, AV_LOG_DEBUG,
"The V4L2 driver set standard: %s, id: %"PRIu64"\n",
s->standard, (uint64_t)standard.id);
if (v4l2_ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
av_log(s1, AV_LOG_ERROR,
"The V4L2 driver ioctl set standard(%s) failed\n",
s->standard);
return AVERROR(EIO);
}
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (v4l2_ioctl(s->fd, VIDIOC_G_PARM, &streamparm) < 0) {
ret = errno;
av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n", strerror(errno));
return AVERROR(ret);
}
if (framerate_q.num && framerate_q.den) {
av_log(s1, AV_LOG_DEBUG, "Setting time per frame to %d/%d\n",
framerate_q.den, framerate_q.num);
tpf->numerator = framerate_q.den;
tpf->denominator = framerate_q.num;
if (v4l2_ioctl(s->fd, VIDIOC_S_PARM, &streamparm) != 0) {
av_log(s1, AV_LOG_ERROR,
"ioctl set time per frame(%d/%d) failed\n",
if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
tpf = &streamparm.parm.capture.timeperframe;
av_log(s1, AV_LOG_DEBUG, "Setting time per frame to %d/%d\n",
framerate_q.den, framerate_q.num);
return AVERROR(EIO);
}
tpf->numerator = framerate_q.den;
tpf->denominator = framerate_q.num;
if (framerate_q.num != tpf->denominator ||
framerate_q.den != tpf->numerator) {
av_log(s1, AV_LOG_INFO,
"The driver changed the time per frame from "
"%d/%d to %d/%d\n",
framerate_q.den, framerate_q.num,
tpf->numerator, tpf->denominator);
}
} else {
if (v4l2_ioctl(s->fd, VIDIOC_G_PARM, &streamparm) != 0) {
av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n",
strerror(errno));
return AVERROR(errno);
if (v4l2_ioctl(s->fd, VIDIOC_S_PARM, &streamparm) < 0) {
ret = errno;
av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_S_PARM): %s\n", strerror(errno));
return AVERROR(ret);
}
if (framerate_q.num != tpf->denominator ||
framerate_q.den != tpf->numerator) {
av_log(s1, AV_LOG_INFO,
"The driver changed the time per frame from "
"%d/%d to %d/%d\n",
framerate_q.den, framerate_q.num,
tpf->numerator, tpf->denominator);
}
} else {
av_log(s1, AV_LOG_WARNING,
"The driver does not allow to change time per frame\n");
}
}
s1->streams[0]->avg_frame_rate.num = tpf->denominator;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment