diff --git a/Changelog b/Changelog index b356980caf..f74295c010 100644 --- a/Changelog +++ b/Changelog @@ -12,6 +12,7 @@ version 10: - uniform options syntax across all filters - new interlace filter - JPEG 2000 decoder +- new asetpts filter (same as setpts, but for audio) version 9: diff --git a/doc/filters.texi b/doc/filters.texi index 0f1fd39a5e..0a36f5f1e0 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -192,6 +192,75 @@ stream ends. The default value is 2 seconds. Pass the audio source unchanged to the output. +@section asetpts + +Change the PTS (presentation timestamp) of the input audio frames. + +This filter accepts the following options: + +@table @option + +@item expr +The expression which is evaluated for each frame to construct its timestamp. + +@end table + +The expression is evaluated through the eval API and can contain the following +constants: + +@table @option +@item PTS +the presentation timestamp in input + +@item PI +Greek PI + +@item PHI +golden ratio + +@item E +Euler number + +@item N +Number of the audio samples pass through the filter so far, starting at 0. + +@item S +Number of the audio samples in the current frame. + +@item SR +Audio sample rate. + +@item STARTPTS +the PTS of the first frame + +@item PREV_INPTS +previous input PTS + +@item PREV_OUTPTS +previous output PTS + +@item RTCTIME +wallclock (RTC) time in microseconds + +@item RTCSTART +wallclock (RTC) time at the start of the movie in microseconds + +@end table + +Some examples follow: + +@example +# start counting PTS from zero +asetpts=expr=PTS-STARTPTS + +#generate timestamps by counting samples +asetpts=expr=N/SR/TB + +# generate timestamps from a "live source" and rebase onto the current timebase +asetpts='(RTCTIME - RTCSTART) / (TB * 1000000)" +@end example + + @section ashowinfo Show a line containing various information for each input audio frame. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 09271574d1..662dcecfd2 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -27,6 +27,7 @@ OBJS = allfilters.o \ OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o +OBJS-$(CONFIG_ASETPTS_FILTER) += setpts.o OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o @@ -70,7 +71,7 @@ OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o OBJS-$(CONFIG_SELECT_FILTER) += vf_select.o OBJS-$(CONFIG_SETDAR_FILTER) += vf_aspect.o -OBJS-$(CONFIG_SETPTS_FILTER) += vf_setpts.o +OBJS-$(CONFIG_SETPTS_FILTER) += setpts.o OBJS-$(CONFIG_SETSAR_FILTER) += vf_aspect.o OBJS-$(CONFIG_SETTB_FILTER) += vf_settb.o OBJS-$(CONFIG_SHOWINFO_FILTER) += vf_showinfo.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 7b407cde16..d3802722d9 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -47,6 +47,7 @@ void avfilter_register_all(void) REGISTER_FILTER(AFORMAT, aformat, af); REGISTER_FILTER(AMIX, amix, af); REGISTER_FILTER(ANULL, anull, af); + REGISTER_FILTER(ASETPTS, asetpts, af); REGISTER_FILTER(ASHOWINFO, ashowinfo, af); REGISTER_FILTER(ASPLIT, asplit, af); REGISTER_FILTER(ASYNCTS, asyncts, af); diff --git a/libavfilter/vf_setpts.c b/libavfilter/setpts.c similarity index 74% rename from libavfilter/vf_setpts.c rename to libavfilter/setpts.c index bc26954eb6..7a1f4f31d8 100644 --- a/libavfilter/vf_setpts.c +++ b/libavfilter/setpts.c @@ -31,23 +31,29 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/time.h" + +#include "audio.h" #include "avfilter.h" #include "internal.h" #include "video.h" +#include "config.h" + static const char *const var_names[] = { "E", ///< Euler number "INTERLACED", ///< tell if the current frame is interlaced - "N", ///< frame number (starting at zero) + "N", ///< frame / sample number (starting at zero) "PHI", ///< golden ratio "PI", ///< greek pi "PREV_INPTS", ///< previous input PTS "PREV_OUTPTS", ///< previous output PTS "PTS", ///< original pts in the file of the frame - "STARTPTS", ///< PTS at start of movie + "STARTPTS", ///< PTS at start of movie "TB", ///< timebase "RTCTIME", ///< wallclock (RTC) time in micro seconds "RTCSTART", ///< wallclock (RTC) time at the start of the movie in micro seconds + "S", // Number of samples in the current frame + "SR", // Audio sample rate NULL }; @@ -64,6 +70,8 @@ enum var_name { VAR_TB, VAR_RTCTIME, VAR_RTCSTART, + VAR_S, + VAR_SR, VAR_VARS_NB }; @@ -87,6 +95,7 @@ static av_cold int init(AVFilterContext *ctx) setpts->var_values[VAR_E] = M_E; setpts->var_values[VAR_N] = 0.0; + setpts->var_values[VAR_S] = 0.0; setpts->var_values[VAR_PHI] = M_PHI; setpts->var_values[VAR_PI] = M_PI; setpts->var_values[VAR_PREV_INPTS] = NAN; @@ -102,6 +111,10 @@ static int config_input(AVFilterLink *inlink) setpts->var_values[VAR_TB] = av_q2d(inlink->time_base); setpts->var_values[VAR_RTCSTART] = av_gettime(); + if (inlink->type == AVMEDIA_TYPE_AUDIO) { + setpts->var_values[VAR_SR] = inlink->sample_rate; + } + av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f\n", setpts->var_values[VAR_TB]); return 0; } @@ -118,10 +131,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (isnan(setpts->var_values[VAR_STARTPTS])) setpts->var_values[VAR_STARTPTS] = TS2D(frame->pts); - setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; setpts->var_values[VAR_PTS ] = TS2D(frame->pts); setpts->var_values[VAR_RTCTIME ] = av_gettime(); + if (inlink->type == AVMEDIA_TYPE_VIDEO) { + setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; + } else { + setpts->var_values[VAR_S] = frame->nb_samples; + } + d = av_expr_eval(setpts->expr, setpts->var_values, NULL); frame->pts = D2TS(d); @@ -135,7 +153,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) #endif - setpts->var_values[VAR_N] += 1.0; + if (inlink->type == AVMEDIA_TYPE_VIDEO) { + setpts->var_values[VAR_N] += 1.0; + } else { + setpts->var_values[VAR_N] += frame->nb_samples; + } + setpts->var_values[VAR_PREV_INPTS ] = TS2D(in_pts); setpts->var_values[VAR_PREV_OUTPTS] = TS2D(frame->pts); return ff_filter_frame(inlink->dst->outputs[0], frame); @@ -149,12 +172,13 @@ static av_cold void uninit(AVFilterContext *ctx) } #define OFFSET(x) offsetof(SetPTSContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM static const AVOption options[] = { { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = FLAGS }, { NULL }, }; +#if CONFIG_SETPTS_FILTER static const AVClass setpts_class = { .class_name = "setpts", .item_name = av_default_item_name, @@ -193,3 +217,45 @@ AVFilter avfilter_vf_setpts = { .inputs = avfilter_vf_setpts_inputs, .outputs = avfilter_vf_setpts_outputs, }; +#endif + +#if CONFIG_ASETPTS_FILTER +static const AVClass asetpts_class = { + .class_name = "asetpts", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVFilterPad asetpts_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .get_audio_buffer = ff_null_get_audio_buffer, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad asetpts_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +AVFilter avfilter_af_asetpts = { + .name = "asetpts", + .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), + .init = init, + .uninit = uninit, + + .priv_size = sizeof(SetPTSContext), + .priv_class = &asetpts_class, + + .inputs = asetpts_inputs, + .outputs = asetpts_outputs, +}; +#endif diff --git a/libavfilter/version.h b/libavfilter/version.h index 36a03f7aa3..e8dc87546f 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -29,7 +29,7 @@ #include "libavutil/avutil.h" #define LIBAVFILTER_VERSION_MAJOR 3 -#define LIBAVFILTER_VERSION_MINOR 8 +#define LIBAVFILTER_VERSION_MINOR 9 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \