• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 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 "config_components.h"
22 
23 #include "libavutil/audio_fifo.h"
24 #include "libavutil/fifo.h"
25 #include "libavutil/internal.h"
26 #include "libavutil/opt.h"
27 #include "avfilter.h"
28 #include "audio.h"
29 #include "filters.h"
30 #include "formats.h"
31 #include "internal.h"
32 #include "video.h"
33 
34 typedef struct LoopContext {
35     const AVClass *class;
36 
37     AVAudioFifo *fifo;
38     AVAudioFifo *left;
39     AVFrame **frames;
40     int nb_frames;
41     int current_frame;
42     int64_t start_pts;
43     int64_t duration;
44     int64_t current_sample;
45     int64_t nb_samples;
46     int64_t ignored_samples;
47 
48     int loop;
49     int eof;
50     int64_t size;
51     int64_t start;
52     int64_t pts;
53 } LoopContext;
54 
55 #define AFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
56 #define VFLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
57 #define OFFSET(x) offsetof(LoopContext, x)
58 
check_size(AVFilterContext * ctx)59 static void check_size(AVFilterContext *ctx)
60 {
61     LoopContext *s = ctx->priv;
62 
63     if (!s->size)
64         av_log(ctx, AV_LOG_WARNING, "Number of %s to loop is not set!\n",
65                ctx->input_pads[0].type == AVMEDIA_TYPE_VIDEO ? "frames" : "samples");
66 }
67 
68 #if CONFIG_ALOOP_FILTER
69 
aconfig_input(AVFilterLink * inlink)70 static int aconfig_input(AVFilterLink *inlink)
71 {
72     AVFilterContext *ctx = inlink->dst;
73     LoopContext *s  = ctx->priv;
74 
75     s->fifo = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192);
76     s->left = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192);
77     if (!s->fifo || !s->left)
78         return AVERROR(ENOMEM);
79 
80     check_size(ctx);
81 
82     return 0;
83 }
84 
auninit(AVFilterContext * ctx)85 static av_cold void auninit(AVFilterContext *ctx)
86 {
87     LoopContext *s = ctx->priv;
88 
89     av_audio_fifo_free(s->fifo);
90     av_audio_fifo_free(s->left);
91 }
92 
push_samples(AVFilterContext * ctx,int nb_samples)93 static int push_samples(AVFilterContext *ctx, int nb_samples)
94 {
95     AVFilterLink *outlink = ctx->outputs[0];
96     LoopContext *s = ctx->priv;
97     AVFrame *out;
98     int ret = 0, i = 0;
99 
100     while (s->loop != 0 && i < nb_samples) {
101         out = ff_get_audio_buffer(outlink, FFMIN(nb_samples, s->nb_samples - s->current_sample));
102         if (!out)
103             return AVERROR(ENOMEM);
104         ret = av_audio_fifo_peek_at(s->fifo, (void **)out->extended_data, out->nb_samples, s->current_sample);
105         if (ret < 0) {
106             av_frame_free(&out);
107             return ret;
108         }
109         out->pts = s->pts;
110         out->nb_samples = ret;
111         s->pts += av_rescale_q(out->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
112         i += out->nb_samples;
113         s->current_sample += out->nb_samples;
114 
115         ret = ff_filter_frame(outlink, out);
116         if (ret < 0)
117             return ret;
118 
119         if (s->current_sample >= s->nb_samples) {
120             s->duration = s->pts;
121             s->current_sample = 0;
122 
123             if (s->loop > 0)
124                 s->loop--;
125         }
126     }
127 
128     return ret;
129 }
130 
afilter_frame(AVFilterLink * inlink,AVFrame * frame)131 static int afilter_frame(AVFilterLink *inlink, AVFrame *frame)
132 {
133     AVFilterContext *ctx = inlink->dst;
134     AVFilterLink *outlink = ctx->outputs[0];
135     LoopContext *s = ctx->priv;
136     int ret = 0;
137 
138     if (s->ignored_samples + frame->nb_samples > s->start && s->size > 0 && s->loop != 0) {
139         if (s->nb_samples < s->size) {
140             int written = FFMIN(frame->nb_samples, s->size - s->nb_samples);
141             int drain = 0;
142 
143             ret = av_audio_fifo_write(s->fifo, (void **)frame->extended_data, written);
144             if (ret < 0)
145                 return ret;
146             if (!s->nb_samples) {
147                 drain = FFMAX(0, s->start - s->ignored_samples);
148                 s->pts = frame->pts;
149                 av_audio_fifo_drain(s->fifo, drain);
150                 s->pts += av_rescale_q(s->start - s->ignored_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
151             }
152             s->nb_samples += ret - drain;
153             drain = frame->nb_samples - written;
154             if (s->nb_samples == s->size && drain > 0) {
155                 int ret2;
156 
157                 ret2 = av_audio_fifo_write(s->left, (void **)frame->extended_data, frame->nb_samples);
158                 if (ret2 < 0)
159                    return ret2;
160                 av_audio_fifo_drain(s->left, drain);
161             }
162             frame->nb_samples = ret;
163             s->pts += av_rescale_q(ret, (AVRational){1, outlink->sample_rate}, outlink->time_base);
164             ret = ff_filter_frame(outlink, frame);
165         } else {
166             int nb_samples = frame->nb_samples;
167 
168             av_frame_free(&frame);
169             ret = push_samples(ctx, nb_samples);
170         }
171     } else {
172         s->ignored_samples += frame->nb_samples;
173         frame->pts = s->pts;
174         s->pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
175         ret = ff_filter_frame(outlink, frame);
176     }
177 
178     return ret;
179 }
180 
arequest_frame(AVFilterLink * outlink)181 static int arequest_frame(AVFilterLink *outlink)
182 {
183     AVFilterContext *ctx = outlink->src;
184     LoopContext *s = ctx->priv;
185     int ret = 0;
186 
187     if ((!s->size) ||
188         (s->nb_samples < s->size) ||
189         (s->nb_samples >= s->size && s->loop == 0)) {
190         int nb_samples = av_audio_fifo_size(s->left);
191 
192         if (s->loop == 0 && nb_samples > 0) {
193             AVFrame *out;
194 
195             out = ff_get_audio_buffer(outlink, nb_samples);
196             if (!out)
197                 return AVERROR(ENOMEM);
198             av_audio_fifo_read(s->left, (void **)out->extended_data, nb_samples);
199             out->pts = s->pts;
200             s->pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
201             ret = ff_filter_frame(outlink, out);
202             if (ret < 0)
203                 return ret;
204         }
205         ret = ff_request_frame(ctx->inputs[0]);
206     } else {
207         ret = push_samples(ctx, 1024);
208     }
209 
210     if (s->eof && s->nb_samples > 0 && s->loop != 0) {
211         ret = push_samples(ctx, 1024);
212     }
213 
214     return ret;
215 }
216 
aactivate(AVFilterContext * ctx)217 static int aactivate(AVFilterContext *ctx)
218 {
219     AVFilterLink *inlink = ctx->inputs[0];
220     AVFilterLink *outlink = ctx->outputs[0];
221     LoopContext *s = ctx->priv;
222     AVFrame *frame = NULL;
223     int ret, status;
224     int64_t pts;
225 
226     FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
227 
228     if (!s->eof && (s->nb_samples < s->size || !s->loop || !s->size)) {
229         ret = ff_inlink_consume_frame(inlink, &frame);
230         if (ret < 0)
231             return ret;
232         if (ret > 0)
233             return afilter_frame(inlink, frame);
234     }
235 
236     if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) {
237         if (status == AVERROR_EOF) {
238             s->size = s->nb_samples;
239             s->eof = 1;
240         }
241     }
242 
243     if (s->eof && (!s->loop || !s->size)) {
244         ff_outlink_set_status(outlink, AVERROR_EOF, s->duration);
245         return 0;
246     }
247 
248     if (!s->eof && (!s->size ||
249         (s->nb_samples < s->size) ||
250         (s->nb_samples >= s->size && s->loop == 0))) {
251         FF_FILTER_FORWARD_WANTED(outlink, inlink);
252     } else if (s->loop && s->nb_samples == s->size) {
253         return arequest_frame(outlink);
254     }
255 
256     return FFERROR_NOT_READY;
257 }
258 
259 static const AVOption aloop_options[] = {
260     { "loop",  "number of loops",               OFFSET(loop),  AV_OPT_TYPE_INT,   {.i64 = 0 }, -1, INT_MAX,   AFLAGS },
261     { "size",  "max number of samples to loop", OFFSET(size),  AV_OPT_TYPE_INT64, {.i64 = 0 },  0, INT32_MAX, AFLAGS },
262     { "start", "set the loop start sample",     OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 },  0, INT64_MAX, AFLAGS },
263     { NULL }
264 };
265 
266 AVFILTER_DEFINE_CLASS(aloop);
267 
268 static const AVFilterPad ainputs[] = {
269     {
270         .name         = "default",
271         .type         = AVMEDIA_TYPE_AUDIO,
272         .config_props = aconfig_input,
273     },
274 };
275 
276 static const AVFilterPad aoutputs[] = {
277     {
278         .name          = "default",
279         .type          = AVMEDIA_TYPE_AUDIO,
280     },
281 };
282 
283 const AVFilter ff_af_aloop = {
284     .name          = "aloop",
285     .description   = NULL_IF_CONFIG_SMALL("Loop audio samples."),
286     .priv_size     = sizeof(LoopContext),
287     .priv_class    = &aloop_class,
288     .activate      = aactivate,
289     .uninit        = auninit,
290     FILTER_INPUTS(ainputs),
291     FILTER_OUTPUTS(aoutputs),
292 };
293 #endif /* CONFIG_ALOOP_FILTER */
294 
295 #if CONFIG_LOOP_FILTER
296 
init(AVFilterContext * ctx)297 static av_cold int init(AVFilterContext *ctx)
298 {
299     LoopContext *s = ctx->priv;
300 
301     s->frames = av_calloc(s->size, sizeof(*s->frames));
302     if (!s->frames)
303         return AVERROR(ENOMEM);
304 
305     check_size(ctx);
306 
307     return 0;
308 }
309 
uninit(AVFilterContext * ctx)310 static av_cold void uninit(AVFilterContext *ctx)
311 {
312     LoopContext *s = ctx->priv;
313     int i;
314 
315     for (i = 0; i < s->nb_frames; i++)
316         av_frame_free(&s->frames[i]);
317 
318     av_freep(&s->frames);
319     s->nb_frames = 0;
320 }
321 
push_frame(AVFilterContext * ctx)322 static int push_frame(AVFilterContext *ctx)
323 {
324     AVFilterLink *outlink = ctx->outputs[0];
325     LoopContext *s = ctx->priv;
326     int64_t pts, duration;
327     int ret;
328 
329     AVFrame *out = av_frame_clone(s->frames[s->current_frame]);
330 
331     if (!out)
332         return AVERROR(ENOMEM);
333     out->pts += s->duration - s->start_pts;
334     if (out->pkt_duration)
335         duration = out->pkt_duration;
336     else
337         duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base);
338     pts = out->pts + duration;
339     ret = ff_filter_frame(outlink, out);
340     s->current_frame++;
341 
342     if (s->current_frame >= s->nb_frames) {
343         s->duration = pts;
344         s->current_frame = 0;
345 
346         if (s->loop > 0)
347             s->loop--;
348     }
349 
350     return ret;
351 }
352 
filter_frame(AVFilterLink * inlink,AVFrame * frame)353 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
354 {
355     AVFilterContext *ctx = inlink->dst;
356     AVFilterLink *outlink = ctx->outputs[0];
357     LoopContext *s = ctx->priv;
358     int64_t duration;
359     int ret = 0;
360 
361     if (inlink->frame_count_out >= s->start && s->size > 0 && s->loop != 0) {
362         if (s->nb_frames < s->size) {
363             if (!s->nb_frames)
364                 s->start_pts = frame->pts;
365             s->frames[s->nb_frames] = av_frame_clone(frame);
366             if (!s->frames[s->nb_frames]) {
367                 av_frame_free(&frame);
368                 return AVERROR(ENOMEM);
369             }
370             s->nb_frames++;
371             if (frame->pkt_duration)
372                 duration = frame->pkt_duration;
373             else
374                 duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base);
375             s->duration = frame->pts + duration;
376             ret = ff_filter_frame(outlink, frame);
377         } else {
378             av_frame_free(&frame);
379             ret = push_frame(ctx);
380         }
381     } else {
382         frame->pts += s->duration;
383         ret = ff_filter_frame(outlink, frame);
384     }
385 
386     return ret;
387 }
388 
activate(AVFilterContext * ctx)389 static int activate(AVFilterContext *ctx)
390 {
391     AVFilterLink *inlink = ctx->inputs[0];
392     AVFilterLink *outlink = ctx->outputs[0];
393     LoopContext *s = ctx->priv;
394     AVFrame *frame = NULL;
395     int ret, status;
396     int64_t pts;
397 
398     FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
399 
400     if (!s->eof && (s->nb_frames < s->size || !s->loop || !s->size)) {
401         ret = ff_inlink_consume_frame(inlink, &frame);
402         if (ret < 0)
403             return ret;
404         if (ret > 0)
405             return filter_frame(inlink, frame);
406     }
407 
408     if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) {
409         if (status == AVERROR_EOF) {
410             s->size = s->nb_frames;
411             s->eof = 1;
412         }
413     }
414 
415     if (s->eof && (!s->loop || !s->size)) {
416         ff_outlink_set_status(outlink, AVERROR_EOF, s->duration);
417         return 0;
418     }
419 
420     if (!s->eof && (!s->size ||
421         (s->nb_frames < s->size) ||
422         (s->nb_frames >= s->size && s->loop == 0))) {
423         FF_FILTER_FORWARD_WANTED(outlink, inlink);
424     } else if (s->loop && s->nb_frames == s->size) {
425         return push_frame(ctx);
426     }
427 
428     return FFERROR_NOT_READY;
429 }
430 
431 static const AVOption loop_options[] = {
432     { "loop",  "number of loops",              OFFSET(loop),  AV_OPT_TYPE_INT,   {.i64 = 0 }, -1, INT_MAX,   VFLAGS },
433     { "size",  "max number of frames to loop", OFFSET(size),  AV_OPT_TYPE_INT64, {.i64 = 0 },  0, INT16_MAX, VFLAGS },
434     { "start", "set the loop start frame",     OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 },  0, INT64_MAX, VFLAGS },
435     { NULL }
436 };
437 
438 AVFILTER_DEFINE_CLASS(loop);
439 
440 static const AVFilterPad inputs[] = {
441     {
442         .name = "default",
443         .type = AVMEDIA_TYPE_VIDEO,
444     },
445 };
446 
447 static const AVFilterPad outputs[] = {
448     {
449         .name = "default",
450         .type = AVMEDIA_TYPE_VIDEO,
451     },
452 };
453 
454 const AVFilter ff_vf_loop = {
455     .name        = "loop",
456     .description = NULL_IF_CONFIG_SMALL("Loop video frames."),
457     .priv_size   = sizeof(LoopContext),
458     .priv_class  = &loop_class,
459     .init        = init,
460     .uninit      = uninit,
461     .activate    = activate,
462     FILTER_INPUTS(inputs),
463     FILTER_OUTPUTS(outputs),
464 };
465 #endif /* CONFIG_LOOP_FILTER */
466