]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_select.c
lavfi: name anonymous structs
[ffmpeg] / libavfilter / vf_select.c
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
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 "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",                 ///< Euler number
37     "PHI",               ///< golden ratio
38     "PI",                ///< greek pi
39
40     "TB",                ///< timebase
41
42     "pts",               ///< original pts in the file of the frame
43     "start_pts",         ///< first PTS in the stream, expressed in TB units
44     "prev_pts",          ///< previous frame PTS
45     "prev_selected_pts", ///< previous selected frame PTS
46
47     "t",                 ///< first PTS in seconds
48     "start_t",           ///< first PTS in the stream, expressed in seconds
49     "prev_t",            ///< previous frame time
50     "prev_selected_t",   ///< previously selected time
51
52     "pict_type",         ///< the type of picture in the movie
53     "I",
54     "P",
55     "B",
56     "S",
57     "SI",
58     "SP",
59     "BI",
60
61     "interlace_type",    ///< the frame interlace type
62     "PROGRESSIVE",
63     "TOPFIRST",
64     "BOTTOMFIRST",
65
66     "n",                 ///< frame number (starting from zero)
67     "selected_n",        ///< selected frame number (starting from zero)
68     "prev_selected_n",   ///< number of the last selected frame
69
70     "key",               ///< tell if the frame is a key frame
71     "pos",               ///< original position in the file of the frame
72
73     NULL
74 };
75
76 enum var_name {
77     VAR_E,
78     VAR_PHI,
79     VAR_PI,
80
81     VAR_TB,
82
83     VAR_PTS,
84     VAR_START_PTS,
85     VAR_PREV_PTS,
86     VAR_PREV_SELECTED_PTS,
87
88     VAR_T,
89     VAR_START_T,
90     VAR_PREV_T,
91     VAR_PREV_SELECTED_T,
92
93     VAR_PICT_TYPE,
94     VAR_PICT_TYPE_I,
95     VAR_PICT_TYPE_P,
96     VAR_PICT_TYPE_B,
97     VAR_PICT_TYPE_S,
98     VAR_PICT_TYPE_SI,
99     VAR_PICT_TYPE_SP,
100     VAR_PICT_TYPE_BI,
101
102     VAR_INTERLACE_TYPE,
103     VAR_INTERLACE_TYPE_P,
104     VAR_INTERLACE_TYPE_T,
105     VAR_INTERLACE_TYPE_B,
106
107     VAR_N,
108     VAR_SELECTED_N,
109     VAR_PREV_SELECTED_N,
110
111     VAR_KEY,
112
113     VAR_VARS_NB
114 };
115
116 #define FIFO_SIZE 8
117
118 typedef struct SelectContext {
119     const AVClass *class;
120     char *expr_str;
121     AVExpr *expr;
122     double var_values[VAR_VARS_NB];
123     double select;
124     int cache_frames;
125     AVFifoBuffer *pending_frames; ///< FIFO buffer of video frames
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;
166     select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
167     select->var_values[VAR_PREV_SELECTED_T]   = NAN;
168     select->var_values[VAR_START_PTS]         = NAN;
169     select->var_values[VAR_START_T]           = NAN;
170
171     select->var_values[VAR_PICT_TYPE_I]  = AV_PICTURE_TYPE_I;
172     select->var_values[VAR_PICT_TYPE_P]  = AV_PICTURE_TYPE_P;
173     select->var_values[VAR_PICT_TYPE_B]  = AV_PICTURE_TYPE_B;
174     select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
175     select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
176
177     select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
178     select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
179     select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;;
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] =
203         !frame->interlaced_frame     ? INTERLACE_TYPE_P :
204         frame->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
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],
214            select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
215            select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
216            select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
217            (int)select->var_values[VAR_KEY],
218            av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]),
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];
225         select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
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
330 static const AVFilterPad avfilter_vf_select_inputs[] = {
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
341 static const AVFilterPad avfilter_vf_select_outputs[] = {
342     {
343         .name          = "default",
344         .type          = AVMEDIA_TYPE_VIDEO,
345         .poll_frame    = poll_frame,
346         .request_frame = request_frame,
347     },
348     { NULL }
349 };
350
351 AVFilter ff_vf_select = {
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 };