[FFmpeg-devel] [PATCH] ALSA: use a different time filter.
Nicolas George
nicolas.george at normalesup.org
Wed Dec 28 20:35:22 CET 2011
The time filter used so far was designed for JACK, which returns data in
small packets with constant size. Reads from ALSA have varying size,
especially in non-blocking mode, and can be much bigger with some plugins.
With these parameters, the filter is likely to diverge.
The new filter is based on a least-square linear fit maintained with an
exponential weighted moving average. It converges somewhat slower but can
handle larger and varying periods and can not diverge under normal
circumstances.
Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
This is one of two options I have found. The other is this: use an arbitrary
period size, maybe sample_rate / 256, and use the current time filter with
interpolated values. That would probably make less code overall, but I am
not comfortable with the arbitrary values and the possible divergence.
Also, is someone currently maintaining ALSA?
Regards,
--
Nicolas George
libavdevice/Makefile | 2 +-
libavdevice/alsa-audio-common.c | 2 -
libavdevice/alsa-audio-dec.c | 61 +++++++++++++++++++++++++++++---------
libavdevice/alsa-audio.h | 11 ++++++-
4 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index d7806ea..1797885 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -10,7 +10,7 @@ OBJS = alldevices.o avdevice.o
# input/output devices
OBJS-$(CONFIG_ALSA_INDEV) += alsa-audio-common.o \
- alsa-audio-dec.o timefilter.o
+ alsa-audio-dec.o
OBJS-$(CONFIG_ALSA_OUTDEV) += alsa-audio-common.o \
alsa-audio-enc.o
OBJS-$(CONFIG_BKTR_INDEV) += bktr.o
diff --git a/libavdevice/alsa-audio-common.c b/libavdevice/alsa-audio-common.c
index cb7ba66..0f5629b 100644
--- a/libavdevice/alsa-audio-common.c
+++ b/libavdevice/alsa-audio-common.c
@@ -320,8 +320,6 @@ av_cold int ff_alsa_close(AVFormatContext *s1)
AlsaData *s = s1->priv_data;
av_freep(&s->reorder_buf);
- if (CONFIG_ALSA_INDEV)
- ff_timefilter_destroy(s->timefilter);
snd_pcm_close(s->h);
return 0;
}
diff --git a/libavdevice/alsa-audio-dec.c b/libavdevice/alsa-audio-dec.c
index bb9d233..aa3c9d0 100644
--- a/libavdevice/alsa-audio-dec.c
+++ b/libavdevice/alsa-audio-dec.c
@@ -53,6 +53,36 @@
#include "avdevice.h"
#include "alsa-audio.h"
+static double fast_exp(double x)
+{
+ return 1 / (1 - x * (1 - 0.5 * x * (1 - (1 / 3.0) * x)));
+}
+
+static void linreg_update(struct linreg *l, double x, double y)
+{
+ double k = fast_exp(l->conv_factor * (l->xlast - x));
+ double mean_x2 = k * l->mean_x + (1 - k) * x;
+ double mean_y2 = k * l->mean_y + (1 - k) * y;
+
+ l->xlast = x;
+ if (l->mean_x == 0) { /* exactly 0 => reset */
+ l->mean_x = x;
+ l->mean_y = y;
+ return;
+ }
+ /* Note: the numbers quickly become too big: computing var_xx as
+ mean(x*x) - mean(x)*mean(x) loses precision. */
+ l->var_xx += (mean_x2 - l->mean_x) * (mean_x2 - l->mean_x);
+ l->var_xy += (mean_x2 - l->mean_x) * (mean_y2 - l->mean_y);
+ l->mean_x = mean_x2;
+ l->mean_y = mean_y2;
+ l->var_xx = k * l->var_xx + (1 - k) * (x - l->mean_x) * (x - l->mean_x);
+ l->var_xy = k * l->var_xy + (1 - k) * (x - l->mean_x) * (y - l->mean_y);
+ if (l->var_xx >= l->var_xx_min)
+ l->a = l->var_xy / l->var_xx;
+
+}
+
static av_cold int audio_read_header(AVFormatContext *s1,
AVFormatParameters *ap)
{
@@ -60,7 +90,6 @@ static av_cold int audio_read_header(AVFormatContext *s1,
AVStream *st;
int ret;
enum CodecID codec_id;
- double o;
st = avformat_new_stream(s1, NULL);
if (!st) {
@@ -82,24 +111,23 @@ static av_cold int audio_read_header(AVFormatContext *s1,
st->codec->sample_rate = s->sample_rate;
st->codec->channels = s->channels;
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
- o = 2 * M_PI * s->period_size / s->sample_rate * 1.5; // bandwidth: 1.5Hz
- s->timefilter = ff_timefilter_new(1000000.0 / s->sample_rate,
- sqrt(2 * o), o * o);
- if (!s->timefilter)
- goto fail;
- return 0;
+ /* init linear regression */
+ s->linreg.conv_factor = 0.5 * 2 * M_PI / s->sample_rate;
+ s->linreg.a = (double)AV_TIME_BASE / s->sample_rate;
+ s->linreg.var_xx = 1 / (s->linreg.conv_factor * s->linreg.conv_factor);
+ s->linreg.var_xy = s->linreg.var_xx * s->linreg.a;
+ s->linreg.var_xx_min = s->linreg.var_xx / 100;
+ s->time_origin = av_gettime();
-fail:
- snd_pcm_close(s->h);
- return AVERROR(EIO);
+ return 0;
}
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
{
AlsaData *s = s1->priv_data;
int res;
- int64_t dts;
+ int64_t endts;
snd_pcm_sframes_t delay = 0;
if (av_new_packet(pkt, s->period_size * s->frame_size) < 0) {
@@ -119,13 +147,16 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
return AVERROR(EIO);
}
- ff_timefilter_reset(s->timefilter);
+ /* reset linear regression */
+ s->linreg.mean_x = s->linreg.mean_y = 0;
}
- dts = av_gettime();
+ endts = av_gettime() - s->time_origin;
snd_pcm_delay(s->h, &delay);
- dts -= av_rescale(delay + res, 1000000, s->sample_rate);
- pkt->pts = ff_timefilter_update(s->timefilter, dts, res);
+ linreg_update(&s->linreg, s->samples + res + delay, endts);
+ pkt->pts = s->time_origin + s->linreg.mean_y +
+ s->linreg.a * (s->samples - s->linreg.mean_x);
+ s->samples += res;
pkt->size = res * s->frame_size;
diff --git a/libavdevice/alsa-audio.h b/libavdevice/alsa-audio.h
index e453a20..c635e45 100644
--- a/libavdevice/alsa-audio.h
+++ b/libavdevice/alsa-audio.h
@@ -52,10 +52,19 @@ typedef struct {
int period_size; ///< preferred size for reads and writes, in frames
int sample_rate; ///< sample rate set by user
int channels; ///< number of channels set by user
- TimeFilter *timefilter;
void (*reorder_func)(const void *, void *, int);
void *reorder_buf;
int reorder_buf_size; ///< in frames
+ int64_t samples; ///< number of samples returned so far
+ int64_t time_origin; ///< initial timestamp
+ struct linreg { ///< exponential weighted moving linear regression
+ double conv_factor;
+ double var_xx_min;
+ double xlast;
+ double mean_x, mean_y;
+ double var_xx, var_xy;
+ double a;
+ } linreg;
} AlsaData;
/**
--
1.7.2.5
More information about the ffmpeg-devel
mailing list