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