diff options
Diffstat (limited to 'libavfilter/af_amix.c')
-rw-r--r-- | libavfilter/af_amix.c | 241 |
1 files changed, 119 insertions, 122 deletions
diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c index bfba1504ea..e18743e0b8 100644 --- a/libavfilter/af_amix.c +++ b/libavfilter/af_amix.c @@ -2,20 +2,20 @@ * Audio Mix Filter * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -44,9 +44,8 @@ #include "formats.h" #include "internal.h" -#define INPUT_OFF 0 /**< input has reached EOF */ #define INPUT_ON 1 /**< input is active */ -#define INPUT_INACTIVE 2 /**< input is on, but is currently inactive */ +#define INPUT_EOF 2 /**< input has reached EOF (may still be active) */ #define DURATION_LONGEST 0 #define DURATION_SHORTEST 1 @@ -110,7 +109,7 @@ static void frame_list_remove_samples(FrameList *frame_list, int nb_samples) int samples = nb_samples; while (samples > 0) { FrameInfo *info = frame_list->list; - av_assert0(info != NULL); + av_assert0(info); if (info->nb_samples <= samples) { samples -= info->nb_samples; frame_list->list = info->next; @@ -142,7 +141,7 @@ static int frame_list_add_frame(FrameList *frame_list, int nb_samples, int64_t p frame_list->list = info; frame_list->end = info; } else { - av_assert0(frame_list->end != NULL); + av_assert0(frame_list->end); frame_list->end->next = info; frame_list->end = info; } @@ -155,7 +154,7 @@ static int frame_list_add_frame(FrameList *frame_list, int nb_samples, int64_t p typedef struct MixContext { const AVClass *class; /**< class for AVOptions */ - AVFloatDSPContext fdsp; + AVFloatDSPContext *fdsp; int nb_inputs; /**< number of inputs */ int active_inputs; /**< number of input currently active */ @@ -175,27 +174,22 @@ typedef struct MixContext { #define OFFSET(x) offsetof(MixContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM -static const AVOption options[] = { +#define F AV_OPT_FLAG_FILTERING_PARAM +static const AVOption amix_options[] = { { "inputs", "Number of inputs.", - OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 32, A }, + OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 32, A|F }, { "duration", "How to determine the end-of-stream.", - OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A, "duration" }, - { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, INT_MIN, INT_MAX, A, "duration" }, - { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, INT_MIN, INT_MAX, A, "duration" }, - { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, INT_MIN, INT_MAX, A, "duration" }, + OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A|F, "duration" }, + { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, INT_MIN, INT_MAX, A|F, "duration" }, + { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, INT_MIN, INT_MAX, A|F, "duration" }, + { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, INT_MIN, INT_MAX, A|F, "duration" }, { "dropout_transition", "Transition time, in seconds, for volume " "renormalization when an input stream ends.", - OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A }, - { NULL }, -}; - -static const AVClass amix_class = { - .class_name = "amix filter", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, + OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F }, + { NULL } }; +AVFILTER_DEFINE_CLASS(amix); /** * Update the scaling factors to apply to each input during mixing. @@ -214,7 +208,7 @@ static void calculate_scales(MixContext *s, int nb_samples) } for (i = 0; i < s->nb_inputs; i++) { - if (s->input_state[i] == INPUT_ON) + if (s->input_state[i] & INPUT_ON) s->input_scale[i] = 1.0f / s->scale_norm; else s->input_scale[i] = 0.0f; @@ -237,11 +231,11 @@ static int config_output(AVFilterLink *outlink) if (!s->frame_list) return AVERROR(ENOMEM); - s->fifos = av_mallocz(s->nb_inputs * sizeof(*s->fifos)); + s->fifos = av_mallocz_array(s->nb_inputs, sizeof(*s->fifos)); if (!s->fifos) return AVERROR(ENOMEM); - s->nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout); + s->nb_channels = outlink->channels; for (i = 0; i < s->nb_inputs; i++) { s->fifos[i] = av_audio_fifo_alloc(outlink->format, s->nb_channels, 1024); if (!s->fifos[i]) @@ -254,7 +248,7 @@ static int config_output(AVFilterLink *outlink) memset(s->input_state, INPUT_ON, s->nb_inputs); s->active_inputs = s->nb_inputs; - s->input_scale = av_mallocz(s->nb_inputs * sizeof(*s->input_scale)); + s->input_scale = av_mallocz_array(s->nb_inputs, sizeof(*s->input_scale)); if (!s->input_scale) return AVERROR(ENOMEM); s->scale_norm = s->active_inputs; @@ -269,18 +263,58 @@ static int config_output(AVFilterLink *outlink) return 0; } +static int calc_active_inputs(MixContext *s); + /** * Read samples from the input FIFOs, mix, and write to the output link. */ -static int output_frame(AVFilterLink *outlink, int nb_samples) +static int output_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; MixContext *s = ctx->priv; AVFrame *out_buf, *in_buf; - int i; + int nb_samples, ns, ret, i; + + ret = calc_active_inputs(s); + if (ret < 0) + return ret; + + if (s->input_state[0] & INPUT_ON) { + /* first input live: use the corresponding frame size */ + nb_samples = frame_list_next_frame_size(s->frame_list); + for (i = 1; i < s->nb_inputs; i++) { + if (s->input_state[i] & INPUT_ON) { + ns = av_audio_fifo_size(s->fifos[i]); + if (ns < nb_samples) { + if (!(s->input_state[i] & INPUT_EOF)) + /* unclosed input with not enough samples */ + return 0; + /* closed input to drain */ + nb_samples = ns; + } + } + } + } else { + /* first input closed: use the available samples */ + nb_samples = INT_MAX; + for (i = 1; i < s->nb_inputs; i++) { + if (s->input_state[i] & INPUT_ON) { + ns = av_audio_fifo_size(s->fifos[i]); + nb_samples = FFMIN(nb_samples, ns); + } + } + if (nb_samples == INT_MAX) + return AVERROR_EOF; + } + + s->next_pts = frame_list_next_pts(s->frame_list); + frame_list_remove_samples(s->frame_list, nb_samples); calculate_scales(s, nb_samples); + if (nb_samples == 0) + return 0; + out_buf = ff_get_audio_buffer(outlink, nb_samples); if (!out_buf) return AVERROR(ENOMEM); @@ -292,7 +326,7 @@ static int output_frame(AVFilterLink *outlink, int nb_samples) } for (i = 0; i < s->nb_inputs; i++) { - if (s->input_state[i] == INPUT_ON) { + if (s->input_state[i] & INPUT_ON) { int planes, plane_size, p; av_audio_fifo_read(s->fifos[i], (void **)in_buf->extended_data, @@ -303,7 +337,7 @@ static int output_frame(AVFilterLink *outlink, int nb_samples) plane_size = FFALIGN(plane_size, 16); for (p = 0; p < planes; p++) { - s->fdsp.vector_fmac_scalar((float *)out_buf->extended_data[p], + s->fdsp->vector_fmac_scalar((float *)out_buf->extended_data[p], (float *) in_buf->extended_data[p], s->input_scale[i], plane_size); } @@ -319,29 +353,6 @@ static int output_frame(AVFilterLink *outlink, int nb_samples) } /** - * Returns the smallest number of samples available in the input FIFOs other - * than that of the first input. - */ -static int get_available_samples(MixContext *s) -{ - int i; - int available_samples = INT_MAX; - - av_assert0(s->nb_inputs > 1); - - for (i = 1; i < s->nb_inputs; i++) { - int nb_samples; - if (s->input_state[i] == INPUT_OFF) - continue; - nb_samples = av_audio_fifo_size(s->fifos[i]); - available_samples = FFMIN(available_samples, nb_samples); - } - if (available_samples == INT_MAX) - return 0; - return available_samples; -} - -/** * Requests a frame, if needed, from each input link other than the first. */ static int request_samples(AVFilterContext *ctx, int min_samples) @@ -353,19 +364,21 @@ static int request_samples(AVFilterContext *ctx, int min_samples) for (i = 1; i < s->nb_inputs; i++) { ret = 0; - if (s->input_state[i] == INPUT_OFF) + if (!(s->input_state[i] & INPUT_ON)) continue; - while (!ret && av_audio_fifo_size(s->fifos[i]) < min_samples) - ret = ff_request_frame(ctx->inputs[i]); + if (av_audio_fifo_size(s->fifos[i]) >= min_samples) + continue; + ret = ff_request_frame(ctx->inputs[i]); if (ret == AVERROR_EOF) { + s->input_state[i] |= INPUT_EOF; if (av_audio_fifo_size(s->fifos[i]) == 0) { - s->input_state[i] = INPUT_OFF; + s->input_state[i] = 0; continue; } } else if (ret < 0) return ret; } - return 0; + return output_frame(ctx->outputs[0]); } /** @@ -379,11 +392,11 @@ static int calc_active_inputs(MixContext *s) int i; int active_inputs = 0; for (i = 0; i < s->nb_inputs; i++) - active_inputs += !!(s->input_state[i] != INPUT_OFF); + active_inputs += !!(s->input_state[i] & INPUT_ON); s->active_inputs = active_inputs; if (!active_inputs || - (s->duration_mode == DURATION_FIRST && s->input_state[0] == INPUT_OFF) || + (s->duration_mode == DURATION_FIRST && !(s->input_state[0] & INPUT_ON)) || (s->duration_mode == DURATION_SHORTEST && active_inputs != s->nb_inputs)) return AVERROR_EOF; return 0; @@ -394,66 +407,30 @@ static int request_frame(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; MixContext *s = ctx->priv; int ret; - int wanted_samples, available_samples; + int wanted_samples; ret = calc_active_inputs(s); if (ret < 0) return ret; - if (s->input_state[0] == INPUT_OFF) { - ret = request_samples(ctx, 1); - if (ret < 0) - return ret; - - ret = calc_active_inputs(s); - if (ret < 0) - return ret; - - available_samples = get_available_samples(s); - if (!available_samples) - return AVERROR(EAGAIN); - - return output_frame(outlink, available_samples); - } + if (!(s->input_state[0] & INPUT_ON)) + return request_samples(ctx, 1); if (s->frame_list->nb_frames == 0) { ret = ff_request_frame(ctx->inputs[0]); if (ret == AVERROR_EOF) { - s->input_state[0] = INPUT_OFF; + s->input_state[0] = 0; if (s->nb_inputs == 1) return AVERROR_EOF; - else - return AVERROR(EAGAIN); - } else if (ret < 0) - return ret; + return output_frame(ctx->outputs[0]); + } + return ret; } av_assert0(s->frame_list->nb_frames > 0); wanted_samples = frame_list_next_frame_size(s->frame_list); - if (s->active_inputs > 1) { - ret = request_samples(ctx, wanted_samples); - if (ret < 0) - return ret; - - ret = calc_active_inputs(s); - if (ret < 0) - return ret; - } - - if (s->active_inputs > 1) { - available_samples = get_available_samples(s); - if (!available_samples) - return AVERROR(EAGAIN); - available_samples = FFMIN(available_samples, wanted_samples); - } else { - available_samples = wanted_samples; - } - - s->next_pts = frame_list_next_pts(s->frame_list); - frame_list_remove_samples(s->frame_list, available_samples); - - return output_frame(outlink, available_samples); + return request_samples(ctx, wanted_samples); } static int filter_frame(AVFilterLink *inlink, AVFrame *buf) @@ -483,6 +460,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) ret = av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data, buf->nb_samples); + av_frame_free(&buf); + return output_frame(outlink); + fail: av_frame_free(&buf); @@ -501,12 +481,16 @@ static av_cold int init(AVFilterContext *ctx) snprintf(name, sizeof(name), "input%d", i); pad.type = AVMEDIA_TYPE_AUDIO; pad.name = av_strdup(name); + if (!pad.name) + return AVERROR(ENOMEM); pad.filter_frame = filter_frame; ff_insert_inpad(ctx, i, &pad); } - avpriv_float_dsp_init(&s->fdsp, 0); + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); return 0; } @@ -525,6 +509,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->frame_list); av_freep(&s->input_state); av_freep(&s->input_scale); + av_freep(&s->fdsp); for (i = 0; i < ctx->nb_inputs; i++) av_freep(&ctx->input_pads[i].name); @@ -533,12 +518,27 @@ static av_cold void uninit(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { AVFilterFormats *formats = NULL; - ff_add_format(&formats, AV_SAMPLE_FMT_FLT); - ff_add_format(&formats, AV_SAMPLE_FMT_FLTP); - ff_set_common_formats(ctx, formats); - ff_set_common_channel_layouts(ctx, ff_all_channel_layouts()); - ff_set_common_samplerates(ctx, ff_all_samplerates()); + AVFilterChannelLayouts *layouts; + int ret; + + layouts = ff_all_channel_counts(); + if (!layouts) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = ff_add_format(&formats, AV_SAMPLE_FMT_FLT )) < 0 || + (ret = ff_add_format(&formats, AV_SAMPLE_FMT_FLTP)) < 0 || + (ret = ff_set_common_formats (ctx, formats)) < 0 || + (ret = ff_set_common_channel_layouts(ctx, layouts)) < 0 || + (ret = ff_set_common_samplerates(ctx, ff_all_samplerates())) < 0) + goto fail; return 0; +fail: + if (layouts) + av_freep(&layouts->channel_layouts); + av_freep(&layouts); + return ret; } static const AVFilterPad avfilter_af_amix_outputs[] = { @@ -552,17 +552,14 @@ static const AVFilterPad avfilter_af_amix_outputs[] = { }; AVFilter ff_af_amix = { - .name = "amix", - .description = NULL_IF_CONFIG_SMALL("Audio mixing."), - .priv_size = sizeof(MixContext), - .priv_class = &amix_class, - + .name = "amix", + .description = NULL_IF_CONFIG_SMALL("Audio mixing."), + .priv_size = sizeof(MixContext), + .priv_class = &amix_class, .init = init, .uninit = uninit, .query_formats = query_formats, - - .inputs = NULL, - .outputs = avfilter_af_amix_outputs, - - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .inputs = NULL, + .outputs = avfilter_af_amix_outputs, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, }; |