mirror of
https://projects.vdr-developer.org/git/vdr-plugin-softhddevice.git
synced 2023-10-10 19:16:51 +02:00
Improved audio drift correction support.
This commit is contained in:
parent
144f22314f
commit
43b48224b5
@ -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
48
audio.c
@ -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
112
codec.c
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user