Libav
vf_select.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  *
4  * This file is part of Libav.
5  *
6  * Libav is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * Libav is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Libav; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
26 #include "libavutil/eval.h"
27 #include "libavutil/fifo.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/mathematics.h"
30 #include "libavutil/opt.h"
31 #include "avfilter.h"
32 #include "internal.h"
33 #include "video.h"
34 
35 static const char *const var_names[] = {
36  "E",
37  "PHI",
38  "PI",
39 
40  "TB",
41 
42  "pts",
43  "start_pts",
44  "prev_pts",
45  "prev_selected_pts",
46 
47  "t",
48  "start_t",
49  "prev_t",
50  "prev_selected_t",
51 
52  "pict_type",
53  "I",
54  "P",
55  "B",
56  "S",
57  "SI",
58  "SP",
59  "BI",
60 
61  "interlace_type",
62  "PROGRESSIVE",
63  "TOPFIRST",
64  "BOTTOMFIRST",
65 
66  "n",
67  "selected_n",
68  "prev_selected_n",
69 
70  "key",
71  "pos",
72 
73  NULL
74 };
75 
76 enum var_name {
80 
82 
87 
92 
101 
106 
110 
112 
114 };
115 
116 #define FIFO_SIZE 8
117 
118 typedef struct {
119  const AVClass *class;
120  char *expr_str;
122  double var_values[VAR_VARS_NB];
123  double select;
126 } SelectContext;
127 
128 static av_cold int init(AVFilterContext *ctx)
129 {
130  SelectContext *select = ctx->priv;
131  int ret;
132 
133  if ((ret = av_expr_parse(&select->expr, select->expr_str,
134  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
135  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n",
136  select->expr_str);
137  return ret;
138  }
139 
140  select->pending_frames = av_fifo_alloc(FIFO_SIZE*sizeof(AVFrame*));
141  if (!select->pending_frames) {
142  av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n");
143  return AVERROR(ENOMEM);
144  }
145  return 0;
146 }
147 
148 #define INTERLACE_TYPE_P 0
149 #define INTERLACE_TYPE_T 1
150 #define INTERLACE_TYPE_B 2
151 
152 static int config_input(AVFilterLink *inlink)
153 {
154  SelectContext *select = inlink->dst->priv;
155 
156  select->var_values[VAR_E] = M_E;
157  select->var_values[VAR_PHI] = M_PHI;
158  select->var_values[VAR_PI] = M_PI;
159 
160  select->var_values[VAR_N] = 0.0;
161  select->var_values[VAR_SELECTED_N] = 0.0;
162 
163  select->var_values[VAR_TB] = av_q2d(inlink->time_base);
164 
165  select->var_values[VAR_PREV_PTS] = NAN;
168  select->var_values[VAR_START_PTS] = NAN;
169  select->var_values[VAR_START_T] = NAN;
170 
176 
180 
181  return 0;
182 }
183 
184 #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
185 #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
186 
187 static int select_frame(AVFilterContext *ctx, AVFrame *frame)
188 {
189  SelectContext *select = ctx->priv;
190  AVFilterLink *inlink = ctx->inputs[0];
191  double res;
192 
193  if (isnan(select->var_values[VAR_START_PTS]))
194  select->var_values[VAR_START_PTS] = TS2D(frame->pts);
195  if (isnan(select->var_values[VAR_START_T]))
196  select->var_values[VAR_START_T] = TS2D(frame->pts) * av_q2d(inlink->time_base);
197 
198  select->var_values[VAR_PTS] = TS2D(frame->pts);
199  select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base);
200  select->var_values[VAR_PREV_PTS] = TS2D(frame->pts);
201 
202  select->var_values[VAR_INTERLACE_TYPE] =
205  select->var_values[VAR_PICT_TYPE] = frame->pict_type;
206 
207  res = av_expr_eval(select->expr, select->var_values, NULL);
208  av_log(inlink->dst, AV_LOG_DEBUG,
209  "n:%d pts:%d t:%f interlace_type:%c key:%d pict_type:%c "
210  "-> select:%f\n",
211  (int)select->var_values[VAR_N],
212  (int)select->var_values[VAR_PTS],
213  select->var_values[VAR_T],
216  select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
217  (int)select->var_values[VAR_KEY],
219  res);
220 
221  select->var_values[VAR_N] += 1.0;
222 
223  if (res) {
224  select->var_values[VAR_PREV_SELECTED_N] = select->var_values[VAR_N];
226  select->var_values[VAR_PREV_SELECTED_T] = select->var_values[VAR_T];
227  select->var_values[VAR_SELECTED_N] += 1.0;
228  }
229  return res;
230 }
231 
232 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
233 {
234  SelectContext *select = inlink->dst->priv;
235 
236  select->select = select_frame(inlink->dst, frame);
237  if (select->select) {
238  /* frame was requested through poll_frame */
239  if (select->cache_frames) {
240  if (!av_fifo_space(select->pending_frames)) {
241  av_log(inlink->dst, AV_LOG_ERROR,
242  "Buffering limit reached, cannot cache more frames\n");
243  av_frame_free(&frame);
244  } else
245  av_fifo_generic_write(select->pending_frames, &frame,
246  sizeof(frame), NULL);
247  return 0;
248  }
249  return ff_filter_frame(inlink->dst->outputs[0], frame);
250  }
251 
252  av_frame_free(&frame);
253  return 0;
254 }
255 
256 static int request_frame(AVFilterLink *outlink)
257 {
258  AVFilterContext *ctx = outlink->src;
259  SelectContext *select = ctx->priv;
260  AVFilterLink *inlink = outlink->src->inputs[0];
261  select->select = 0;
262 
263  if (av_fifo_size(select->pending_frames)) {
264  AVFrame *frame;
265 
266  av_fifo_generic_read(select->pending_frames, &frame, sizeof(frame), NULL);
267  return ff_filter_frame(outlink, frame);
268  }
269 
270  while (!select->select) {
271  int ret = ff_request_frame(inlink);
272  if (ret < 0)
273  return ret;
274  }
275 
276  return 0;
277 }
278 
279 static int poll_frame(AVFilterLink *outlink)
280 {
281  SelectContext *select = outlink->src->priv;
282  AVFilterLink *inlink = outlink->src->inputs[0];
283  int count, ret;
284 
285  if (!av_fifo_size(select->pending_frames)) {
286  if ((count = ff_poll_frame(inlink)) <= 0)
287  return count;
288  /* request frame from input, and apply select condition to it */
289  select->cache_frames = 1;
290  while (count-- && av_fifo_space(select->pending_frames)) {
291  ret = ff_request_frame(inlink);
292  if (ret < 0)
293  break;
294  }
295  select->cache_frames = 0;
296  }
297 
298  return av_fifo_size(select->pending_frames)/sizeof(AVFrame*);
299 }
300 
301 static av_cold void uninit(AVFilterContext *ctx)
302 {
303  SelectContext *select = ctx->priv;
304  AVFrame *frame;
305 
306  av_expr_free(select->expr);
307  select->expr = NULL;
308 
309  while (select->pending_frames &&
310  av_fifo_generic_read(select->pending_frames, &frame, sizeof(frame), NULL) == sizeof(frame))
311  av_frame_free(&frame);
312  av_fifo_free(select->pending_frames);
313  select->pending_frames = NULL;
314 }
315 
316 #define OFFSET(x) offsetof(SelectContext, x)
317 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM
318 static const AVOption options[] = {
319  { "expr", "An expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = FLAGS },
320  { NULL },
321 };
322 
323 static const AVClass select_class = {
324  .class_name = "select",
325  .item_name = av_default_item_name,
326  .option = options,
327  .version = LIBAVUTIL_VERSION_INT,
328 };
329 
331  {
332  .name = "default",
333  .type = AVMEDIA_TYPE_VIDEO,
334  .get_video_buffer = ff_null_get_video_buffer,
335  .config_props = config_input,
336  .filter_frame = filter_frame,
337  },
338  { NULL }
339 };
340 
342  {
343  .name = "default",
344  .type = AVMEDIA_TYPE_VIDEO,
345  .poll_frame = poll_frame,
346  .request_frame = request_frame,
347  },
348  { NULL }
349 };
350 
352  .name = "select",
353  .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
354  .init = init,
355  .uninit = uninit,
356 
357  .priv_size = sizeof(SelectContext),
358  .priv_class = &select_class,
359 
360  .inputs = avfilter_vf_select_inputs,
361  .outputs = avfilter_vf_select_outputs,
362 };