From 12292f35a3affe1265d63acba98f651cf82ae233 Mon Sep 17 00:00:00 2001 From: Nicolas George Date: Sat, 3 Mar 2012 15:56:11 +0100 Subject: v4l2: allow to convert monotonic timestamps. --- libavdevice/v4l2.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'libavdevice/v4l2.c') diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c index 642df1d08a..cecc18dfda 100644 --- a/libavdevice/v4l2.c +++ b/libavdevice/v4l2.c @@ -51,6 +51,7 @@ #include "libavutil/log.h" #include "libavutil/opt.h" #include "avdevice.h" +#include "timefilter.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/avstring.h" @@ -73,6 +74,28 @@ static const int desired_video_buffers = 256; #define V4L_RAWFORMATS 1 #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 { AVClass *class; int fd; @@ -81,6 +104,9 @@ struct video_data { int frame_size; int interlaced; int top_field_first; + int ts_mode; + TimeFilter *timefilter; + int64_t last_time_m; int buffers; void **buf_start; @@ -455,6 +481,66 @@ static void mmap_release_buffer(AVPacket *pkt) 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) { 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->size = buf.bytesused; 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; buf_descriptor = av_malloc(sizeof(struct buff_data)); 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" }, { "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" }, + { "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 }, }; -- cgit v1.2.3