FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/f_reverse.c
Date: 2024-07-14 13:34:57
Exec Total Coverage
Lines: 0 147 0.0%
Functions: 0 7 0.0%
Branches: 0 84 0.0%

Line Branch Exec Source
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 "config_components.h"
22
23 #include "libavutil/mem.h"
24 #include "avfilter.h"
25 #include "internal.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 unsigned int duration_size;
35 int64_t *pts;
36 int64_t *duration;
37 int flush_idx;
38 int64_t nb_samples;
39 } ReverseContext;
40
41 static av_cold int init(AVFilterContext *ctx)
42 {
43 ReverseContext *s = ctx->priv;
44
45 s->pts = av_fast_realloc(NULL, &s->pts_size,
46 DEFAULT_LENGTH * sizeof(*(s->pts)));
47 if (!s->pts)
48 return AVERROR(ENOMEM);
49
50 s->duration = av_fast_realloc(NULL, &s->duration_size,
51 DEFAULT_LENGTH * sizeof(*(s->duration)));
52 if (!s->duration)
53 return AVERROR(ENOMEM);
54
55 s->frames = av_fast_realloc(NULL, &s->frames_size,
56 DEFAULT_LENGTH * sizeof(*(s->frames)));
57 if (!s->frames)
58 return AVERROR(ENOMEM);
59
60 return 0;
61 }
62
63 static av_cold void uninit(AVFilterContext *ctx)
64 {
65 ReverseContext *s = ctx->priv;
66
67 while (s->nb_frames > 0) {
68 av_frame_free(&s->frames[s->nb_frames - 1]);
69 s->nb_frames--;
70 }
71
72 av_freep(&s->pts);
73 av_freep(&s->duration);
74 av_freep(&s->frames);
75 }
76
77 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
78 {
79 AVFilterContext *ctx = inlink->dst;
80 ReverseContext *s = ctx->priv;
81 void *ptr;
82
83 if (s->nb_frames + 1 > s->pts_size / sizeof(*(s->pts))) {
84 ptr = av_fast_realloc(s->pts, &s->pts_size, s->pts_size * 2);
85 if (!ptr)
86 return AVERROR(ENOMEM);
87 s->pts = ptr;
88 }
89
90 if (s->nb_frames + 1 > s->duration_size / sizeof(*(s->duration))) {
91 ptr = av_fast_realloc(s->duration, &s->duration_size, s->duration_size * 2);
92 if (!ptr)
93 return AVERROR(ENOMEM);
94 s->duration = ptr;
95 }
96
97 if (s->nb_frames + 1 > s->frames_size / sizeof(*(s->frames))) {
98 ptr = av_fast_realloc(s->frames, &s->frames_size, s->frames_size * 2);
99 if (!ptr)
100 return AVERROR(ENOMEM);
101 s->frames = ptr;
102 }
103
104 s->frames[s->nb_frames] = in;
105 s->pts[s->nb_frames] = in->pts;
106 s->duration[s->nb_frames] = in->duration;
107 s->nb_frames++;
108
109 return 0;
110 }
111
112 #if CONFIG_REVERSE_FILTER
113
114 static int request_frame(AVFilterLink *outlink)
115 {
116 AVFilterContext *ctx = outlink->src;
117 ReverseContext *s = ctx->priv;
118 int ret;
119
120 ret = ff_request_frame(ctx->inputs[0]);
121
122 if (ret == AVERROR_EOF && s->nb_frames > 0) {
123 AVFrame *out = s->frames[s->nb_frames - 1];
124 out->duration= s->duration[s->flush_idx];
125 out->pts = s->pts[s->flush_idx++];
126 ret = ff_filter_frame(outlink, out);
127 s->frames[s->nb_frames - 1] = NULL;
128 s->nb_frames--;
129 }
130
131 return ret;
132 }
133
134 static const AVFilterPad reverse_inputs[] = {
135 {
136 .name = "default",
137 .type = AVMEDIA_TYPE_VIDEO,
138 .filter_frame = filter_frame,
139 },
140 };
141
142 static const AVFilterPad reverse_outputs[] = {
143 {
144 .name = "default",
145 .type = AVMEDIA_TYPE_VIDEO,
146 .request_frame = request_frame,
147 },
148 };
149
150 const AVFilter ff_vf_reverse = {
151 .name = "reverse",
152 .description = NULL_IF_CONFIG_SMALL("Reverse a clip."),
153 .priv_size = sizeof(ReverseContext),
154 .init = init,
155 .uninit = uninit,
156 FILTER_INPUTS(reverse_inputs),
157 FILTER_OUTPUTS(reverse_outputs),
158 };
159
160 #endif /* CONFIG_REVERSE_FILTER */
161
162 #if CONFIG_AREVERSE_FILTER
163
164 static void reverse_samples_planar(AVFrame *out)
165 {
166 for (int p = 0; p < out->ch_layout.nb_channels; p++) {
167 switch (out->format) {
168 case AV_SAMPLE_FMT_U8P: {
169 uint8_t *dst = (uint8_t *)out->extended_data[p];
170 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
171 FFSWAP(uint8_t, dst[i], dst[j]);
172 }
173 break;
174 case AV_SAMPLE_FMT_S16P: {
175 int16_t *dst = (int16_t *)out->extended_data[p];
176 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
177 FFSWAP(int16_t, dst[i], dst[j]);
178 }
179 break;
180 case AV_SAMPLE_FMT_S32P: {
181 int32_t *dst = (int32_t *)out->extended_data[p];
182 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
183 FFSWAP(int32_t, dst[i], dst[j]);
184 }
185 break;
186 case AV_SAMPLE_FMT_S64P: {
187 int64_t *dst = (int64_t *)out->extended_data[p];
188 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
189 FFSWAP(int64_t, dst[i], dst[j]);
190 }
191 break;
192 case AV_SAMPLE_FMT_FLTP: {
193 float *dst = (float *)out->extended_data[p];
194 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
195 FFSWAP(float, dst[i], dst[j]);
196 }
197 break;
198 case AV_SAMPLE_FMT_DBLP: {
199 double *dst = (double *)out->extended_data[p];
200 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
201 FFSWAP(double, dst[i], dst[j]);
202 }
203 break;
204 }
205 }
206 }
207
208 static void reverse_samples_packed(AVFrame *out)
209 {
210 const int channels = out->ch_layout.nb_channels;
211
212 switch (out->format) {
213 case AV_SAMPLE_FMT_U8: {
214 uint8_t *dst = (uint8_t *)out->extended_data[0];
215 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
216 for (int p = 0; p < channels; p++)
217 FFSWAP(uint8_t, dst[i * channels + p], dst[j * channels + p]);
218 }
219 break;
220 case AV_SAMPLE_FMT_S16: {
221 int16_t *dst = (int16_t *)out->extended_data[0];
222 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
223 for (int p = 0; p < channels; p++)
224 FFSWAP(int16_t, dst[i * channels + p], dst[j * channels + p]);
225 }
226 break;
227 case AV_SAMPLE_FMT_S32: {
228 int32_t *dst = (int32_t *)out->extended_data[0];
229 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
230 for (int p = 0; p < channels; p++)
231 FFSWAP(int32_t, dst[i * channels + p], dst[j * channels + p]);
232 }
233 break;
234 case AV_SAMPLE_FMT_S64: {
235 int64_t *dst = (int64_t *)out->extended_data[0];
236 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
237 for (int p = 0; p < channels; p++)
238 FFSWAP(int64_t, dst[i * channels + p], dst[j * channels + p]);
239 }
240 break;
241 case AV_SAMPLE_FMT_FLT: {
242 float *dst = (float *)out->extended_data[0];
243 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
244 for (int p = 0; p < channels; p++)
245 FFSWAP(float, dst[i * channels + p], dst[j * channels + p]);
246 }
247 break;
248 case AV_SAMPLE_FMT_DBL: {
249 double *dst = (double *)out->extended_data[0];
250 for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
251 for (int p = 0; p < channels; p++)
252 FFSWAP(double, dst[i * channels + p], dst[j * channels + p]);
253 }
254 break;
255 }
256 }
257
258 static int areverse_request_frame(AVFilterLink *outlink)
259 {
260 AVFilterContext *ctx = outlink->src;
261 ReverseContext *s = ctx->priv;
262 int ret;
263
264 ret = ff_request_frame(ctx->inputs[0]);
265
266 if (ret == AVERROR_EOF && s->nb_frames > 0) {
267 AVFrame *out = s->frames[s->nb_frames - 1];
268 out->duration = s->duration[s->flush_idx];
269 out->pts = s->pts[s->flush_idx++] - s->nb_samples;
270 if (s->nb_frames > 1)
271 s->nb_samples += s->pts[s->flush_idx] - s->pts[s->flush_idx - 1] - out->nb_samples;
272
273 if (av_sample_fmt_is_planar(out->format))
274 reverse_samples_planar(out);
275 else
276 reverse_samples_packed(out);
277 ret = ff_filter_frame(outlink, out);
278 s->frames[s->nb_frames - 1] = NULL;
279 s->nb_frames--;
280 }
281
282 return ret;
283 }
284
285 static const AVFilterPad areverse_inputs[] = {
286 {
287 .name = "default",
288 .type = AVMEDIA_TYPE_AUDIO,
289 .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
290 .filter_frame = filter_frame,
291 },
292 };
293
294 static const AVFilterPad areverse_outputs[] = {
295 {
296 .name = "default",
297 .type = AVMEDIA_TYPE_AUDIO,
298 .request_frame = areverse_request_frame,
299 },
300 };
301
302 const AVFilter ff_af_areverse = {
303 .name = "areverse",
304 .description = NULL_IF_CONFIG_SMALL("Reverse an audio clip."),
305 .priv_size = sizeof(ReverseContext),
306 .init = init,
307 .uninit = uninit,
308 FILTER_INPUTS(areverse_inputs),
309 FILTER_OUTPUTS(areverse_outputs),
310 };
311
312 #endif /* CONFIG_AREVERSE_FILTER */
313