/* * Copyright (c) 2010 Stefano Sabatini * Copyright (c) 2008 Victor Paesa * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * video presentation timestamp (PTS) modification filter */ /* #define DEBUG */ #include "libavutil/eval.h" #include "avfilter.h" static const char *var_names[] = { "E", ///< Euler number "INTERLACED", ///< tell if the current frame is interlaced "N", ///< frame number (starting at zero) "PHI", ///< golden ratio "PI", ///< greek pi "POS", ///< original position in the file of the frame "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 "TB", ///< timebase NULL }; enum var_name { VAR_E, VAR_INTERLACED, VAR_N, VAR_PHI, VAR_PI, VAR_POS, VAR_PREV_INPTS, VAR_PREV_OUTPTS, VAR_PTS, VAR_STARTPTS, VAR_TB, VAR_VARS_NB }; typedef struct { AVExpr *expr; double var_values[VAR_VARS_NB]; } SetPTSContext; static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) { SetPTSContext *setpts = ctx->priv; int ret; if ((ret = av_expr_parse(&setpts->expr, args ? args : "PTS", var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) { av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args); return ret; } setpts->var_values[VAR_E ] = M_E; setpts->var_values[VAR_N ] = 0.0; setpts->var_values[VAR_PHI ] = M_PHI; setpts->var_values[VAR_PI ] = M_PI; setpts->var_values[VAR_PREV_INPTS ] = NAN; setpts->var_values[VAR_PREV_OUTPTS] = NAN; setpts->var_values[VAR_STARTPTS ] = NAN; return 0; } static int config_input(AVFilterLink *inlink) { SetPTSContext *setpts = inlink->dst->priv; setpts->var_values[VAR_TB] = av_q2d(inlink->time_base); av_log(inlink->src, AV_LOG_INFO, "TB:%f\n", setpts->var_values[VAR_TB]); return 0; } #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d)) #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)) static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) { SetPTSContext *setpts = inlink->dst->priv; double d; AVFilterBufferRef *outpicref = avfilter_ref_buffer(inpicref, ~0); if (isnan(setpts->var_values[VAR_STARTPTS])) setpts->var_values[VAR_STARTPTS] = TS2D(inpicref->pts); setpts->var_values[VAR_INTERLACED] = inpicref->video->interlaced; setpts->var_values[VAR_PTS ] = TS2D(inpicref->pts); setpts->var_values[VAR_POS ] = inpicref->pos == -1 ? NAN : inpicref->pos; d = av_expr_eval(setpts->expr, setpts->var_values, NULL); outpicref->pts = D2TS(d); #ifdef DEBUG av_log(inlink->dst, AV_LOG_DEBUG, "n:%"PRId64" interlaced:%d pos:%"PRId64" pts:%"PRId64" t:%f -> pts:%"PRId64" t:%f\n", (int64_t)setpts->var_values[VAR_N], (int)setpts->var_values[VAR_INTERLACED], inpicref ->pos, inpicref ->pts, inpicref ->pts * av_q2d(inlink->time_base), outpicref->pts, outpicref->pts * av_q2d(inlink->time_base)); #endif setpts->var_values[VAR_N] += 1.0; setpts->var_values[VAR_PREV_INPTS ] = TS2D(inpicref ->pts); setpts->var_values[VAR_PREV_OUTPTS] = TS2D(outpicref->pts); avfilter_start_frame(inlink->dst->outputs[0], outpicref); } static av_cold void uninit(AVFilterContext *ctx) { SetPTSContext *setpts = ctx->priv; av_expr_free(setpts->expr); setpts->expr = NULL; } AVFilter avfilter_vf_setpts = { .name = "setpts", .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), .init = init, .uninit = uninit, .priv_size = sizeof(SetPTSContext), .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, .get_video_buffer = avfilter_null_get_video_buffer, .config_props = config_input, .start_frame = start_frame, }, { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, }, { .name = NULL}}, };