X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Faf_amix.c;h=2df0507589cbe0025297f3dfd881670e19b0ecf2;hb=77cc958f60f73963be4281d6e82ef81707e40c26;hp=8f8ae3e340a9c86d632ad0738518f2468a4a0d30;hpb=fa417fcd278a003530fe6bf851f8194177a5e71f;p=ffmpeg diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c index 8f8ae3e340a..2df0507589c 100644 --- a/libavfilter/af_amix.c +++ b/libavfilter/af_amix.c @@ -28,10 +28,13 @@ * output. */ -#include "libavutil/audioconvert.h" +#include "libavutil/attributes.h" #include "libavutil/audio_fifo.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/float_dsp.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/samplefmt.h" @@ -152,6 +155,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; int nb_inputs; /**< number of inputs */ int active_inputs; /**< number of input currently active */ @@ -160,6 +164,7 @@ typedef struct MixContext { int nb_channels; /**< number of channels */ int sample_rate; /**< sample rate */ + int planar; AVAudioFifo **fifos; /**< audio fifo for each input */ uint8_t *input_state; /**< current state of each input */ float *input_scale; /**< mixing scale factor for each input */ @@ -172,15 +177,15 @@ typedef struct MixContext { #define A AV_OPT_FLAG_AUDIO_PARAM static const AVOption options[] = { { "inputs", "Number of inputs.", - OFFSET(nb_inputs), AV_OPT_TYPE_INT, { 2 }, 1, 32, A }, + OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 32, A }, { "duration", "How to determine the end-of-stream.", - OFFSET(duration_mode), AV_OPT_TYPE_INT, { DURATION_LONGEST }, 0, 2, A, "duration" }, - { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { DURATION_LONGEST }, INT_MIN, INT_MAX, A, "duration" }, - { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { DURATION_SHORTEST }, INT_MIN, INT_MAX, A, "duration" }, - { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { DURATION_FIRST }, INT_MIN, INT_MAX, A, "duration" }, + 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" }, { "dropout_transition", "Transition time, in seconds, for volume " "renormalization when an input stream ends.", - OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { 2.0 }, 0, INT_MAX, A }, + OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A }, { NULL }, }; @@ -223,6 +228,7 @@ static int config_output(AVFilterLink *outlink) int i; char buf[64]; + s->planar = av_sample_fmt_is_planar(outlink->format); s->sample_rate = outlink->sample_rate; outlink->time_base = (AVRational){ 1, outlink->sample_rate }; s->next_pts = AV_NOPTS_VALUE; @@ -257,20 +263,12 @@ static int config_output(AVFilterLink *outlink) av_get_channel_layout_string(buf, sizeof(buf), -1, outlink->channel_layout); av_log(ctx, AV_LOG_VERBOSE, - "inputs:%d fmt:%s srate:%"PRId64" cl:%s\n", s->nb_inputs, + "inputs:%d fmt:%s srate:%d cl:%s\n", s->nb_inputs, av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf); return 0; } -/* TODO: move optimized version from DSPContext to libavutil */ -static void vector_fmac_scalar(float *dst, const float *src, float mul, int len) -{ - int i; - for (i = 0; i < len; i++) - dst[i] += src[i] * mul; -} - /** * Read samples from the input FIFOs, mix, and write to the output link. */ @@ -278,37 +276,46 @@ static int output_frame(AVFilterLink *outlink, int nb_samples) { AVFilterContext *ctx = outlink->src; MixContext *s = ctx->priv; - AVFilterBufferRef *out_buf, *in_buf; + AVFrame *out_buf, *in_buf; int i; calculate_scales(s, nb_samples); - out_buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples); + out_buf = ff_get_audio_buffer(outlink, nb_samples); if (!out_buf) return AVERROR(ENOMEM); - in_buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples); - if (!in_buf) + in_buf = ff_get_audio_buffer(outlink, nb_samples); + if (!in_buf) { + av_frame_free(&out_buf); return AVERROR(ENOMEM); + } for (i = 0; i < s->nb_inputs; i++) { if (s->input_state[i] == INPUT_ON) { + int planes, plane_size, p; + av_audio_fifo_read(s->fifos[i], (void **)in_buf->extended_data, nb_samples); - vector_fmac_scalar((float *)out_buf->extended_data[0], - (float *) in_buf->extended_data[0], - s->input_scale[i], nb_samples * s->nb_channels); + + planes = s->planar ? s->nb_channels : 1; + plane_size = nb_samples * (s->planar ? 1 : s->nb_channels); + plane_size = FFALIGN(plane_size, 16); + + for (p = 0; p < planes; p++) { + s->fdsp.vector_fmac_scalar((float *)out_buf->extended_data[p], + (float *) in_buf->extended_data[p], + s->input_scale[i], plane_size); + } } } - avfilter_unref_buffer(in_buf); + av_frame_free(&in_buf); out_buf->pts = s->next_pts; if (s->next_pts != AV_NOPTS_VALUE) s->next_pts += nb_samples; - ff_filter_samples(outlink, out_buf); - - return 0; + return ff_filter_frame(outlink, out_buf); } /** @@ -349,13 +356,13 @@ static int request_samples(AVFilterContext *ctx, int min_samples) if (s->input_state[i] == INPUT_OFF) continue; while (!ret && av_audio_fifo_size(s->fifos[i]) < min_samples) - ret = avfilter_request_frame(ctx->inputs[i]); + ret = ff_request_frame(ctx->inputs[i]); if (ret == AVERROR_EOF) { if (av_audio_fifo_size(s->fifos[i]) == 0) { s->input_state[i] = INPUT_OFF; continue; } - } else if (ret) + } else if (ret < 0) return ret; } return 0; @@ -404,20 +411,20 @@ static int request_frame(AVFilterLink *outlink) available_samples = get_available_samples(s); if (!available_samples) - return 0; + return AVERROR(EAGAIN); return output_frame(outlink, available_samples); } if (s->frame_list->nb_frames == 0) { - ret = avfilter_request_frame(ctx->inputs[0]); + ret = ff_request_frame(ctx->inputs[0]); if (ret == AVERROR_EOF) { s->input_state[0] = INPUT_OFF; if (s->nb_inputs == 1) return AVERROR_EOF; else return AVERROR(EAGAIN); - } else if (ret) + } else if (ret < 0) return ret; } av_assert0(s->frame_list->nb_frames > 0); @@ -432,10 +439,12 @@ static int request_frame(AVFilterLink *outlink) 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 0; + return AVERROR(EAGAIN); available_samples = FFMIN(available_samples, wanted_samples); } else { available_samples = wanted_samples; @@ -447,46 +456,43 @@ static int request_frame(AVFilterLink *outlink) return output_frame(outlink, available_samples); } -static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf) +static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { AVFilterContext *ctx = inlink->dst; MixContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - int i; + int i, ret = 0; - for (i = 0; i < ctx->input_count; i++) + for (i = 0; i < ctx->nb_inputs; i++) if (ctx->inputs[i] == inlink) break; - if (i >= ctx->input_count) { + if (i >= ctx->nb_inputs) { av_log(ctx, AV_LOG_ERROR, "unknown input link\n"); - return; + ret = AVERROR(EINVAL); + goto fail; } if (i == 0) { int64_t pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); - frame_list_add_frame(s->frame_list, buf->audio->nb_samples, pts); + ret = frame_list_add_frame(s->frame_list, buf->nb_samples, pts); + if (ret < 0) + goto fail; } - av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data, - buf->audio->nb_samples); + ret = av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data, + buf->nb_samples); - avfilter_unref_buffer(buf); +fail: + av_frame_free(&buf); + + return ret; } -static int init(AVFilterContext *ctx, const char *args, void *opaque) +static av_cold int init(AVFilterContext *ctx) { MixContext *s = ctx->priv; - int i, ret; - - s->class = &amix_class; - av_opt_set_defaults(s); - - if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { - av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); - return ret; - } - av_opt_free(s); + int i; for (i = 0; i < s->nb_inputs; i++) { char name[32]; @@ -495,15 +501,17 @@ static int init(AVFilterContext *ctx, const char *args, void *opaque) snprintf(name, sizeof(name), "input%d", i); pad.type = AVMEDIA_TYPE_AUDIO; pad.name = av_strdup(name); - pad.filter_samples = filter_samples; + pad.filter_frame = filter_frame; ff_insert_inpad(ctx, i, &pad); } + avpriv_float_dsp_init(&s->fdsp, 0); + return 0; } -static void uninit(AVFilterContext *ctx) +static av_cold void uninit(AVFilterContext *ctx) { int i; MixContext *s = ctx->priv; @@ -518,7 +526,7 @@ static void uninit(AVFilterContext *ctx) av_freep(&s->input_state); av_freep(&s->input_scale); - for (i = 0; i < ctx->input_count; i++) + for (i = 0; i < ctx->nb_inputs; i++) av_freep(&ctx->input_pads[i].name); } @@ -526,25 +534,35 @@ 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()); return 0; } +static const AVFilterPad avfilter_af_amix_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + .request_frame = request_frame + }, + { NULL } +}; + AVFilter avfilter_af_amix = { .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 = (const AVFilterPad[]) {{ .name = NULL}}, - .outputs = (const AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .config_props = config_output, - .request_frame = request_frame }, - { .name = NULL}}, + .inputs = NULL, + .outputs = avfilter_af_amix_outputs, + + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, };