diff options
author | Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> | 2012-10-22 16:42:15 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-10-23 16:13:48 +0200 |
commit | 4eeaaeaea1cec60a25979678182720dc91308550 (patch) | |
tree | ef6895d3ff86454cc6348e2efde2313eceb24471 /sound/core | |
parent | 0e8014d772a7639f48d234b23dc4ce97335cce7f (diff) |
ALSA: core: add hooks for audio timestamps
ALSA did not provide any direct means to infer the audio time for A/V
sync and system/audio time correlations (eg. PulseAudio).
Applications had to track the number of samples read/written and
add/subtract the number of samples queued in the ring buffer. This
accounting led to small errors, typically several samples, due to the
two-step process. Computing the audio time in the kernel is more
direct, as all the information is available in the same routines.
Also add new .audio_wallclock routine to enable fine-grain synchronization
between monotonic system time and audio hardware time.
Using the wallclock, if supported in hardware, allows for a
much better sub-microsecond precision and a common drift tracking for
all devices sharing the same wall clock (master clock).
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/pcm_compat.c | 19 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 32 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 2 |
3 files changed, 43 insertions, 10 deletions
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 91cdf9435fe..af2a3fdb882 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -190,7 +190,8 @@ struct snd_pcm_status32 { u32 avail_max; u32 overrange; s32 suspended_state; - unsigned char reserved[60]; + struct compat_timespec audio_tstamp; + unsigned char reserved[60-sizeof(struct compat_timespec)]; } __attribute__((packed)); @@ -205,17 +206,16 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, return err; if (put_user(status.state, &src->state) || - put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) || - put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) || - put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || - put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || + compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || + compat_put_timespec(&status.tstamp, &src->tstamp) || put_user(status.appl_ptr, &src->appl_ptr) || put_user(status.hw_ptr, &src->hw_ptr) || put_user(status.delay, &src->delay) || put_user(status.avail, &src->avail) || put_user(status.avail_max, &src->avail_max) || put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state)) + put_user(status.suspended_state, &src->suspended_state) || + compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp)) return -EFAULT; return err; @@ -364,6 +364,7 @@ struct snd_pcm_mmap_status32 { u32 hw_ptr; struct compat_timespec tstamp; s32 suspended_state; + struct compat_timespec audio_tstamp; } __attribute__((packed)); struct snd_pcm_mmap_control32 { @@ -426,12 +427,14 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, sstatus.hw_ptr = status->hw_ptr % boundary; sstatus.tstamp = status->tstamp; sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) || + compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || put_user(sstatus.suspended_state, &src->s.status.suspended_state) || + compat_put_timespec(&sstatus.audio_tstamp, + &src->s.status.audio_tstamp) || put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || put_user(scontrol.avail_min, &src->c.control.avail_min)) return -EFAULT; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 3dc029e106a..c4840ff75d0 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -316,6 +316,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, unsigned long jdelta; unsigned long curr_jiffies; struct timespec curr_tstamp; + struct timespec audio_tstamp; int crossed_boundary = 0; old_hw_ptr = runtime->status->hw_ptr; @@ -328,9 +329,14 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, */ pos = substream->ops->pointer(substream); curr_jiffies = jiffies; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && + (substream->ops->wall_clock)) + substream->ops->wall_clock(substream, &audio_tstamp); + } + if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; @@ -520,9 +526,31 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_BUG_ON(crossed_boundary != 1); runtime->hw_ptr_wrap += runtime->boundary; } - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { runtime->status->tstamp = curr_tstamp; + if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { + /* + * no wall clock available, provide audio timestamp + * derived from pointer position+delay + */ + u64 audio_frames, audio_nsecs; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames = runtime->hw_ptr_wrap + + runtime->status->hw_ptr + - runtime->delay; + else + audio_frames = runtime->hw_ptr_wrap + + runtime->status->hw_ptr + + runtime->delay; + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = audio_tstamp; + } + return snd_pcm_update_state(substream, runtime); } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5e12e5bacbb..7c800012fff 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -594,6 +594,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->audio_tstamp = + runtime->status->audio_tstamp; goto _tstamp_end; } } |