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 "avfilter.h"
31 #include "internal.h"
32 #include "video.h"
33 
34 static const char *const var_names[] = {
35  "E",
36  "PHI",
37  "PI",
38 
39  "TB",
40 
41  "pts",
42  "start_pts",
43  "prev_pts",
44  "prev_selected_pts",
45 
46  "t",
47  "start_t",
48  "prev_t",
49  "prev_selected_t",
50 
51  "pict_type",
52  "I",
53  "P",
54  "B",
55  "S",
56  "SI",
57  "SP",
58  "BI",
59 
60  "interlace_type",
61  "PROGRESSIVE",
62  "TOPFIRST",
63  "BOTTOMFIRST",
64 
65  "n",
66  "selected_n",
67  "prev_selected_n",
68 
69  "key",
70  "pos",
71 
72  NULL
73 };
74 
75 enum var_name {
79 
81 
86 
91 
100 
105 
109 
112 
114 };
115 
116 #define FIFO_SIZE 8
117 
118 typedef struct {
120  double var_values[VAR_VARS_NB];
121  double select;
124 } SelectContext;
125 
126 static av_cold int init(AVFilterContext *ctx, const char *args)
127 {
128  SelectContext *select = ctx->priv;
129  int ret;
130 
131  if ((ret = av_expr_parse(&select->expr, args ? args : "1",
132  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
133  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args);
134  return ret;
135  }
136 
138  if (!select->pending_frames) {
139  av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n");
140  return AVERROR(ENOMEM);
141  }
142  return 0;
143 }
144 
145 #define INTERLACE_TYPE_P 0
146 #define INTERLACE_TYPE_T 1
147 #define INTERLACE_TYPE_B 2
148 
149 static int config_input(AVFilterLink *inlink)
150 {
151  SelectContext *select = inlink->dst->priv;
152 
153  select->var_values[VAR_E] = M_E;
154  select->var_values[VAR_PHI] = M_PHI;
155  select->var_values[VAR_PI] = M_PI;
156 
157  select->var_values[VAR_N] = 0.0;
158  select->var_values[VAR_SELECTED_N] = 0.0;
159 
160  select->var_values[VAR_TB] = av_q2d(inlink->time_base);
161 
162  select->var_values[VAR_PREV_PTS] = NAN;
165  select->var_values[VAR_START_PTS] = NAN;
166  select->var_values[VAR_START_T] = NAN;
167 
173 
177 
178  return 0;
179 }
180 
181 #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
182 #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
183 
185 {
186  SelectContext *select = ctx->priv;
187  AVFilterLink *inlink = ctx->inputs[0];
188  double res;
189 
190  if (isnan(select->var_values[VAR_START_PTS]))
191  select->var_values[VAR_START_PTS] = TS2D(picref->pts);
192  if (isnan(select->var_values[VAR_START_T]))
193  select->var_values[VAR_START_T] = TS2D(picref->pts) * av_q2d(inlink->time_base);
194 
195  select->var_values[VAR_PTS] = TS2D(picref->pts);
196  select->var_values[VAR_T ] = TS2D(picref->pts) * av_q2d(inlink->time_base);
197  select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
198  select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
199 
200  select->var_values[VAR_INTERLACE_TYPE] =
201  !picref->video->interlaced ? INTERLACE_TYPE_P :
203  select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
204 
205  res = av_expr_eval(select->expr, select->var_values, NULL);
206  av_log(inlink->dst, AV_LOG_DEBUG,
207  "n:%d pts:%d t:%f pos:%d interlace_type:%c key:%d pict_type:%c "
208  "-> select:%f\n",
209  (int)select->var_values[VAR_N],
210  (int)select->var_values[VAR_PTS],
211  select->var_values[VAR_T],
212  (int)select->var_values[VAR_POS],
215  select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
216  (int)select->var_values[VAR_KEY],
218  res);
219 
220  select->var_values[VAR_N] += 1.0;
221 
222  if (res) {
223  select->var_values[VAR_PREV_SELECTED_N] = select->var_values[VAR_N];
225  select->var_values[VAR_PREV_SELECTED_T] = select->var_values[VAR_T];
226  select->var_values[VAR_SELECTED_N] += 1.0;
227  }
228  return res;
229 }
230 
231 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
232 {
233  SelectContext *select = inlink->dst->priv;
234 
235  select->select = select_frame(inlink->dst, frame);
236  if (select->select) {
237  /* frame was requested through poll_frame */
238  if (select->cache_frames) {
239  if (!av_fifo_space(select->pending_frames)) {
240  av_log(inlink->dst, AV_LOG_ERROR,
241  "Buffering limit reached, cannot cache more frames\n");
242  avfilter_unref_bufferp(&frame);
243  } else
244  av_fifo_generic_write(select->pending_frames, &frame,
245  sizeof(frame), NULL);
246  return 0;
247  }
248  return ff_filter_frame(inlink->dst->outputs[0], frame);
249  }
250 
251  avfilter_unref_bufferp(&frame);
252  return 0;
253 }
254 
255 static int request_frame(AVFilterLink *outlink)
256 {
257  AVFilterContext *ctx = outlink->src;
258  SelectContext *select = ctx->priv;
259  AVFilterLink *inlink = outlink->src->inputs[0];
260  select->select = 0;
261 
262  if (av_fifo_size(select->pending_frames)) {
263  AVFilterBufferRef *picref;
264 
265  av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
266  return ff_filter_frame(outlink, picref);
267  }
268 
269  while (!select->select) {
270  int ret = ff_request_frame(inlink);
271  if (ret < 0)
272  return ret;
273  }
274 
275  return 0;
276 }
277 
278 static int poll_frame(AVFilterLink *outlink)
279 {
280  SelectContext *select = outlink->src->priv;
281  AVFilterLink *inlink = outlink->src->inputs[0];
282  int count, ret;
283 
284  if (!av_fifo_size(select->pending_frames)) {
285  if ((count = ff_poll_frame(inlink)) <= 0)
286  return count;
287  /* request frame from input, and apply select condition to it */
288  select->cache_frames = 1;
289  while (count-- && av_fifo_space(select->pending_frames)) {
290  ret = ff_request_frame(inlink);
291  if (ret < 0)
292  break;
293  }
294  select->cache_frames = 0;
295  }
296 
297  return av_fifo_size(select->pending_frames)/sizeof(AVFilterBufferRef *);
298 }
299 
300 static av_cold void uninit(AVFilterContext *ctx)
301 {
302  SelectContext *select = ctx->priv;
303  AVFilterBufferRef *picref;
304 
305  av_expr_free(select->expr);
306  select->expr = NULL;
307 
308  while (select->pending_frames &&
309  av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL) == sizeof(picref))
310  avfilter_unref_buffer(picref);
311  av_fifo_free(select->pending_frames);
312  select->pending_frames = NULL;
313 }
314 
316  {
317  .name = "default",
318  .type = AVMEDIA_TYPE_VIDEO,
319  .get_video_buffer = ff_null_get_video_buffer,
320  .config_props = config_input,
321  .filter_frame = filter_frame,
322  },
323  { NULL }
324 };
325 
327  {
328  .name = "default",
329  .type = AVMEDIA_TYPE_VIDEO,
330  .poll_frame = poll_frame,
331  .request_frame = request_frame,
332  },
333  { NULL }
334 };
335 
337  .name = "select",
338  .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
339  .init = init,
340  .uninit = uninit,
341 
342  .priv_size = sizeof(SelectContext),
343 
344  .inputs = avfilter_vf_select_inputs,
345  .outputs = avfilter_vf_select_outputs,
346 };