From 43b48224b557146dbac75b7efae5edeb9c79c2ab Mon Sep 17 00:00:00 2001 From: Johns Date: Wed, 29 Feb 2012 16:35:49 +0100 Subject: [PATCH] Improved audio drift correction support. --- ChangeLog | 1 + audio.c | 48 ++++++++--------------- codec.c | 112 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 78 insertions(+), 83 deletions(-) diff --git a/ChangeLog b/ChangeLog index e9b2d9f..0e26ade 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ User johns Date: + Improved audio drift correction support. Experimental audio drift correction support. Add SVDRP HOTK command support. Increased audio buffer time for PES packets. diff --git a/audio.c b/audio.c index c6df0f2..af79c4d 100644 --- a/audio.c +++ b/audio.c @@ -140,8 +140,6 @@ static volatile char AudioPaused; ///< audio paused static unsigned AudioSampleRate; ///< audio sample rate in hz static unsigned AudioChannels; ///< number of audio channels static const int AudioBytesProSample = 2; ///< number of bytes per sample -static uint32_t AudioTicks; ///< audio ticks -static uint32_t AudioTickPTS; ///< audio pts of tick static int64_t AudioPTS; ///< audio pts clock static int AudioBufferTime = 336; ///< audio buffer time in ms @@ -294,7 +292,7 @@ static int AlsaAddToRingbuffer(const void *samples, int count) } if (!AudioRunning) { - Debug(3, "audio/alsa: start %zd ms %d\n", + Debug(3, "audio/alsa: start %4zd ms %d v-buf\n", (RingBufferUsedBytes(AlsaRingBuffer) * 1000) / (AudioSampleRate * AudioChannels * AudioBytesProSample), VideoGetBuffers()); @@ -333,7 +331,8 @@ static int AlsaPlayRingbuffer(void) if (n == -EAGAIN) { continue; } - Error(_("audio/alsa: underrun error?\n")); + Error(_("audio/alsa: avail underrun error? '%s'\n"), + snd_strerror(n)); err = snd_pcm_recover(AlsaPCMHandle, n, 0); if (err >= 0) { continue; @@ -401,7 +400,8 @@ static int AlsaPlayRingbuffer(void) goto again; } */ - Error(_("audio/alsa: underrun error?\n")); + Error(_("audio/alsa: writei underrun error? '%s'\n"), + snd_strerror(err)); err = snd_pcm_recover(AlsaPCMHandle, err, 0); if (err >= 0) { goto again; @@ -433,7 +433,7 @@ static void AlsaFlushBuffers(void) RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer)); state = snd_pcm_state(AlsaPCMHandle); - Debug(3, "audio/alsa: state %d - %s\n", state, + Debug(3, "audio/alsa: flush state %d - %s\n", state, snd_pcm_state_name(state)); if (state != SND_PCM_STATE_OPEN) { if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { @@ -446,7 +446,6 @@ static void AlsaFlushBuffers(void) } } AudioRunning = 0; - AudioTicks = 0; AudioPTS = INT64_C(0x8000000000000000); } @@ -651,14 +650,15 @@ static void AlsaThread(void) break; } // wait for space in kernel buffers - if ((err = snd_pcm_wait(AlsaPCMHandle, 100)) < 0) { - Error(_("audio/alsa: wait underrun error?\n")); + if ((err = snd_pcm_wait(AlsaPCMHandle, 24)) < 0) { + Error(_("audio/alsa: wait underrun error? '%s'\n"), + snd_strerror(err)); err = snd_pcm_recover(AlsaPCMHandle, err, 0); if (err >= 0) { continue; } Error(_("audio/alsa: snd_pcm_wait(): %s\n"), snd_strerror(err)); - usleep(100 * 1000); + usleep(24 * 1000); continue; } if (AlsaFlushBuffer || AudioPaused) { @@ -888,6 +888,9 @@ static uint64_t AlsaGetDelay(void) if (!AlsaPCMHandle || !AudioSampleRate) { return 0UL; } + if (!AudioRunning) { // audio not running + return 0UL; + } // FIXME: thread safe? __assert_fail_base in snd_pcm_delay // delay in frames in alsa + kernel buffers @@ -1303,7 +1306,7 @@ static int OssAddToRingbuffer(const void *samples, int count) } if (!AudioRunning) { - Debug(3, "audio/oss: start %zd ms %d\n", + Debug(3, "audio/oss: start %4zd ms %d v-buf\n", (RingBufferUsedBytes(OssRingBuffer) * 1000) / (AudioSampleRate * AudioChannels * AudioBytesProSample), VideoGetBuffers()); @@ -1387,7 +1390,6 @@ static void OssFlushBuffers(void) } } AudioRunning = 0; - AudioTicks = 0; AudioPTS = INT64_C(0x8000000000000000); } @@ -1672,8 +1674,7 @@ static uint64_t OssGetDelay(void) if (OssPcmFildes == -1) { // setup failure return 0UL; } - - if (!AudioRunning) { + if (!AudioRunning) { // audio not running return 0UL; } // delay in bytes in kernel buffers @@ -2185,25 +2186,6 @@ int64_t AudioGetClock(void) int64_t delay; if ((delay = AudioGetDelay())) { -#if 0 - int64_t pts; - uint32_t ticks; - - pts = AudioPTS - delay; - ticks = GetMsTicks(); - if (AudioTicks) { - static int64_t drift; - - drift += ((pts - AudioTickPTS) - (ticks - AudioTicks) * 90); - drift /= 2; - if (abs(drift) > 90) { - printf("audio-drift: %d\n", (int)drift); - } - } - AudioTicks = ticks; - AudioTickPTS = pts; - return pts; -#endif return AudioPTS - delay; } } diff --git a/codec.c b/codec.c index 3b60c5a..b247c4f 100644 --- a/codec.c +++ b/codec.c @@ -609,13 +609,12 @@ struct _audio_decoder_ ReSampleContext *ReSample; ///< audio resampling context - int64_t StartPTS; ///< start PTS - struct timespec StartTime; ///< start time + int64_t LastDelay; ///< last delay + struct timespec LastTime; ///< last time int64_t LastPTS; ///< last PTS - int Drift; ///< drift correction value -#define AVERAGE 10 ///< number of average values - int Average[AVERAGE]; ///< average for drift calculation + int Drift; ///< accumulated audio drift + int DriftCorr; ///< audio drift correction value struct AVResampleContext *AvResample; ///< second audio resample context #define MAX_CHANNELS 8 ///< max number of channels supported @@ -728,7 +727,7 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name, audio_decoder->Channels = 0; audio_decoder->HwSampleRate = 0; audio_decoder->HwChannels = 0; - audio_decoder->StartPTS = AV_NOPTS_VALUE; + audio_decoder->LastDelay = 0; } /** @@ -851,56 +850,75 @@ static void CodecReorderAudioFrame(int16_t * buf, int size, int channels) static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts) { struct timespec nowtime; + int64_t delay; int64_t tim_diff; int64_t pts_diff; int64_t drift; + int corr; AudioSetClock(pts); - // start drift detection - if (audio_decoder->StartPTS == (int64_t) AV_NOPTS_VALUE && AudioGetDelay()) { - audio_decoder->StartPTS = AudioGetClock(); - audio_decoder->LastPTS = audio_decoder->StartPTS; - clock_gettime(CLOCK_REALTIME, &audio_decoder->StartTime); + delay = AudioGetDelay(); + if (!delay) { return; } - - pts = AudioGetClock(); clock_gettime(CLOCK_REALTIME, &nowtime); - pts_diff = pts - audio_decoder->StartPTS; - - tim_diff = (nowtime.tv_sec - audio_decoder->StartTime.tv_sec) - * 1000 * 1000 * 1000 + (nowtime.tv_nsec - - audio_decoder->StartTime.tv_nsec); - drift = pts_diff * 1000 * 1000 / 90 - tim_diff; - - if (abs(drift) > 100 * 1000 * 1000) { - // drift too big, pts changed? - audio_decoder->StartPTS = pts; - audio_decoder->LastPTS = audio_decoder->StartPTS; - audio_decoder->StartTime = nowtime; + if (!audio_decoder->LastDelay) { + audio_decoder->LastTime = nowtime; + audio_decoder->LastPTS = pts; + audio_decoder->LastDelay = delay; + audio_decoder->Drift = 0; + Debug(3, "codec/audio: inital delay %zd ms\n", delay / 90); return; } // collect over some time - if (pts - audio_decoder->LastPTS < 10 * 1000 * 90) { + pts_diff = pts - audio_decoder->LastPTS; + if (pts_diff < 10 * 1000 * 90) { return; } + + tim_diff = (nowtime.tv_sec - audio_decoder->LastTime.tv_sec) + * 1000 * 1000 * 1000 + (nowtime.tv_nsec - + audio_decoder->LastTime.tv_nsec); + + drift = + (tim_diff * 90) / (1000 * 1000) - pts_diff + delay - + audio_decoder->LastDelay; + + audio_decoder->LastTime = nowtime; audio_decoder->LastPTS = pts; + audio_decoder->LastDelay = delay; - audio_decoder->Drift += - (int)((10 * audio_decoder->SampleRate * drift) / tim_diff); - - if (audio_decoder->AvResample) { - av_resample_compensate(audio_decoder->AvResample, audio_decoder->Drift, - 10 * audio_decoder->SampleRate); + if (1) { + Debug(3, "codec/audio: interval P:%5zdms T:%5zdms D:%4zdms %f %d\n", + pts_diff / 90, tim_diff / (1000 * 1000), delay / 90, drift / 90.0, + audio_decoder->DriftCorr); } - Info("codec/audio: drift(%3d) %3" PRId64 "ms %8" PRId64 " %g\n", - audio_decoder->Drift, drift / (1000 * 1000), drift, - (double)drift / tim_diff); - printf("codec/audio: drift(%3d) %3" PRId64 "ms %8" PRId64 " %d\n", - audio_decoder->Drift, drift / (1000 * 1000), drift, - (int)((10 * audio_decoder->SampleRate * drift) / tim_diff)); + if (abs(drift) > 5 * 90) { + // drift too big, pts changed? + Debug(3, "codec/audio: drift(%5d) %3" PRId64 "ms reset\n", + audio_decoder->DriftCorr, drift); + audio_decoder->LastDelay = 0; + return; + } + + drift += audio_decoder->Drift; + audio_decoder->Drift = drift; + corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000); + audio_decoder->DriftCorr -= corr; + + if (audio_decoder->DriftCorr < -20000) { // limit correction + audio_decoder->DriftCorr = -20000; + } else if (audio_decoder->DriftCorr > 20000) { + audio_decoder->DriftCorr = 20000; + } + if (audio_decoder->AvResample && audio_decoder->DriftCorr) { + av_resample_compensate(audio_decoder->AvResample, + audio_decoder->DriftCorr / 10, 10 * audio_decoder->HwSampleRate); + } + printf("codec/audio: drift(%5d) %8" PRId64 "us %4d\n", + audio_decoder->DriftCorr, drift * 1000 / 90, corr); } /** @@ -977,6 +995,12 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) audio_decoder->HwSampleRate, 16, 10, 0, 0.8); if (!audio_decoder->AvResample) { Error(_("codec/audio: AvResample setup error\n")); + } else { + // reset drift to some default value + audio_decoder->DriftCorr /= 2; + av_resample_compensate(audio_decoder->AvResample, + audio_decoder->DriftCorr / 10, + 10 * audio_decoder->HwSampleRate); } } } @@ -1053,18 +1077,6 @@ void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count) } n *= 2; -#if 0 - // FIXME: must split channels, filter, join channels - n = av_resample(audio_decoder->AvResample, buf, data, &consumed, count, - sizeof(buf), 1); - if (n < 0) { - Error(_("codec/audio: can't av_resample\n")); - return; - } - if (consumed != count) { - Error(_("codec/audio: av_resample didn't consume all samples\n")); - } -#endif n *= audio_decoder->HwChannels; CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels); AudioEnqueue(buf, n);