ALSA demuxer: use av_gettime and a timefilter.

The PTS for captured audio was measured using snd_pcm_htimestamp.

snd_pcm_htimestamp hangs when the input is a dsnoop plugin.

Furthermore, at some point, snd_pcm_htimestamp started returning monotonic
timestamps rather than wall clock timestamps, in most but not all
situations.
Monotonic timestamps are fine, but ffmpeg uses wall clock timestamps
everywhere else, and we have no API to inform the user which kind of
timestamps it is.

A separate snd_pcm_htimestamp is only slightly less accurate than
snd_pcm_htimestamp: the standard deviation for the difference between two
consecutive timestamps is (on my hardware):
- ~13 µs with snd_pcm_htimestamp;
- ~35 µs with av_gettime;
-  ~5 µs with av_gettime and a timefilter.
pull/2/head
Nicolas George 14 years ago
parent 3074f03a07
commit 5d35b279e2
  1. 1
      libavdevice/alsa-audio-common.c
  2. 44
      libavdevice/alsa-audio-dec.c
  3. 2
      libavdevice/alsa-audio.h
  4. 1
      libavformat/Makefile

@ -316,6 +316,7 @@ av_cold int ff_alsa_close(AVFormatContext *s1)
AlsaData *s = s1->priv_data; AlsaData *s = s1->priv_data;
av_freep(&s->reorder_buf); av_freep(&s->reorder_buf);
ff_timefilter_destroy(s->timefilter);
snd_pcm_close(s->h); snd_pcm_close(s->h);
return 0; return 0;
} }

@ -59,6 +59,7 @@ static av_cold int audio_read_header(AVFormatContext *s1,
int ret; int ret;
enum CodecID codec_id; enum CodecID codec_id;
snd_pcm_sw_params_t *sw_params; snd_pcm_sw_params_t *sw_params;
double o;
#if FF_API_FORMAT_PARAMETERS #if FF_API_FORMAT_PARAMETERS
if (ap->sample_rate > 0) if (ap->sample_rate > 0)
@ -82,35 +83,17 @@ static av_cold int audio_read_header(AVFormatContext *s1,
return AVERROR(EIO); return AVERROR(EIO);
} }
if (snd_pcm_type(s->h) != SND_PCM_TYPE_HW)
av_log(s1, AV_LOG_WARNING,
"capture with some ALSA plugins, especially dsnoop, "
"may hang.\n");
ret = snd_pcm_sw_params_malloc(&sw_params);
if (ret < 0) {
av_log(s1, AV_LOG_ERROR, "cannot allocate software parameters structure (%s)\n",
snd_strerror(ret));
goto fail;
}
snd_pcm_sw_params_current(s->h, sw_params);
snd_pcm_sw_params_set_tstamp_mode(s->h, sw_params, SND_PCM_TSTAMP_ENABLE);
ret = snd_pcm_sw_params(s->h, sw_params);
snd_pcm_sw_params_free(sw_params);
if (ret < 0) {
av_log(s1, AV_LOG_ERROR, "cannot install ALSA software parameters (%s)\n",
snd_strerror(ret));
goto fail;
}
/* take real parameters */ /* take real parameters */
st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = codec_id; st->codec->codec_id = codec_id;
st->codec->sample_rate = s->sample_rate; st->codec->sample_rate = s->sample_rate;
st->codec->channels = s->channels; st->codec->channels = s->channels;
av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ av_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; return 0;
@ -124,8 +107,8 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
AlsaData *s = s1->priv_data; AlsaData *s = s1->priv_data;
AVStream *st = s1->streams[0]; AVStream *st = s1->streams[0];
int res; int res;
snd_htimestamp_t timestamp; int64_t dts;
snd_pcm_uframes_t ts_delay; snd_pcm_sframes_t delay = 0;
if (av_new_packet(pkt, s->period_size * s->frame_size) < 0) { if (av_new_packet(pkt, s->period_size * s->frame_size) < 0) {
return AVERROR(EIO); return AVERROR(EIO);
@ -144,14 +127,13 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
return AVERROR(EIO); return AVERROR(EIO);
} }
ff_timefilter_reset(s->timefilter);
} }
snd_pcm_htimestamp(s->h, &ts_delay, &timestamp); dts = av_gettime();
ts_delay += res; snd_pcm_delay(s->h, &delay);
pkt->pts = timestamp.tv_sec * 1000000LL dts -= av_rescale(delay + res, 1000000, s->sample_rate);
+ (timestamp.tv_nsec * st->codec->sample_rate pkt->pts = ff_timefilter_update(s->timefilter, dts, res);
- ts_delay * 1000000000LL + st->codec->sample_rate * 500LL)
/ (st->codec->sample_rate * 1000LL);
pkt->size = res * s->frame_size; pkt->size = res * s->frame_size;

@ -33,6 +33,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "config.h" #include "config.h"
#include "libavutil/log.h" #include "libavutil/log.h"
#include "libavformat/timefilter.h"
#include "avdevice.h" #include "avdevice.h"
/* XXX: we make the assumption that the soundcard accepts this format */ /* XXX: we make the assumption that the soundcard accepts this format */
@ -49,6 +50,7 @@ typedef struct {
int period_size; ///< preferred size for reads and writes, in frames int period_size; ///< preferred size for reads and writes, in frames
int sample_rate; ///< sample rate set by user int sample_rate; ///< sample rate set by user
int channels; ///< number of channels set by user int channels; ///< number of channels set by user
TimeFilter *timefilter;
void (*reorder_func)(const void *, void *, int); void (*reorder_func)(const void *, void *, int);
void *reorder_buf; void *reorder_buf;
int reorder_buf_size; ///< in frames int reorder_buf_size; ///< in frames

@ -338,6 +338,7 @@ OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o
OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o
# libavdevice dependencies # libavdevice dependencies
OBJS-$(CONFIG_ALSA_INDEV) += timefilter.o
OBJS-$(CONFIG_JACK_INDEV) += timefilter.o OBJS-$(CONFIG_JACK_INDEV) += timefilter.o
TESTPROGS = timefilter TESTPROGS = timefilter

Loading…
Cancel
Save