v4l2: allow to convert monotonic timestamps.

pull/5/head
Nicolas George 13 years ago
parent 8a9ae37fee
commit 12292f35a3
  1. 2
      configure
  2. 7
      doc/indevs.texi
  3. 2
      libavdevice/Makefile
  4. 94
      libavdevice/v4l2.c

2
configure vendored

@ -1157,6 +1157,7 @@ HAVE_LIST="
attribute_may_alias attribute_may_alias
attribute_packed attribute_packed
cbrtf cbrtf
clock_gettime
closesocket closesocket
cmov cmov
dcbzl dcbzl
@ -3048,6 +3049,7 @@ fi
# Solaris has nanosleep in -lrt, OpenSolaris no longer needs that # Solaris has nanosleep in -lrt, OpenSolaris no longer needs that
check_func nanosleep || { check_func nanosleep -lrt && add_extralibs -lrt; } check_func nanosleep || { check_func nanosleep -lrt && add_extralibs -lrt; }
check_func clock_gettime || { check_func clock_gettime -lrt && add_extralibs -lrt; }
check_func fcntl check_func fcntl
check_func fork check_func fork
check_func getaddrinfo $network_extralibs check_func getaddrinfo $network_extralibs

@ -520,6 +520,13 @@ supported using @command{-list_formats all} for Video4Linux2 devices.
Some usage examples of the video4linux2 devices with ffmpeg and ffplay: Some usage examples of the video4linux2 devices with ffmpeg and ffplay:
The time base for the timestamps is 1 microsecond. Depending on the kernel
version and configuration, the timestamps may be derived from the real time
clock (origin at the Unix Epoch) or the monotonic clock (origin usually at
boot time, unaffected by NTP or manual changes to the clock). The
@option{-timestamps abs} or @option{-ts abs} option can be used to force
conversion into the real time clock.
Note that if FFmpeg is build with v4l-utils support ("--enable-libv4l2" Note that if FFmpeg is build with v4l-utils support ("--enable-libv4l2"
option), it will always be used. option), it will always be used.
@example @example

@ -28,7 +28,7 @@ OBJS-$(CONFIG_PULSE_INDEV) += pulse.o
OBJS-$(CONFIG_SDL_OUTDEV) += sdl.o OBJS-$(CONFIG_SDL_OUTDEV) += sdl.o
OBJS-$(CONFIG_SNDIO_INDEV) += sndio_common.o sndio_dec.o OBJS-$(CONFIG_SNDIO_INDEV) += sndio_common.o sndio_dec.o
OBJS-$(CONFIG_SNDIO_OUTDEV) += sndio_common.o sndio_enc.o OBJS-$(CONFIG_SNDIO_OUTDEV) += sndio_common.o sndio_enc.o
OBJS-$(CONFIG_V4L2_INDEV) += v4l2.o OBJS-$(CONFIG_V4L2_INDEV) += v4l2.o timefilter.o
OBJS-$(CONFIG_V4L_INDEV) += v4l.o OBJS-$(CONFIG_V4L_INDEV) += v4l.o
OBJS-$(CONFIG_VFWCAP_INDEV) += vfwcap.o OBJS-$(CONFIG_VFWCAP_INDEV) += vfwcap.o
OBJS-$(CONFIG_X11_GRAB_DEVICE_INDEV) += x11grab.o OBJS-$(CONFIG_X11_GRAB_DEVICE_INDEV) += x11grab.o

@ -51,6 +51,7 @@
#include "libavutil/log.h" #include "libavutil/log.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
#include "avdevice.h" #include "avdevice.h"
#include "timefilter.h"
#include "libavutil/parseutils.h" #include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h" #include "libavutil/pixdesc.h"
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
@ -73,6 +74,28 @@ static const int desired_video_buffers = 256;
#define V4L_RAWFORMATS 1 #define V4L_RAWFORMATS 1
#define V4L_COMPFORMATS 2 #define V4L_COMPFORMATS 2
/**
* Return timestamps to the user exactly as returned by the kernel
*/
#define V4L_TS_DEFAULT 0
/**
* Autodetect the kind of timestamps returned by the kernel and convert to
* absolute (wall clock) timestamps.
*/
#define V4L_TS_ABS 1
/**
* Assume kernel timestamps are from the monotonic clock and convert to
* absolute timestamps.
*/
#define V4L_TS_MONO2ABS 2
/**
* Once the kind of timestamps returned by the kernel have been detected,
* the value of the timefilter (NULL or not) determines whether a conversion
* takes place.
*/
#define V4L_TS_CONVERT_READY V4L_TS_DEFAULT
struct video_data { struct video_data {
AVClass *class; AVClass *class;
int fd; int fd;
@ -81,6 +104,9 @@ struct video_data {
int frame_size; int frame_size;
int interlaced; int interlaced;
int top_field_first; int top_field_first;
int ts_mode;
TimeFilter *timefilter;
int64_t last_time_m;
int buffers; int buffers;
void **buf_start; void **buf_start;
@ -455,6 +481,66 @@ static void mmap_release_buffer(AVPacket *pkt)
pkt->size = 0; pkt->size = 0;
} }
#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
static int64_t av_gettime_monotonic(void)
{
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
return (int64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
}
#endif
static int init_convert_timestamp(AVFormatContext *ctx, int64_t ts)
{
struct video_data *s = ctx->priv_data;
int64_t now;
now = av_gettime();
if (s->ts_mode == V4L_TS_ABS &&
ts <= now + 1 * AV_TIME_BASE && ts >= now - 10 * AV_TIME_BASE) {
av_log(ctx, AV_LOG_INFO, "Detected absolute timestamps\n");
s->ts_mode = V4L_TS_CONVERT_READY;
return 0;
}
#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
now = av_gettime_monotonic();
if (s->ts_mode == V4L_TS_MONO2ABS ||
(ts <= now + 1 * AV_TIME_BASE && ts >= now - 10 * AV_TIME_BASE)) {
int64_t period = av_rescale_q(1, ctx->streams[0]->codec->time_base,
AV_TIME_BASE_Q);
av_log(ctx, AV_LOG_INFO, "Detected monotonic timestamps, converting\n");
/* microseconds instead of seconds, MHz instead of Hz */
s->timefilter = ff_timefilter_new(1, period, 1.0E-6);
s->ts_mode = V4L_TS_CONVERT_READY;
return 0;
}
#endif
av_log(ctx, AV_LOG_ERROR, "Unknown timestamps\n");
return AVERROR(EIO);
}
static int convert_timestamp(AVFormatContext *ctx, int64_t *ts)
{
struct video_data *s = ctx->priv_data;
if (s->ts_mode) {
int r = init_convert_timestamp(ctx, *ts);
if (r < 0)
return r;
}
#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
if (s->timefilter) {
int64_t nowa = av_gettime();
int64_t nowm = av_gettime_monotonic();
ff_timefilter_update(s->timefilter, nowa, nowm - s->last_time_m);
s->last_time_m = nowm;
*ts = ff_timefilter_eval(s->timefilter, *ts - nowm);
}
#endif
return 0;
}
static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
{ {
struct video_data *s = ctx->priv_data; struct video_data *s = ctx->priv_data;
@ -490,6 +576,9 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
pkt->data= s->buf_start[buf.index]; pkt->data= s->buf_start[buf.index];
pkt->size = buf.bytesused; pkt->size = buf.bytesused;
pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec;
res = convert_timestamp(ctx, &pkt->pts);
if (res < 0)
return res;
pkt->destruct = mmap_release_buffer; pkt->destruct = mmap_release_buffer;
buf_descriptor = av_malloc(sizeof(struct buff_data)); buf_descriptor = av_malloc(sizeof(struct buff_data));
if (buf_descriptor == NULL) { if (buf_descriptor == NULL) {
@ -847,6 +936,11 @@ static const AVOption options[] = {
{ "all", "Show all available formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_ALLFORMATS }, 0, INT_MAX, DEC, "list_formats" }, { "all", "Show all available formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_ALLFORMATS }, 0, INT_MAX, DEC, "list_formats" },
{ "raw", "Show only non-compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_RAWFORMATS }, 0, INT_MAX, DEC, "list_formats" }, { "raw", "Show only non-compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_RAWFORMATS }, 0, INT_MAX, DEC, "list_formats" },
{ "compressed", "Show only compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_COMPFORMATS }, 0, INT_MAX, DEC, "list_formats" }, { "compressed", "Show only compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.dbl = V4L_COMPFORMATS }, 0, INT_MAX, DEC, "list_formats" },
{ "timestamps", "Kind of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, 2, DEC, "timestamps" },
{ "default", "Use timestamps from the kernel", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.dbl = V4L_TS_DEFAULT }, 0, 2, DEC, "timestamps" },
{ "abs", "Use absolute timestamps (wall clock)", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.dbl = V4L_TS_ABS }, 0, 2, DEC, "timestamps" },
{ "mono2abs", "Force conversion from monotonic to absolute timestamps", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.dbl = V4L_TS_MONO2ABS }, 0, 2, DEC, "timestamps" },
{ "ts", "Kind of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, 2, DEC, "timestamps" },
{ NULL }, { NULL },
}; };

Loading…
Cancel
Save