Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Audio Mix Filter | ||
3 | * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | /** | ||
23 | * @file | ||
24 | * Audio Mix Filter | ||
25 | * | ||
26 | * Mixes audio from multiple sources into a single output. The channel layout, | ||
27 | * sample rate, and sample format will be the same for all inputs and the | ||
28 | * output. | ||
29 | */ | ||
30 | |||
31 | #include "libavutil/attributes.h" | ||
32 | #include "libavutil/audio_fifo.h" | ||
33 | #include "libavutil/avassert.h" | ||
34 | #include "libavutil/avstring.h" | ||
35 | #include "libavutil/channel_layout.h" | ||
36 | #include "libavutil/common.h" | ||
37 | #include "libavutil/eval.h" | ||
38 | #include "libavutil/float_dsp.h" | ||
39 | #include "libavutil/mathematics.h" | ||
40 | #include "libavutil/mem.h" | ||
41 | #include "libavutil/opt.h" | ||
42 | #include "libavutil/samplefmt.h" | ||
43 | |||
44 | #include "audio.h" | ||
45 | #include "avfilter.h" | ||
46 | #include "filters.h" | ||
47 | #include "internal.h" | ||
48 | |||
49 | #define INPUT_ON 1 /**< input is active */ | ||
50 | #define INPUT_EOF 2 /**< input has reached EOF (may still be active) */ | ||
51 | |||
52 | #define DURATION_LONGEST 0 | ||
53 | #define DURATION_SHORTEST 1 | ||
54 | #define DURATION_FIRST 2 | ||
55 | |||
56 | |||
57 | typedef struct FrameInfo { | ||
58 | int nb_samples; | ||
59 | int64_t pts; | ||
60 | struct FrameInfo *next; | ||
61 | } FrameInfo; | ||
62 | |||
63 | /** | ||
64 | * Linked list used to store timestamps and frame sizes of all frames in the | ||
65 | * FIFO for the first input. | ||
66 | * | ||
67 | * This is needed to keep timestamps synchronized for the case where multiple | ||
68 | * input frames are pushed to the filter for processing before a frame is | ||
69 | * requested by the output link. | ||
70 | */ | ||
71 | typedef struct FrameList { | ||
72 | int nb_frames; | ||
73 | int nb_samples; | ||
74 | FrameInfo *list; | ||
75 | FrameInfo *end; | ||
76 | } FrameList; | ||
77 | |||
78 | 1042 | static void frame_list_clear(FrameList *frame_list) | |
79 | { | ||
80 |
2/2✓ Branch 0 taken 1036 times.
✓ Branch 1 taken 6 times.
|
1042 | if (frame_list) { |
81 |
2/2✓ Branch 0 taken 726 times.
✓ Branch 1 taken 1036 times.
|
1762 | while (frame_list->list) { |
82 | 726 | FrameInfo *info = frame_list->list; | |
83 | 726 | frame_list->list = info->next; | |
84 | 726 | av_free(info); | |
85 | } | ||
86 | 1036 | frame_list->nb_frames = 0; | |
87 | 1036 | frame_list->nb_samples = 0; | |
88 | 1036 | frame_list->end = NULL; | |
89 | } | ||
90 | 1042 | } | |
91 | |||
92 | 2066 | static int frame_list_next_frame_size(FrameList *frame_list) | |
93 | { | ||
94 |
2/2✓ Branch 0 taken 304 times.
✓ Branch 1 taken 1762 times.
|
2066 | if (!frame_list->list) |
95 | 304 | return 0; | |
96 | 1762 | return frame_list->list->nb_samples; | |
97 | } | ||
98 | |||
99 | 1240 | static int64_t frame_list_next_pts(FrameList *frame_list) | |
100 | { | ||
101 |
2/2✓ Branch 0 taken 304 times.
✓ Branch 1 taken 936 times.
|
1240 | if (!frame_list->list) |
102 | 304 | return AV_NOPTS_VALUE; | |
103 | 936 | return frame_list->list->pts; | |
104 | } | ||
105 | |||
106 | 1240 | static void frame_list_remove_samples(FrameList *frame_list, int nb_samples) | |
107 | { | ||
108 |
2/2✓ Branch 0 taken 1030 times.
✓ Branch 1 taken 210 times.
|
1240 | if (nb_samples >= frame_list->nb_samples) { |
109 | 1030 | frame_list_clear(frame_list); | |
110 | } else { | ||
111 | 210 | int samples = nb_samples; | |
112 |
2/2✓ Branch 0 taken 210 times.
✓ Branch 1 taken 210 times.
|
420 | while (samples > 0) { |
113 | 210 | FrameInfo *info = frame_list->list; | |
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
|
210 | av_assert0(info); |
115 |
2/2✓ Branch 0 taken 204 times.
✓ Branch 1 taken 6 times.
|
210 | if (info->nb_samples <= samples) { |
116 | 204 | samples -= info->nb_samples; | |
117 | 204 | frame_list->list = info->next; | |
118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
|
204 | if (!frame_list->list) |
119 | ✗ | frame_list->end = NULL; | |
120 | 204 | frame_list->nb_frames--; | |
121 | 204 | frame_list->nb_samples -= info->nb_samples; | |
122 | 204 | av_free(info); | |
123 | } else { | ||
124 | 6 | info->nb_samples -= samples; | |
125 | 6 | info->pts += samples; | |
126 | 6 | frame_list->nb_samples -= samples; | |
127 | 6 | samples = 0; | |
128 | } | ||
129 | } | ||
130 | } | ||
131 | 1240 | } | |
132 | |||
133 | 930 | static int frame_list_add_frame(FrameList *frame_list, int nb_samples, int64_t pts) | |
134 | { | ||
135 | 930 | FrameInfo *info = av_malloc(sizeof(*info)); | |
136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 930 times.
|
930 | if (!info) |
137 | ✗ | return AVERROR(ENOMEM); | |
138 | 930 | info->nb_samples = nb_samples; | |
139 | 930 | info->pts = pts; | |
140 | 930 | info->next = NULL; | |
141 | |||
142 |
2/2✓ Branch 0 taken 726 times.
✓ Branch 1 taken 204 times.
|
930 | if (!frame_list->list) { |
143 | 726 | frame_list->list = info; | |
144 | 726 | frame_list->end = info; | |
145 | } else { | ||
146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
|
204 | av_assert0(frame_list->end); |
147 | 204 | frame_list->end->next = info; | |
148 | 204 | frame_list->end = info; | |
149 | } | ||
150 | 930 | frame_list->nb_frames++; | |
151 | 930 | frame_list->nb_samples += nb_samples; | |
152 | |||
153 | 930 | return 0; | |
154 | } | ||
155 | |||
156 | /* FIXME: use directly links fifo */ | ||
157 | |||
158 | typedef struct MixContext { | ||
159 | const AVClass *class; /**< class for AVOptions */ | ||
160 | AVFloatDSPContext *fdsp; | ||
161 | |||
162 | int nb_inputs; /**< number of inputs */ | ||
163 | int active_inputs; /**< number of input currently active */ | ||
164 | int duration_mode; /**< mode for determining duration */ | ||
165 | float dropout_transition; /**< transition time when an input drops out */ | ||
166 | char *weights_str; /**< string for custom weights for every input */ | ||
167 | int normalize; /**< if inputs are scaled */ | ||
168 | |||
169 | int nb_channels; /**< number of channels */ | ||
170 | int sample_rate; /**< sample rate */ | ||
171 | int planar; | ||
172 | AVAudioFifo **fifos; /**< audio fifo for each input */ | ||
173 | uint8_t *input_state; /**< current state of each input */ | ||
174 | float *input_scale; /**< mixing scale factor for each input */ | ||
175 | float *weights; /**< custom weights for every input */ | ||
176 | float weight_sum; /**< sum of custom weights for every input */ | ||
177 | float *scale_norm; /**< normalization factor for every input */ | ||
178 | int64_t next_pts; /**< calculated pts for next output frame */ | ||
179 | FrameList *frame_list; /**< list of frame info for the first input */ | ||
180 | } MixContext; | ||
181 | |||
182 | #define OFFSET(x) offsetof(MixContext, x) | ||
183 | #define A AV_OPT_FLAG_AUDIO_PARAM | ||
184 | #define F AV_OPT_FLAG_FILTERING_PARAM | ||
185 | #define T AV_OPT_FLAG_RUNTIME_PARAM | ||
186 | static const AVOption amix_options[] = { | ||
187 | { "inputs", "Number of inputs.", | ||
188 | OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT16_MAX, A|F }, | ||
189 | { "duration", "How to determine the end-of-stream.", | ||
190 | OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A|F, .unit = "duration" }, | ||
191 | { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, A|F, .unit = "duration" }, | ||
192 | { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, A|F, .unit = "duration" }, | ||
193 | { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, A|F, .unit = "duration" }, | ||
194 | { "dropout_transition", "Transition time, in seconds, for volume " | ||
195 | "renormalization when an input stream ends.", | ||
196 | OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F }, | ||
197 | { "weights", "Set weight for each input.", | ||
198 | OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, A|F|T }, | ||
199 | { "normalize", "Scale inputs", | ||
200 | OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, A|F|T }, | ||
201 | { NULL } | ||
202 | }; | ||
203 | |||
204 | AVFILTER_DEFINE_CLASS(amix); | ||
205 | |||
206 | /** | ||
207 | * Update the scaling factors to apply to each input during mixing. | ||
208 | * | ||
209 | * This balances the full volume range between active inputs and handles | ||
210 | * volume transitions when EOF is encountered on an input but mixing continues | ||
211 | * with the remaining inputs. | ||
212 | */ | ||
213 | 1246 | static void calculate_scales(MixContext *s, int nb_samples) | |
214 | { | ||
215 | 1246 | float weight_sum = 0.f; | |
216 | int i; | ||
217 | |||
218 |
2/2✓ Branch 0 taken 2860 times.
✓ Branch 1 taken 1246 times.
|
4106 | for (i = 0; i < s->nb_inputs; i++) |
219 |
2/2✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 634 times.
|
2860 | if (s->input_state[i] & INPUT_ON) |
220 | 2226 | weight_sum += FFABS(s->weights[i]); | |
221 | |||
222 |
2/2✓ Branch 0 taken 2860 times.
✓ Branch 1 taken 1246 times.
|
4106 | for (i = 0; i < s->nb_inputs; i++) { |
223 |
2/2✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 634 times.
|
2860 | if (s->input_state[i] & INPUT_ON) { |
224 |
2/2✓ Branch 0 taken 383 times.
✓ Branch 1 taken 1843 times.
|
2226 | if (s->scale_norm[i] > weight_sum / FFABS(s->weights[i])) { |
225 | 383 | s->scale_norm[i] -= ((s->weight_sum / FFABS(s->weights[i])) / s->nb_inputs) * | |
226 | 383 | nb_samples / (s->dropout_transition * s->sample_rate); | |
227 |
2/2✓ Branch 0 taken 379 times.
✓ Branch 1 taken 4 times.
|
383 | s->scale_norm[i] = FFMAX(s->scale_norm[i], weight_sum / FFABS(s->weights[i])); |
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 |
2/2✓ Branch 0 taken 2860 times.
✓ Branch 1 taken 1246 times.
|
4106 | for (i = 0; i < s->nb_inputs; i++) { |
233 |
2/2✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 634 times.
|
2860 | if (s->input_state[i] & INPUT_ON) { |
234 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2226 times.
|
2226 | if (!s->normalize) |
235 | ✗ | s->input_scale[i] = FFABS(s->weights[i]); | |
236 | else | ||
237 |
1/2✓ Branch 0 taken 2226 times.
✗ Branch 1 not taken.
|
2226 | s->input_scale[i] = 1.0f / s->scale_norm[i] * FFSIGN(s->weights[i]); |
238 | } else { | ||
239 | 634 | s->input_scale[i] = 0.0f; | |
240 | } | ||
241 | } | ||
242 | 1246 | } | |
243 | |||
244 | 6 | static int config_output(AVFilterLink *outlink) | |
245 | { | ||
246 | 6 | AVFilterContext *ctx = outlink->src; | |
247 | 6 | MixContext *s = ctx->priv; | |
248 | int i; | ||
249 | char buf[64]; | ||
250 | |||
251 | 6 | s->planar = av_sample_fmt_is_planar(outlink->format); | |
252 | 6 | s->sample_rate = outlink->sample_rate; | |
253 | 6 | outlink->time_base = (AVRational){ 1, outlink->sample_rate }; | |
254 | 6 | s->next_pts = AV_NOPTS_VALUE; | |
255 | |||
256 | 6 | s->frame_list = av_mallocz(sizeof(*s->frame_list)); | |
257 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!s->frame_list) |
258 | ✗ | return AVERROR(ENOMEM); | |
259 | |||
260 | 6 | s->fifos = av_calloc(s->nb_inputs, sizeof(*s->fifos)); | |
261 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!s->fifos) |
262 | ✗ | return AVERROR(ENOMEM); | |
263 | |||
264 | 6 | s->nb_channels = outlink->ch_layout.nb_channels; | |
265 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 6 times.
|
19 | for (i = 0; i < s->nb_inputs; i++) { |
266 | 13 | s->fifos[i] = av_audio_fifo_alloc(outlink->format, s->nb_channels, 1024); | |
267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if (!s->fifos[i]) |
268 | ✗ | return AVERROR(ENOMEM); | |
269 | } | ||
270 | |||
271 | 6 | s->input_state = av_malloc(s->nb_inputs); | |
272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!s->input_state) |
273 | ✗ | return AVERROR(ENOMEM); | |
274 | 6 | memset(s->input_state, INPUT_ON, s->nb_inputs); | |
275 | 6 | s->active_inputs = s->nb_inputs; | |
276 | |||
277 | 6 | s->input_scale = av_calloc(s->nb_inputs, sizeof(*s->input_scale)); | |
278 | 6 | s->scale_norm = av_calloc(s->nb_inputs, sizeof(*s->scale_norm)); | |
279 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (!s->input_scale || !s->scale_norm) |
280 | ✗ | return AVERROR(ENOMEM); | |
281 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 6 times.
|
19 | for (i = 0; i < s->nb_inputs; i++) |
282 | 13 | s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); | |
283 | 6 | calculate_scales(s, 0); | |
284 | |||
285 | 6 | av_channel_layout_describe(&outlink->ch_layout, buf, sizeof(buf)); | |
286 | |||
287 | 6 | av_log(ctx, AV_LOG_VERBOSE, | |
288 | "inputs:%d fmt:%s srate:%d cl:%s\n", s->nb_inputs, | ||
289 | 6 | av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf); | |
290 | |||
291 | 6 | return 0; | |
292 | } | ||
293 | |||
294 | /** | ||
295 | * Read samples from the input FIFOs, mix, and write to the output link. | ||
296 | */ | ||
297 | 1551 | static int output_frame(AVFilterLink *outlink) | |
298 | { | ||
299 | 1551 | AVFilterContext *ctx = outlink->src; | |
300 | 1551 | MixContext *s = ctx->priv; | |
301 | AVFrame *out_buf, *in_buf; | ||
302 | int nb_samples, ns, i; | ||
303 | |||
304 |
1/2✓ Branch 0 taken 1551 times.
✗ Branch 1 not taken.
|
1551 | if (s->input_state[0] & INPUT_ON) { |
305 | /* first input live: use the corresponding frame size */ | ||
306 | 1551 | nb_samples = frame_list_next_frame_size(s->frame_list); | |
307 |
2/2✓ Branch 0 taken 1956 times.
✓ Branch 1 taken 1240 times.
|
3196 | for (i = 1; i < s->nb_inputs; i++) { |
308 |
2/2✓ Branch 0 taken 1322 times.
✓ Branch 1 taken 634 times.
|
1956 | if (s->input_state[i] & INPUT_ON) { |
309 | 1322 | ns = av_audio_fifo_size(s->fifos[i]); | |
310 |
2/2✓ Branch 0 taken 317 times.
✓ Branch 1 taken 1005 times.
|
1322 | if (ns < nb_samples) { |
311 |
2/2✓ Branch 0 taken 311 times.
✓ Branch 1 taken 6 times.
|
317 | if (!(s->input_state[i] & INPUT_EOF)) |
312 | /* unclosed input with not enough samples */ | ||
313 | 311 | return 0; | |
314 | /* closed input to drain */ | ||
315 | 6 | nb_samples = ns; | |
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | 1240 | s->next_pts = frame_list_next_pts(s->frame_list); | |
321 | } else { | ||
322 | /* first input closed: use the available samples */ | ||
323 | ✗ | nb_samples = INT_MAX; | |
324 | ✗ | for (i = 1; i < s->nb_inputs; i++) { | |
325 | ✗ | if (s->input_state[i] & INPUT_ON) { | |
326 | ✗ | ns = av_audio_fifo_size(s->fifos[i]); | |
327 | ✗ | nb_samples = FFMIN(nb_samples, ns); | |
328 | } | ||
329 | } | ||
330 | ✗ | if (nb_samples == INT_MAX) { | |
331 | ✗ | ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); | |
332 | ✗ | return 0; | |
333 | } | ||
334 | } | ||
335 | |||
336 | 1240 | frame_list_remove_samples(s->frame_list, nb_samples); | |
337 | |||
338 | 1240 | calculate_scales(s, nb_samples); | |
339 | |||
340 |
2/2✓ Branch 0 taken 304 times.
✓ Branch 1 taken 936 times.
|
1240 | if (nb_samples == 0) |
341 | 304 | return 0; | |
342 | |||
343 | 936 | out_buf = ff_get_audio_buffer(outlink, nb_samples); | |
344 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 936 times.
|
936 | if (!out_buf) |
345 | ✗ | return AVERROR(ENOMEM); | |
346 | |||
347 | 936 | in_buf = ff_get_audio_buffer(outlink, nb_samples); | |
348 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 936 times.
|
936 | if (!in_buf) { |
349 | ✗ | av_frame_free(&out_buf); | |
350 | ✗ | return AVERROR(ENOMEM); | |
351 | } | ||
352 | |||
353 |
2/2✓ Branch 0 taken 2133 times.
✓ Branch 1 taken 936 times.
|
3069 | for (i = 0; i < s->nb_inputs; i++) { |
354 |
2/2✓ Branch 0 taken 1546 times.
✓ Branch 1 taken 587 times.
|
2133 | if (s->input_state[i] & INPUT_ON) { |
355 | int planes, plane_size, p; | ||
356 | |||
357 | 1546 | av_audio_fifo_read(s->fifos[i], (void **)in_buf->extended_data, | |
358 | nb_samples); | ||
359 | |||
360 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1546 times.
|
1546 | planes = s->planar ? s->nb_channels : 1; |
361 |
1/2✓ Branch 0 taken 1546 times.
✗ Branch 1 not taken.
|
1546 | plane_size = nb_samples * (s->planar ? 1 : s->nb_channels); |
362 | 1546 | plane_size = FFALIGN(plane_size, 16); | |
363 | |||
364 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1546 times.
|
1546 | if (out_buf->format == AV_SAMPLE_FMT_FLT || |
365 | ✗ | out_buf->format == AV_SAMPLE_FMT_FLTP) { | |
366 |
2/2✓ Branch 0 taken 1546 times.
✓ Branch 1 taken 1546 times.
|
3092 | for (p = 0; p < planes; p++) { |
367 | 1546 | s->fdsp->vector_fmac_scalar((float *)out_buf->extended_data[p], | |
368 | 1546 | (float *) in_buf->extended_data[p], | |
369 | 1546 | s->input_scale[i], plane_size); | |
370 | } | ||
371 | } else { | ||
372 | ✗ | for (p = 0; p < planes; p++) { | |
373 | ✗ | s->fdsp->vector_dmac_scalar((double *)out_buf->extended_data[p], | |
374 | ✗ | (double *) in_buf->extended_data[p], | |
375 | ✗ | s->input_scale[i], plane_size); | |
376 | } | ||
377 | } | ||
378 | } | ||
379 | } | ||
380 | 936 | av_frame_free(&in_buf); | |
381 | |||
382 | 936 | out_buf->pts = s->next_pts; | |
383 | 936 | out_buf->duration = av_rescale_q(out_buf->nb_samples, av_make_q(1, outlink->sample_rate), | |
384 | outlink->time_base); | ||
385 | |||
386 |
1/2✓ Branch 0 taken 936 times.
✗ Branch 1 not taken.
|
936 | if (s->next_pts != AV_NOPTS_VALUE) |
387 | 936 | s->next_pts += nb_samples; | |
388 | |||
389 | 936 | return ff_filter_frame(outlink, out_buf); | |
390 | } | ||
391 | |||
392 | /** | ||
393 | * Requests a frame, if needed, from each input link other than the first. | ||
394 | */ | ||
395 | 515 | static int request_samples(AVFilterContext *ctx, int min_samples) | |
396 | { | ||
397 | 515 | MixContext *s = ctx->priv; | |
398 | int i; | ||
399 | |||
400 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 515 times.
|
515 | av_assert0(s->nb_inputs > 1); |
401 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 515 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
515 | if (min_samples == 1 && s->duration_mode == DURATION_FIRST) |
402 | ✗ | min_samples = av_audio_fifo_size(s->fifos[0]); | |
403 | |||
404 |
2/2✓ Branch 0 taken 575 times.
✓ Branch 1 taken 6 times.
|
581 | for (i = 1; i < s->nb_inputs; i++) { |
405 |
2/2✓ Branch 0 taken 568 times.
✓ Branch 1 taken 7 times.
|
575 | if (!(s->input_state[i] & INPUT_ON) || |
406 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 568 times.
|
568 | (s->input_state[i] & INPUT_EOF)) |
407 | 7 | continue; | |
408 |
2/2✓ Branch 1 taken 59 times.
✓ Branch 2 taken 509 times.
|
568 | if (av_audio_fifo_size(s->fifos[i]) >= min_samples) |
409 | 59 | continue; | |
410 | 509 | ff_inlink_request_frame(ctx->inputs[i]); | |
411 | 509 | return 0; | |
412 | } | ||
413 | 6 | return output_frame(ctx->outputs[0]); | |
414 | } | ||
415 | |||
416 | /** | ||
417 | * Calculates the number of active inputs and determines EOF based on the | ||
418 | * duration option. | ||
419 | * | ||
420 | * @return 0 if mixing should continue, or AVERROR_EOF if mixing should stop. | ||
421 | */ | ||
422 | 2491 | static int calc_active_inputs(MixContext *s) | |
423 | { | ||
424 | int i; | ||
425 | 2491 | int active_inputs = 0; | |
426 |
2/2✓ Branch 0 taken 5765 times.
✓ Branch 1 taken 2491 times.
|
8256 | for (i = 0; i < s->nb_inputs; i++) |
427 | 5765 | active_inputs += !!(s->input_state[i] & INPUT_ON); | |
428 | 2491 | s->active_inputs = active_inputs; | |
429 | |||
430 |
2/2✓ Branch 0 taken 2487 times.
✓ Branch 1 taken 4 times.
|
2491 | if (!active_inputs || |
431 |
4/4✓ Branch 0 taken 268 times.
✓ Branch 1 taken 2219 times.
✓ Branch 2 taken 267 times.
✓ Branch 3 taken 1 times.
|
2487 | (s->duration_mode == DURATION_FIRST && !(s->input_state[0] & INPUT_ON)) || |
432 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2486 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2486 | (s->duration_mode == DURATION_SHORTEST && active_inputs != s->nb_inputs)) |
433 | 5 | return AVERROR_EOF; | |
434 | 2486 | return 0; | |
435 | } | ||
436 | |||
437 | 2494 | static int activate(AVFilterContext *ctx) | |
438 | { | ||
439 | 2494 | AVFilterLink *outlink = ctx->outputs[0]; | |
440 | 2494 | MixContext *s = ctx->priv; | |
441 | 2494 | AVFrame *buf = NULL; | |
442 | int i, ret; | ||
443 | |||
444 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2491 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 3 times.
|
2501 | FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); |
445 | |||
446 |
2/2✓ Branch 0 taken 5765 times.
✓ Branch 1 taken 2491 times.
|
8256 | for (i = 0; i < s->nb_inputs; i++) { |
447 | 5765 | AVFilterLink *inlink = ctx->inputs[i]; | |
448 | |||
449 |
2/2✓ Branch 1 taken 1545 times.
✓ Branch 2 taken 4220 times.
|
5765 | if ((ret = ff_inlink_consume_frame(ctx->inputs[i], &buf)) > 0) { |
450 |
2/2✓ Branch 0 taken 930 times.
✓ Branch 1 taken 615 times.
|
1545 | if (i == 0) { |
451 | 930 | int64_t pts = av_rescale_q(buf->pts, inlink->time_base, | |
452 | outlink->time_base); | ||
453 | 930 | ret = frame_list_add_frame(s->frame_list, buf->nb_samples, pts); | |
454 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 930 times.
|
930 | if (ret < 0) { |
455 | ✗ | av_frame_free(&buf); | |
456 | ✗ | return ret; | |
457 | } | ||
458 | } | ||
459 | |||
460 | 1545 | ret = av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data, | |
461 | 1545 | buf->nb_samples); | |
462 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1545 times.
|
1545 | if (ret < 0) { |
463 | ✗ | av_frame_free(&buf); | |
464 | ✗ | return ret; | |
465 | } | ||
466 | |||
467 | 1545 | av_frame_free(&buf); | |
468 | |||
469 | 1545 | ret = output_frame(outlink); | |
470 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1545 times.
|
1545 | if (ret < 0) |
471 | ✗ | return ret; | |
472 | } | ||
473 | } | ||
474 | |||
475 |
2/2✓ Branch 0 taken 5765 times.
✓ Branch 1 taken 2491 times.
|
8256 | for (i = 0; i < s->nb_inputs; i++) { |
476 | int64_t pts; | ||
477 | int status; | ||
478 | |||
479 |
2/2✓ Branch 1 taken 1421 times.
✓ Branch 2 taken 4344 times.
|
5765 | if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { |
480 |
1/2✓ Branch 0 taken 1421 times.
✗ Branch 1 not taken.
|
1421 | if (status == AVERROR_EOF) { |
481 | 1421 | s->input_state[i] |= INPUT_EOF; | |
482 |
2/2✓ Branch 1 taken 1270 times.
✓ Branch 2 taken 151 times.
|
1421 | if (av_audio_fifo_size(s->fifos[i]) == 0) { |
483 | 1270 | s->input_state[i] &= ~INPUT_ON; | |
484 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1270 times.
|
1270 | if (s->nb_inputs == 1) { |
485 | ✗ | ff_outlink_set_status(outlink, status, pts); | |
486 | ✗ | return 0; | |
487 | } | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | |||
493 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2486 times.
|
2491 | if (calc_active_inputs(s)) { |
494 | 5 | ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); | |
495 | 5 | return 0; | |
496 | } | ||
497 | |||
498 |
2/2✓ Branch 1 taken 1549 times.
✓ Branch 2 taken 937 times.
|
2486 | if (ff_outlink_frame_wanted(outlink)) { |
499 | int wanted_samples; | ||
500 | |||
501 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1549 times.
|
1549 | if (!(s->input_state[0] & INPUT_ON)) |
502 | ✗ | return request_samples(ctx, 1); | |
503 | |||
504 |
2/2✓ Branch 0 taken 1034 times.
✓ Branch 1 taken 515 times.
|
1549 | if (s->frame_list->nb_frames == 0) { |
505 | 1034 | ff_inlink_request_frame(ctx->inputs[0]); | |
506 | 1034 | return 0; | |
507 | } | ||
508 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 515 times.
|
515 | av_assert0(s->frame_list->nb_frames > 0); |
509 | |||
510 | 515 | wanted_samples = frame_list_next_frame_size(s->frame_list); | |
511 | |||
512 | 515 | return request_samples(ctx, wanted_samples); | |
513 | } | ||
514 | |||
515 | 937 | return 0; | |
516 | } | ||
517 | |||
518 | 12 | static void parse_weights(AVFilterContext *ctx) | |
519 | { | ||
520 | 12 | MixContext *s = ctx->priv; | |
521 | 12 | float last_weight = 1.f; | |
522 | char *p; | ||
523 | int i; | ||
524 | |||
525 | 12 | s->weight_sum = 0.f; | |
526 | 12 | p = s->weights_str; | |
527 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | for (i = 0; i < s->nb_inputs; i++) { |
528 | 24 | last_weight = av_strtod(p, &p); | |
529 | 24 | s->weights[i] = last_weight; | |
530 | 24 | s->weight_sum += FFABS(last_weight); | |
531 |
3/4✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 12 times.
|
24 | if (p && *p) { |
532 | 12 | p++; | |
533 | } else { | ||
534 | 12 | i++; | |
535 | 12 | break; | |
536 | } | ||
537 | } | ||
538 | |||
539 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
|
14 | for (; i < s->nb_inputs; i++) { |
540 | 2 | s->weights[i] = last_weight; | |
541 | 2 | s->weight_sum += FFABS(last_weight); | |
542 | } | ||
543 | 12 | } | |
544 | |||
545 | 12 | static av_cold int init(AVFilterContext *ctx) | |
546 | { | ||
547 | 12 | MixContext *s = ctx->priv; | |
548 | int i, ret; | ||
549 | |||
550 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
|
38 | for (i = 0; i < s->nb_inputs; i++) { |
551 | 26 | AVFilterPad pad = { 0 }; | |
552 | |||
553 | 26 | pad.type = AVMEDIA_TYPE_AUDIO; | |
554 | 26 | pad.name = av_asprintf("input%d", i); | |
555 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (!pad.name) |
556 | ✗ | return AVERROR(ENOMEM); | |
557 | |||
558 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
|
26 | if ((ret = ff_append_inpad_free_name(ctx, &pad)) < 0) |
559 | ✗ | return ret; | |
560 | } | ||
561 | |||
562 | 12 | s->fdsp = avpriv_float_dsp_alloc(0); | |
563 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!s->fdsp) |
564 | ✗ | return AVERROR(ENOMEM); | |
565 | |||
566 | 12 | s->weights = av_calloc(s->nb_inputs, sizeof(*s->weights)); | |
567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!s->weights) |
568 | ✗ | return AVERROR(ENOMEM); | |
569 | |||
570 | 12 | parse_weights(ctx); | |
571 | |||
572 | 12 | return 0; | |
573 | } | ||
574 | |||
575 | 12 | static av_cold void uninit(AVFilterContext *ctx) | |
576 | { | ||
577 | int i; | ||
578 | 12 | MixContext *s = ctx->priv; | |
579 | |||
580 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (s->fifos) { |
581 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 6 times.
|
19 | for (i = 0; i < s->nb_inputs; i++) |
582 | 13 | av_audio_fifo_free(s->fifos[i]); | |
583 | 6 | av_freep(&s->fifos); | |
584 | } | ||
585 | 12 | frame_list_clear(s->frame_list); | |
586 | 12 | av_freep(&s->frame_list); | |
587 | 12 | av_freep(&s->input_state); | |
588 | 12 | av_freep(&s->input_scale); | |
589 | 12 | av_freep(&s->scale_norm); | |
590 | 12 | av_freep(&s->weights); | |
591 | 12 | av_freep(&s->fdsp); | |
592 | 12 | } | |
593 | |||
594 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
595 | char *res, int res_len, int flags) | ||
596 | { | ||
597 | ✗ | MixContext *s = ctx->priv; | |
598 | int ret; | ||
599 | |||
600 | ✗ | ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); | |
601 | ✗ | if (ret < 0) | |
602 | ✗ | return ret; | |
603 | |||
604 | ✗ | parse_weights(ctx); | |
605 | ✗ | for (int i = 0; i < s->nb_inputs; i++) | |
606 | ✗ | s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); | |
607 | ✗ | calculate_scales(s, 0); | |
608 | |||
609 | ✗ | return 0; | |
610 | } | ||
611 | |||
612 | static const AVFilterPad avfilter_af_amix_outputs[] = { | ||
613 | { | ||
614 | .name = "default", | ||
615 | .type = AVMEDIA_TYPE_AUDIO, | ||
616 | .config_props = config_output, | ||
617 | }, | ||
618 | }; | ||
619 | |||
620 | const AVFilter ff_af_amix = { | ||
621 | .name = "amix", | ||
622 | .description = NULL_IF_CONFIG_SMALL("Audio mixing."), | ||
623 | .priv_size = sizeof(MixContext), | ||
624 | .priv_class = &amix_class, | ||
625 | .init = init, | ||
626 | .uninit = uninit, | ||
627 | .activate = activate, | ||
628 | .inputs = NULL, | ||
629 | FILTER_OUTPUTS(avfilter_af_amix_outputs), | ||
630 | FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, | ||
631 | AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), | ||
632 | .process_command = process_command, | ||
633 | .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, | ||
634 | }; | ||
635 |