|
|
@ -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 }, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|