[FFmpeg-devel] [PATCH] lavfi/setpts: allow to parse a metadata tag as a timestamp.
Nicolas George
george at nsup.org
Thu Jul 24 20:01:24 CEST 2014
Signed-off-by: Nicolas George <george at nsup.org>
---
doc/filters.texi | 22 ++++++++++++++++++
libavfilter/setpts.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-
libavfilter/version.h | 2 +-
3 files changed, 86 insertions(+), 2 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index a7919a3..bb0000b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -10317,6 +10317,19 @@ This filter accepts the following options:
@item expr
The expression which is evaluated for each frame to construct its timestamp.
+ at item parsemeta
+The name of a metadata tag to parse as a timestamp.
+The timestamp is always parsed as UTC.
+
+ at item parsemeta_fmt
+The format to use when parsing the metadata tag specified in parsemeta.
+The syntax is the one used by @code{av_small_strptime()}.
+The value @code{exif} can be used as an abbreviation for
+ at code{%Y:%m:%d %H:%M:%S} and @code{iso} for @code{%Y-%m-%dT%H:%M:%SZ}.
+If not specified, all the known formats are tried in turn.
+
+ at item
+
@end table
The expression is evaluated through the eval API and can contain the following
@@ -10378,6 +10391,9 @@ instead.
@item RTCSTART
The wallclock (RTC) time at the start of the movie in microseconds.
+ at item TAG
+The parsed metadata tag, in seconds since the Unix Epoch.
+
@item TB
The timebase of the input timestamps.
@@ -10434,6 +10450,12 @@ Generate timestamps by counting samples:
asetpts=N/SR/TB
@end example
+ at item
+Use the EXIF metadata as a timestamp:
+ at example
+asetpts=setpts=TAG/TB:parsemeta=DateTime:parsemeta_fmt=exif
+ at end example
+
@end itemize
@section settb, asettb
diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
index 0db0218..a476e24 100644
--- a/libavfilter/setpts.c
+++ b/libavfilter/setpts.c
@@ -24,10 +24,12 @@
* video presentation timestamp (PTS) modification filter
*/
+#include <time.h>
#include "libavutil/eval.h"
#include "libavutil/internal.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
#include "libavutil/time.h"
#include "audio.h"
#include "avfilter.h"
@@ -50,6 +52,7 @@ static const char *const var_names[] = {
"STARTPTS", ///< PTS at start of movie
"STARTT", ///< time at start of movie
"T", ///< original time in the file of the frame
+ "TAG", ///< date parsed from metadata, in seconds since the Epoch
"TB", ///< timebase
"RTCTIME", ///< wallclock (RTC) time in micro seconds
"RTCSTART", ///< wallclock (RTC) time at the start of the movie in micro seconds
@@ -74,6 +77,7 @@ enum var_name {
VAR_STARTPTS,
VAR_STARTT,
VAR_T,
+ VAR_TAG,
VAR_TB,
VAR_RTCTIME,
VAR_RTCSTART,
@@ -82,9 +86,16 @@ enum var_name {
VAR_VARS_NB
};
+static const char *const date_formats[][2] = {
+ { "exif", "%Y:%m:%d %H:%M:%S" },
+ { "iso", "%Y-%m-%dT%H:%M:%SZ" },
+};
+
typedef struct SetPTSContext {
const AVClass *class;
char *expr_str;
+ char *parsemeta;
+ char *parsemeta_fmt;
AVExpr *expr;
double var_values[VAR_VARS_NB];
enum AVMediaType type;
@@ -93,7 +104,7 @@ typedef struct SetPTSContext {
static av_cold int init(AVFilterContext *ctx)
{
SetPTSContext *setpts = ctx->priv;
- int ret;
+ int i, ret;
if ((ret = av_expr_parse(&setpts->expr, setpts->expr_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
@@ -101,6 +112,23 @@ static av_cold int init(AVFilterContext *ctx)
return ret;
}
+ if (setpts->parsemeta_fmt) {
+ if (!setpts->parsemeta)
+ av_log(ctx, AV_LOG_WARNING,
+ "No metadata tag to parse, format ignored\n");
+ for (i = 0; i < FF_ARRAY_ELEMS(date_formats); i++)
+ if (!strcmp(date_formats[i][0], setpts->parsemeta_fmt))
+ break;
+ if (i < FF_ARRAY_ELEMS(date_formats)) {
+ av_log(ctx, AV_LOG_VERBOSE,
+ "Using format '%s' for metadata\n", date_formats[i][1]);
+ av_free(setpts->parsemeta_fmt);
+ setpts->parsemeta_fmt = av_strdup(date_formats[i][1]);
+ if (!setpts->parsemeta_fmt)
+ return AVERROR(ENOMEM);
+ }
+ }
+
setpts->var_values[VAR_N] = 0.0;
setpts->var_values[VAR_S] = 0.0;
setpts->var_values[VAR_PREV_INPTS] = NAN;
@@ -109,6 +137,7 @@ static av_cold int init(AVFilterContext *ctx)
setpts->var_values[VAR_PREV_OUTT] = NAN;
setpts->var_values[VAR_STARTPTS] = NAN;
setpts->var_values[VAR_STARTT] = NAN;
+ setpts->var_values[VAR_TAG] = NAN;
return 0;
}
@@ -135,6 +164,36 @@ static int config_input(AVFilterLink *inlink)
return 0;
}
+static double parse_metadata(AVFilterContext *ctx, const AVFrame *frame)
+{
+ SetPTSContext *setpts = ctx->priv;
+ AVDictionaryEntry *tag;
+ struct tm tm = { 0 };
+ char *rest;
+ int i;
+
+ if (!setpts->parsemeta)
+ return NAN;
+ tag = av_dict_get(av_frame_get_metadata(frame), setpts->parsemeta, NULL, 0);
+ if (!tag)
+ return NAN;
+ if (setpts->parsemeta_fmt) {
+ rest = av_small_strptime(tag->value, setpts->parsemeta_fmt, &tm);
+ } else {
+ for (i = 0; i < FF_ARRAY_ELEMS(date_formats); i++) {
+ rest = av_small_strptime(tag->value, date_formats[i][1], &tm);
+ if (rest && !*rest)
+ break;
+ }
+ }
+ if (!rest || *rest) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Impossible to parse '%s' as a timestamp\n", tag->value);
+ return NAN;
+ }
+ return av_timegm(&tm);
+}
+
#define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
@@ -162,6 +221,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
}
setpts->var_values[VAR_PTS ] = TS2D(frame->pts);
setpts->var_values[VAR_T ] = TS2T(frame->pts, inlink->time_base);
+ setpts->var_values[VAR_TAG ] = parse_metadata(inlink->dst, frame);
setpts->var_values[VAR_POS ] = av_frame_get_pkt_pos(frame) == -1 ? NAN : av_frame_get_pkt_pos(frame);
setpts->var_values[VAR_RTCTIME ] = av_gettime();
@@ -221,6 +281,8 @@ static av_cold void uninit(AVFilterContext *ctx)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
static const AVOption options[] = {
{ "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = FLAGS },
+ { "parsemeta", "Parse a metadata tag as a timestamp", OFFSET(parsemeta), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
+ { "parsemeta_fmt", "Format for the medatada tag to parse", OFFSET(parsemeta_fmt), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
{ NULL }
};
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 9f2c364..7b231ec 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
#define LIBAVFILTER_VERSION_MAJOR 4
#define LIBAVFILTER_VERSION_MINOR 11
-#define LIBAVFILTER_VERSION_MICRO 102
+#define LIBAVFILTER_VERSION_MICRO 103
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \
--
2.0.1
More information about the ffmpeg-devel
mailing list