Browse code

lavfi: add select filter

Signed-off-by: Anton Khirnov <anton@khirnov.net>

Stefano Sabatini authored on 2011/05/07 09:06:25
Showing 6 changed files
... ...
@@ -46,6 +46,7 @@ easier to use. The changes are:
46 46
 - showinfo filter
47 47
 - split filter
48 48
 - libcdio-paranoia input device for audio CD grabbing
49
+- select filter
49 50
 
50 51
 
51 52
 version 0.7:
... ...
@@ -1064,6 +1064,122 @@ scale="trunc(3/2*iw/hsub)*hsub:trunc(3/2*ih/vsub)*vsub"
1064 1064
 scale='min(500\, iw*3/2):-1'
1065 1065
 @end example
1066 1066
 
1067
+@section select
1068
+Select frames to pass in output.
1069
+
1070
+It accepts in input an expression, which is evaluated for each input
1071
+frame. If the expression is evaluated to a non-zero value, the frame
1072
+is selected and passed to the output, otherwise it is discarded.
1073
+
1074
+The expression can contain the following constants:
1075
+
1076
+@table @option
1077
+@item PI
1078
+Greek PI
1079
+
1080
+@item PHI
1081
+golden ratio
1082
+
1083
+@item E
1084
+Euler number
1085
+
1086
+@item n
1087
+the sequential number of the filtered frame, starting from 0
1088
+
1089
+@item selected_n
1090
+the sequential number of the selected frame, starting from 0
1091
+
1092
+@item prev_selected_n
1093
+the sequential number of the last selected frame, NAN if undefined
1094
+
1095
+@item TB
1096
+timebase of the input timestamps
1097
+
1098
+@item pts
1099
+the PTS (Presentation TimeStamp) of the filtered video frame,
1100
+expressed in @var{TB} units, NAN if undefined
1101
+
1102
+@item t
1103
+the PTS (Presentation TimeStamp) of the filtered video frame,
1104
+expressed in seconds, NAN if undefined
1105
+
1106
+@item prev_pts
1107
+the PTS of the previously filtered video frame, NAN if undefined
1108
+
1109
+@item prev_selected_pts
1110
+the PTS of the last previously filtered video frame, NAN if undefined
1111
+
1112
+@item prev_selected_t
1113
+the PTS of the last previously selected video frame, NAN if undefined
1114
+
1115
+@item start_pts
1116
+the PTS of the first video frame in the video, NAN if undefined
1117
+
1118
+@item start_t
1119
+the time of the first video frame in the video, NAN if undefined
1120
+
1121
+@item pict_type
1122
+the type of the filtered frame, can assume one of the following
1123
+values:
1124
+@table @option
1125
+@item I
1126
+@item P
1127
+@item B
1128
+@item S
1129
+@item SI
1130
+@item SP
1131
+@item BI
1132
+@end table
1133
+
1134
+@item interlace_type
1135
+the frame interlace type, can assume one of the following values:
1136
+@table @option
1137
+@item PROGRESSIVE
1138
+the frame is progressive (not interlaced)
1139
+@item TOPFIRST
1140
+the frame is top-field-first
1141
+@item BOTTOMFIRST
1142
+the frame is bottom-field-first
1143
+@end table
1144
+
1145
+@item key
1146
+1 if the filtered frame is a key-frame, 0 otherwise
1147
+
1148
+@item pos
1149
+the position in the file of the filtered frame, -1 if the information
1150
+is not available (e.g. for synthetic video)
1151
+@end table
1152
+
1153
+The default value of the select expression is "1".
1154
+
1155
+Some examples follow:
1156
+
1157
+@example
1158
+# select all frames in input
1159
+select
1160
+
1161
+# the above is the same as:
1162
+select=1
1163
+
1164
+# skip all frames:
1165
+select=0
1166
+
1167
+# select only I-frames
1168
+select='eq(pict_type\,I)'
1169
+
1170
+# select one frame every 100
1171
+select='not(mod(n\,100))'
1172
+
1173
+# select only frames contained in the 10-20 time interval
1174
+select='gte(t\,10)*lte(t\,20)'
1175
+
1176
+# select only I frames contained in the 10-20 time interval
1177
+select='gte(t\,10)*lte(t\,20)*eq(pict_type\,I)'
1178
+
1179
+# select frames with a minimum distance of 10 seconds
1180
+select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)'
1181
+@end example
1182
+
1067 1183
 @anchor{setdar}
1068 1184
 @section setdar
1069 1185
 
... ...
@@ -40,6 +40,7 @@ OBJS-$(CONFIG_OVERLAY_FILTER)                += vf_overlay.o
40 40
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
41 41
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
42 42
 OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o
43
+OBJS-$(CONFIG_SELECT_FILTER)                 += vf_select.o
43 44
 OBJS-$(CONFIG_SETDAR_FILTER)                 += vf_aspect.o
44 45
 OBJS-$(CONFIG_SETPTS_FILTER)                 += vf_setpts.o
45 46
 OBJS-$(CONFIG_SETSAR_FILTER)                 += vf_aspect.o
... ...
@@ -61,6 +61,7 @@ void avfilter_register_all(void)
61 61
     REGISTER_FILTER (PAD,         pad,         vf);
62 62
     REGISTER_FILTER (PIXDESCTEST, pixdesctest, vf);
63 63
     REGISTER_FILTER (SCALE,       scale,       vf);
64
+    REGISTER_FILTER (SELECT,      select,      vf);
64 65
     REGISTER_FILTER (SETDAR,      setdar,      vf);
65 66
     REGISTER_FILTER (SETPTS,      setpts,      vf);
66 67
     REGISTER_FILTER (SETSAR,      setsar,      vf);
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/rational.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  2
32
-#define LIBAVFILTER_VERSION_MINOR  6
32
+#define LIBAVFILTER_VERSION_MINOR  7
33 33
 #define LIBAVFILTER_VERSION_MICRO  0
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
new file mode 100644
... ...
@@ -0,0 +1,353 @@
0
+/*
1
+ * Copyright (c) 2011 Stefano Sabatini
2
+ *
3
+ * This file is part of Libav.
4
+ *
5
+ * Libav is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * Libav is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with Libav; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/**
21
+ * @file
22
+ * filter for selecting which frame passes in the filterchain
23
+ */
24
+
25
+#include "libavutil/eval.h"
26
+#include "libavutil/fifo.h"
27
+#include "libavutil/mathematics.h"
28
+#include "avfilter.h"
29
+
30
+static const char *var_names[] = {
31
+    "E",                 ///< Euler number
32
+    "PHI",               ///< golden ratio
33
+    "PI",                ///< greek pi
34
+
35
+    "TB",                ///< timebase
36
+
37
+    "pts",               ///< original pts in the file of the frame
38
+    "start_pts",         ///< first PTS in the stream, expressed in TB units
39
+    "prev_pts",          ///< previous frame PTS
40
+    "prev_selected_pts", ///< previous selected frame PTS
41
+
42
+    "t",                 ///< first PTS in seconds
43
+    "start_t",           ///< first PTS in the stream, expressed in seconds
44
+    "prev_t",            ///< previous frame time
45
+    "prev_selected_t",   ///< previously selected time
46
+
47
+    "pict_type",         ///< the type of picture in the movie
48
+    "I",
49
+    "P",
50
+    "B",
51
+    "S",
52
+    "SI",
53
+    "SP",
54
+    "BI",
55
+
56
+    "interlace_type",    ///< the frame interlace type
57
+    "PROGRESSIVE",
58
+    "TOPFIRST",
59
+    "BOTTOMFIRST",
60
+
61
+    "n",                 ///< frame number (starting from zero)
62
+    "selected_n",        ///< selected frame number (starting from zero)
63
+    "prev_selected_n",   ///< number of the last selected frame
64
+
65
+    "key",               ///< tell if the frame is a key frame
66
+    "pos",               ///< original position in the file of the frame
67
+
68
+    NULL
69
+};
70
+
71
+enum var_name {
72
+    VAR_E,
73
+    VAR_PHI,
74
+    VAR_PI,
75
+
76
+    VAR_TB,
77
+
78
+    VAR_PTS,
79
+    VAR_START_PTS,
80
+    VAR_PREV_PTS,
81
+    VAR_PREV_SELECTED_PTS,
82
+
83
+    VAR_T,
84
+    VAR_START_T,
85
+    VAR_PREV_T,
86
+    VAR_PREV_SELECTED_T,
87
+
88
+    VAR_PICT_TYPE,
89
+    VAR_PICT_TYPE_I,
90
+    VAR_PICT_TYPE_P,
91
+    VAR_PICT_TYPE_B,
92
+    VAR_PICT_TYPE_S,
93
+    VAR_PICT_TYPE_SI,
94
+    VAR_PICT_TYPE_SP,
95
+    VAR_PICT_TYPE_BI,
96
+
97
+    VAR_INTERLACE_TYPE,
98
+    VAR_INTERLACE_TYPE_P,
99
+    VAR_INTERLACE_TYPE_T,
100
+    VAR_INTERLACE_TYPE_B,
101
+
102
+    VAR_N,
103
+    VAR_SELECTED_N,
104
+    VAR_PREV_SELECTED_N,
105
+
106
+    VAR_KEY,
107
+    VAR_POS,
108
+
109
+    VAR_VARS_NB
110
+};
111
+
112
+#define FIFO_SIZE 8
113
+
114
+typedef struct {
115
+    AVExpr *expr;
116
+    double var_values[VAR_VARS_NB];
117
+    double select;
118
+    int cache_frames;
119
+    AVFifoBuffer *pending_frames; ///< FIFO buffer of video frames
120
+} SelectContext;
121
+
122
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
123
+{
124
+    SelectContext *select = ctx->priv;
125
+    int ret;
126
+
127
+    if ((ret = av_expr_parse(&select->expr, args ? args : "1",
128
+                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
129
+        av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args);
130
+        return ret;
131
+    }
132
+
133
+    select->pending_frames = av_fifo_alloc(FIFO_SIZE*sizeof(AVFilterBufferRef*));
134
+    if (!select->pending_frames) {
135
+        av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n");
136
+        return AVERROR(ENOMEM);
137
+    }
138
+    return 0;
139
+}
140
+
141
+#define INTERLACE_TYPE_P 0
142
+#define INTERLACE_TYPE_T 1
143
+#define INTERLACE_TYPE_B 2
144
+
145
+static int config_input(AVFilterLink *inlink)
146
+{
147
+    SelectContext *select = inlink->dst->priv;
148
+
149
+    select->var_values[VAR_E]   = M_E;
150
+    select->var_values[VAR_PHI] = M_PHI;
151
+    select->var_values[VAR_PI]  = M_PI;
152
+
153
+    select->var_values[VAR_N]          = 0.0;
154
+    select->var_values[VAR_SELECTED_N] = 0.0;
155
+
156
+    select->var_values[VAR_TB] = av_q2d(inlink->time_base);
157
+
158
+    select->var_values[VAR_PREV_PTS]          = NAN;
159
+    select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
160
+    select->var_values[VAR_PREV_SELECTED_T]   = NAN;
161
+    select->var_values[VAR_START_PTS]         = NAN;
162
+    select->var_values[VAR_START_T]           = NAN;
163
+
164
+    select->var_values[VAR_PICT_TYPE_I]  = AV_PICTURE_TYPE_I;
165
+    select->var_values[VAR_PICT_TYPE_P]  = AV_PICTURE_TYPE_P;
166
+    select->var_values[VAR_PICT_TYPE_B]  = AV_PICTURE_TYPE_B;
167
+    select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
168
+    select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
169
+
170
+    select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
171
+    select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
172
+    select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;;
173
+
174
+    return 0;
175
+}
176
+
177
+#define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
178
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
179
+
180
+static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref)
181
+{
182
+    SelectContext *select = ctx->priv;
183
+    AVFilterLink *inlink = ctx->inputs[0];
184
+    double res;
185
+
186
+    if (isnan(select->var_values[VAR_START_PTS]))
187
+        select->var_values[VAR_START_PTS] = TS2D(picref->pts);
188
+    if (isnan(select->var_values[VAR_START_T]))
189
+        select->var_values[VAR_START_T] = TS2D(picref->pts) * av_q2d(inlink->time_base);
190
+
191
+    select->var_values[VAR_PTS] = TS2D(picref->pts);
192
+    select->var_values[VAR_T  ] = TS2D(picref->pts) * av_q2d(inlink->time_base);
193
+    select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
194
+    select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
195
+
196
+    select->var_values[VAR_INTERLACE_TYPE] =
197
+        !picref->video->interlaced     ? INTERLACE_TYPE_P :
198
+        picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
199
+    select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
200
+
201
+    res = av_expr_eval(select->expr, select->var_values, NULL);
202
+    av_log(inlink->dst, AV_LOG_DEBUG,
203
+           "n:%d pts:%d t:%f pos:%d interlace_type:%c key:%d pict_type:%c "
204
+           "-> select:%f\n",
205
+           (int)select->var_values[VAR_N],
206
+           (int)select->var_values[VAR_PTS],
207
+           select->var_values[VAR_T],
208
+           (int)select->var_values[VAR_POS],
209
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
210
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
211
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
212
+           (int)select->var_values[VAR_KEY],
213
+           av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]),
214
+           res);
215
+
216
+    select->var_values[VAR_N] += 1.0;
217
+
218
+    if (res) {
219
+        select->var_values[VAR_PREV_SELECTED_N]   = select->var_values[VAR_N];
220
+        select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
221
+        select->var_values[VAR_PREV_SELECTED_T]   = select->var_values[VAR_T];
222
+        select->var_values[VAR_SELECTED_N] += 1.0;
223
+    }
224
+    return res;
225
+}
226
+
227
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
228
+{
229
+    SelectContext *select = inlink->dst->priv;
230
+
231
+    select->select = select_frame(inlink->dst, picref);
232
+    if (select->select) {
233
+        /* frame was requested through poll_frame */
234
+        if (select->cache_frames) {
235
+            if (!av_fifo_space(select->pending_frames))
236
+                av_log(inlink->dst, AV_LOG_ERROR,
237
+                       "Buffering limit reached, cannot cache more frames\n");
238
+            else
239
+                av_fifo_generic_write(select->pending_frames, &picref,
240
+                                      sizeof(picref), NULL);
241
+            return;
242
+        }
243
+        avfilter_start_frame(inlink->dst->outputs[0], avfilter_ref_buffer(picref, ~0));
244
+    }
245
+}
246
+
247
+static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
248
+{
249
+    SelectContext *select = inlink->dst->priv;
250
+
251
+    if (select->select && !select->cache_frames)
252
+        avfilter_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
253
+}
254
+
255
+static void end_frame(AVFilterLink *inlink)
256
+{
257
+    SelectContext *select = inlink->dst->priv;
258
+    AVFilterBufferRef *picref = inlink->cur_buf;
259
+
260
+    if (select->select) {
261
+        if (select->cache_frames)
262
+            return;
263
+        avfilter_end_frame(inlink->dst->outputs[0]);
264
+    }
265
+    avfilter_unref_buffer(picref);
266
+}
267
+
268
+static int request_frame(AVFilterLink *outlink)
269
+{
270
+    AVFilterContext *ctx = outlink->src;
271
+    SelectContext *select = ctx->priv;
272
+    AVFilterLink *inlink = outlink->src->inputs[0];
273
+    select->select = 0;
274
+
275
+    if (av_fifo_size(select->pending_frames)) {
276
+        AVFilterBufferRef *picref;
277
+        av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
278
+        avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
279
+        avfilter_draw_slice(outlink, 0, outlink->h, 1);
280
+        avfilter_end_frame(outlink);
281
+        avfilter_unref_buffer(picref);
282
+        return 0;
283
+    }
284
+
285
+    while (!select->select) {
286
+        int ret = avfilter_request_frame(inlink);
287
+        if (ret < 0)
288
+            return ret;
289
+    }
290
+
291
+    return 0;
292
+}
293
+
294
+static int poll_frame(AVFilterLink *outlink)
295
+{
296
+    SelectContext *select = outlink->src->priv;
297
+    AVFilterLink *inlink = outlink->src->inputs[0];
298
+    int count, ret;
299
+
300
+    if (!av_fifo_size(select->pending_frames)) {
301
+        if ((count = avfilter_poll_frame(inlink)) <= 0)
302
+            return count;
303
+        /* request frame from input, and apply select condition to it */
304
+        select->cache_frames = 1;
305
+        while (count-- && av_fifo_space(select->pending_frames)) {
306
+            ret = avfilter_request_frame(inlink);
307
+            if (ret < 0)
308
+                break;
309
+        }
310
+        select->cache_frames = 0;
311
+    }
312
+
313
+    return av_fifo_size(select->pending_frames)/sizeof(AVFilterBufferRef *);
314
+}
315
+
316
+static av_cold void uninit(AVFilterContext *ctx)
317
+{
318
+    SelectContext *select = ctx->priv;
319
+    AVFilterBufferRef *picref;
320
+
321
+    av_expr_free(select->expr);
322
+    select->expr = NULL;
323
+
324
+    while (select->pending_frames &&
325
+           av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL) == sizeof(picref))
326
+        avfilter_unref_buffer(picref);
327
+    av_fifo_free(select->pending_frames);
328
+    select->pending_frames = NULL;
329
+}
330
+
331
+AVFilter avfilter_vf_select = {
332
+    .name      = "select",
333
+    .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
334
+    .init      = init,
335
+    .uninit    = uninit,
336
+
337
+    .priv_size = sizeof(SelectContext),
338
+
339
+    .inputs    = (AVFilterPad[]) {{ .name             = "default",
340
+                                    .type             = AVMEDIA_TYPE_VIDEO,
341
+                                    .get_video_buffer = avfilter_null_get_video_buffer,
342
+                                    .config_props     = config_input,
343
+                                    .start_frame      = start_frame,
344
+                                    .draw_slice       = draw_slice,
345
+                                    .end_frame        = end_frame },
346
+                                  { .name = NULL }},
347
+    .outputs   = (AVFilterPad[]) {{ .name             = "default",
348
+                                    .type             = AVMEDIA_TYPE_VIDEO,
349
+                                    .poll_frame       = poll_frame,
350
+                                    .request_frame    = request_frame, },
351
+                                  { .name = NULL}},
352
+};