FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/f_reverse.c
Date: 2026-10-19 01:23:10
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 "filters.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 FFFilter ff_vf_reverse = {
151 .p.name = "reverse",
152 .p.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 .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
159 };
160
161 #endif /* CONFIG_REVERSE_FILTER */
162
163 #if CONFIG_AREVERSE_FILTER
164
165 ✗ static void reverse_samples_planar(AVFrame *out)
166 {
167 ✗ for (int p = 0; p < out->ch_layout.nb_channels; p++) {
168 ✗ switch (out->format) {
169 ✗ case AV_SAMPLE_FMT_U8P: {
170 ✗ uint8_t *dst = (uint8_t *)out->extended_data[p];
171 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
172 ✗ FFSWAP(uint8_t, dst[i], dst[j]);
173 }
174 ✗ break;
175 ✗ case AV_SAMPLE_FMT_S16P: {
176 ✗ int16_t *dst = (int16_t *)out->extended_data[p];
177 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
178 ✗ FFSWAP(int16_t, dst[i], dst[j]);
179 }
180 ✗ break;
181 ✗ case AV_SAMPLE_FMT_S32P: {
182 ✗ int32_t *dst = (int32_t *)out->extended_data[p];
183 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
184 ✗ FFSWAP(int32_t, dst[i], dst[j]);
185 }
186 ✗ break;
187 ✗ case AV_SAMPLE_FMT_S64P: {
188 ✗ int64_t *dst = (int64_t *)out->extended_data[p];
189 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
190 ✗ FFSWAP(int64_t, dst[i], dst[j]);
191 }
192 ✗ break;
193 ✗ case AV_SAMPLE_FMT_FLTP: {
194 ✗ float *dst = (float *)out->extended_data[p];
195 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
196 ✗ FFSWAP(float, dst[i], dst[j]);
197 }
198 ✗ break;
199 ✗ case AV_SAMPLE_FMT_DBLP: {
200 ✗ double *dst = (double *)out->extended_data[p];
201 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
202 ✗ FFSWAP(double, dst[i], dst[j]);
203 }
204 ✗ break;
205 }
206 }
207 ✗ }
208
209 ✗ static void reverse_samples_packed(AVFrame *out)
210 {
211 ✗ const int channels = out->ch_layout.nb_channels;
212
213 ✗ switch (out->format) {
214 ✗ case AV_SAMPLE_FMT_U8: {
215 ✗ uint8_t *dst = (uint8_t *)out->extended_data[0];
216 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
217 ✗ for (int p = 0; p < channels; p++)
218 ✗ FFSWAP(uint8_t, dst[i * channels + p], dst[j * channels + p]);
219 }
220 ✗ break;
221 ✗ case AV_SAMPLE_FMT_S16: {
222 ✗ int16_t *dst = (int16_t *)out->extended_data[0];
223 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
224 ✗ for (int p = 0; p < channels; p++)
225 ✗ FFSWAP(int16_t, dst[i * channels + p], dst[j * channels + p]);
226 }
227 ✗ break;
228 ✗ case AV_SAMPLE_FMT_S32: {
229 ✗ int32_t *dst = (int32_t *)out->extended_data[0];
230 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
231 ✗ for (int p = 0; p < channels; p++)
232 ✗ FFSWAP(int32_t, dst[i * channels + p], dst[j * channels + p]);
233 }
234 ✗ break;
235 ✗ case AV_SAMPLE_FMT_S64: {
236 ✗ int64_t *dst = (int64_t *)out->extended_data[0];
237 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
238 ✗ for (int p = 0; p < channels; p++)
239 ✗ FFSWAP(int64_t, dst[i * channels + p], dst[j * channels + p]);
240 }
241 ✗ break;
242 ✗ case AV_SAMPLE_FMT_FLT: {
243 ✗ float *dst = (float *)out->extended_data[0];
244 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
245 ✗ for (int p = 0; p < channels; p++)
246 ✗ FFSWAP(float, dst[i * channels + p], dst[j * channels + p]);
247 }
248 ✗ break;
249 ✗ case AV_SAMPLE_FMT_DBL: {
250 ✗ double *dst = (double *)out->extended_data[0];
251 ✗ for (int i = 0, j = out->nb_samples - 1; i < j; i++, j--)
252 ✗ for (int p = 0; p < channels; p++)
253 ✗ FFSWAP(double, dst[i * channels + p], dst[j * channels + p]);
254 }
255 ✗ break;
256 }
257 ✗ }
258
259 ✗ static int areverse_request_frame(AVFilterLink *outlink)
260 {
261 ✗ AVFilterContext *ctx = outlink->src;
262 ✗ ReverseContext *s = ctx->priv;
263 int ret;
264
265 ✗ ret = ff_request_frame(ctx->inputs[0]);
266
267 ✗ if (ret == AVERROR_EOF && s->nb_frames > 0) {
268 ✗ AVFrame *out = s->frames[s->nb_frames - 1];
269 ✗ out->duration = s->duration[s->flush_idx];
270 ✗ out->pts = s->pts[s->flush_idx++] - s->nb_samples;
271 ✗ if (s->nb_frames > 1)
272 ✗ s->nb_samples += s->pts[s->flush_idx] - s->pts[s->flush_idx - 1] - out->nb_samples;
273
274 ✗ if (av_sample_fmt_is_planar(out->format))
275 ✗ reverse_samples_planar(out);
276 else
277 ✗ reverse_samples_packed(out);
278 ✗ ret = ff_filter_frame(outlink, out);
279 ✗ s->frames[s->nb_frames - 1] = NULL;
280 ✗ s->nb_frames--;
281 }
282
283 ✗ return ret;
284 }
285
286 static const AVFilterPad areverse_inputs[] = {
287 {
288 .name = "default",
289 .type = AVMEDIA_TYPE_AUDIO,
290 .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
291 .filter_frame = filter_frame,
292 },
293 };
294
295 static const AVFilterPad areverse_outputs[] = {
296 {
297 .name = "default",
298 .type = AVMEDIA_TYPE_AUDIO,
299 .request_frame = areverse_request_frame,
300 },
301 };
302
303 const FFFilter ff_af_areverse = {
304 .p.name = "areverse",
305 .p.description = NULL_IF_CONFIG_SMALL("Reverse an audio clip."),
306 .priv_size = sizeof(ReverseContext),
307 .init = init,
308 .uninit = uninit,
309 FILTER_INPUTS(areverse_inputs),
310 FILTER_OUTPUTS(areverse_outputs),
311 };
312
313 #endif /* CONFIG_AREVERSE_FILTER */
314