Improved audio drift correction support.

This commit is contained in:
Johns 2012-02-29 16:35:49 +01:00
parent 144f22314f
commit 43b48224b5
3 changed files with 78 additions and 83 deletions

View File

@ -1,6 +1,7 @@
User johns User johns
Date: Date:
Improved audio drift correction support.
Experimental audio drift correction support. Experimental audio drift correction support.
Add SVDRP HOTK command support. Add SVDRP HOTK command support.
Increased audio buffer time for PES packets. Increased audio buffer time for PES packets.

48
audio.c
View File

@ -140,8 +140,6 @@ static volatile char AudioPaused; ///< audio paused
static unsigned AudioSampleRate; ///< audio sample rate in hz static unsigned AudioSampleRate; ///< audio sample rate in hz
static unsigned AudioChannels; ///< number of audio channels static unsigned AudioChannels; ///< number of audio channels
static const int AudioBytesProSample = 2; ///< number of bytes per sample 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 int64_t AudioPTS; ///< audio pts clock
static int AudioBufferTime = 336; ///< audio buffer time in ms static int AudioBufferTime = 336; ///< audio buffer time in ms
@ -294,7 +292,7 @@ static int AlsaAddToRingbuffer(const void *samples, int count)
} }
if (!AudioRunning) { 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) (RingBufferUsedBytes(AlsaRingBuffer) * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample), / (AudioSampleRate * AudioChannels * AudioBytesProSample),
VideoGetBuffers()); VideoGetBuffers());
@ -333,7 +331,8 @@ static int AlsaPlayRingbuffer(void)
if (n == -EAGAIN) { if (n == -EAGAIN) {
continue; 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); err = snd_pcm_recover(AlsaPCMHandle, n, 0);
if (err >= 0) { if (err >= 0) {
continue; continue;
@ -401,7 +400,8 @@ static int AlsaPlayRingbuffer(void)
goto again; 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); err = snd_pcm_recover(AlsaPCMHandle, err, 0);
if (err >= 0) { if (err >= 0) {
goto again; goto again;
@ -433,7 +433,7 @@ static void AlsaFlushBuffers(void)
RingBufferReadAdvance(AlsaRingBuffer, RingBufferReadAdvance(AlsaRingBuffer,
RingBufferUsedBytes(AlsaRingBuffer)); RingBufferUsedBytes(AlsaRingBuffer));
state = snd_pcm_state(AlsaPCMHandle); 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)); snd_pcm_state_name(state));
if (state != SND_PCM_STATE_OPEN) { if (state != SND_PCM_STATE_OPEN) {
if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) {
@ -446,7 +446,6 @@ static void AlsaFlushBuffers(void)
} }
} }
AudioRunning = 0; AudioRunning = 0;
AudioTicks = 0;
AudioPTS = INT64_C(0x8000000000000000); AudioPTS = INT64_C(0x8000000000000000);
} }
@ -651,14 +650,15 @@ static void AlsaThread(void)
break; break;
} }
// wait for space in kernel buffers // wait for space in kernel buffers
if ((err = snd_pcm_wait(AlsaPCMHandle, 100)) < 0) { if ((err = snd_pcm_wait(AlsaPCMHandle, 24)) < 0) {
Error(_("audio/alsa: wait underrun error?\n")); Error(_("audio/alsa: wait underrun error? '%s'\n"),
snd_strerror(err));
err = snd_pcm_recover(AlsaPCMHandle, err, 0); err = snd_pcm_recover(AlsaPCMHandle, err, 0);
if (err >= 0) { if (err >= 0) {
continue; continue;
} }
Error(_("audio/alsa: snd_pcm_wait(): %s\n"), snd_strerror(err)); Error(_("audio/alsa: snd_pcm_wait(): %s\n"), snd_strerror(err));
usleep(100 * 1000); usleep(24 * 1000);
continue; continue;
} }
if (AlsaFlushBuffer || AudioPaused) { if (AlsaFlushBuffer || AudioPaused) {
@ -888,6 +888,9 @@ static uint64_t AlsaGetDelay(void)
if (!AlsaPCMHandle || !AudioSampleRate) { if (!AlsaPCMHandle || !AudioSampleRate) {
return 0UL; return 0UL;
} }
if (!AudioRunning) { // audio not running
return 0UL;
}
// FIXME: thread safe? __assert_fail_base in snd_pcm_delay // FIXME: thread safe? __assert_fail_base in snd_pcm_delay
// delay in frames in alsa + kernel buffers // delay in frames in alsa + kernel buffers
@ -1303,7 +1306,7 @@ static int OssAddToRingbuffer(const void *samples, int count)
} }
if (!AudioRunning) { 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) (RingBufferUsedBytes(OssRingBuffer) * 1000)
/ (AudioSampleRate * AudioChannels * AudioBytesProSample), / (AudioSampleRate * AudioChannels * AudioBytesProSample),
VideoGetBuffers()); VideoGetBuffers());
@ -1387,7 +1390,6 @@ static void OssFlushBuffers(void)
} }
} }
AudioRunning = 0; AudioRunning = 0;
AudioTicks = 0;
AudioPTS = INT64_C(0x8000000000000000); AudioPTS = INT64_C(0x8000000000000000);
} }
@ -1672,8 +1674,7 @@ static uint64_t OssGetDelay(void)
if (OssPcmFildes == -1) { // setup failure if (OssPcmFildes == -1) { // setup failure
return 0UL; return 0UL;
} }
if (!AudioRunning) { // audio not running
if (!AudioRunning) {
return 0UL; return 0UL;
} }
// delay in bytes in kernel buffers // delay in bytes in kernel buffers
@ -2185,25 +2186,6 @@ int64_t AudioGetClock(void)
int64_t delay; int64_t delay;
if ((delay = AudioGetDelay())) { 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; return AudioPTS - delay;
} }
} }

112
codec.c
View File

@ -609,13 +609,12 @@ struct _audio_decoder_
ReSampleContext *ReSample; ///< audio resampling context ReSampleContext *ReSample; ///< audio resampling context
int64_t StartPTS; ///< start PTS int64_t LastDelay; ///< last delay
struct timespec StartTime; ///< start time struct timespec LastTime; ///< last time
int64_t LastPTS; ///< last PTS int64_t LastPTS; ///< last PTS
int Drift; ///< drift correction value int Drift; ///< accumulated audio drift
#define AVERAGE 10 ///< number of average values int DriftCorr; ///< audio drift correction value
int Average[AVERAGE]; ///< average for drift calculation
struct AVResampleContext *AvResample; ///< second audio resample context struct AVResampleContext *AvResample; ///< second audio resample context
#define MAX_CHANNELS 8 ///< max number of channels supported #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->Channels = 0;
audio_decoder->HwSampleRate = 0; audio_decoder->HwSampleRate = 0;
audio_decoder->HwChannels = 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) static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
{ {
struct timespec nowtime; struct timespec nowtime;
int64_t delay;
int64_t tim_diff; int64_t tim_diff;
int64_t pts_diff; int64_t pts_diff;
int64_t drift; int64_t drift;
int corr;
AudioSetClock(pts); AudioSetClock(pts);
// start drift detection delay = AudioGetDelay();
if (audio_decoder->StartPTS == (int64_t) AV_NOPTS_VALUE && AudioGetDelay()) { if (!delay) {
audio_decoder->StartPTS = AudioGetClock();
audio_decoder->LastPTS = audio_decoder->StartPTS;
clock_gettime(CLOCK_REALTIME, &audio_decoder->StartTime);
return; return;
} }
pts = AudioGetClock();
clock_gettime(CLOCK_REALTIME, &nowtime); clock_gettime(CLOCK_REALTIME, &nowtime);
pts_diff = pts - audio_decoder->StartPTS; if (!audio_decoder->LastDelay) {
audio_decoder->LastTime = nowtime;
tim_diff = (nowtime.tv_sec - audio_decoder->StartTime.tv_sec) audio_decoder->LastPTS = pts;
* 1000 * 1000 * 1000 + (nowtime.tv_nsec - audio_decoder->LastDelay = delay;
audio_decoder->StartTime.tv_nsec); audio_decoder->Drift = 0;
drift = pts_diff * 1000 * 1000 / 90 - tim_diff; Debug(3, "codec/audio: inital delay %zd ms\n", delay / 90);
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;
return; return;
} }
// collect over some time // 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; 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->LastPTS = pts;
audio_decoder->LastDelay = delay;
audio_decoder->Drift += if (1) {
(int)((10 * audio_decoder->SampleRate * drift) / tim_diff); 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,
if (audio_decoder->AvResample) { audio_decoder->DriftCorr);
av_resample_compensate(audio_decoder->AvResample, audio_decoder->Drift,
10 * audio_decoder->SampleRate);
} }
Info("codec/audio: drift(%3d) %3" PRId64 "ms %8" PRId64 " %g\n", if (abs(drift) > 5 * 90) {
audio_decoder->Drift, drift / (1000 * 1000), drift, // drift too big, pts changed?
(double)drift / tim_diff); Debug(3, "codec/audio: drift(%5d) %3" PRId64 "ms reset\n",
printf("codec/audio: drift(%3d) %3" PRId64 "ms %8" PRId64 " %d\n", audio_decoder->DriftCorr, drift);
audio_decoder->Drift, drift / (1000 * 1000), drift, audio_decoder->LastDelay = 0;
(int)((10 * audio_decoder->SampleRate * drift) / tim_diff)); 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); audio_decoder->HwSampleRate, 16, 10, 0, 0.8);
if (!audio_decoder->AvResample) { if (!audio_decoder->AvResample) {
Error(_("codec/audio: AvResample setup error\n")); 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; 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; n *= audio_decoder->HwChannels;
CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels); CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels);
AudioEnqueue(buf, n); AudioEnqueue(buf, n);