Browse code

lavfi: add select filter

Address trac issue #92.

Stefano Sabatini authored on 2011/05/07 09:06:25
Showing 6 changed files
... ...
@@ -18,6 +18,7 @@ version <next>:
18 18
 - 9bit and 10bit H.264 decoding
19 19
 - 9bit and 10bit FFV1 encoding / decoding
20 20
 - split filter added
21
+- select filter added
21 22
 
22 23
 
23 24
 version 0.7_beta1:
... ...
@@ -1163,6 +1163,122 @@ scale="trunc(3/2*iw/hsub)*hsub:trunc(3/2*ih/vsub)*vsub"
1163 1163
 scale='min(500\, iw*3/2):-1'
1164 1164
 @end example
1165 1165
 
1166
+@section select
1167
+Select frames to pass in output.
1168
+
1169
+It accepts in input an expression, which is evaluated for each input
1170
+frame. If the expression is evaluated to a non-zero value, the frame
1171
+is selected and passed to the output, otherwise it is discarded.
1172
+
1173
+The expression can contain the following constants:
1174
+
1175
+@table @option
1176
+@item PI
1177
+Greek PI
1178
+
1179
+@item PHI
1180
+golden ratio
1181
+
1182
+@item E
1183
+Euler number
1184
+
1185
+@item n
1186
+the sequential number of the filtered frame, starting from 0
1187
+
1188
+@item selected_n
1189
+the sequential number of the selected frame, starting from 0
1190
+
1191
+@item prev_selected_n
1192
+the sequential number of the last selected frame, NAN if undefined
1193
+
1194
+@item TB
1195
+timebase of the input timestamps
1196
+
1197
+@item pts
1198
+the PTS (Presentation TimeStamp) of the filtered video frame,
1199
+expressed in @var{TB} units, NAN if undefined
1200
+
1201
+@item t
1202
+the PTS (Presentation TimeStamp) of the filtered video frame,
1203
+expressed in seconds, NAN if undefined
1204
+
1205
+@item prev_pts
1206
+the PTS of the previously filtered video frame, NAN if undefined
1207
+
1208
+@item prev_selected_pts
1209
+the PTS of the last previously filtered video frame, NAN if undefined
1210
+
1211
+@item prev_selected_t
1212
+the PTS of the last previously selected video frame, NAN if undefined
1213
+
1214
+@item start_pts
1215
+the PTS of the first video frame in the video, NAN if undefined
1216
+
1217
+@item start_t
1218
+the time of the first video frame in the video, NAN if undefined
1219
+
1220
+@item pict_type
1221
+the picture type of the filtered frame, can assume one of the following
1222
+values:
1223
+@table @option
1224
+@item PICT_TYPE_I
1225
+@item PICT_TYPE_P
1226
+@item PICT_TYPE_B
1227
+@item PICT_TYPE_S
1228
+@item PICT_TYPE_SI
1229
+@item PICT_TYPE_SP
1230
+@item PICT_TYPE_BI
1231
+@end table
1232
+
1233
+@item interlace_type
1234
+the frame interlace type, can assume one of the following values:
1235
+@table @option
1236
+@item INTERLACE_TYPE_P
1237
+the frame is progressive (not interlaced)
1238
+@item INTERLACE_TYPE_T
1239
+the frame is top-field-first
1240
+@item INTERLACE_TYPE_B
1241
+the frame is bottom-field-first
1242
+@end table
1243
+
1244
+@item key
1245
+1 if the filtered frame is a key-frame, 0 otherwise
1246
+
1247
+@item pos
1248
+the position in the file of the filtered frame, -1 if the information
1249
+is not available (e.g. for synthetic video)
1250
+@end table
1251
+
1252
+The default value of the select expression is "1".
1253
+
1254
+Some examples follow:
1255
+
1256
+@example
1257
+# select all frames in input
1258
+select
1259
+
1260
+# the above is the same as:
1261
+select=1
1262
+
1263
+# skip all frames:
1264
+select=0
1265
+
1266
+# select only I-frames
1267
+select='eq(pict_type\,PICT_TYPE_I)'
1268
+
1269
+# select one frame every 100
1270
+select='not(mod(n\,100))'
1271
+
1272
+# select only frames contained in the 10-20 time interval
1273
+select='gte(t\,10)*lte(t\,20)'
1274
+
1275
+# select only I frames contained in the 10-20 time interval
1276
+select='gte(t\,10)*lte(t\,20)*eq(pict_type\,PICT_TYPE_I)'
1277
+
1278
+# select frames with a minimum distance of 10 seconds
1279
+select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)'
1280
+@end example
1281
+
1166 1282
 @anchor{setdar}
1167 1283
 @section setdar
1168 1284
 
... ...
@@ -46,6 +46,7 @@ OBJS-$(CONFIG_OVERLAY_FILTER)                += vf_overlay.o
46 46
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
47 47
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
48 48
 OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o
49
+OBJS-$(CONFIG_SELECT_FILTER)                 += vf_select.o
49 50
 OBJS-$(CONFIG_SETDAR_FILTER)                 += vf_aspect.o
50 51
 OBJS-$(CONFIG_SETPTS_FILTER)                 += vf_setpts.o
51 52
 OBJS-$(CONFIG_SETSAR_FILTER)                 += vf_aspect.o
... ...
@@ -62,6 +62,7 @@ void avfilter_register_all(void)
62 62
     REGISTER_FILTER (PAD,         pad,         vf);
63 63
     REGISTER_FILTER (PIXDESCTEST, pixdesctest, vf);
64 64
     REGISTER_FILTER (SCALE,       scale,       vf);
65
+    REGISTER_FILTER (SELECT,      select,      vf);
65 66
     REGISTER_FILTER (SETDAR,      setdar,      vf);
66 67
     REGISTER_FILTER (SETPTS,      setpts,      vf);
67 68
     REGISTER_FILTER (SETSAR,      setsar,      vf);
... ...
@@ -26,7 +26,7 @@
26 26
 #include "libavutil/samplefmt.h"
27 27
 
28 28
 #define LIBAVFILTER_VERSION_MAJOR  2
29
-#define LIBAVFILTER_VERSION_MINOR 10
29
+#define LIBAVFILTER_VERSION_MINOR 11
30 30
 #define LIBAVFILTER_VERSION_MICRO  0
31 31
 
32 32
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
33 33
new file mode 100644
... ...
@@ -0,0 +1,351 @@
0
+/*
1
+ * Copyright (c) 2011 Stefano Sabatini
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg 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
+ * FFmpeg 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 FFmpeg; 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 "avfilter.h"
28
+
29
+static const char *var_names[] = {
30
+    "E",                 ///< Euler number
31
+    "PHI",               ///< golden ratio
32
+    "PI",                ///< greek pi
33
+
34
+    "TB",                ///< timebase
35
+
36
+    "pts",               ///< original pts in the file of the frame
37
+    "start_pts",         ///< first PTS in the stream, expressed in TB units
38
+    "prev_pts",          ///< previous frame PTS
39
+    "prev_selected_pts", ///< previous selected frame PTS
40
+
41
+    "t",                 ///< first PTS in seconds
42
+    "start_t",           ///< first PTS in the stream, expressed in seconds
43
+    "prev_t",            ///< previous frame time
44
+    "prev_selected_t",   ///< previously selected time
45
+
46
+    "pict_type",         ///< the type of picture in the movie
47
+    "PICT_TYPE_I",
48
+    "PICT_TYPE_P",
49
+    "PICT_TYPE_B",
50
+    "PICT_TYPE_S",
51
+    "PICT_TYPE_SI",
52
+    "PICT_TYPE_SP",
53
+    "PICT_TYPE_BI",
54
+
55
+    "interlace_type",    ///< the frame interlace type
56
+    "INTERLACE_TYPE_P",
57
+    "INTERLACE_TYPE_T",
58
+    "INTERLACE_TYPE_B",
59
+
60
+    "n",                 ///< frame number (starting from zero)
61
+    "selected_n",        ///< selected frame number (starting from zero)
62
+    "prev_selected_n",   ///< number of the last selected frame
63
+
64
+    "key",               ///< tell if the frame is a key frame
65
+    "pos",               ///< original position in the file of the frame
66
+
67
+    NULL
68
+};
69
+
70
+enum var_name {
71
+    VAR_E,
72
+    VAR_PHI,
73
+    VAR_PI,
74
+
75
+    VAR_TB,
76
+
77
+    VAR_PTS,
78
+    VAR_START_PTS,
79
+    VAR_PREV_PTS,
80
+    VAR_PREV_SELECTED_PTS,
81
+
82
+    VAR_T,
83
+    VAR_START_T,
84
+    VAR_PREV_T,
85
+    VAR_PREV_SELECTED_T,
86
+
87
+    VAR_PICT_TYPE,
88
+    VAR_PICT_TYPE_I,
89
+    VAR_PICT_TYPE_P,
90
+    VAR_PICT_TYPE_B,
91
+    VAR_PICT_TYPE_S,
92
+    VAR_PICT_TYPE_SI,
93
+    VAR_PICT_TYPE_SP,
94
+    VAR_PICT_TYPE_BI,
95
+
96
+    VAR_INTERLACE_TYPE,
97
+    VAR_INTERLACE_TYPE_P,
98
+    VAR_INTERLACE_TYPE_T,
99
+    VAR_INTERLACE_TYPE_B,
100
+
101
+    VAR_N,
102
+    VAR_SELECTED_N,
103
+    VAR_PREV_SELECTED_N,
104
+
105
+    VAR_KEY,
106
+    VAR_POS,
107
+
108
+    VAR_VARS_NB
109
+};
110
+
111
+#define FIFO_SIZE 8
112
+
113
+typedef struct {
114
+    AVExpr *expr;
115
+    double var_values[VAR_VARS_NB];
116
+    double select;
117
+    int cache_frames;
118
+    AVFifoBuffer *pending_frames; ///< FIFO buffer of video frames
119
+} SelectContext;
120
+
121
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
122
+{
123
+    SelectContext *select = ctx->priv;
124
+    int ret;
125
+
126
+    if ((ret = av_expr_parse(&select->expr, args ? args : "1",
127
+                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
128
+        av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args);
129
+        return ret;
130
+    }
131
+
132
+    select->pending_frames = av_fifo_alloc(FIFO_SIZE*sizeof(AVFilterBufferRef*));
133
+    if (!select->pending_frames) {
134
+        av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n");
135
+        return AVERROR(ENOMEM);
136
+    }
137
+    return 0;
138
+}
139
+
140
+#define INTERLACE_TYPE_P 0
141
+#define INTERLACE_TYPE_T 1
142
+#define INTERLACE_TYPE_B 2
143
+
144
+static int config_input(AVFilterLink *inlink)
145
+{
146
+    SelectContext *select = inlink->dst->priv;
147
+
148
+    select->var_values[VAR_E]   = M_E;
149
+    select->var_values[VAR_PHI] = M_PHI;
150
+    select->var_values[VAR_PI]  = M_PI;
151
+
152
+    select->var_values[VAR_N]          = 0.0;
153
+    select->var_values[VAR_SELECTED_N] = 0.0;
154
+
155
+    select->var_values[VAR_TB] = av_q2d(inlink->time_base);
156
+
157
+    select->var_values[VAR_PREV_PTS]          = NAN;
158
+    select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
159
+    select->var_values[VAR_PREV_SELECTED_T]   = NAN;
160
+    select->var_values[VAR_START_PTS]         = NAN;
161
+    select->var_values[VAR_START_T]           = NAN;
162
+
163
+    select->var_values[VAR_PICT_TYPE_I]  = AV_PICTURE_TYPE_I;
164
+    select->var_values[VAR_PICT_TYPE_P]  = AV_PICTURE_TYPE_P;
165
+    select->var_values[VAR_PICT_TYPE_B]  = AV_PICTURE_TYPE_B;
166
+    select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
167
+    select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
168
+
169
+    select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
170
+    select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
171
+    select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;;
172
+
173
+    return 0;
174
+}
175
+
176
+#define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
177
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
178
+
179
+static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref)
180
+{
181
+    SelectContext *select = ctx->priv;
182
+    AVFilterLink *inlink = ctx->inputs[0];
183
+    double res;
184
+
185
+    if (isnan(select->var_values[VAR_START_PTS]))
186
+        select->var_values[VAR_START_PTS] = TS2D(picref->pts);
187
+
188
+    select->var_values[VAR_PTS] = TS2D(picref->pts);
189
+    select->var_values[VAR_T  ] = picref->pts * av_q2d(inlink->time_base);
190
+    select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
191
+    select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
192
+
193
+    select->var_values[VAR_INTERLACE_TYPE] =
194
+        !picref->video->interlaced     ? INTERLACE_TYPE_P :
195
+        picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
196
+    select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
197
+
198
+    res = av_expr_eval(select->expr, select->var_values, NULL);
199
+    av_log(inlink->dst, AV_LOG_DEBUG,
200
+           "n:%d pts:%d t:%f pos:%d interlace_type:%c key:%d pict_type:%c "
201
+           "-> select:%f\n",
202
+           (int)select->var_values[VAR_N],
203
+           (int)select->var_values[VAR_PTS],
204
+           select->var_values[VAR_T],
205
+           (int)select->var_values[VAR_POS],
206
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
207
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
208
+           select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
209
+           (int)select->var_values[VAR_KEY],
210
+           av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]),
211
+           res);
212
+
213
+    select->var_values[VAR_N] += 1.0;
214
+
215
+    if (res) {
216
+        select->var_values[VAR_PREV_SELECTED_N]   = select->var_values[VAR_N];
217
+        select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
218
+        select->var_values[VAR_PREV_SELECTED_T]   = select->var_values[VAR_T];
219
+        select->var_values[VAR_SELECTED_N] += 1.0;
220
+    }
221
+    return res;
222
+}
223
+
224
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
225
+{
226
+    SelectContext *select = inlink->dst->priv;
227
+
228
+    select->select = select_frame(inlink->dst, picref);
229
+    if (select->select) {
230
+        /* frame was requested through poll_frame */
231
+        if (select->cache_frames) {
232
+            if (!av_fifo_space(select->pending_frames))
233
+                av_log(inlink->dst, AV_LOG_ERROR,
234
+                       "Buffering limit reached, cannot cache more frames\n");
235
+            else
236
+                av_fifo_generic_write(select->pending_frames, &picref,
237
+                                      sizeof(picref), NULL);
238
+            return;
239
+        }
240
+        avfilter_start_frame(inlink->dst->outputs[0], avfilter_ref_buffer(picref, ~0));
241
+    }
242
+}
243
+
244
+static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
245
+{
246
+    SelectContext *select = inlink->dst->priv;
247
+
248
+    if (select->select && !select->cache_frames)
249
+        avfilter_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
250
+}
251
+
252
+static void end_frame(AVFilterLink *inlink)
253
+{
254
+    SelectContext *select = inlink->dst->priv;
255
+    AVFilterBufferRef *picref = inlink->cur_buf;
256
+
257
+    if (select->select) {
258
+        if (select->cache_frames)
259
+            return;
260
+        avfilter_end_frame(inlink->dst->outputs[0]);
261
+    }
262
+    avfilter_unref_buffer(picref);
263
+}
264
+
265
+static int request_frame(AVFilterLink *outlink)
266
+{
267
+    AVFilterContext *ctx = outlink->src;
268
+    SelectContext *select = ctx->priv;
269
+    AVFilterLink *inlink = outlink->src->inputs[0];
270
+    select->select = 0;
271
+
272
+    if (av_fifo_size(select->pending_frames)) {
273
+        AVFilterBufferRef *picref;
274
+        av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
275
+        avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
276
+        avfilter_draw_slice(outlink, 0, outlink->h, 1);
277
+        avfilter_end_frame(outlink);
278
+        avfilter_unref_buffer(picref);
279
+        return 0;
280
+    }
281
+
282
+    while (!select->select) {
283
+        int ret = avfilter_request_frame(inlink);
284
+        if (ret < 0)
285
+            return ret;
286
+    }
287
+
288
+    return 0;
289
+}
290
+
291
+static int poll_frame(AVFilterLink *outlink)
292
+{
293
+    SelectContext *select = outlink->src->priv;
294
+    AVFilterLink *inlink = outlink->src->inputs[0];
295
+    int count, ret;
296
+
297
+    if (!av_fifo_size(select->pending_frames)) {
298
+        if ((count = avfilter_poll_frame(inlink)) <= 0)
299
+            return count;
300
+        /* request frame from input, and apply select condition to it */
301
+        select->cache_frames = 1;
302
+        while (count-- && av_fifo_space(select->pending_frames)) {
303
+            ret = avfilter_request_frame(inlink);
304
+            if (ret < 0)
305
+                break;
306
+        }
307
+        select->cache_frames = 0;
308
+    }
309
+
310
+    return av_fifo_size(select->pending_frames)/sizeof(AVFilterBufferRef *);
311
+}
312
+
313
+static av_cold void uninit(AVFilterContext *ctx)
314
+{
315
+    SelectContext *select = ctx->priv;
316
+    AVFilterBufferRef *picref;
317
+    int i;
318
+
319
+    av_expr_free(select->expr);
320
+    select->expr = NULL;
321
+
322
+    for (i = 0; i < av_fifo_size(select->pending_frames)/sizeof(picref); i++) {
323
+        av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
324
+        avfilter_unref_buffer(picref);
325
+    }
326
+    av_fifo_free(select->pending_frames);
327
+}
328
+
329
+AVFilter avfilter_vf_select = {
330
+    .name      = "select",
331
+    .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
332
+    .init      = init,
333
+    .uninit    = uninit,
334
+
335
+    .priv_size = sizeof(SelectContext),
336
+
337
+    .inputs    = (AVFilterPad[]) {{ .name             = "default",
338
+                                    .type             = AVMEDIA_TYPE_VIDEO,
339
+                                    .get_video_buffer = avfilter_null_get_video_buffer,
340
+                                    .config_props     = config_input,
341
+                                    .start_frame      = start_frame,
342
+                                    .draw_slice       = draw_slice,
343
+                                    .end_frame        = end_frame },
344
+                                  { .name = NULL }},
345
+    .outputs   = (AVFilterPad[]) {{ .name             = "default",
346
+                                    .type             = AVMEDIA_TYPE_VIDEO,
347
+                                    .poll_frame       = poll_frame,
348
+                                    .request_frame    = request_frame, },
349
+                                  { .name = NULL}},
350
+};