Signed-off-by: Nicolas George <geo...@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. +@item parsemeta +The name of a metadata tag to parse as a timestamp. +The timestamp is always parsed as UTC. + +@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 +@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. + +@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. +@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 +@item +Use the EXIF metadata as a timestamp: +@example +asetpts=setpts=TAG/TB:parsemeta=DateTime:parsemeta_fmt=exif +@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel