[FFmpeg-devel] avfilter/avf_showwaves: make output frame rate actually follow user supplied value

Submitted by Paul B Mahol on Oct. 8, 2019, 2:53 p.m.

Details

Message ID 20191008145312.22000-1-onemda@gmail.com
State New
Headers show

Commit Message

Paul B Mahol Oct. 8, 2019, 2:53 p.m.
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 libavfilter/avf_showwaves.c | 77 ++++++++++++++++++++++++++++---------
 1 file changed, 58 insertions(+), 19 deletions(-)

Patch hide | download patch | download mbox

diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c
index afe05fb504..532a75b684 100644
--- a/libavfilter/avf_showwaves.c
+++ b/libavfilter/avf_showwaves.c
@@ -23,6 +23,7 @@ 
  * audio to video multimedia filter
  */
 
+#include "libavutil/audio_fifo.h"
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/channel_layout.h"
@@ -70,6 +71,7 @@  typedef struct ShowWavesContext {
     int buf_idx;
     int16_t *buf_idy;    /* y coordinate of previous sample for each channel */
     AVFrame *outpicref;
+    AVFrame *tmp;
     int n;
     int pixstep;
     int sample_count_mod;
@@ -89,6 +91,11 @@  typedef struct ShowWavesContext {
     struct frame_node *last_frame;
     int64_t total_samples;
     int64_t *sum; /* abs sum of the samples per channel */
+    int hop_size;
+    int64_t pts;
+    int64_t samples_in;
+    int64_t samples_out;
+    AVAudioFifo *fifo;
 } ShowWavesContext;
 
 #define OFFSET(x) offsetof(ShowWavesContext, x)
@@ -124,6 +131,7 @@  static av_cold void uninit(AVFilterContext *ctx)
 {
     ShowWavesContext *showwaves = ctx->priv;
 
+    av_frame_free(&showwaves->tmp);
     av_frame_free(&showwaves->outpicref);
     av_freep(&showwaves->buf_idy);
     av_freep(&showwaves->fg);
@@ -139,6 +147,8 @@  static av_cold void uninit(AVFilterContext *ctx)
         }
         av_freep(&showwaves->sum);
         showwaves->last_frame = NULL;
+    } else {
+        av_audio_fifo_free(showwaves->fifo);
     }
 }
 
@@ -413,11 +423,22 @@  static int config_output(AVFilterLink *outlink)
     uint8_t x;
     int ch;
 
-    if (showwaves->single_pic)
+    if (showwaves->single_pic) {
         showwaves->n = 1;
+    } else {
+        showwaves->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, 1024);
+        if (!showwaves->fifo)
+            return AVERROR(ENOMEM);
+    }
 
     if (!showwaves->n)
         showwaves->n = FFMAX(1, av_rescale_q(inlink->sample_rate, av_make_q(1, showwaves->w), showwaves->rate));
+    showwaves->hop_size = FFMIN(showwaves->n * showwaves->w, av_rescale(inlink->sample_rate, showwaves->rate.den, showwaves->rate.num));
+    if (!showwaves->single_pic) {
+        showwaves->tmp = ff_get_audio_buffer(inlink, showwaves->n * showwaves->w);
+        if (!showwaves->tmp)
+            return AVERROR(ENOMEM);
+    }
 
     showwaves->buf_idx = 0;
     if (!(showwaves->buf_idy = av_mallocz_array(nb_channels, sizeof(*showwaves->buf_idy)))) {
@@ -428,11 +449,12 @@  static int config_output(AVFilterLink *outlink)
     outlink->h = showwaves->h;
     outlink->sample_aspect_ratio = (AVRational){1,1};
 
-    outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n},
-                                   (AVRational){showwaves->w,1});
+    outlink->frame_rate = showwaves->rate;
+    outlink->time_base = inlink->time_base;
 
-    av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n",
-           showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n);
+    av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d hop:%d tb:%d/%d\n",
+           showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n,
+           showwaves->hop_size, outlink->time_base.num, outlink->time_base.den);
 
     switch (outlink->format) {
     case AV_PIX_FMT_GRAY8:
@@ -635,7 +657,7 @@  static int request_frame(AVFilterLink *outlink)
 
 static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p,
                            const AVFilterLink *inlink, AVFilterLink *outlink,
-                           const AVFrame *in)
+                           const AVFrame *in, int64_t pts)
 {
     if (!showwaves->outpicref) {
         int j;
@@ -645,9 +667,9 @@  static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p,
             return AVERROR(ENOMEM);
         out->width  = outlink->w;
         out->height = outlink->h;
-        out->pts = in->pts + av_rescale_q((p - (int16_t *)in->data[0]) / inlink->channels,
-                                          av_make_q(1, inlink->sample_rate),
-                                          outlink->time_base);
+        out->pts = pts + av_rescale_q(showwaves->samples_out + showwaves->n * showwaves->w - showwaves->samples_in,
+                                      av_make_q(1, inlink->sample_rate),
+                                      outlink->time_base);
         for (j = 0; j < outlink->h; j++)
             memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep);
     }
@@ -668,7 +690,7 @@  static av_cold int init(AVFilterContext *ctx)
 
 #if CONFIG_SHOWWAVES_FILTER
 
-static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
+static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples, int64_t pts)
 {
     AVFilterContext *ctx = inlink->dst;
     AVFilterLink *outlink = ctx->outputs[0];
@@ -685,7 +707,7 @@  static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
     /* draw data in the buffer */
     for (i = 0; i < nb_samples; i++) {
 
-        ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
+        ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples, pts);
         if (ret < 0)
             goto end;
         outpicref = showwaves->outpicref;
@@ -708,14 +730,16 @@  static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
             showwaves->buf_idx++;
         }
         if (showwaves->buf_idx == showwaves->w ||
-            (ff_outlink_get_status(inlink) && i == nb_samples - 1))
+            av_audio_fifo_size(showwaves->fifo) <= 0) {
+            av_audio_fifo_drain(showwaves->fifo, showwaves->hop_size);
+            showwaves->samples_out += showwaves->hop_size;
             if ((ret = push_frame(outlink)) < 0)
                 break;
+        }
         outpicref = showwaves->outpicref;
     }
 
 end:
-    av_frame_free(&insamples);
     return ret;
 }
 
@@ -730,11 +754,26 @@  static int activate(AVFilterContext *ctx)
 
     FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
 
-    ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in);
-    if (ret < 0)
-        return ret;
-    if (ret > 0)
-        return showwaves_filter_frame(inlink, in);
+    if (av_audio_fifo_size(showwaves->fifo) < nb_samples) {
+        ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in);
+        if (ret < 0)
+            return ret;
+        if (ret > 0) {
+            showwaves->pts = in->pts;
+            ret = av_audio_fifo_write(showwaves->fifo, (void **)in->extended_data,
+                                      in->nb_samples);
+            showwaves->samples_in += in->nb_samples;
+            av_frame_free(&in);
+            if (ret < 0)
+                return ret;
+        }
+    }
+    if (av_audio_fifo_size(showwaves->fifo) >= nb_samples) {
+        ret = av_audio_fifo_peek(showwaves->fifo, (void **)showwaves->tmp->extended_data, nb_samples);
+        if (ret < 0)
+            return ret;
+        return showwaves_filter_frame(inlink, showwaves->tmp, showwaves->pts);
+    }
 
     FF_FILTER_FORWARD_STATUS(inlink, outlink);
     FF_FILTER_FORWARD_WANTED(outlink, inlink);
@@ -822,7 +861,7 @@  static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
     if (showwaves->single_pic) {
         struct frame_node *f;
 
-        ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
+        ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples, 0);
         if (ret < 0)
             goto end;