avfilter: Add reverse filter
[ffmpeg.git] / libavfilter / vf_reverse.c
1 /*
2  * Copyright (c) 2015 Derek Buitenhuis
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 #include "libavutil/opt.h"
22 #include "avfilter.h"
23 #include "formats.h"
24 #include "internal.h"
25 #include "video.h"
26
27 #define DEFAULT_LENGTH 300
28
29 typedef struct ReverseContext {
30     int nb_frames;
31     AVFrame **frames;
32     unsigned int frames_size;
33     unsigned int pts_size;
34     int64_t *pts;
35     int flush_idx;
36 } ReverseContext;
37
38 static av_cold int init(AVFilterContext *ctx)
39 {
40     ReverseContext *s = ctx->priv;
41
42     s->pts = av_fast_realloc(NULL, &s->pts_size,
43                              DEFAULT_LENGTH * sizeof(*(s->pts)));
44     if (!s->pts)
45         return AVERROR(ENOMEM);
46
47     s->frames = av_fast_realloc(NULL, &s->frames_size,
48                                 DEFAULT_LENGTH * sizeof(*(s->frames)));
49     if (!s->frames) {
50         av_freep(&s->pts);
51         return AVERROR(ENOMEM);
52     }
53
54     return 0;
55 }
56
57 static av_cold void uninit(AVFilterContext *ctx)
58 {
59     ReverseContext *s = ctx->priv;
60
61     av_freep(&s->pts);
62     av_freep(&s->frames);
63 }
64
65 static int config_output(AVFilterLink *outlink)
66 {
67     outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
68     return 0;
69 }
70
71 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
72 {
73     AVFilterContext *ctx = inlink->dst;
74     ReverseContext *s    = ctx->priv;
75
76     if (s->nb_frames + 1 > s->frames_size / sizeof(*(s->frames))) {
77         void *ptr;
78
79         ptr = av_fast_realloc(s->pts, &s->pts_size, s->pts_size * 2);
80         if (!ptr)
81             return AVERROR(ENOMEM);
82         s->pts = ptr;
83
84         ptr = av_fast_realloc(s->frames, &s->frames_size, s->frames_size * 2);
85         if (!ptr)
86             return AVERROR(ENOMEM);
87         s->frames = ptr;
88     }
89
90     s->frames[s->nb_frames] = in;
91     s->pts[s->nb_frames]    = in->pts;
92     s->nb_frames++;
93
94     return 0;
95 }
96
97 static int request_frame(AVFilterLink *outlink)
98 {
99     AVFilterContext *ctx = outlink->src;
100     ReverseContext *s = ctx->priv;
101     int ret;
102
103     ret = ff_request_frame(ctx->inputs[0]);
104
105     if (ret == AVERROR_EOF && s->nb_frames > 0) {
106         AVFrame *out = s->frames[s->nb_frames - 1];
107         out->pts     = s->pts[s->flush_idx++];
108         ret          = ff_filter_frame(outlink, out);
109         s->nb_frames--;
110     }
111
112     return ret;
113 }
114
115 static const AVFilterPad reverse_inputs[] = {
116     {
117         .name         = "default",
118         .type         = AVMEDIA_TYPE_VIDEO,
119         .filter_frame = filter_frame,
120     },
121     { NULL }
122 };
123
124 static const AVFilterPad reverse_outputs[] = {
125     {
126         .name          = "default",
127         .type          = AVMEDIA_TYPE_VIDEO,
128         .request_frame = request_frame,
129         .config_props  = config_output,
130     },
131     { NULL }
132 };
133
134 AVFilter ff_vf_reverse = {
135     .name        = "reverse",
136     .description = NULL_IF_CONFIG_SMALL("Reverse a clip."),
137     .priv_size   = sizeof(ReverseContext),
138     .init        = init,
139     .uninit      = uninit,
140     .inputs      = reverse_inputs,
141     .outputs     = reverse_outputs,
142 };