This makes synchronization simpler for filters with multiple inputs.
Anton Khirnov authored on 2012/05/27 21:18:49... | ... |
@@ -595,6 +595,15 @@ struct AVFilterLink { |
595 | 595 |
AVFilterFormats *out_samplerates; |
596 | 596 |
struct AVFilterChannelLayouts *in_channel_layouts; |
597 | 597 |
struct AVFilterChannelLayouts *out_channel_layouts; |
598 |
+ |
|
599 |
+ /** |
|
600 |
+ * Audio only, the destination filter sets this to a non-zero value to |
|
601 |
+ * request that buffers with the given number of samples should be sent to |
|
602 |
+ * it. AVFilterPad.needs_fifo must also be set on the corresponding input |
|
603 |
+ * pad. |
|
604 |
+ * Last buffer before EOF will be padded with silence. |
|
605 |
+ */ |
|
606 |
+ int request_samples; |
|
598 | 607 |
}; |
599 | 608 |
|
600 | 609 |
/** |
... | ... |
@@ -23,6 +23,11 @@ |
23 | 23 |
* FIFO buffering filter |
24 | 24 |
*/ |
25 | 25 |
|
26 |
+#include "libavutil/avassert.h" |
|
27 |
+#include "libavutil/audioconvert.h" |
|
28 |
+#include "libavutil/mathematics.h" |
|
29 |
+#include "libavutil/samplefmt.h" |
|
30 |
+ |
|
26 | 31 |
#include "audio.h" |
27 | 32 |
#include "avfilter.h" |
28 | 33 |
#include "internal.h" |
... | ... |
@@ -36,6 +41,13 @@ typedef struct Buf { |
36 | 36 |
typedef struct { |
37 | 37 |
Buf root; |
38 | 38 |
Buf *last; ///< last buffered frame |
39 |
+ |
|
40 |
+ /** |
|
41 |
+ * When a specific number of output samples is requested, the partial |
|
42 |
+ * buffer is stored here |
|
43 |
+ */ |
|
44 |
+ AVFilterBufferRef *buf_out; |
|
45 |
+ int allocated_samples; ///< number of samples buf_out was allocated for |
|
39 | 46 |
} FifoContext; |
40 | 47 |
|
41 | 48 |
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) |
... | ... |
@@ -57,6 +69,8 @@ static av_cold void uninit(AVFilterContext *ctx) |
57 | 57 |
avfilter_unref_buffer(buf->buf); |
58 | 58 |
av_free(buf); |
59 | 59 |
} |
60 |
+ |
|
61 |
+ avfilter_unref_buffer(fifo->buf_out); |
|
60 | 62 |
} |
61 | 63 |
|
62 | 64 |
static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) |
... | ... |
@@ -68,14 +82,143 @@ static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) |
68 | 68 |
fifo->last->buf = buf; |
69 | 69 |
} |
70 | 70 |
|
71 |
+static void queue_pop(FifoContext *s) |
|
72 |
+{ |
|
73 |
+ Buf *tmp = s->root.next->next; |
|
74 |
+ if (s->last == s->root.next) |
|
75 |
+ s->last = &s->root; |
|
76 |
+ av_freep(&s->root.next); |
|
77 |
+ s->root.next = tmp; |
|
78 |
+} |
|
79 |
+ |
|
71 | 80 |
static void end_frame(AVFilterLink *inlink) { } |
72 | 81 |
|
73 | 82 |
static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } |
74 | 83 |
|
84 |
+/** |
|
85 |
+ * Move data pointers and pts offset samples forward. |
|
86 |
+ */ |
|
87 |
+static void buffer_offset(AVFilterLink *link, AVFilterBufferRef *buf, |
|
88 |
+ int offset) |
|
89 |
+{ |
|
90 |
+ int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); |
|
91 |
+ int planar = av_sample_fmt_is_planar(link->format); |
|
92 |
+ int planes = planar ? nb_channels : 1; |
|
93 |
+ int block_align = av_get_bytes_per_sample(link->format) * (planar ? 1 : nb_channels); |
|
94 |
+ int i; |
|
95 |
+ |
|
96 |
+ av_assert0(buf->audio->nb_samples > offset); |
|
97 |
+ |
|
98 |
+ for (i = 0; i < planes; i++) |
|
99 |
+ buf->extended_data[i] += block_align*offset; |
|
100 |
+ if (buf->data != buf->extended_data) |
|
101 |
+ memcpy(buf->data, buf->extended_data, |
|
102 |
+ FFMIN(planes, FF_ARRAY_ELEMS(buf->data)) * sizeof(*buf->data)); |
|
103 |
+ buf->linesize[0] -= block_align*offset; |
|
104 |
+ buf->audio->nb_samples -= offset; |
|
105 |
+ |
|
106 |
+ if (buf->pts != AV_NOPTS_VALUE) { |
|
107 |
+ buf->pts += av_rescale_q(offset, (AVRational){1, link->sample_rate}, |
|
108 |
+ link->time_base); |
|
109 |
+ } |
|
110 |
+} |
|
111 |
+ |
|
112 |
+static int calc_ptr_alignment(AVFilterBufferRef *buf) |
|
113 |
+{ |
|
114 |
+ int planes = av_sample_fmt_is_planar(buf->format) ? |
|
115 |
+ av_get_channel_layout_nb_channels(buf->audio->channel_layout) : 1; |
|
116 |
+ int min_align = 128; |
|
117 |
+ int p; |
|
118 |
+ |
|
119 |
+ for (p = 0; p < planes; p++) { |
|
120 |
+ int cur_align = 128; |
|
121 |
+ while ((intptr_t)buf->extended_data[p] % cur_align) |
|
122 |
+ cur_align >>= 1; |
|
123 |
+ if (cur_align < min_align) |
|
124 |
+ min_align = cur_align; |
|
125 |
+ } |
|
126 |
+ return min_align; |
|
127 |
+} |
|
128 |
+ |
|
129 |
+static int return_audio_frame(AVFilterContext *ctx) |
|
130 |
+{ |
|
131 |
+ AVFilterLink *link = ctx->outputs[0]; |
|
132 |
+ FifoContext *s = ctx->priv; |
|
133 |
+ AVFilterBufferRef *head = s->root.next->buf; |
|
134 |
+ AVFilterBufferRef *buf_out; |
|
135 |
+ int ret; |
|
136 |
+ |
|
137 |
+ if (!s->buf_out && |
|
138 |
+ head->audio->nb_samples >= link->request_samples && |
|
139 |
+ calc_ptr_alignment(head) >= 32) { |
|
140 |
+ if (head->audio->nb_samples == link->request_samples) { |
|
141 |
+ buf_out = head; |
|
142 |
+ queue_pop(s); |
|
143 |
+ } else { |
|
144 |
+ buf_out = avfilter_ref_buffer(head, AV_PERM_READ); |
|
145 |
+ buf_out->audio->nb_samples = link->request_samples; |
|
146 |
+ buffer_offset(link, head, link->request_samples); |
|
147 |
+ } |
|
148 |
+ } else { |
|
149 |
+ int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); |
|
150 |
+ |
|
151 |
+ if (!s->buf_out) { |
|
152 |
+ s->buf_out = ff_get_audio_buffer(link, AV_PERM_WRITE, |
|
153 |
+ link->request_samples); |
|
154 |
+ if (!s->buf_out) |
|
155 |
+ return AVERROR(ENOMEM); |
|
156 |
+ |
|
157 |
+ s->buf_out->audio->nb_samples = 0; |
|
158 |
+ s->buf_out->pts = head->pts; |
|
159 |
+ s->allocated_samples = link->request_samples; |
|
160 |
+ } else if (link->request_samples != s->allocated_samples) { |
|
161 |
+ av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " |
|
162 |
+ "buffer was returned.\n"); |
|
163 |
+ return AVERROR(EINVAL); |
|
164 |
+ } |
|
165 |
+ |
|
166 |
+ while (s->buf_out->audio->nb_samples < s->allocated_samples) { |
|
167 |
+ int len = FFMIN(s->allocated_samples - s->buf_out->audio->nb_samples, |
|
168 |
+ head->audio->nb_samples); |
|
169 |
+ |
|
170 |
+ av_samples_copy(s->buf_out->extended_data, head->extended_data, |
|
171 |
+ s->buf_out->audio->nb_samples, 0, len, nb_channels, |
|
172 |
+ link->format); |
|
173 |
+ s->buf_out->audio->nb_samples += len; |
|
174 |
+ |
|
175 |
+ if (len == head->audio->nb_samples) { |
|
176 |
+ avfilter_unref_buffer(head); |
|
177 |
+ queue_pop(s); |
|
178 |
+ |
|
179 |
+ if (!s->root.next && |
|
180 |
+ (ret = ff_request_frame(ctx->inputs[0])) < 0) { |
|
181 |
+ if (ret == AVERROR_EOF) { |
|
182 |
+ av_samples_set_silence(s->buf_out->extended_data, |
|
183 |
+ s->buf_out->audio->nb_samples, |
|
184 |
+ s->allocated_samples - |
|
185 |
+ s->buf_out->audio->nb_samples, |
|
186 |
+ nb_channels, link->format); |
|
187 |
+ s->buf_out->audio->nb_samples = s->allocated_samples; |
|
188 |
+ break; |
|
189 |
+ } |
|
190 |
+ return ret; |
|
191 |
+ } |
|
192 |
+ head = s->root.next->buf; |
|
193 |
+ } else { |
|
194 |
+ buffer_offset(link, head, len); |
|
195 |
+ } |
|
196 |
+ } |
|
197 |
+ buf_out = s->buf_out; |
|
198 |
+ s->buf_out = NULL; |
|
199 |
+ } |
|
200 |
+ ff_filter_samples(link, buf_out); |
|
201 |
+ |
|
202 |
+ return 0; |
|
203 |
+} |
|
204 |
+ |
|
75 | 205 |
static int request_frame(AVFilterLink *outlink) |
76 | 206 |
{ |
77 | 207 |
FifoContext *fifo = outlink->src->priv; |
78 |
- Buf *tmp; |
|
79 | 208 |
int ret; |
80 | 209 |
|
81 | 210 |
if (!fifo->root.next) { |
... | ... |
@@ -90,20 +233,20 @@ static int request_frame(AVFilterLink *outlink) |
90 | 90 |
ff_start_frame(outlink, fifo->root.next->buf); |
91 | 91 |
ff_draw_slice (outlink, 0, outlink->h, 1); |
92 | 92 |
ff_end_frame (outlink); |
93 |
+ queue_pop(fifo); |
|
93 | 94 |
break; |
94 | 95 |
case AVMEDIA_TYPE_AUDIO: |
95 |
- ff_filter_samples(outlink, fifo->root.next->buf); |
|
96 |
+ if (outlink->request_samples) { |
|
97 |
+ return return_audio_frame(outlink->src); |
|
98 |
+ } else { |
|
99 |
+ ff_filter_samples(outlink, fifo->root.next->buf); |
|
100 |
+ queue_pop(fifo); |
|
101 |
+ } |
|
96 | 102 |
break; |
97 | 103 |
default: |
98 | 104 |
return AVERROR(EINVAL); |
99 | 105 |
} |
100 | 106 |
|
101 |
- if (fifo->last == fifo->root.next) |
|
102 |
- fifo->last = &fifo->root; |
|
103 |
- tmp = fifo->root.next->next; |
|
104 |
- av_free(fifo->root.next); |
|
105 |
- fifo->root.next = tmp; |
|
106 |
- |
|
107 | 107 |
return 0; |
108 | 108 |
} |
109 | 109 |
|