2 * Copyright (c) 2007 Bobby Bingham
4 * This file is part of FFmpeg.
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.
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.
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
23 * FIFO buffering filter
26 #include "libavutil/avassert.h"
27 #include "libavutil/channel_layout.h"
28 #include "libavutil/common.h"
29 #include "libavutil/mathematics.h"
30 #include "libavutil/samplefmt.h"
42 typedef struct FifoContext
{
44 Buf
*last
; ///< last buffered frame
47 * When a specific number of output samples is requested, the partial
48 * buffer is stored here
51 int allocated_samples
; ///< number of samples out was allocated for
54 static av_cold
int init(AVFilterContext
*ctx
)
56 FifoContext
*fifo
= ctx
->priv
;
57 fifo
->last
= &fifo
->root
;
62 static av_cold
void uninit(AVFilterContext
*ctx
)
64 FifoContext
*fifo
= ctx
->priv
;
67 for (buf
= fifo
->root
.next
; buf
; buf
= tmp
) {
69 av_frame_free(&buf
->frame
);
73 av_frame_free(&fifo
->out
);
76 static int add_to_queue(AVFilterLink
*inlink
, AVFrame
*frame
)
78 FifoContext
*fifo
= inlink
->dst
->priv
;
80 fifo
->last
->next
= av_mallocz(sizeof(Buf
));
81 if (!fifo
->last
->next
) {
82 av_frame_free(&frame
);
83 return AVERROR(ENOMEM
);
86 fifo
->last
= fifo
->last
->next
;
87 fifo
->last
->frame
= frame
;
92 static void queue_pop(FifoContext
*s
)
94 Buf
*tmp
= s
->root
.next
->next
;
95 if (s
->last
== s
->root
.next
)
97 av_freep(&s
->root
.next
);
102 * Move data pointers and pts offset samples forward.
104 static void buffer_offset(AVFilterLink
*link
, AVFrame
*frame
,
107 int nb_channels
= av_get_channel_layout_nb_channels(link
->channel_layout
);
108 int planar
= av_sample_fmt_is_planar(link
->format
);
109 int planes
= planar
? nb_channels
: 1;
110 int block_align
= av_get_bytes_per_sample(link
->format
) * (planar
? 1 : nb_channels
);
113 av_assert0(frame
->nb_samples
> offset
);
115 for (i
= 0; i
< planes
; i
++)
116 frame
->extended_data
[i
] += block_align
* offset
;
117 if (frame
->data
!= frame
->extended_data
)
118 memcpy(frame
->data
, frame
->extended_data
,
119 FFMIN(planes
, FF_ARRAY_ELEMS(frame
->data
)) * sizeof(*frame
->data
));
120 frame
->linesize
[0] -= block_align
*offset
;
121 frame
->nb_samples
-= offset
;
123 if (frame
->pts
!= AV_NOPTS_VALUE
) {
124 frame
->pts
+= av_rescale_q(offset
, (AVRational
){1, link
->sample_rate
},
129 static int calc_ptr_alignment(AVFrame
*frame
)
131 int planes
= av_sample_fmt_is_planar(frame
->format
) ?
132 av_get_channel_layout_nb_channels(frame
->channel_layout
) : 1;
136 for (p
= 0; p
< planes
; p
++) {
138 while ((intptr_t)frame
->extended_data
[p
] % cur_align
)
140 if (cur_align
< min_align
)
141 min_align
= cur_align
;
146 static int return_audio_frame(AVFilterContext
*ctx
)
148 AVFilterLink
*link
= ctx
->outputs
[0];
149 FifoContext
*s
= ctx
->priv
;
150 AVFrame
*head
= s
->root
.next
? s
->root
.next
->frame
: NULL
;
154 /* if head is NULL then we're flushing the remaining samples in out */
155 if (!head
&& !s
->out
)
159 head
->nb_samples
>= link
->request_samples
&&
160 calc_ptr_alignment(head
) >= 32) {
161 if (head
->nb_samples
== link
->request_samples
) {
165 out
= av_frame_clone(head
);
167 return AVERROR(ENOMEM
);
169 out
->nb_samples
= link
->request_samples
;
170 buffer_offset(link
, head
, link
->request_samples
);
173 int nb_channels
= av_get_channel_layout_nb_channels(link
->channel_layout
);
176 s
->out
= ff_get_audio_buffer(link
, link
->request_samples
);
178 return AVERROR(ENOMEM
);
180 s
->out
->nb_samples
= 0;
181 s
->out
->pts
= head
->pts
;
182 s
->allocated_samples
= link
->request_samples
;
183 } else if (link
->request_samples
!= s
->allocated_samples
) {
184 av_log(ctx
, AV_LOG_ERROR
, "request_samples changed before the "
185 "buffer was returned.\n");
186 return AVERROR(EINVAL
);
189 while (s
->out
->nb_samples
< s
->allocated_samples
) {
193 ret
= ff_request_frame(ctx
->inputs
[0]);
194 if (ret
== AVERROR_EOF
) {
195 av_samples_set_silence(s
->out
->extended_data
,
197 s
->allocated_samples
-
199 nb_channels
, link
->format
);
200 s
->out
->nb_samples
= s
->allocated_samples
;
204 av_assert0(s
->root
.next
); // If ff_request_frame() succeeded then we should have a frame
206 head
= s
->root
.next
->frame
;
208 len
= FFMIN(s
->allocated_samples
- s
->out
->nb_samples
,
211 av_samples_copy(s
->out
->extended_data
, head
->extended_data
,
212 s
->out
->nb_samples
, 0, len
, nb_channels
,
214 s
->out
->nb_samples
+= len
;
216 if (len
== head
->nb_samples
) {
217 av_frame_free(&head
);
220 buffer_offset(link
, head
, len
);
226 return ff_filter_frame(link
, out
);
229 static int request_frame(AVFilterLink
*outlink
)
231 FifoContext
*fifo
= outlink
->src
->priv
;
234 if (!fifo
->root
.next
) {
235 if ((ret
= ff_request_frame(outlink
->src
->inputs
[0])) < 0) {
236 if (ret
== AVERROR_EOF
&& outlink
->request_samples
)
237 return return_audio_frame(outlink
->src
);
240 av_assert0(fifo
->root
.next
);
243 if (outlink
->request_samples
) {
244 return return_audio_frame(outlink
->src
);
246 ret
= ff_filter_frame(outlink
, fifo
->root
.next
->frame
);
253 static const AVFilterPad avfilter_vf_fifo_inputs
[] = {
256 .type
= AVMEDIA_TYPE_VIDEO
,
257 .filter_frame
= add_to_queue
,
262 static const AVFilterPad avfilter_vf_fifo_outputs
[] = {
265 .type
= AVMEDIA_TYPE_VIDEO
,
266 .request_frame
= request_frame
,
271 AVFilter ff_vf_fifo
= {
273 .description
= NULL_IF_CONFIG_SMALL("Buffer input images and send them when they are requested."),
278 .priv_size
= sizeof(FifoContext
),
280 .inputs
= avfilter_vf_fifo_inputs
,
281 .outputs
= avfilter_vf_fifo_outputs
,
284 static const AVFilterPad avfilter_af_afifo_inputs
[] = {
287 .type
= AVMEDIA_TYPE_AUDIO
,
288 .filter_frame
= add_to_queue
,
293 static const AVFilterPad avfilter_af_afifo_outputs
[] = {
296 .type
= AVMEDIA_TYPE_AUDIO
,
297 .request_frame
= request_frame
,
302 AVFilter ff_af_afifo
= {
304 .description
= NULL_IF_CONFIG_SMALL("Buffer input frames and send them when they are requested."),
309 .priv_size
= sizeof(FifoContext
),
311 .inputs
= avfilter_af_afifo_inputs
,
312 .outputs
= avfilter_af_afifo_outputs
,