Libav
setpts.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 Stefano Sabatini
3  * Copyright (c) 2008 Victor Paesa
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
27 #include "libavutil/eval.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/mathematics.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/time.h"
32 
33 #include "audio.h"
34 #include "avfilter.h"
35 #include "internal.h"
36 #include "video.h"
37 
38 #include "config.h"
39 
40 static const char *const var_names[] = {
41  "E",
42  "INTERLACED",
43  "N",
44  "PHI",
45  "PI",
46  "PREV_INPTS",
47  "PREV_OUTPTS",
48  "PTS",
49  "STARTPTS",
50  "TB",
51  "RTCTIME",
52  "RTCSTART",
53  "S", // Number of samples in the current frame
54  "SR", // Audio sample rate
55  NULL
56 };
57 
58 enum var_name {
74 };
75 
76 typedef struct {
77  const AVClass *class;
78  char *expr_str;
80  double var_values[VAR_VARS_NB];
82 
83 static av_cold int init(AVFilterContext *ctx)
84 {
85  SetPTSContext *setpts = ctx->priv;
86  int ret;
87 
88  if ((ret = av_expr_parse(&setpts->expr, setpts->expr_str,
89  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
90  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", setpts->expr_str);
91  return ret;
92  }
93 
94  setpts->var_values[VAR_E] = M_E;
95  setpts->var_values[VAR_N] = 0.0;
96  setpts->var_values[VAR_S] = 0.0;
97  setpts->var_values[VAR_PHI] = M_PHI;
98  setpts->var_values[VAR_PI] = M_PI;
99  setpts->var_values[VAR_PREV_INPTS] = NAN;
100  setpts->var_values[VAR_PREV_OUTPTS] = NAN;
101  setpts->var_values[VAR_STARTPTS] = NAN;
102  return 0;
103 }
104 
105 static int config_input(AVFilterLink *inlink)
106 {
107  SetPTSContext *setpts = inlink->dst->priv;
108 
109  setpts->var_values[VAR_TB] = av_q2d(inlink->time_base);
110  setpts->var_values[VAR_RTCSTART] = av_gettime();
111 
112  if (inlink->type == AVMEDIA_TYPE_AUDIO) {
113  setpts->var_values[VAR_SR] = inlink->sample_rate;
114  }
115 
116  av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f\n", setpts->var_values[VAR_TB]);
117  return 0;
118 }
119 
120 #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
121 #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
122 
123 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
124 {
125  SetPTSContext *setpts = inlink->dst->priv;
126  int64_t in_pts = frame->pts;
127  double d;
128 
129  if (isnan(setpts->var_values[VAR_STARTPTS]))
130  setpts->var_values[VAR_STARTPTS] = TS2D(frame->pts);
131 
132  setpts->var_values[VAR_PTS ] = TS2D(frame->pts);
133  setpts->var_values[VAR_RTCTIME ] = av_gettime();
134 
135  if (inlink->type == AVMEDIA_TYPE_VIDEO) {
136  setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame;
137  } else {
138  setpts->var_values[VAR_S] = frame->nb_samples;
139  }
140 
141  d = av_expr_eval(setpts->expr, setpts->var_values, NULL);
142  frame->pts = D2TS(d);
143 
144 #ifdef DEBUG
145  av_log(inlink->dst, AV_LOG_DEBUG,
146  "n:%"PRId64" interlaced:%d pts:%"PRId64" t:%f -> pts:%"PRId64" t:%f\n",
147  (int64_t)setpts->var_values[VAR_N],
148  (int)setpts->var_values[VAR_INTERLACED],
149  in_pts, in_pts * av_q2d(inlink->time_base),
150  frame->pts, frame->pts * av_q2d(inlink->time_base));
151 #endif
152 
153 
154  if (inlink->type == AVMEDIA_TYPE_VIDEO) {
155  setpts->var_values[VAR_N] += 1.0;
156  } else {
157  setpts->var_values[VAR_N] += frame->nb_samples;
158  }
159 
160  setpts->var_values[VAR_PREV_INPTS ] = TS2D(in_pts);
161  setpts->var_values[VAR_PREV_OUTPTS] = TS2D(frame->pts);
162  return ff_filter_frame(inlink->dst->outputs[0], frame);
163 }
164 
165 static av_cold void uninit(AVFilterContext *ctx)
166 {
167  SetPTSContext *setpts = ctx->priv;
168  av_expr_free(setpts->expr);
169  setpts->expr = NULL;
170 }
171 
172 #define OFFSET(x) offsetof(SetPTSContext, x)
173 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM
174 static const AVOption options[] = {
175  { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = FLAGS },
176  { NULL },
177 };
178 
179 #if CONFIG_SETPTS_FILTER
180 static const AVClass setpts_class = {
181  .class_name = "setpts",
182  .item_name = av_default_item_name,
183  .option = options,
184  .version = LIBAVUTIL_VERSION_INT,
185 };
186 
187 static const AVFilterPad avfilter_vf_setpts_inputs[] = {
188  {
189  .name = "default",
190  .type = AVMEDIA_TYPE_VIDEO,
191  .get_video_buffer = ff_null_get_video_buffer,
192  .config_props = config_input,
193  .filter_frame = filter_frame,
194  },
195  { NULL }
196 };
197 
198 static const AVFilterPad avfilter_vf_setpts_outputs[] = {
199  {
200  .name = "default",
201  .type = AVMEDIA_TYPE_VIDEO,
202  },
203  { NULL }
204 };
205 
206 AVFilter ff_vf_setpts = {
207  .name = "setpts",
208  .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."),
209  .init = init,
210  .uninit = uninit,
211 
212  .priv_size = sizeof(SetPTSContext),
213  .priv_class = &setpts_class,
214 
215  .inputs = avfilter_vf_setpts_inputs,
216  .outputs = avfilter_vf_setpts_outputs,
217 };
218 #endif
219 
220 #if CONFIG_ASETPTS_FILTER
221 static const AVClass asetpts_class = {
222  .class_name = "asetpts",
223  .item_name = av_default_item_name,
224  .option = options,
225  .version = LIBAVUTIL_VERSION_INT,
226 };
227 
228 static const AVFilterPad asetpts_inputs[] = {
229  {
230  .name = "default",
231  .type = AVMEDIA_TYPE_AUDIO,
232  .get_audio_buffer = ff_null_get_audio_buffer,
233  .config_props = config_input,
234  .filter_frame = filter_frame,
235  },
236  { NULL }
237 };
238 
239 static const AVFilterPad asetpts_outputs[] = {
240  {
241  .name = "default",
242  .type = AVMEDIA_TYPE_AUDIO,
243  },
244  { NULL }
245 };
246 
247 AVFilter ff_af_asetpts = {
248  .name = "asetpts",
249  .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."),
250  .init = init,
251  .uninit = uninit,
252 
253  .priv_size = sizeof(SetPTSContext),
254  .priv_class = &asetpts_class,
255 
256  .inputs = asetpts_inputs,
257  .outputs = asetpts_outputs,
258 };
259 #endif