• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
init(AVFilterContext * ctx)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] < -1 || s->map[n] >= nb_items) {
71             av_log(ctx, AV_LOG_ERROR, "Index %d out of range: [-1, %d].\n", s->map[n], nb_items - 1);
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 
filter_frame(AVFilterLink * inlink,AVFrame * frame)82 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
83 {
84     AVFilterContext    *ctx = inlink->dst;
85     ShuffleFramesContext *s = ctx->priv;
86     int ret = 0;
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     }
93 
94     if (s->in_frames == s->nb_frames) {
95         int n, x;
96 
97         for (n = 0; n < s->nb_frames; n++) {
98             AVFrame *out;
99 
100             x = s->map[n];
101             if (x >= 0) {
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             }
108             s->in_frames--;
109         }
110 
111         for (n = 0; n < s->nb_frames; n++)
112             av_frame_free(&s->frames[n]);
113     }
114 
115     return ret;
116 }
117 
uninit(AVFilterContext * ctx)118 static av_cold void uninit(AVFilterContext *ctx)
119 {
120     ShuffleFramesContext *s = ctx->priv;
121 
122     while (s->in_frames > 0) {
123         s->in_frames--;
124         av_frame_free(&s->frames[s->in_frames]);
125     }
126 
127     av_freep(&s->frames);
128     av_freep(&s->map);
129     av_freep(&s->pts);
130 }
131 
132 #define OFFSET(x) offsetof(ShuffleFramesContext, x)
133 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
134 static const AVOption shuffleframes_options[] = {
135     { "mapping", "set destination indexes of input frames",  OFFSET(mapping), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS },
136     { NULL },
137 };
138 
139 AVFILTER_DEFINE_CLASS(shuffleframes);
140 
141 static const AVFilterPad shuffleframes_inputs[] = {
142     {
143         .name         = "default",
144         .type         = AVMEDIA_TYPE_VIDEO,
145         .filter_frame = filter_frame,
146     },
147 };
148 
149 static const AVFilterPad shuffleframes_outputs[] = {
150     {
151         .name = "default",
152         .type = AVMEDIA_TYPE_VIDEO,
153     },
154 };
155 
156 const AVFilter ff_vf_shuffleframes = {
157     .name          = "shuffleframes",
158     .description   = NULL_IF_CONFIG_SMALL("Shuffle video frames."),
159     .priv_size     = sizeof(ShuffleFramesContext),
160     .priv_class    = &shuffleframes_class,
161     .init          = init,
162     .uninit        = uninit,
163     FILTER_INPUTS(shuffleframes_inputs),
164     FILTER_OUTPUTS(shuffleframes_outputs),
165     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
166 };
167