cmdutils: fix new API break the "ffmpeg -muxers/demuxers"
[ffmpeg.git] / libavfilter / af_atempo.c
index 74bf9cc..8b214bc 100644 (file)
@@ -53,7 +53,7 @@
 /**
  * A fragment of audio waveform
  */
-typedef struct {
+typedef struct AudioFragment {
     // index of the first sample of this fragment in the overall waveform;
     // 0: input sample position
     // 1: output sample position
@@ -84,7 +84,9 @@ typedef enum {
 /**
  * Filter state machine
  */
-typedef struct {
+typedef struct ATempoContext {
+    const AVClass *class;
+
     // ring-buffer of input samples, necessary because some times
     // input fragment position may be adjusted backwards:
     uint8_t *buffer;
@@ -121,8 +123,9 @@ typedef struct {
     // tempo scaling factor:
     double tempo;
 
-    // cumulative alignment drift:
-    int drift;
+    // a snapshot of previous fragment input and output position values
+    // captured when the tempo scale factor was set most recently:
+    int64_t origin[2];
 
     // current/previous fragment ring-buffer:
     AudioFragment frag[2];
@@ -139,14 +142,34 @@ typedef struct {
     FFTSample *correlation;
 
     // for managing AVFilterPad.request_frame and AVFilterPad.filter_frame
-    int request_fulfilled;
-    AVFilterBufferRef *dst_buffer;
+    AVFrame *dst_buffer;
     uint8_t *dst;
     uint8_t *dst_end;
     uint64_t nsamples_in;
     uint64_t nsamples_out;
 } ATempoContext;
 
+#define OFFSET(x) offsetof(ATempoContext, x)
+
+static const AVOption atempo_options[] = {
+    { "tempo", "set tempo scale factor",
+      OFFSET(tempo), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0.5, 2.0,
+      AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(atempo);
+
+inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
+{
+    return &atempo->frag[atempo->nfrag % 2];
+}
+
+inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
+{
+    return &atempo->frag[(atempo->nfrag + 1) % 2];
+}
+
 /**
  * Reset filter to initial state, do not deallocate existing local buffers.
  */
@@ -156,13 +179,15 @@ static void yae_clear(ATempoContext *atempo)
     atempo->head = 0;
     atempo->tail = 0;
 
-    atempo->drift = 0;
     atempo->nfrag = 0;
     atempo->state = YAE_LOAD_FRAGMENT;
 
     atempo->position[0] = 0;
     atempo->position[1] = 0;
 
+    atempo->origin[0] = 0;
+    atempo->origin[1] = 0;
+
     atempo->frag[0].position[0] = 0;
     atempo->frag[0].position[1] = 0;
     atempo->frag[0].nsamples    = 0;
@@ -177,11 +202,10 @@ static void yae_clear(ATempoContext *atempo)
     atempo->frag[0].position[0] = -(int64_t)(atempo->window / 2);
     atempo->frag[0].position[1] = -(int64_t)(atempo->window / 2);
 
-    avfilter_unref_bufferp(&atempo->dst_buffer);
+    av_frame_free(&atempo->dst_buffer);
     atempo->dst     = NULL;
     atempo->dst_end = NULL;
 
-    atempo->request_fulfilled = 0;
     atempo->nsamples_in       = 0;
     atempo->nsamples_out      = 0;
 }
@@ -297,6 +321,7 @@ static int yae_reset(ATempoContext *atempo,
 
 static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
 {
+    const AudioFragment *prev;
     ATempoContext *atempo = ctx->priv;
     char   *tail = NULL;
     double tempo = av_strtod(arg_tempo, &tail);
@@ -312,20 +337,13 @@ static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
         return AVERROR(EINVAL);
     }
 
+    prev = yae_prev_frag(atempo);
+    atempo->origin[0] = prev->position[0] + atempo->window / 2;
+    atempo->origin[1] = prev->position[1] + atempo->window / 2;
     atempo->tempo = tempo;
     return 0;
 }
 
-inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
-{
-    return &atempo->frag[atempo->nfrag % 2];
-}
-
-inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
-{
-    return &atempo->frag[(atempo->nfrag + 1) % 2];
-}
-
 /**
  * A helper macro for initializing complex data buffer with scalar data
  * of a given type.
@@ -678,12 +696,21 @@ static int yae_adjust_position(ATempoContext *atempo)
     const AudioFragment *prev = yae_prev_frag(atempo);
     AudioFragment       *frag = yae_curr_frag(atempo);
 
+    const double prev_output_position =
+        (double)(prev->position[1] - atempo->origin[1] + atempo->window / 2) *
+        atempo->tempo;
+
+    const double ideal_output_position =
+        (double)(prev->position[0] - atempo->origin[0] + atempo->window / 2);
+
+    const int drift = (int)(prev_output_position - ideal_output_position);
+
     const int delta_max  = atempo->window / 2;
     const int correction = yae_align(frag,
                                      prev,
                                      atempo->window,
                                      delta_max,
-                                     atempo->drift,
+                                     drift,
                                      atempo->correlation,
                                      atempo->complex_to_real);
 
@@ -693,9 +720,6 @@ static int yae_adjust_position(ATempoContext *atempo)
 
         // clear so that the fragment can be reloaded:
         frag->nsamples = 0;
-
-        // update cumulative correction drift counter:
-        atempo->drift += correction;
     }
 
     return correction;
@@ -890,6 +914,11 @@ static int yae_flush(ATempoContext *atempo,
 
     atempo->state = YAE_FLUSH_OUTPUT;
 
+    if (!atempo->nfrag) {
+        // there is nothing to flush:
+        return 0;
+    }
+
     if (atempo->position[0] == frag->position[0] + frag->nsamples &&
         atempo->position[1] == frag->position[1] + frag->nsamples) {
         // the current fragment is already flushed:
@@ -925,7 +954,13 @@ static int yae_flush(ATempoContext *atempo,
         }
     }
 
-    // flush the remaininder of the current fragment:
+    // check whether all of the input samples have been consumed:
+    if (frag->position[0] + frag->nsamples < atempo->position[0]) {
+        yae_advance_to_next_frag(atempo);
+        return AVERROR(EAGAIN);
+    }
+
+    // flush the remainder of the current fragment:
     start_here = FFMAX(atempo->position[1], overlap_end);
     stop_here  = frag->position[1] + frag->nsamples;
     offset     = start_here - frag->position[1];
@@ -949,16 +984,12 @@ static int yae_flush(ATempoContext *atempo,
     return atempo->position[1] == stop_here ? 0 : AVERROR(EAGAIN);
 }
 
-static av_cold int init(AVFilterContext *ctx, const char *args)
+static av_cold int init(AVFilterContext *ctx)
 {
     ATempoContext *atempo = ctx->priv;
-
-    // NOTE: this assumes that the caller has memset ctx->priv to 0:
     atempo->format = AV_SAMPLE_FMT_NONE;
-    atempo->tempo  = 1.0;
     atempo->state  = YAE_LOAD_FRAGMENT;
-
-    return args ? yae_set_tempo(ctx, args) : 0;
+    return 0;
 }
 
 static av_cold void uninit(AVFilterContext *ctx)
@@ -978,7 +1009,7 @@ static int query_formats(AVFilterContext *ctx)
     // Planar sample formats are too cumbersome to store in a ring buffer,
     // therefore planar sample formats are not supported.
     //
-    enum AVSampleFormat sample_fmts[] = {
+    static const enum AVSampleFormat sample_fmts[] = {
         AV_SAMPLE_FMT_U8,
         AV_SAMPLE_FMT_S16,
         AV_SAMPLE_FMT_S32,
@@ -986,26 +1017,29 @@ static int query_formats(AVFilterContext *ctx)
         AV_SAMPLE_FMT_DBL,
         AV_SAMPLE_FMT_NONE
     };
+    int ret;
 
-    layouts = ff_all_channel_layouts();
+    layouts = ff_all_channel_counts();
     if (!layouts) {
         return AVERROR(ENOMEM);
     }
-    ff_set_common_channel_layouts(ctx, layouts);
+    ret = ff_set_common_channel_layouts(ctx, layouts);
+    if (ret < 0)
+        return ret;
 
     formats = ff_make_format_list(sample_fmts);
     if (!formats) {
         return AVERROR(ENOMEM);
     }
-    ff_set_common_formats(ctx, formats);
+    ret = ff_set_common_formats(ctx, formats);
+    if (ret < 0)
+        return ret;
 
     formats = ff_all_samplerates();
     if (!formats) {
         return AVERROR(ENOMEM);
     }
-    ff_set_common_samplerates(ctx, formats);
-
-    return 0;
+    return ff_set_common_samplerates(ctx, formats);
 }
 
 static int config_props(AVFilterLink *inlink)
@@ -1015,17 +1049,18 @@ static int config_props(AVFilterLink *inlink)
 
     enum AVSampleFormat format = inlink->format;
     int sample_rate = (int)inlink->sample_rate;
-    int channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
 
-    return yae_reset(atempo, format, sample_rate, channels);
+    return yae_reset(atempo, format, sample_rate, inlink->channels);
 }
 
-static void push_samples(ATempoContext *atempo,
-                         AVFilterLink *outlink,
-                         int n_out)
+static int push_samples(ATempoContext *atempo,
+                        AVFilterLink *outlink,
+                        int n_out)
 {
-    atempo->dst_buffer->audio->sample_rate = outlink->sample_rate;
-    atempo->dst_buffer->audio->nb_samples  = n_out;
+    int ret;
+
+    atempo->dst_buffer->sample_rate = outlink->sample_rate;
+    atempo->dst_buffer->nb_samples  = n_out;
 
     // adjust the PTS:
     atempo->dst_buffer->pts =
@@ -1033,22 +1068,25 @@ static void push_samples(ATempoContext *atempo,
                      (AVRational){ 1, outlink->sample_rate },
                      outlink->time_base);
 
-    ff_filter_frame(outlink, atempo->dst_buffer);
+    ret = ff_filter_frame(outlink, atempo->dst_buffer);
     atempo->dst_buffer = NULL;
     atempo->dst        = NULL;
     atempo->dst_end    = NULL;
+    if (ret < 0)
+        return ret;
 
     atempo->nsamples_out += n_out;
+    return 0;
 }
 
-static int filter_frame(AVFilterLink *inlink,
-                           AVFilterBufferRef *src_buffer)
+static int filter_frame(AVFilterLink *inlink, AVFrame *src_buffer)
 {
     AVFilterContext  *ctx = inlink->dst;
     ATempoContext *atempo = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
 
-    int n_in = src_buffer->audio->nb_samples;
+    int ret = 0;
+    int n_in = src_buffer->nb_samples;
     int n_out = (int)(0.5 + ((double)n_in) / atempo->tempo);
 
     const uint8_t *src = src_buffer->data[0];
@@ -1056,10 +1094,12 @@ static int filter_frame(AVFilterLink *inlink,
 
     while (src < src_end) {
         if (!atempo->dst_buffer) {
-            atempo->dst_buffer = ff_get_audio_buffer(outlink,
-                                                     AV_PERM_WRITE,
-                                                     n_out);
-            avfilter_copy_buffer_ref_props(atempo->dst_buffer, src_buffer);
+            atempo->dst_buffer = ff_get_audio_buffer(outlink, n_out);
+            if (!atempo->dst_buffer) {
+                av_frame_free(&src_buffer);
+                return AVERROR(ENOMEM);
+            }
+            av_frame_copy_props(atempo->dst_buffer, src_buffer);
 
             atempo->dst = atempo->dst_buffer->data[0];
             atempo->dst_end = atempo->dst + n_out * atempo->stride;
@@ -1068,14 +1108,18 @@ static int filter_frame(AVFilterLink *inlink,
         yae_apply(atempo, &src, src_end, &atempo->dst, atempo->dst_end);
 
         if (atempo->dst == atempo->dst_end) {
-            push_samples(atempo, outlink, n_out);
-            atempo->request_fulfilled = 1;
+            int n_samples = ((atempo->dst - atempo->dst_buffer->data[0]) /
+                             atempo->stride);
+            ret = push_samples(atempo, outlink, n_samples);
+            if (ret < 0)
+                goto end;
         }
     }
 
     atempo->nsamples_in += n_in;
-    avfilter_unref_bufferp(&src_buffer);
-    return 0;
+end:
+    av_frame_free(&src_buffer);
+    return ret;
 }
 
 static int request_frame(AVFilterLink *outlink)
@@ -1084,11 +1128,7 @@ static int request_frame(AVFilterLink *outlink)
     ATempoContext *atempo = ctx->priv;
     int ret;
 
-    atempo->request_fulfilled = 0;
-    do {
-        ret = ff_request_frame(ctx->inputs[0]);
-    }
-    while (!atempo->request_fulfilled && ret >= 0);
+    ret = ff_request_frame(ctx->inputs[0]);
 
     if (ret == AVERROR_EOF) {
         // flush the filter:
@@ -1098,9 +1138,9 @@ static int request_frame(AVFilterLink *outlink)
 
         while (err == AVERROR(EAGAIN)) {
             if (!atempo->dst_buffer) {
-                atempo->dst_buffer = ff_get_audio_buffer(outlink,
-                                                         AV_PERM_WRITE,
-                                                         n_max);
+                atempo->dst_buffer = ff_get_audio_buffer(outlink, n_max);
+                if (!atempo->dst_buffer)
+                    return AVERROR(ENOMEM);
 
                 atempo->dst = atempo->dst_buffer->data[0];
                 atempo->dst_end = atempo->dst + n_max * atempo->stride;
@@ -1112,11 +1152,13 @@ static int request_frame(AVFilterLink *outlink)
                      atempo->stride);
 
             if (n_out) {
-                push_samples(atempo, outlink, n_out);
+                ret = push_samples(atempo, outlink, n_out);
+                if (ret < 0)
+                    return ret;
             }
         }
 
-        avfilter_unref_bufferp(&atempo->dst_buffer);
+        av_frame_free(&atempo->dst_buffer);
         atempo->dst     = NULL;
         atempo->dst_end = NULL;
 
@@ -1142,7 +1184,6 @@ static const AVFilterPad atempo_inputs[] = {
         .type         = AVMEDIA_TYPE_AUDIO,
         .filter_frame = filter_frame,
         .config_props = config_props,
-        .min_perms    = AV_PERM_READ,
     },
     { NULL }
 };
@@ -1156,7 +1197,7 @@ static const AVFilterPad atempo_outputs[] = {
     { NULL }
 };
 
-AVFilter avfilter_af_atempo = {
+AVFilter ff_af_atempo = {
     .name            = "atempo",
     .description     = NULL_IF_CONFIG_SMALL("Adjust audio tempo."),
     .init            = init,
@@ -1164,6 +1205,7 @@ AVFilter avfilter_af_atempo = {
     .query_formats   = query_formats,
     .process_command = process_command,
     .priv_size       = sizeof(ATempoContext),
+    .priv_class      = &atempo_class,
     .inputs          = atempo_inputs,
     .outputs         = atempo_outputs,
 };