diff --git a/ChangeLog b/ChangeLog index 81b0738..be3d639 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,13 @@ User johns +Date: + + Audio module cleanup: + Alsa + OSS can be included/build at the same time. + Alsa or OSS can be runtime selected with -a. + Add audio thread support to OSS module. + Add polled audio support to alsa module. + Removed some debug source code. + Date: Sun Jan 15 16:56:04 CET 2012 Release Version 0.3.1 diff --git a/Makefile b/Makefile index fdc69aa..568b40f 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ PLUGIN = softhddevice ### The version number of this plugin (taken from the main source file): VERSION = $(shell grep 'static const char \*const VERSION *=' $(PLUGIN).cpp | awk '{ print $$7 }' | sed -e 's/[";]//g') +GIT_REV = $(shell git describe --always 2>/dev/null) ### Configuration (edit this for your needs) @@ -22,7 +23,7 @@ CONFIG := #-DDEBUG CONFIG += $(shell pkg-config --exists vdpau && echo "-DUSE_VDPAU") CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI") CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA") -#CONFIG += -DUSE_OSS +CONFIG += -DUSE_OSS ### The C++ compiler and options: @@ -59,7 +60,8 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR)/include -DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \ + $(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"') _CFLAGS = $(DEFINES) $(INCLUDES) \ $(shell pkg-config --cflags libavcodec libavformat) \ diff --git a/Todo b/Todo index 519c0c3..4dcef94 100644 --- a/Todo +++ b/Todo @@ -27,6 +27,10 @@ missing: Option deinterlace off / deinterlace force! Make output drivers better moduluar. +video: + subtitle not cleared + subtitle could be asyncron + vdpau: 1080i with temporal spatial and level 1 scaling too slow with my GT 520 1080i with temporal spatial too slow with my GT 520 on some channels @@ -56,21 +60,18 @@ libva-xvba-driver: x11: disable screensaver -audio/alsa: - done? video/audio asyncron - random crashes in av_parser_parse2, when switching channels - sometimes alsa hangs - fixed? snd_pcm_state: Assertion `pcm' failed. while switching channels - (thread problem) +audio: + write TS -> PES parser, which feeds audio before the next start packet + CodecAudioOpen can fail "can't open audio codec" and does Fatal exit. + Combine alsa+oss ringbuffer code. + Make alsa thread/polled and oss thread/polled output module runtime + selectable. +audio/alsa: better downmix of >2 channels on 2 channel hardware remix support of unsupported sample rates libav supports only resample of mono to 2 channels ffmpeg didn't support resample of 5 to 2 channels - CodecAudioOpen can fail "can't open audio codec" and does Fatal exit. - -audio: - write TS -> PES parser, which feeds audio before the next start packet audio/oss: alsa oss emulation mixer "pcm" not working @@ -80,6 +81,7 @@ HDMI/SPDIF Passthrough: only AC-3 written Channels are wrong setup, if changing setting during operation. split pcm and ac-3 out into two devices + support oss pass-through playback of recording pause is not reset, when replay exit @@ -94,6 +96,10 @@ setup: Can a notice be added to the setup menu? 576i, 720p, fake 1080i, 1080i +unsorted: + Menu -> Setup -> Plugins -> skingenigmang -> General + -> Try 8bpp single area: no, has missing parts. + future features (not planed for 1.0 - 1.5) video out with xv diff --git a/audio.c b/audio.c index 1ef155c..f9527fc 100644 --- a/audio.c +++ b/audio.c @@ -35,21 +35,19 @@ /// /// /// @todo FIXME: there can be problems with little/big endian. -/// @todo FIXME: can combine oss and alsa ring buffer +/// @todo FIXME: can combine OSS and alsa ring buffer /// -#ifdef USE_ALSA // only with alsa supported -#define USE_AUDIO_THREAD ///< use thread for audio playback -#endif //#define USE_ALSA ///< enable alsa support -//#define USE_OSS ///< enable oss support -#define noSEARCH_HDMI_BUG -#define noSEARCH_HDMI_BUG2 +//#define USE_OSS ///< enable OSS support +#define USE_AUDIO_THREAD ///< use thread for audio playback +#define noUSE_AUDIORING ///< new audio ring code (incomplete) #include #include #include #include +#include #include #define _(str) gettext(str) ///< gettext shortcut @@ -73,10 +71,10 @@ # error "No valid SNDCTL_DSP_HALT_OUTPUT found." # endif #endif +#include #include #include #include -#include #endif #ifdef USE_AUDIO_THREAD @@ -96,13 +94,38 @@ #include "misc.h" #include "audio.h" +//---------------------------------------------------------------------------- +// Declarations +//---------------------------------------------------------------------------- + +/** +** Audio output module typedef. +*/ +typedef struct _audio_module_ +{ + const char *Name; ///< audio output module name + + void (*Thread) (void); ///< module thread handler + void (*Enqueue) (const void *, int); ///< enqueue samples for output + void (*FlushBuffers) (void); ///< flush sample buffers + void (*Poller) (void); ///< output poller + int (*FreeBytes) (void); ///< number of bytes free in buffer + uint64_t(*GetDelay) (void); ///< get current audio delay + void (*SetVolume) (int); ///< set output volume + int (*Setup) (int *, int *); ///< setup channels, samplerate + void (*Init) (void); ///< initialize audio output module + void (*Exit) (void); ///< cleanup audio output module +} AudioModule; + //---------------------------------------------------------------------------- // Variables //---------------------------------------------------------------------------- -static const char *AudioPCMDevice; ///< alsa/oss PCM device name -static const char *AudioMixerDevice; ///< alsa/oss mixer device name -static const char *AudioMixerChannel; ///< alsa/oss mixer channel name +static const char *AudioModuleName; ///< which audio module to use +static const AudioModule *UsedAudioModule; ///< Selected audio module. +static const char *AudioPCMDevice; ///< alsa/OSS PCM device name +static const char *AudioMixerDevice; ///< alsa/OSS mixer device name +static const char *AudioMixerChannel; ///< alsa/OSS mixer channel name static volatile char AudioRunning; ///< thread running / stopped static int AudioPaused; ///< audio paused static unsigned AudioSampleRate; ///< audio sample rate in hz @@ -111,16 +134,20 @@ static const int AudioBytesProSample = 2; ///< number of bytes per sample static int64_t AudioPTS; ///< audio pts clock #ifdef USE_AUDIO_THREAD +static pthread_t AudioThread; ///< audio play thread +static pthread_mutex_t AudioMutex; ///< audio condition mutex static pthread_cond_t AudioStartCond; ///< condition variable +#else +static const int AudioThread; ///< dummy audio thread #endif -#ifdef SEARCH_HDMI_BUG2 +#ifdef USE_AUDIORING //---------------------------------------------------------------------------- // ring buffer //---------------------------------------------------------------------------- -// FIXME: use this code, to combine alsa&oss ring buffers +// FIXME: use this code, to combine alsa&OSS ring buffers #define AUDIO_RING_MAX 8 ///< number of audio ring buffers @@ -219,7 +246,10 @@ static int AlsaUseMmap; ///< use mmap static RingBuffer *AlsaRingBuffer; ///< audio ring buffer static unsigned AlsaStartThreshold; ///< start play, if filled + +#ifdef USE_AUDIO_THREAD static volatile char AlsaFlushBuffer; ///< flag empty buffer +#endif static snd_mixer_t *AlsaMixer; ///< alsa mixer handle static snd_mixer_elem_t *AlsaMixerElem; ///< alsa pcm mixer element @@ -295,26 +325,15 @@ static int AlsaPlayRingbuffer(void) if (avail < 256) { // too much overhead if (first) { // happens with broken alsa drivers - Error(_("audio/alsa: broken driver %d\n"), avail); - usleep(5 * 1000); + if (AudioThread) { + Error(_("audio/alsa: broken driver %d\n"), avail); + usleep(5 * 1000); + } } Debug(4, "audio/alsa: break state %s\n", snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle))); break; } -#ifdef SEARCH_HDMI_BUG - { - uint16_t buf[8192]; - unsigned u; - - for (u = 0; u < sizeof(buf) / 2; u++) { - buf[u] = random() & 0xffff; - } - - n = sizeof(buf); - p = buf; - } -#else n = RingBufferGetReadPointer(AlsaRingBuffer, &p); if (!n) { // ring buffer empty if (first) { // only error on first loop @@ -322,7 +341,6 @@ static int AlsaPlayRingbuffer(void) } return 0; } -#endif if (n < avail) { // not enough bytes in ring buffer avail = n; } @@ -376,21 +394,47 @@ static void AlsaFlushBuffers(void) int err; snd_pcm_state_t state; - RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer)); - state = snd_pcm_state(AlsaPCMHandle); - Debug(3, "audio/alsa: state %d - %s\n", state, snd_pcm_state_name(state)); - if (state != SND_PCM_STATE_OPEN) { - if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { - Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err)); - } - // ****ing alsa crash, when in open state here - if ((err = snd_pcm_prepare(AlsaPCMHandle)) < 0) { - Error(_("audio: snd_pcm_prepare(): %s\n"), snd_strerror(err)); + if (AlsaRingBuffer && AlsaPCMHandle) { + RingBufferReadAdvance(AlsaRingBuffer, + RingBufferUsedBytes(AlsaRingBuffer)); + state = snd_pcm_state(AlsaPCMHandle); + Debug(3, "audio/alsa: state %d - %s\n", state, + snd_pcm_state_name(state)); + if (state != SND_PCM_STATE_OPEN) { + if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { + Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err)); + } + // ****ing alsa crash, when in open state here + if ((err = snd_pcm_prepare(AlsaPCMHandle)) < 0) { + Error(_("audio: snd_pcm_prepare(): %s\n"), snd_strerror(err)); + } } } + AudioRunning = 0; AudioPTS = INT64_C(0x8000000000000000); } +/** +** Call back to play audio polled. +*/ +static void AlsaPoller(void) +{ + if (!AlsaPCMHandle) { // setup failure + return; + } + if (!AudioThread && AudioRunning) { + AlsaPlayRingbuffer(); + } +} + +/** +** Get free bytes in audio output. +*/ +static int AlsaFreeBytes(void) +{ + return AlsaRingBuffer ? RingBufferFreeBytes(AlsaRingBuffer) : INT32_MAX; +} + #if 0 //---------------------------------------------------------------------------- @@ -520,8 +564,6 @@ static void AlsaEnqueue(const void *samples, int count) #endif -#if 0 - //---------------------------------------------------------------------------- // direct playback //---------------------------------------------------------------------------- @@ -536,39 +578,11 @@ static void AlsaEnqueue(const void *samples, int count) */ static void AlsaEnqueue(const void *samples, int count) { - snd_pcm_state_t state; - int avail; - int n; - int err; - int frames; - const void *p; - - Debug(3, "audio/alsa: %6zd + %4d\n", RingBufferUsedBytes(AlsaRingBuffer), - count); - n = RingBufferWrite(AlsaRingBuffer, samples, count); - if (n != count) { - Error(_("audio/alsa: can't place %d samples in ring buffer\n"), count); + if (AlsaAddToRingbuffer(samples, count)) { + AudioRunning = 1; } - // check if running, wait until enough buffered - state = snd_pcm_state(AlsaPCMHandle); - Debug(4, "audio/alsa: state %d - %s\n", state, snd_pcm_state_name(state)); - if (state == SND_PCM_STATE_PREPARED) { - // FIXME: adjust start ratio - if (RingBufferFreeBytes(AlsaRingBuffer) - > RingBufferUsedBytes(AlsaRingBuffer)) { - return; - } - Debug(3, "audio/alsa: state %d - %s start play\n", state, - snd_pcm_state_name(state)); - } - // Update audio clock - AudioPTS += - (size * 90000) / (AudioSampleRate * AudioChannels * - AudioBytesProSample); } -#endif - #ifdef USE_AUDIO_THREAD //---------------------------------------------------------------------------- @@ -622,7 +636,7 @@ static void AlsaThread(void) break; } pthread_yield(); - usleep(20 * 1000); // let fill the buffers + usleep(20 * 1000); // let fill/empty the buffers } } } @@ -633,10 +647,9 @@ static void AlsaThread(void) ** @param samples sample buffer ** @param count number of bytes in sample buffer */ -static void AlsaEnqueue(const void *samples, int count) +static void AlsaThreadEnqueue(const void *samples, int count) { if (!AlsaRingBuffer || !AlsaPCMHandle || !AudioSampleRate) { - printf("%p %p %d\n", AlsaRingBuffer, AlsaPCMHandle, AudioSampleRate); Debug(3, "audio/alsa: enqueue not ready\n"); return; } @@ -652,8 +665,26 @@ static void AlsaEnqueue(const void *samples, int count) } } +/** +** Flush alsa buffers with thread. +*/ +static void AlsaThreadFlushBuffers(void) +{ + // signal thread to flush buffers + if (AudioThread) { + AlsaFlushBuffer = 1; + do { + AudioRunning = 1; // wakeup in case of sleeping + pthread_cond_signal(&AudioStartCond); + usleep(1 * 1000); + } while (AlsaFlushBuffer); // wait until flushed + } +} + #endif +//---------------------------------------------------------------------------- + /** ** Open alsa pcm device. */ @@ -1098,6 +1129,28 @@ static void AlsaExit(void) } } +/** +** Alsa module. +*/ +static const AudioModule AlsaModule = { + .Name = "alsa", +#ifdef USE_AUDIO_THREAD + .Thread = AlsaThread, + .Enqueue = AlsaThreadEnqueue, + .FlushBuffers = AlsaThreadFlushBuffers, +#else + .Enqueue = AlsaEnqueue, + .FlushBuffers = AlsaFlushBuffers, +#endif + .Poller = AlsaPoller, + .FreeBytes = AlsaFreeBytes, + .GetDelay = AlsaGetDelay, + .SetVolume = AlsaSetVolume, + .Setup = AlsaSetup, + .Init = AlsaInit, + .Exit = AlsaExit, +}; + #endif // USE_ALSA #ifdef USE_OSS @@ -1116,6 +1169,10 @@ static int OssMixerChannel; ///< mixer channel index static RingBuffer *OssRingBuffer; ///< audio ring buffer static unsigned OssStartThreshold; ///< start play, if filled +#ifdef USE_AUDIO_THREAD +static volatile char OssFlushBuffer; ///< flag empty buffer +#endif + //---------------------------------------------------------------------------- // OSS pcm //---------------------------------------------------------------------------- @@ -1204,17 +1261,20 @@ static int OssPlayRingbuffer(void) } /** -** Flush oss buffers. +** Flush OSS buffers. */ static void OssFlushBuffers(void) { - RingBufferReadAdvance(OssRingBuffer, RingBufferUsedBytes(OssRingBuffer)); - // flush kernel buffers - if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) < 0) { - Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"), - strerror(errno)); - return; + if (OssRingBuffer && OssPcmFildes != -1) { + RingBufferReadAdvance(OssRingBuffer, + RingBufferUsedBytes(OssRingBuffer)); + // flush kernel buffers + if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) < 0) { + Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"), + strerror(errno)); + } } + AudioRunning = 0; AudioPTS = INT64_C(0x8000000000000000); } @@ -1256,15 +1316,108 @@ static void OssPoller(void) if (OssPcmFildes == -1) { // setup failure return; } - if (AudioRunning) { + if (!AudioThread && AudioRunning) { OssPlayRingbuffer(); } } +/** +** Get free bytes in audio output. +*/ +static int OssFreeBytes(void) +{ + return OssRingBuffer ? RingBufferFreeBytes(OssRingBuffer) : INT32_MAX; +} + +#ifdef USE_AUDIO_THREAD + +//---------------------------------------------------------------------------- +// thread playback //---------------------------------------------------------------------------- /** -** Initialize oss pcm device. +** OSS thread +*/ +static void OssThread(void) +{ + for (;;) { + struct pollfd fds[1]; + int err; + + pthread_testcancel(); + if (OssFlushBuffer) { + // we can flush too many, but wo cares + Debug(3, "audio/oss: flushing buffers\n"); + OssFlushBuffers(); + OssFlushBuffer = 0; + break; + } + + fds[0].fd = OssPcmFildes; + fds[0].events = POLLOUT | POLLERR; + // wait for space in kernel buffers + err = poll(fds, 1, 100); + if (err < 0) { + Error(_("audio/oss: error poll %s\n"), strerror(errno)); + usleep(100 * 1000); + continue; + } + + if (OssFlushBuffer) { + continue; + } + + if ((err = OssPlayRingbuffer())) { // empty / error + if (err < 0) { // underrun error + break; + } + pthread_yield(); + usleep(20 * 1000); // let fill/empty the buffers + } + } +} + +/** +** Place samples in audio output queue. +** +** @param samples sample buffer +** @param count number of bytes in sample buffer +*/ +static void OssThreadEnqueue(const void *samples, int count) +{ + if (!OssRingBuffer || OssPcmFildes == -1 || !AudioSampleRate) { + Debug(3, "audio/oss: enqueue not ready\n"); + return; + } + if (OssAddToRingbuffer(samples, count)) { + // no lock needed, can wakeup next time + AudioRunning = 1; + pthread_cond_signal(&AudioStartCond); + } +} + +/** +** Flush OSS buffers with thread. +*/ +static void OssThreadFlushBuffers(void) +{ + // signal thread to flush buffers + if (AudioThread) { + OssFlushBuffer = 1; + do { + AudioRunning = 1; // wakeup in case of sleeping + pthread_cond_signal(&AudioStartCond); + usleep(1 * 1000); + } while (OssFlushBuffer); // wait until flushed + } +} + +#endif + +//---------------------------------------------------------------------------- + +/** +** Initialize OSS pcm device. ** ** @see AudioPCMDevice */ @@ -1292,7 +1445,7 @@ static void OssInitPCM(void) //---------------------------------------------------------------------------- /** -** Set oss mixer volume (0-100) +** Set OSS mixer volume (0-100) ** ** @param volume volume (0 .. 100) */ @@ -1317,7 +1470,7 @@ static const char *OssMixerChannelNames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; /** -** Initialize oss mixer. +** Initialize OSS mixer. */ static void OssInitMixer(void) { @@ -1371,7 +1524,7 @@ static void OssInitMixer(void) //---------------------------------------------------------------------------- /** -** Get oss audio delay in time stamps. +** Get OSS audio delay in time stamps. ** ** @returns audio delay in time stamps. */ @@ -1411,7 +1564,7 @@ static uint64_t OssGetDelay(void) } /** -** Setup oss audio for requested format. +** Setup OSS audio for requested format. ** ** @param freq sample frequency ** @param channels number of channels @@ -1427,14 +1580,11 @@ static int OssSetup(int *freq, int *channels) int ret; int tmp; - if (OssPcmFildes == -1) { // oss not ready + if (OssPcmFildes == -1) { // OSS not ready return -1; } // flush any buffered data - { - AudioRunning = 0; - OssFlushBuffers(); - } + AudioFlushBuffers(); ret = 0; @@ -1519,7 +1669,7 @@ static int OssSetup(int *freq, int *channels) } /** -** Initialize oss audio output module. +** Initialize OSS audio output module. */ static void OssInit(void) { @@ -1530,7 +1680,7 @@ static void OssInit(void) } /** -** Cleanup oss audio output module. +** Cleanup OSS audio output module. */ static void OssExit(void) { @@ -1544,17 +1694,116 @@ static void OssExit(void) } } +/** +** OSS module. +*/ +static const AudioModule OssModule = { + .Name = "oss", +#ifdef USE_AUDIO_THREAD + .Thread = OssThread, + .Enqueue = OssThreadEnqueue, + .FlushBuffers = OssThreadFlushBuffers, +#else + .Enqueue = OssEnqueue, + .FlushBuffers = OssFlushBuffers, +#endif + .Poller = OssPoller, + .FreeBytes = OssFreeBytes, + .GetDelay = OssGetDelay, + .SetVolume = OssSetVolume, + .Setup = OssSetup, + .Init = OssInit, + .Exit = OssExit, +}; + #endif // USE_OSS +//============================================================================ +// Noop +//============================================================================ + +/** +** Noop enqueue samples. +** +** @param samples sample buffer +** @param count number of bytes in sample buffer +*/ +static void NoopEnqueue( __attribute__ ((unused)) + const void *samples, __attribute__ ((unused)) + int count) +{ +} + +/** +** Get free bytes in audio output. +*/ +static int NoopFreeBytes(void) +{ + return INT32_MAX; // no driver, much space +} + +/** +** Get audio delay in time stamps. +** +** @returns audio delay in time stamps. +*/ +static uint64_t NoopGetDelay(void) +{ + return 0UL; +} + +/** +** Set mixer volume (0-100) +** +** @param volume volume (0 .. 100) +*/ +static void NoopSetVolume( __attribute__ ((unused)) + int volume) +{ +} + +/** +** Noop setup. +** +** @param freq sample frequency +** @param channels number of channels +*/ +static int NoopSetup( __attribute__ ((unused)) + int *channels, __attribute__ ((unused)) + int *freq) +{ + return -1; +} + +/** +** Noop void +*/ +static void NoopVoid(void) +{ +} + +/** +** Noop module. +*/ +static const AudioModule NoopModule = { + .Name = "noop", + .Enqueue = NoopEnqueue, + .FlushBuffers = NoopVoid, + .Poller = NoopVoid, + .FreeBytes = NoopFreeBytes, + .GetDelay = NoopGetDelay, + .SetVolume = NoopSetVolume, + .Setup = NoopSetup, + .Init = NoopVoid, + .Exit = NoopVoid, +}; + //---------------------------------------------------------------------------- // thread playback //---------------------------------------------------------------------------- #ifdef USE_AUDIO_THREAD -static pthread_t AudioThread; ///< audio play thread -static pthread_mutex_t AudioMutex; ///< audio condition mutex - /** ** Audio play thread. */ @@ -1565,18 +1814,13 @@ static void *AudioPlayHandlerThread(void *dummy) Debug(3, "audio: wait on start condition\n"); pthread_mutex_lock(&AudioMutex); AudioRunning = 0; -#ifndef SEARCH_HDMI_BUG do { pthread_cond_wait(&AudioStartCond, &AudioMutex); // cond_wait can return, without signal! } while (!AudioRunning); -#else - usleep(1 * 1000); - AudioRunning = 1; -#endif pthread_mutex_unlock(&AudioMutex); -#ifdef SEARCH_HDMI_BUG2 +#ifdef USE_AUDIORING if (atomic_read(&AudioRingFilled) > 1) { int sample_rate; int channels; @@ -1619,9 +1863,7 @@ static void *AudioPlayHandlerThread(void *dummy) #endif Debug(3, "audio: play start\n"); -#ifdef USE_ALSA - AlsaThread(); -#endif + UsedAudioModule->Thread(); } return dummy; @@ -1636,15 +1878,9 @@ static void AudioInitThread(void) pthread_cond_init(&AudioStartCond, NULL); pthread_create(&AudioThread, NULL, AudioPlayHandlerThread, NULL); pthread_setname_np(AudioThread, "softhddev audio"); - //pthread_detach(AudioThread); -#ifdef very_old_unused_USE_ALSA - // wait until thread has opened and is ready - do { - pthread_yield(); - } while (!AlsaPCMHandle); -#endif + pthread_yield(); - usleep(5 * 1000); + usleep(5 * 1000); // give thread some time to start } /** @@ -1671,6 +1907,19 @@ static void AudioExitThread(void) //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- + /** + ** Table of all audio modules. + */ +static const AudioModule *AudioModules[] = { +#ifdef USE_ALSA + &AlsaModule, +#endif +#ifdef USE_OSS + &OssModule, +#endif + &NoopModule, +}; + /** ** Place samples in audio output queue. ** @@ -1679,14 +1928,7 @@ static void AudioExitThread(void) */ void AudioEnqueue(const void *samples, int count) { -#ifdef USE_ALSA - AlsaEnqueue(samples, count); -#endif -#ifdef USE_OSS - OssEnqueue(samples, count); -#endif - (void)samples; - (void)count; + UsedAudioModule->Enqueue(samples, count); } /** @@ -1694,24 +1936,7 @@ void AudioEnqueue(const void *samples, int count) */ void AudioFlushBuffers(void) { -#ifdef USE_ALSA -#ifdef USE_AUDIO_THREAD - // signal thread to flush buffers - if (AudioThread) { - AlsaFlushBuffer = 1; - do { - AudioRunning = 1; // wakeup in case of sleeping - pthread_cond_signal(&AudioStartCond); - usleep(1 * 1000); - } while (AlsaFlushBuffer); // wait until flushed - } -#else - AlsaFlushBuffers(); -#endif -#endif -#ifdef USE_OSS - OssFlushBuffers(); -#endif + UsedAudioModule->FlushBuffers(); } /** @@ -1719,14 +1944,7 @@ void AudioFlushBuffers(void) */ void AudioPoller(void) { -#ifndef USE_AUDIO_THREAD -#ifdef USE_ALSA - Error(_("audio/alsa: poller not implemented\n")); -#endif -#ifdef USE_OSS - OssPoller(); -#endif -#endif + UsedAudioModule->Poller(); } /** @@ -1734,13 +1952,17 @@ void AudioPoller(void) */ int AudioFreeBytes(void) { -#ifdef USE_ALSA - return AlsaRingBuffer ? RingBufferFreeBytes(AlsaRingBuffer) : INT32_MAX; -#endif -#ifdef USE_OSS - return OssRingBuffer ? RingBufferFreeBytes(OssRingBuffer) : INT32_MAX; -#endif - return INT32_MAX; // no driver, much space + return UsedAudioModule->FreeBytes(); +} + +/** +** Get audio delay in time stamps. +** +** @returns audio delay in time stamps. +*/ +uint64_t AudioGetDelay(void) +{ + return UsedAudioModule->GetDelay(); } /** @@ -1760,22 +1982,6 @@ void AudioSetClock(int64_t pts) AudioPTS = pts; } -/** -** Get audio delay in time stamps. -** -** @returns audio delay in time stamps. -*/ -uint64_t AudioGetDelay(void) -{ -#ifdef USE_ALSA - return AlsaGetDelay(); -#endif -#ifdef USE_OSS - return OssGetDelay(); -#endif - return 0UL; -} - /** ** Get current audio clock. ** @@ -1783,11 +1989,12 @@ uint64_t AudioGetDelay(void) */ int64_t AudioGetClock(void) { - int64_t delay; + if ((uint64_t) AudioPTS != INT64_C(0x8000000000000000)) { + int64_t delay; - delay = AudioGetDelay(); - if (delay && (uint64_t) AudioPTS != INT64_C(0x8000000000000000)) { - return AudioPTS - delay; + if ((delay = AudioGetDelay())) { + return AudioPTS - delay; + } } return INT64_C(0x8000000000000000); } @@ -1830,53 +2037,81 @@ int AudioSetup(int *freq, int *channels) // FIXME: set flag invalid setup return -1; } -#if defined(SEARCH_HDMI_BUG) || defined(SEARCH_HDMI_BUG2) +#ifdef USE_AUDIORING // FIXME: need to store possible combination and report this return AudioRingAdd(*freq, *channels); #endif -#ifdef USE_ALSA - return AlsaSetup(freq, channels); -#endif -#ifdef USE_OSS - return OssSetup(freq, channels); -#endif - return -1; + return UsedAudioModule->Setup(freq, channels); } /** ** Set pcm audio device. ** ** @param device name of pcm device (fe. "hw:0,9" or "/dev/dsp") +** +** @note this is currently used to select alsa/OSS output module. */ void AudioSetDevice(const char *device) { + AudioModuleName = "alsa"; // detect alsa/OSS + if (!device[0]) { + AudioModuleName = "noop"; + } else if (device[0] == '/') { + AudioModuleName = "oss"; + } AudioPCMDevice = device; } /** ** Initialize audio output module. +** +** @todo FIXME: make audio output module selectable. */ void AudioInit(void) { int freq; int chan; + unsigned u; + const char *name; -#ifdef SEARCH_HDMI_BUG2 - AudioRingInit(); + name = "noop"; +#ifdef USE_OSS + name = "oss"; #endif #ifdef USE_ALSA - AlsaInit(); + name = "alsa"; #endif -#ifdef USE_OSS - OssInit(); + if (AudioModuleName) { + name = AudioModuleName; + } + // + // search selected audio module. + // + for (u = 0; u < sizeof(AudioModules) / sizeof(*AudioModules); ++u) { + if (!strcasecmp(name, AudioModules[u]->Name)) { + UsedAudioModule = AudioModules[u]; + Info(_("audio: '%s' output module used\n"), UsedAudioModule->Name); + goto found; + } + } + Error(_("audio: '%s' output module isn't supported\n"), name); + UsedAudioModule = &NoopModule; + return; + + found: +#ifdef USE_AUDIORING + AudioRingInit(); #endif + UsedAudioModule->Init(); freq = 48000; chan = 2; if (AudioSetup(&freq, &chan)) { // set default parameters Error(_("audio: can't do initial setup\n")); } #ifdef USE_AUDIO_THREAD - AudioInitThread(); + if (UsedAudioModule->Thread) { // supports threads + AudioInitThread(); + } #endif AudioPaused = 1; @@ -1890,13 +2125,8 @@ void AudioExit(void) #ifdef USE_AUDIO_THREAD AudioExitThread(); #endif -#ifdef USE_ALSA - AlsaExit(); -#endif -#ifdef USE_OSS - OssExit(); -#endif -#ifdef SEARCH_HDMI_BUG2 + UsedAudioModule->Exit(); +#ifdef USE_AUDIORING AudioRingExit(); #endif } @@ -1942,7 +2172,7 @@ static void PrintVersion(void) #ifdef GIT_REV "(GIT-" GIT_REV ")" #endif - ",\n\t(c) 2009 - 2011 by Johns\n" + ",\n\t(c) 2009 - 2012 by Johns\n" "\tLicense AGPLv3: GNU Affero General Public License version 3\n"); } diff --git a/audio.h b/audio.h index eec0163..f031c7d 100644 --- a/audio.h +++ b/audio.h @@ -30,21 +30,19 @@ extern void AudioEnqueue(const void *, int); ///< buffer audio samples extern void AudioFlushBuffers(void); ///< flush audio buffers extern void AudioPoller(void); ///< poll audio events/handling - extern int AudioFreeBytes(void); ///< free bytes in audio output //extern int AudioUsedBytes(void); ///< used bytes in audio output +extern uint64_t AudioGetDelay(void); ///< get current audio delay extern void AudioSetClock(int64_t); ///< set audio clock base extern int64_t AudioGetClock(); ///< get current audio clock -extern uint64_t AudioGetDelay(void); ///< get current audio delay - +extern void AudioSetVolume(int); ///< set volume extern int AudioSetup(int *, int *); ///< setup audio output //extern void AudioPlay(void); ///< play audio //extern void AudioPause(void); ///< pause audio -extern void AudioSetVolume(int); ///< set volume -extern void AudioSetDevice(const char *); ///< set alsa PCM audio device +extern void AudioSetDevice(const char *); ///< set PCM audio device extern void AudioInit(void); ///< setup audio module extern void AudioExit(void); ///< cleanup and exit audio module