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/opt.h" |
41 |
|
|
#include "libavutil/samplefmt.h" |
42 |
|
|
|
43 |
|
|
#include "audio.h" |
44 |
|
|
#include "avfilter.h" |
45 |
|
|
#include "filters.h" |
46 |
|
|
#include "formats.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 |
|
612 |
static void frame_list_clear(FrameList *frame_list) |
79 |
|
|
{ |
80 |
✓✓ |
612 |
if (frame_list) { |
81 |
✓✓ |
1214 |
while (frame_list->list) { |
82 |
|
605 |
FrameInfo *info = frame_list->list; |
83 |
|
605 |
frame_list->list = info->next; |
84 |
|
605 |
av_free(info); |
85 |
|
|
} |
86 |
|
609 |
frame_list->nb_frames = 0; |
87 |
|
609 |
frame_list->nb_samples = 0; |
88 |
|
609 |
frame_list->end = NULL; |
89 |
|
|
} |
90 |
|
612 |
} |
91 |
|
|
|
92 |
|
2046 |
static int frame_list_next_frame_size(FrameList *frame_list) |
93 |
|
|
{ |
94 |
✓✓ |
2046 |
if (!frame_list->list) |
95 |
|
1 |
return 0; |
96 |
|
2045 |
return frame_list->list->nb_samples; |
97 |
|
|
} |
98 |
|
|
|
99 |
|
609 |
static int64_t frame_list_next_pts(FrameList *frame_list) |
100 |
|
|
{ |
101 |
✓✓ |
609 |
if (!frame_list->list) |
102 |
|
1 |
return AV_NOPTS_VALUE; |
103 |
|
608 |
return frame_list->list->pts; |
104 |
|
|
} |
105 |
|
|
|
106 |
|
609 |
static void frame_list_remove_samples(FrameList *frame_list, int nb_samples) |
107 |
|
|
{ |
108 |
✓✓ |
609 |
if (nb_samples >= frame_list->nb_samples) { |
109 |
|
606 |
frame_list_clear(frame_list); |
110 |
|
|
} else { |
111 |
|
3 |
int samples = nb_samples; |
112 |
✓✓ |
6 |
while (samples > 0) { |
113 |
|
3 |
FrameInfo *info = frame_list->list; |
114 |
✗✓ |
3 |
av_assert0(info); |
115 |
✗✓ |
3 |
if (info->nb_samples <= samples) { |
116 |
|
|
samples -= info->nb_samples; |
117 |
|
|
frame_list->list = info->next; |
118 |
|
|
if (!frame_list->list) |
119 |
|
|
frame_list->end = NULL; |
120 |
|
|
frame_list->nb_frames--; |
121 |
|
|
frame_list->nb_samples -= info->nb_samples; |
122 |
|
|
av_free(info); |
123 |
|
|
} else { |
124 |
|
3 |
info->nb_samples -= samples; |
125 |
|
3 |
info->pts += samples; |
126 |
|
3 |
frame_list->nb_samples -= samples; |
127 |
|
3 |
samples = 0; |
128 |
|
|
} |
129 |
|
|
} |
130 |
|
|
} |
131 |
|
609 |
} |
132 |
|
|
|
133 |
|
605 |
static int frame_list_add_frame(FrameList *frame_list, int nb_samples, int64_t pts) |
134 |
|
|
{ |
135 |
|
605 |
FrameInfo *info = av_malloc(sizeof(*info)); |
136 |
✗✓ |
605 |
if (!info) |
137 |
|
|
return AVERROR(ENOMEM); |
138 |
|
605 |
info->nb_samples = nb_samples; |
139 |
|
605 |
info->pts = pts; |
140 |
|
605 |
info->next = NULL; |
141 |
|
|
|
142 |
✓✗ |
605 |
if (!frame_list->list) { |
143 |
|
605 |
frame_list->list = info; |
144 |
|
605 |
frame_list->end = info; |
145 |
|
|
} else { |
146 |
|
|
av_assert0(frame_list->end); |
147 |
|
|
frame_list->end->next = info; |
148 |
|
|
frame_list->end = info; |
149 |
|
|
} |
150 |
|
605 |
frame_list->nb_frames++; |
151 |
|
605 |
frame_list->nb_samples += nb_samples; |
152 |
|
|
|
153 |
|
605 |
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 |
|
|
|
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, "duration" }, |
190 |
|
|
{ "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, A|F, "duration" }, |
191 |
|
|
{ "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, A|F, "duration" }, |
192 |
|
|
{ "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, A|F, "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 |
|
|
{ NULL } |
199 |
|
|
}; |
200 |
|
|
|
201 |
|
|
AVFILTER_DEFINE_CLASS(amix); |
202 |
|
|
|
203 |
|
|
/** |
204 |
|
|
* Update the scaling factors to apply to each input during mixing. |
205 |
|
|
* |
206 |
|
|
* This balances the full volume range between active inputs and handles |
207 |
|
|
* volume transitions when EOF is encountered on an input but mixing continues |
208 |
|
|
* with the remaining inputs. |
209 |
|
|
*/ |
210 |
|
612 |
static void calculate_scales(MixContext *s, int nb_samples) |
211 |
|
|
{ |
212 |
|
612 |
float weight_sum = 0.f; |
213 |
|
|
int i; |
214 |
|
|
|
215 |
✓✓ |
2098 |
for (i = 0; i < s->nb_inputs; i++) |
216 |
✓✓ |
1486 |
if (s->input_state[i] & INPUT_ON) |
217 |
|
1095 |
weight_sum += FFABS(s->weights[i]); |
218 |
|
|
|
219 |
✓✓ |
2098 |
for (i = 0; i < s->nb_inputs; i++) { |
220 |
✓✓ |
1486 |
if (s->input_state[i] & INPUT_ON) { |
221 |
✓✓ |
1095 |
if (s->scale_norm[i] > weight_sum / FFABS(s->weights[i])) { |
222 |
|
153 |
s->scale_norm[i] -= ((s->weight_sum / FFABS(s->weights[i])) / s->nb_inputs) * |
223 |
|
153 |
nb_samples / (s->dropout_transition * s->sample_rate); |
224 |
✓✓ |
153 |
s->scale_norm[i] = FFMAX(s->scale_norm[i], weight_sum / FFABS(s->weights[i])); |
225 |
|
|
} |
226 |
|
|
} |
227 |
|
|
} |
228 |
|
|
|
229 |
✓✓ |
2098 |
for (i = 0; i < s->nb_inputs; i++) { |
230 |
✓✓ |
1486 |
if (s->input_state[i] & INPUT_ON) |
231 |
✓✗ |
1095 |
s->input_scale[i] = 1.0f / s->scale_norm[i] * FFSIGN(s->weights[i]); |
232 |
|
|
else |
233 |
|
391 |
s->input_scale[i] = 0.0f; |
234 |
|
|
} |
235 |
|
612 |
} |
236 |
|
|
|
237 |
|
3 |
static int config_output(AVFilterLink *outlink) |
238 |
|
|
{ |
239 |
|
3 |
AVFilterContext *ctx = outlink->src; |
240 |
|
3 |
MixContext *s = ctx->priv; |
241 |
|
|
int i; |
242 |
|
|
char buf[64]; |
243 |
|
|
|
244 |
|
3 |
s->planar = av_sample_fmt_is_planar(outlink->format); |
245 |
|
3 |
s->sample_rate = outlink->sample_rate; |
246 |
|
3 |
outlink->time_base = (AVRational){ 1, outlink->sample_rate }; |
247 |
|
3 |
s->next_pts = AV_NOPTS_VALUE; |
248 |
|
|
|
249 |
|
3 |
s->frame_list = av_mallocz(sizeof(*s->frame_list)); |
250 |
✗✓ |
3 |
if (!s->frame_list) |
251 |
|
|
return AVERROR(ENOMEM); |
252 |
|
|
|
253 |
|
3 |
s->fifos = av_mallocz_array(s->nb_inputs, sizeof(*s->fifos)); |
254 |
✗✓ |
3 |
if (!s->fifos) |
255 |
|
|
return AVERROR(ENOMEM); |
256 |
|
|
|
257 |
|
3 |
s->nb_channels = outlink->channels; |
258 |
✓✓ |
10 |
for (i = 0; i < s->nb_inputs; i++) { |
259 |
|
7 |
s->fifos[i] = av_audio_fifo_alloc(outlink->format, s->nb_channels, 1024); |
260 |
✗✓ |
7 |
if (!s->fifos[i]) |
261 |
|
|
return AVERROR(ENOMEM); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
3 |
s->input_state = av_malloc(s->nb_inputs); |
265 |
✗✓ |
3 |
if (!s->input_state) |
266 |
|
|
return AVERROR(ENOMEM); |
267 |
|
3 |
memset(s->input_state, INPUT_ON, s->nb_inputs); |
268 |
|
3 |
s->active_inputs = s->nb_inputs; |
269 |
|
|
|
270 |
|
3 |
s->input_scale = av_mallocz_array(s->nb_inputs, sizeof(*s->input_scale)); |
271 |
|
3 |
s->scale_norm = av_mallocz_array(s->nb_inputs, sizeof(*s->scale_norm)); |
272 |
✓✗✗✓
|
3 |
if (!s->input_scale || !s->scale_norm) |
273 |
|
|
return AVERROR(ENOMEM); |
274 |
✓✓ |
10 |
for (i = 0; i < s->nb_inputs; i++) |
275 |
|
7 |
s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); |
276 |
|
3 |
calculate_scales(s, 0); |
277 |
|
|
|
278 |
|
3 |
av_get_channel_layout_string(buf, sizeof(buf), -1, outlink->channel_layout); |
279 |
|
|
|
280 |
|
3 |
av_log(ctx, AV_LOG_VERBOSE, |
281 |
|
|
"inputs:%d fmt:%s srate:%d cl:%s\n", s->nb_inputs, |
282 |
|
3 |
av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf); |
283 |
|
|
|
284 |
|
3 |
return 0; |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
/** |
288 |
|
|
* Read samples from the input FIFOs, mix, and write to the output link. |
289 |
|
|
*/ |
290 |
|
1564 |
static int output_frame(AVFilterLink *outlink) |
291 |
|
|
{ |
292 |
|
1564 |
AVFilterContext *ctx = outlink->src; |
293 |
|
1564 |
MixContext *s = ctx->priv; |
294 |
|
|
AVFrame *out_buf, *in_buf; |
295 |
|
|
int nb_samples, ns, i; |
296 |
|
|
|
297 |
✓✗ |
1564 |
if (s->input_state[0] & INPUT_ON) { |
298 |
|
|
/* first input live: use the corresponding frame size */ |
299 |
|
1564 |
nb_samples = frame_list_next_frame_size(s->frame_list); |
300 |
✓✓ |
2609 |
for (i = 1; i < s->nb_inputs; i++) { |
301 |
✓✓ |
2000 |
if (s->input_state[i] & INPUT_ON) { |
302 |
|
1609 |
ns = av_audio_fifo_size(s->fifos[i]); |
303 |
✓✓ |
1609 |
if (ns < nb_samples) { |
304 |
✓✓ |
958 |
if (!(s->input_state[i] & INPUT_EOF)) |
305 |
|
|
/* unclosed input with not enough samples */ |
306 |
|
955 |
return 0; |
307 |
|
|
/* closed input to drain */ |
308 |
|
3 |
nb_samples = ns; |
309 |
|
|
} |
310 |
|
|
} |
311 |
|
|
} |
312 |
|
|
|
313 |
|
609 |
s->next_pts = frame_list_next_pts(s->frame_list); |
314 |
|
|
} else { |
315 |
|
|
/* first input closed: use the available samples */ |
316 |
|
|
nb_samples = INT_MAX; |
317 |
|
|
for (i = 1; i < s->nb_inputs; i++) { |
318 |
|
|
if (s->input_state[i] & INPUT_ON) { |
319 |
|
|
ns = av_audio_fifo_size(s->fifos[i]); |
320 |
|
|
nb_samples = FFMIN(nb_samples, ns); |
321 |
|
|
} |
322 |
|
|
} |
323 |
|
|
if (nb_samples == INT_MAX) { |
324 |
|
|
ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); |
325 |
|
|
return 0; |
326 |
|
|
} |
327 |
|
|
} |
328 |
|
|
|
329 |
|
609 |
frame_list_remove_samples(s->frame_list, nb_samples); |
330 |
|
|
|
331 |
|
609 |
calculate_scales(s, nb_samples); |
332 |
|
|
|
333 |
✓✓ |
609 |
if (nb_samples == 0) |
334 |
|
1 |
return 0; |
335 |
|
|
|
336 |
|
608 |
out_buf = ff_get_audio_buffer(outlink, nb_samples); |
337 |
✗✓ |
608 |
if (!out_buf) |
338 |
|
|
return AVERROR(ENOMEM); |
339 |
|
|
|
340 |
|
608 |
in_buf = ff_get_audio_buffer(outlink, nb_samples); |
341 |
✗✓ |
608 |
if (!in_buf) { |
342 |
|
|
av_frame_free(&out_buf); |
343 |
|
|
return AVERROR(ENOMEM); |
344 |
|
|
} |
345 |
|
|
|
346 |
✓✓ |
2085 |
for (i = 0; i < s->nb_inputs; i++) { |
347 |
✓✓ |
1477 |
if (s->input_state[i] & INPUT_ON) { |
348 |
|
|
int planes, plane_size, p; |
349 |
|
|
|
350 |
|
1086 |
av_audio_fifo_read(s->fifos[i], (void **)in_buf->extended_data, |
351 |
|
|
nb_samples); |
352 |
|
|
|
353 |
✗✓ |
1086 |
planes = s->planar ? s->nb_channels : 1; |
354 |
✓✗ |
1086 |
plane_size = nb_samples * (s->planar ? 1 : s->nb_channels); |
355 |
|
1086 |
plane_size = FFALIGN(plane_size, 16); |
356 |
|
|
|
357 |
✗✓ |
1086 |
if (out_buf->format == AV_SAMPLE_FMT_FLT || |
358 |
|
|
out_buf->format == AV_SAMPLE_FMT_FLTP) { |
359 |
✓✓ |
2172 |
for (p = 0; p < planes; p++) { |
360 |
|
1086 |
s->fdsp->vector_fmac_scalar((float *)out_buf->extended_data[p], |
361 |
|
1086 |
(float *) in_buf->extended_data[p], |
362 |
|
1086 |
s->input_scale[i], plane_size); |
363 |
|
|
} |
364 |
|
|
} else { |
365 |
|
|
for (p = 0; p < planes; p++) { |
366 |
|
|
s->fdsp->vector_dmac_scalar((double *)out_buf->extended_data[p], |
367 |
|
|
(double *) in_buf->extended_data[p], |
368 |
|
|
s->input_scale[i], plane_size); |
369 |
|
|
} |
370 |
|
|
} |
371 |
|
|
} |
372 |
|
|
} |
373 |
|
608 |
av_frame_free(&in_buf); |
374 |
|
|
|
375 |
|
608 |
out_buf->pts = s->next_pts; |
376 |
✓✗ |
608 |
if (s->next_pts != AV_NOPTS_VALUE) |
377 |
|
608 |
s->next_pts += nb_samples; |
378 |
|
|
|
379 |
|
608 |
return ff_filter_frame(outlink, out_buf); |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
/** |
383 |
|
|
* Requests a frame, if needed, from each input link other than the first. |
384 |
|
|
*/ |
385 |
|
482 |
static int request_samples(AVFilterContext *ctx, int min_samples) |
386 |
|
|
{ |
387 |
|
482 |
MixContext *s = ctx->priv; |
388 |
|
|
int i; |
389 |
|
|
|
390 |
✗✓ |
482 |
av_assert0(s->nb_inputs > 1); |
391 |
|
|
|
392 |
✓✓ |
1228 |
for (i = 1; i < s->nb_inputs; i++) { |
393 |
✓✓ |
746 |
if (!(s->input_state[i] & INPUT_ON) || |
394 |
✓✓ |
654 |
(s->input_state[i] & INPUT_EOF)) |
395 |
|
95 |
continue; |
396 |
✓✓ |
651 |
if (av_audio_fifo_size(s->fifos[i]) >= min_samples) |
397 |
|
89 |
continue; |
398 |
|
562 |
ff_inlink_request_frame(ctx->inputs[i]); |
399 |
|
|
} |
400 |
|
482 |
return output_frame(ctx->outputs[0]); |
401 |
|
|
} |
402 |
|
|
|
403 |
|
|
/** |
404 |
|
|
* Calculates the number of active inputs and determines EOF based on the |
405 |
|
|
* duration option. |
406 |
|
|
* |
407 |
|
|
* @return 0 if mixing should continue, or AVERROR_EOF if mixing should stop. |
408 |
|
|
*/ |
409 |
|
1696 |
static int calc_active_inputs(MixContext *s) |
410 |
|
|
{ |
411 |
|
|
int i; |
412 |
|
1696 |
int active_inputs = 0; |
413 |
✓✓ |
5871 |
for (i = 0; i < s->nb_inputs; i++) |
414 |
|
4175 |
active_inputs += !!(s->input_state[i] & INPUT_ON); |
415 |
|
1696 |
s->active_inputs = active_inputs; |
416 |
|
|
|
417 |
✓✓ |
1696 |
if (!active_inputs || |
418 |
✓✓✓✓
|
1694 |
(s->duration_mode == DURATION_FIRST && !(s->input_state[0] & INPUT_ON)) || |
419 |
✗✓✗✗
|
1693 |
(s->duration_mode == DURATION_SHORTEST && active_inputs != s->nb_inputs)) |
420 |
|
3 |
return AVERROR_EOF; |
421 |
|
1693 |
return 0; |
422 |
|
|
} |
423 |
|
|
|
424 |
|
1699 |
static int activate(AVFilterContext *ctx) |
425 |
|
|
{ |
426 |
|
1699 |
AVFilterLink *outlink = ctx->outputs[0]; |
427 |
|
1699 |
MixContext *s = ctx->priv; |
428 |
|
1699 |
AVFrame *buf = NULL; |
429 |
|
|
int i, ret; |
430 |
|
|
|
431 |
✓✓✓✓
|
1706 |
FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); |
432 |
|
|
|
433 |
✓✓ |
5871 |
for (i = 0; i < s->nb_inputs; i++) { |
434 |
|
4175 |
AVFilterLink *inlink = ctx->inputs[i]; |
435 |
|
|
|
436 |
✓✓ |
4175 |
if ((ret = ff_inlink_consume_frame(ctx->inputs[i], &buf)) > 0) { |
437 |
✓✓ |
1082 |
if (i == 0) { |
438 |
|
605 |
int64_t pts = av_rescale_q(buf->pts, inlink->time_base, |
439 |
|
|
outlink->time_base); |
440 |
|
605 |
ret = frame_list_add_frame(s->frame_list, buf->nb_samples, pts); |
441 |
✗✓ |
605 |
if (ret < 0) { |
442 |
|
|
av_frame_free(&buf); |
443 |
|
|
return ret; |
444 |
|
|
} |
445 |
|
|
} |
446 |
|
|
|
447 |
|
1082 |
ret = av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data, |
448 |
|
1082 |
buf->nb_samples); |
449 |
✗✓ |
1082 |
if (ret < 0) { |
450 |
|
|
av_frame_free(&buf); |
451 |
|
|
return ret; |
452 |
|
|
} |
453 |
|
|
|
454 |
|
1082 |
av_frame_free(&buf); |
455 |
|
|
|
456 |
|
1082 |
ret = output_frame(outlink); |
457 |
✗✓ |
1082 |
if (ret < 0) |
458 |
|
|
return ret; |
459 |
|
|
} |
460 |
|
|
} |
461 |
|
|
|
462 |
✓✓ |
5871 |
for (i = 0; i < s->nb_inputs; i++) { |
463 |
|
|
int64_t pts; |
464 |
|
|
int status; |
465 |
|
|
|
466 |
✓✓ |
4175 |
if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { |
467 |
✓✗ |
877 |
if (status == AVERROR_EOF) { |
468 |
✓✓ |
877 |
if (i == 0) { |
469 |
|
3 |
s->input_state[i] = 0; |
470 |
✗✓ |
3 |
if (s->nb_inputs == 1) { |
471 |
|
|
ff_outlink_set_status(outlink, status, pts); |
472 |
|
|
return 0; |
473 |
|
|
} |
474 |
|
|
} else { |
475 |
|
874 |
s->input_state[i] |= INPUT_EOF; |
476 |
✓✓ |
874 |
if (av_audio_fifo_size(s->fifos[i]) == 0) { |
477 |
|
871 |
s->input_state[i] = 0; |
478 |
|
|
} |
479 |
|
|
} |
480 |
|
|
} |
481 |
|
|
} |
482 |
|
|
} |
483 |
|
|
|
484 |
✓✓ |
1696 |
if (calc_active_inputs(s)) { |
485 |
|
3 |
ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); |
486 |
|
3 |
return 0; |
487 |
|
|
} |
488 |
|
|
|
489 |
✓✓ |
1693 |
if (ff_outlink_frame_wanted(outlink)) { |
490 |
|
|
int wanted_samples; |
491 |
|
|
|
492 |
✗✓ |
1087 |
if (!(s->input_state[0] & INPUT_ON)) |
493 |
|
|
return request_samples(ctx, 1); |
494 |
|
|
|
495 |
✓✓ |
1087 |
if (s->frame_list->nb_frames == 0) { |
496 |
|
605 |
ff_inlink_request_frame(ctx->inputs[0]); |
497 |
|
605 |
return 0; |
498 |
|
|
} |
499 |
✗✓ |
482 |
av_assert0(s->frame_list->nb_frames > 0); |
500 |
|
|
|
501 |
|
482 |
wanted_samples = frame_list_next_frame_size(s->frame_list); |
502 |
|
|
|
503 |
|
482 |
return request_samples(ctx, wanted_samples); |
504 |
|
|
} |
505 |
|
|
|
506 |
|
606 |
return 0; |
507 |
|
|
} |
508 |
|
|
|
509 |
|
6 |
static void parse_weights(AVFilterContext *ctx) |
510 |
|
|
{ |
511 |
|
6 |
MixContext *s = ctx->priv; |
512 |
|
6 |
float last_weight = 1.f; |
513 |
|
|
char *p; |
514 |
|
|
int i; |
515 |
|
|
|
516 |
|
6 |
s->weight_sum = 0.f; |
517 |
|
6 |
p = s->weights_str; |
518 |
✓✗ |
12 |
for (i = 0; i < s->nb_inputs; i++) { |
519 |
|
12 |
last_weight = av_strtod(p, &p); |
520 |
|
12 |
s->weights[i] = last_weight; |
521 |
|
12 |
s->weight_sum += FFABS(last_weight); |
522 |
✓✗✓✓
|
12 |
if (p && *p) { |
523 |
|
6 |
p++; |
524 |
|
|
} else { |
525 |
|
6 |
i++; |
526 |
|
6 |
break; |
527 |
|
|
} |
528 |
|
|
} |
529 |
|
|
|
530 |
✓✓ |
8 |
for (; i < s->nb_inputs; i++) { |
531 |
|
2 |
s->weights[i] = last_weight; |
532 |
|
2 |
s->weight_sum += FFABS(last_weight); |
533 |
|
|
} |
534 |
|
6 |
} |
535 |
|
|
|
536 |
|
6 |
static av_cold int init(AVFilterContext *ctx) |
537 |
|
|
{ |
538 |
|
6 |
MixContext *s = ctx->priv; |
539 |
|
|
int i, ret; |
540 |
|
|
|
541 |
✓✓ |
20 |
for (i = 0; i < s->nb_inputs; i++) { |
542 |
|
14 |
AVFilterPad pad = { 0 }; |
543 |
|
|
|
544 |
|
14 |
pad.type = AVMEDIA_TYPE_AUDIO; |
545 |
|
14 |
pad.name = av_asprintf("input%d", i); |
546 |
✗✓ |
14 |
if (!pad.name) |
547 |
|
|
return AVERROR(ENOMEM); |
548 |
|
|
|
549 |
✗✓ |
14 |
if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0) { |
550 |
|
|
av_freep(&pad.name); |
551 |
|
|
return ret; |
552 |
|
|
} |
553 |
|
|
} |
554 |
|
|
|
555 |
|
6 |
s->fdsp = avpriv_float_dsp_alloc(0); |
556 |
✗✓ |
6 |
if (!s->fdsp) |
557 |
|
|
return AVERROR(ENOMEM); |
558 |
|
|
|
559 |
|
6 |
s->weights = av_mallocz_array(s->nb_inputs, sizeof(*s->weights)); |
560 |
✗✓ |
6 |
if (!s->weights) |
561 |
|
|
return AVERROR(ENOMEM); |
562 |
|
|
|
563 |
|
6 |
parse_weights(ctx); |
564 |
|
|
|
565 |
|
6 |
return 0; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
6 |
static av_cold void uninit(AVFilterContext *ctx) |
569 |
|
|
{ |
570 |
|
|
int i; |
571 |
|
6 |
MixContext *s = ctx->priv; |
572 |
|
|
|
573 |
✓✓ |
6 |
if (s->fifos) { |
574 |
✓✓ |
10 |
for (i = 0; i < s->nb_inputs; i++) |
575 |
|
7 |
av_audio_fifo_free(s->fifos[i]); |
576 |
|
3 |
av_freep(&s->fifos); |
577 |
|
|
} |
578 |
|
6 |
frame_list_clear(s->frame_list); |
579 |
|
6 |
av_freep(&s->frame_list); |
580 |
|
6 |
av_freep(&s->input_state); |
581 |
|
6 |
av_freep(&s->input_scale); |
582 |
|
6 |
av_freep(&s->scale_norm); |
583 |
|
6 |
av_freep(&s->weights); |
584 |
|
6 |
av_freep(&s->fdsp); |
585 |
|
|
|
586 |
✓✓ |
20 |
for (i = 0; i < ctx->nb_inputs; i++) |
587 |
|
14 |
av_freep(&ctx->input_pads[i].name); |
588 |
|
6 |
} |
589 |
|
|
|
590 |
|
3 |
static int query_formats(AVFilterContext *ctx) |
591 |
|
|
{ |
592 |
|
|
static const enum AVSampleFormat sample_fmts[] = { |
593 |
|
|
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, |
594 |
|
|
AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP, |
595 |
|
|
AV_SAMPLE_FMT_NONE |
596 |
|
|
}; |
597 |
|
|
int ret; |
598 |
|
|
|
599 |
✓✗✗✓
|
6 |
if ((ret = ff_set_common_formats(ctx, ff_make_format_list(sample_fmts))) < 0 || |
600 |
|
3 |
(ret = ff_set_common_samplerates(ctx, ff_all_samplerates())) < 0) |
601 |
|
|
return ret; |
602 |
|
|
|
603 |
|
3 |
return ff_set_common_channel_layouts(ctx, ff_all_channel_counts()); |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
607 |
|
|
char *res, int res_len, int flags) |
608 |
|
|
{ |
609 |
|
|
MixContext *s = ctx->priv; |
610 |
|
|
int ret; |
611 |
|
|
|
612 |
|
|
ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); |
613 |
|
|
if (ret < 0) |
614 |
|
|
return ret; |
615 |
|
|
|
616 |
|
|
parse_weights(ctx); |
617 |
|
|
for (int i = 0; i < s->nb_inputs; i++) |
618 |
|
|
s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); |
619 |
|
|
calculate_scales(s, 0); |
620 |
|
|
|
621 |
|
|
return 0; |
622 |
|
|
} |
623 |
|
|
|
624 |
|
|
static const AVFilterPad avfilter_af_amix_outputs[] = { |
625 |
|
|
{ |
626 |
|
|
.name = "default", |
627 |
|
|
.type = AVMEDIA_TYPE_AUDIO, |
628 |
|
|
.config_props = config_output, |
629 |
|
|
}, |
630 |
|
|
{ NULL } |
631 |
|
|
}; |
632 |
|
|
|
633 |
|
|
AVFilter ff_af_amix = { |
634 |
|
|
.name = "amix", |
635 |
|
|
.description = NULL_IF_CONFIG_SMALL("Audio mixing."), |
636 |
|
|
.priv_size = sizeof(MixContext), |
637 |
|
|
.priv_class = &amix_class, |
638 |
|
|
.init = init, |
639 |
|
|
.uninit = uninit, |
640 |
|
|
.activate = activate, |
641 |
|
|
.query_formats = query_formats, |
642 |
|
|
.inputs = NULL, |
643 |
|
|
.outputs = avfilter_af_amix_outputs, |
644 |
|
|
.process_command = process_command, |
645 |
|
|
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS, |
646 |
|
|
}; |