[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