lavfi/af_amix: mostly fix scheduling.
authorNicolas George <george@nsup.org>
Sat, 24 Oct 2015 13:19:11 +0000 (15:19 +0200)
committerNicolas George <george@nsup.org>
Sat, 7 Nov 2015 15:43:36 +0000 (16:43 +0100)
libavfilter/af_amix.c

index 434dd90..223328b 100644 (file)
@@ -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,
@@ -314,29 +350,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)
@@ -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);