History

lavfi: add select filter

Address trac issue #92.

Stefano Sabatini committed 15 years ago

Commit 07586b61 parent 3c2c52b6 files · +471 −1Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. doc/filters.texi +116 −0
  3. libavfilter/Makefile +1 −0
  4. libavfilter/allfilters.c +1 −0
  5. libavfilter/avfilter.h +1 −1
  6. libavfilter/vf_select.c +351 −0
Changelog+1−0
@@ -18,6 +18,7 @@ version <next>: · 17 unmodified lines
1818 - 9bit and 10bit H.264 decoding
1919 - 9bit and 10bit FFV1 encoding / decoding
2020 - split filter added
21+- select filter added
2122
2223
2324 version 0.7_beta1:
doc/filters.texi+116−0
@@ -1163,6 +1163,122 @@ scale="trunc(3/2*iw/hsub)*hsub:trunc(3/2*ih/vsub)*vsub" · 1162 unmodified lines
11631163 scale='min(500\, iw*3/2):-1'
11641164 @end example
11651165
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+
11661282 @anchor{setdar}
11671283 @section setdar
11681284
libavfilter/Makefile+1−0
@@ -46,6 +46,7 @@ OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o · 45 unmodified lines
4646 OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
4747 OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o
4848 OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o
49+OBJS-$(CONFIG_SELECT_FILTER) += vf_select.o
4950 OBJS-$(CONFIG_SETDAR_FILTER) += vf_aspect.o
5051 OBJS-$(CONFIG_SETPTS_FILTER) += vf_setpts.o
5152 OBJS-$(CONFIG_SETSAR_FILTER) += vf_aspect.o
libavfilter/allfilters.c+1−0
@@ -62,6 +62,7 @@ void avfilter_register_all(void) · 61 unmodified lines
6262 REGISTER_FILTER (PAD, pad, vf);
6363 REGISTER_FILTER (PIXDESCTEST, pixdesctest, vf);
6464 REGISTER_FILTER (SCALE, scale, vf);
65+ REGISTER_FILTER (SELECT, select, vf);
6566 REGISTER_FILTER (SETDAR, setdar, vf);
6667 REGISTER_FILTER (SETPTS, setpts, vf);
6768 REGISTER_FILTER (SETSAR, setsar, vf);
libavfilter/avfilter.h+1−1
@@ -26,7 +26,7 @@ · 25 unmodified lines
2626 #include "libavutil/samplefmt.h"
2727
2828 #define LIBAVFILTER_VERSION_MAJOR 2
29−#define LIBAVFILTER_VERSION_MINOR 10
29+#define LIBAVFILTER_VERSION_MINOR 11
3030 #define LIBAVFILTER_VERSION_MICRO 0
3131
3232 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
libavfilter/vf_select.c+351−0
@@ -0,0 +1,351 @@
1+/*
2+ * Copyright (c) 2011 Stefano Sabatini
3+ *
4+ * This file is part of FFmpeg.
5+ *
6+ * FFmpeg 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+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
18+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19+ */
20+
21+/**
22+ * @file
23+ * filter for selecting which frame passes in the filterchain
24+ */
25+
26+#include "libavutil/eval.h"
27+#include "libavutil/fifo.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+ "PICT_TYPE_I",
49+ "PICT_TYPE_P",
50+ "PICT_TYPE_B",
51+ "PICT_TYPE_S",
52+ "PICT_TYPE_SI",
53+ "PICT_TYPE_SP",
54+ "PICT_TYPE_BI",
55+
56+ "interlace_type", ///< the frame interlace type
57+ "INTERLACE_TYPE_P",
58+ "INTERLACE_TYPE_T",
59+ "INTERLACE_TYPE_B",
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+
189+ select->var_values[VAR_PTS] = TS2D(picref->pts);
190+ select->var_values[VAR_T ] = picref->pts * av_q2d(inlink->time_base);
191+ select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
192+ select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
193+
194+ select->var_values[VAR_INTERLACE_TYPE] =
195+ !picref->video->interlaced ? INTERLACE_TYPE_P :
196+ picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
197+ select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
198+
199+ res = av_expr_eval(select->expr, select->var_values, NULL);
200+ av_log(inlink->dst, AV_LOG_DEBUG,
201+ "n:%d pts:%d t:%f pos:%d interlace_type:%c key:%d pict_type:%c "
202+ "-> select:%f\n",
203+ (int)select->var_values[VAR_N],
204+ (int)select->var_values[VAR_PTS],
205+ select->var_values[VAR_T],
206+ (int)select->var_values[VAR_POS],
207+ select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
208+ select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
209+ select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
210+ (int)select->var_values[VAR_KEY],
211+ av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]),
212+ res);
213+
214+ select->var_values[VAR_N] += 1.0;
215+
216+ if (res) {
217+ select->var_values[VAR_PREV_SELECTED_N] = select->var_values[VAR_N];
218+ select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
219+ select->var_values[VAR_PREV_SELECTED_T] = select->var_values[VAR_T];
220+ select->var_values[VAR_SELECTED_N] += 1.0;
221+ }
222+ return res;
223+}
224+
225+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
226+{
227+ SelectContext *select = inlink->dst->priv;
228+
229+ select->select = select_frame(inlink->dst, picref);
230+ if (select->select) {
231+ /* frame was requested through poll_frame */
232+ if (select->cache_frames) {
233+ if (!av_fifo_space(select->pending_frames))
234+ av_log(inlink->dst, AV_LOG_ERROR,
235+ "Buffering limit reached, cannot cache more frames\n");
236+ else
237+ av_fifo_generic_write(select->pending_frames, &picref,
238+ sizeof(picref), NULL);
239+ return;
240+ }
241+ avfilter_start_frame(inlink->dst->outputs[0], avfilter_ref_buffer(picref, ~0));
242+ }
243+}
244+
245+static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
246+{
247+ SelectContext *select = inlink->dst->priv;
248+
249+ if (select->select && !select->cache_frames)
250+ avfilter_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
251+}
252+
253+static void end_frame(AVFilterLink *inlink)
254+{
255+ SelectContext *select = inlink->dst->priv;
256+ AVFilterBufferRef *picref = inlink->cur_buf;
257+
258+ if (select->select) {
259+ if (select->cache_frames)
260+ return;
261+ avfilter_end_frame(inlink->dst->outputs[0]);
262+ }
263+ avfilter_unref_buffer(picref);
264+}
265+
266+static int request_frame(AVFilterLink *outlink)
267+{
268+ AVFilterContext *ctx = outlink->src;
269+ SelectContext *select = ctx->priv;
270+ AVFilterLink *inlink = outlink->src->inputs[0];
271+ select->select = 0;
272+
273+ if (av_fifo_size(select->pending_frames)) {
274+ AVFilterBufferRef *picref;
275+ av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
276+ avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
277+ avfilter_draw_slice(outlink, 0, outlink->h, 1);
278+ avfilter_end_frame(outlink);
279+ avfilter_unref_buffer(picref);
280+ return 0;
281+ }
282+
283+ while (!select->select) {
284+ int ret = avfilter_request_frame(inlink);
285+ if (ret < 0)
286+ return ret;
287+ }
288+
289+ return 0;
290+}
291+
292+static int poll_frame(AVFilterLink *outlink)
293+{
294+ SelectContext *select = outlink->src->priv;
295+ AVFilterLink *inlink = outlink->src->inputs[0];
296+ int count, ret;
297+
298+ if (!av_fifo_size(select->pending_frames)) {
299+ if ((count = avfilter_poll_frame(inlink)) <= 0)
300+ return count;
301+ /* request frame from input, and apply select condition to it */
302+ select->cache_frames = 1;
303+ while (count-- && av_fifo_space(select->pending_frames)) {
304+ ret = avfilter_request_frame(inlink);
305+ if (ret < 0)
306+ break;
307+ }
308+ select->cache_frames = 0;
309+ }
310+
311+ return av_fifo_size(select->pending_frames)/sizeof(AVFilterBufferRef *);
312+}
313+
314+static av_cold void uninit(AVFilterContext *ctx)
315+{
316+ SelectContext *select = ctx->priv;
317+ AVFilterBufferRef *picref;
318+ int i;
319+
320+ av_expr_free(select->expr);
321+ select->expr = NULL;
322+
323+ for (i = 0; i < av_fifo_size(select->pending_frames)/sizeof(picref); i++) {
324+ av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL);
325+ avfilter_unref_buffer(picref);
326+ }
327+ av_fifo_free(select->pending_frames);
328+}
329+
330+AVFilter avfilter_vf_select = {
331+ .name = "select",
332+ .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
333+ .init = init,
334+ .uninit = uninit,
335+
336+ .priv_size = sizeof(SelectContext),
337+
338+ .inputs = (AVFilterPad[]) {{ .name = "default",
339+ .type = AVMEDIA_TYPE_VIDEO,
340+ .get_video_buffer = avfilter_null_get_video_buffer,
341+ .config_props = config_input,
342+ .start_frame = start_frame,
343+ .draw_slice = draw_slice,
344+ .end_frame = end_frame },
345+ { .name = NULL }},
346+ .outputs = (AVFilterPad[]) {{ .name = "default",
347+ .type = AVMEDIA_TYPE_VIDEO,
348+ .poll_frame = poll_frame,
349+ .request_frame = request_frame, },
350+ { .name = NULL}},
351+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t