From 4c4d573f9986a92aea6fea9febf39665a69f51c1 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sat, 16 Nov 2019 14:04:22 -0600 Subject: Switch FFmpeg to use raw monotonic timestamps Use ALSA high resolution hardware timestamps if available --- libavdevice/alsa.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ libavdevice/alsa.h | 2 ++ libavdevice/alsa_dec.c | 26 ++++++++++++++++-- libavutil/time.c | 4 +++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/libavdevice/alsa.c b/libavdevice/alsa.c index 117b2ea..10ffe56 100644 --- a/libavdevice/alsa.c +++ b/libavdevice/alsa.c @@ -174,6 +174,8 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, snd_pcm_stream_t mode, snd_pcm_format_t format; snd_pcm_t *h; snd_pcm_hw_params_t *hw_params; + snd_pcm_sw_params_t *sw_params; + snd_pcm_status_t *alsa_pcm_status; snd_pcm_uframes_t buffer_size, period_size; uint64_t layout = ctx->streams[0]->codecpar->channel_layout; @@ -269,6 +271,72 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, snd_pcm_stream_t mode, goto fail; } + s->use_driver_timestamps = 0; + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 0)) { + av_log(ctx, AV_LOG_INFO, "capture device supports compat timestamps\n"); + s->use_driver_timestamps |= 1; + } + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 1)) { + av_log(ctx, AV_LOG_INFO, "capture device supports default timestamps\n"); + s->use_driver_timestamps |= 2; + } + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 2)) { + av_log(ctx, AV_LOG_INFO, "capture device supports link timestamps\n"); + s->use_driver_timestamps |= 4; + } + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 3)) { + av_log(ctx, AV_LOG_INFO, "capture device supports link absolute timestamps\n"); + s->use_driver_timestamps |= 8; + } + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 4)) { + av_log(ctx, AV_LOG_INFO, "capture device supports link estimated timestamps\n"); + s->use_driver_timestamps |= 16; + } + if (snd_pcm_hw_params_supports_audio_ts_type(hw_params, 5)) { + av_log(ctx, AV_LOG_INFO, "capture device supports link synchronized timestamps\n"); + s->use_driver_timestamps |= 32; + } + + res = snd_pcm_sw_params_malloc(&sw_params); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "cannot allocate software parameter structure (%s)\n", + snd_strerror(res)); + goto fail; + } + + res = snd_pcm_sw_params_current(h, sw_params); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "cannot read parameters from device (%s)\n", + snd_strerror(res)); + snd_pcm_sw_params_free(sw_params); + goto fail; + } + + res = snd_pcm_sw_params_set_tstamp_mode(h, sw_params, SND_PCM_TSTAMP_ENABLE); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "cannot enable timestamps (%s)\n", + snd_strerror(res)); + snd_pcm_sw_params_free(sw_params); + goto fail; + } + + res = snd_pcm_sw_params_set_tstamp_type(h, sw_params, SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "cannot set timestamp mode (%s)\n", + snd_strerror(res)); + snd_pcm_sw_params_free(sw_params); + goto fail; + } + + res = snd_pcm_sw_params(h, sw_params); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "cannot set parameters (%s)\n", + snd_strerror(res)); + snd_pcm_sw_params_free(sw_params); + goto fail; + } + + snd_pcm_sw_params_free(sw_params); snd_pcm_hw_params_free(hw_params); if (channels > 2 && layout) { @@ -286,7 +354,10 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, snd_pcm_stream_t mode, } } + snd_pcm_status_malloc(&alsa_pcm_status); + s->h = h; + s->st = alsa_pcm_status; return 0; fail: @@ -308,6 +379,7 @@ av_cold int ff_alsa_close(AVFormatContext *s1) if (CONFIG_ALSA_INDEV) ff_timefilter_destroy(s->timefilter); snd_pcm_close(s->h); + snd_pcm_status_free(s->st); return 0; } diff --git a/libavdevice/alsa.h b/libavdevice/alsa.h index 1ed8c82..93fa2d3 100644 --- a/libavdevice/alsa.h +++ b/libavdevice/alsa.h @@ -48,6 +48,7 @@ typedef void (*ff_reorder_func)(const void *, void *, int); typedef struct AlsaData { AVClass *class; snd_pcm_t *h; + snd_pcm_status_t *st; int frame_size; ///< bytes per sample * channels int period_size; ///< preferred size for reads and writes, in frames int sample_rate; ///< sample rate set by user @@ -58,6 +59,7 @@ typedef struct AlsaData { void *reorder_buf; int reorder_buf_size; ///< in frames int64_t timestamp; ///< current timestamp, without latency applied. + int use_driver_timestamps; ///< bitfield w/ detected driver ts support } AlsaData; /** diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c index 36494e9..902fba3 100644 --- a/libavdevice/alsa_dec.c +++ b/libavdevice/alsa_dec.c @@ -104,6 +104,7 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) int res; int64_t dts; snd_pcm_sframes_t delay = 0; + snd_pcm_audio_tstamp_config_t tstamp_config; if (av_new_packet(pkt, s->period_size * s->frame_size) < 0) { return AVERROR(EIO); @@ -125,8 +126,29 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) ff_timefilter_reset(s->timefilter); } - dts = av_gettime(); - snd_pcm_delay(s->h, &delay); + dts = 0; + if (s->use_driver_timestamps) { + tstamp_config.type_requested = 1; + tstamp_config.report_delay = 1; + snd_pcm_status_set_audio_htstamp_config(s->st, &tstamp_config); + if (snd_pcm_status(s->h, s->st) >= 0) { + struct timespec ts; + snd_pcm_status_get_driver_htstamp(s->st, &ts); + if ((ts.tv_sec == 0) && (ts.tv_nsec == 0)) { + // Try alternate access method + snd_pcm_status_get_htstamp(s->st, &ts); + } + dts = (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + delay = snd_pcm_status_get_delay(s->st); + } + } + if (dts == 0) { + // Driver timestamps not supported + // Fall back to system timestamps + dts = av_gettime(); + snd_pcm_delay(s->h, &delay); + } + dts -= av_rescale(delay + res, 1000000, s->sample_rate); pkt->pts = ff_timefilter_update(s->timefilter, dts, s->last_period); s->last_period = res; diff --git a/libavutil/time.c b/libavutil/time.c index afa6658..9cc1898 100644 --- a/libavutil/time.c +++ b/libavutil/time.c @@ -61,7 +61,11 @@ int64_t av_gettime_relative(void) #endif { struct timespec ts; +#if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); +#else clock_gettime(CLOCK_MONOTONIC, &ts); +#endif return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; } #endif -- cgit v1.1