avfilter: add shuffleframes filter
[ffmpeg.git] / libavfilter / vf_shuffleframes.c
1 /*
2  * Copyright (c) 2015 Paul B Mahol
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/avstring.h"
22 #include "libavutil/common.h"
23 #include "libavutil/internal.h"
24 #include "libavutil/opt.h"
25
26 #include "avfilter.h"
27 #include "internal.h"
28 #include "video.h"
29
30 typedef struct ShuffleFramesContext {
31     const AVClass *class;
32     char *mapping;
33     AVFrame **frames;
34     int *map;
35     int64_t *pts;
36     int in_frames;
37     int nb_frames;
38 } ShuffleFramesContext;
39
40 static av_cold int init(AVFilterContext *ctx)
41 {
42     ShuffleFramesContext *s = ctx->priv;
43     char *mapping, *saveptr = NULL, *p;
44     int n, nb_items;
45
46     nb_items = 1;
47     for (p = s->mapping; *p; p++) {
48         if (*p == '|' || *p == ' ')
49             nb_items++;
50     }
51
52     s->frames = av_calloc(nb_items, sizeof(*s->frames));
53     s->map    = av_calloc(nb_items, sizeof(*s->map));
54     s->pts    = av_calloc(nb_items, sizeof(*s->pts));
55     if (!s->map || !s->frames || !s->pts) {
56         return AVERROR(ENOMEM);
57     }
58
59     mapping = av_strdup(s->mapping);
60     if (!mapping)
61         return AVERROR(ENOMEM);
62
63     for (n = 0; n < nb_items; n++) {
64         char *map = av_strtok(n == 0 ? mapping : NULL, " |", &saveptr);
65         if (!map || sscanf(map, "%d", &s->map[n]) != 1) {
66             av_free(mapping);
67             return AVERROR(EINVAL);
68         }
69
70         if (s->map[n] < 0 || s->map[n] >= nb_items) {
71             av_log(ctx, AV_LOG_ERROR, "Index out of range.\n");
72             av_free(mapping);
73             return AVERROR(EINVAL);
74         }
75     }
76
77     s->nb_frames = nb_items;
78     av_free(mapping);
79     return 0;
80 }
81
82 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
83 {
84     AVFilterContext    *ctx = inlink->dst;
85     ShuffleFramesContext *s = ctx->priv;
86     int ret;
87
88     if (s->in_frames < s->nb_frames) {
89         s->frames[s->in_frames] = frame;
90         s->pts[s->in_frames] = frame->pts;
91         s->in_frames++;
92         ret = 0;
93     }
94
95     if (s->in_frames == s->nb_frames) {
96         int n, x;
97
98         for (n = 0; n < s->nb_frames; n++) {
99             AVFrame *out;
100
101             x = s->map[n];
102             out = av_frame_clone(s->frames[x]);
103             if (!out)
104                 return AVERROR(ENOMEM);
105             out->pts = s->pts[n];
106             ret = ff_filter_frame(ctx->outputs[0], out);
107             s->in_frames--;
108         }
109
110         for (n = 0; n < s->nb_frames; n++)
111             av_frame_free(&s->frames[n]);
112     }
113
114     return ret;
115 }
116
117 static av_cold void uninit(AVFilterContext *ctx)
118 {
119     ShuffleFramesContext *s = ctx->priv;
120
121     av_freep(&s->frames);
122     av_freep(&s->map);
123     av_freep(&s->pts);
124 }
125
126 #define OFFSET(x) offsetof(ShuffleFramesContext, x)
127 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
128 static const AVOption shuffleframes_options[] = {
129     { "mapping", "set destination indexes of input frames",  OFFSET(mapping), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS },
130     { NULL },
131 };
132
133 AVFILTER_DEFINE_CLASS(shuffleframes);
134
135 static const AVFilterPad shuffleframes_inputs[] = {
136     {
137         .name         = "default",
138         .type         = AVMEDIA_TYPE_VIDEO,
139         .filter_frame = filter_frame,
140     },
141     { NULL },
142 };
143
144 static const AVFilterPad shuffleframes_outputs[] = {
145     {
146         .name = "default",
147         .type = AVMEDIA_TYPE_VIDEO,
148     },
149     { NULL },
150 };
151
152 AVFilter ff_vf_shuffleframes = {
153     .name          = "shuffleframes",
154     .description   = NULL_IF_CONFIG_SMALL("Shuffle video frames"),
155     .priv_size     = sizeof(ShuffleFramesContext),
156     .priv_class    = &shuffleframes_class,
157     .init          = init,
158     .uninit        = uninit,
159     .inputs        = shuffleframes_inputs,
160     .outputs       = shuffleframes_outputs,
161     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
162 };