| @@ -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 | |||
| @@ -209,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; | |||
| @@ -264,15 +263,52 @@ 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); | |||
| @@ -287,7 +323,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, | |||
| @@ -313,29 +349,6 @@ static int output_frame(AVFilterLink *outlink, int nb_samples) | |||
| return ff_filter_frame(outlink, out_buf); | |||
| } | |||
| /** | |||
| * 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. | |||
| */ | |||
| @@ -348,19 +361,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]); | |||
| } | |||
| /** | |||
| @@ -374,11 +389,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; | |||
| @@ -389,66 +404,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) | |||
| @@ -478,6 +457,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); | |||