Audio module cleanup (more to come).

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.
This commit is contained in:
Johns 2012-01-16 15:42:17 +01:00
parent e619f5c836
commit c0d0a4ae7c
5 changed files with 459 additions and 214 deletions

View File

@ -1,4 +1,13 @@
User johns 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 Date: Sun Jan 15 16:56:04 CET 2012
Release Version 0.3.1 Release Version 0.3.1

View File

@ -14,6 +14,7 @@ PLUGIN = softhddevice
### The version number of this plugin (taken from the main source file): ### 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') 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) ### 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 vdpau && echo "-DUSE_VDPAU")
CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI") CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI")
CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA") CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA")
#CONFIG += -DUSE_OSS CONFIG += -DUSE_OSS
### The C++ compiler and options: ### The C++ compiler and options:
@ -59,7 +60,8 @@ PACKAGE = vdr-$(ARCHIVE)
INCLUDES += -I$(VDRDIR)/include 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) \ _CFLAGS = $(DEFINES) $(INCLUDES) \
$(shell pkg-config --cflags libavcodec libavformat) \ $(shell pkg-config --cflags libavcodec libavformat) \

26
Todo
View File

@ -27,6 +27,10 @@ missing:
Option deinterlace off / deinterlace force! Option deinterlace off / deinterlace force!
Make output drivers better moduluar. Make output drivers better moduluar.
video:
subtitle not cleared
subtitle could be asyncron
vdpau: vdpau:
1080i with temporal spatial and level 1 scaling too slow with my GT 520 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 1080i with temporal spatial too slow with my GT 520 on some channels
@ -56,21 +60,18 @@ libva-xvba-driver:
x11: x11:
disable screensaver disable screensaver
audio/alsa: audio:
done? video/audio asyncron write TS -> PES parser, which feeds audio before the next start packet
random crashes in av_parser_parse2, when switching channels CodecAudioOpen can fail "can't open audio codec" and does Fatal exit.
sometimes alsa hangs Combine alsa+oss ringbuffer code.
fixed? snd_pcm_state: Assertion `pcm' failed. while switching channels Make alsa thread/polled and oss thread/polled output module runtime
(thread problem) selectable.
audio/alsa:
better downmix of >2 channels on 2 channel hardware better downmix of >2 channels on 2 channel hardware
remix support of unsupported sample rates remix support of unsupported sample rates
libav supports only resample of mono to 2 channels libav supports only resample of mono to 2 channels
ffmpeg didn't support resample of 5 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: audio/oss:
alsa oss emulation mixer "pcm" not working alsa oss emulation mixer "pcm" not working
@ -80,6 +81,7 @@ HDMI/SPDIF Passthrough:
only AC-3 written only AC-3 written
Channels are wrong setup, if changing setting during operation. Channels are wrong setup, if changing setting during operation.
split pcm and ac-3 out into two devices split pcm and ac-3 out into two devices
support oss pass-through
playback of recording playback of recording
pause is not reset, when replay exit pause is not reset, when replay exit
@ -94,6 +96,10 @@ setup:
Can a notice be added to the setup menu? Can a notice be added to the setup menu?
576i, 720p, fake 1080i, 1080i 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) future features (not planed for 1.0 - 1.5)
video out with xv video out with xv

624
audio.c
View File

@ -35,21 +35,19 @@
/// ///
/// ///
/// @todo FIXME: there can be problems with little/big endian. /// @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_ALSA ///< enable alsa support
//#define USE_OSS ///< enable oss support //#define USE_OSS ///< enable OSS support
#define noSEARCH_HDMI_BUG #define USE_AUDIO_THREAD ///< use thread for audio playback
#define noSEARCH_HDMI_BUG2 #define noUSE_AUDIORING ///< new audio ring code (incomplete)
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <string.h>
#include <libintl.h> #include <libintl.h>
#define _(str) gettext(str) ///< gettext shortcut #define _(str) gettext(str) ///< gettext shortcut
@ -73,10 +71,10 @@
# error "No valid SNDCTL_DSP_HALT_OUTPUT found." # error "No valid SNDCTL_DSP_HALT_OUTPUT found."
# endif # endif
#endif #endif
#include <poll.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#endif #endif
#ifdef USE_AUDIO_THREAD #ifdef USE_AUDIO_THREAD
@ -96,13 +94,38 @@
#include "misc.h" #include "misc.h"
#include "audio.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 // Variables
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
static const char *AudioPCMDevice; ///< alsa/oss PCM device name static const char *AudioModuleName; ///< which audio module to use
static const char *AudioMixerDevice; ///< alsa/oss mixer device name static const AudioModule *UsedAudioModule; ///< Selected audio module.
static const char *AudioMixerChannel; ///< alsa/oss mixer channel name 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 volatile char AudioRunning; ///< thread running / stopped
static int AudioPaused; ///< audio paused static int AudioPaused; ///< audio paused
static unsigned AudioSampleRate; ///< audio sample rate in hz 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 static int64_t AudioPTS; ///< audio pts clock
#ifdef USE_AUDIO_THREAD #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 static pthread_cond_t AudioStartCond; ///< condition variable
#else
static const int AudioThread; ///< dummy audio thread
#endif #endif
#ifdef SEARCH_HDMI_BUG2 #ifdef USE_AUDIORING
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// ring buffer // 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 #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 RingBuffer *AlsaRingBuffer; ///< audio ring buffer
static unsigned AlsaStartThreshold; ///< start play, if filled static unsigned AlsaStartThreshold; ///< start play, if filled
#ifdef USE_AUDIO_THREAD
static volatile char AlsaFlushBuffer; ///< flag empty buffer static volatile char AlsaFlushBuffer; ///< flag empty buffer
#endif
static snd_mixer_t *AlsaMixer; ///< alsa mixer handle static snd_mixer_t *AlsaMixer; ///< alsa mixer handle
static snd_mixer_elem_t *AlsaMixerElem; ///< alsa pcm mixer element 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 (avail < 256) { // too much overhead
if (first) { if (first) {
// happens with broken alsa drivers // happens with broken alsa drivers
Error(_("audio/alsa: broken driver %d\n"), avail); if (AudioThread) {
usleep(5 * 1000); Error(_("audio/alsa: broken driver %d\n"), avail);
usleep(5 * 1000);
}
} }
Debug(4, "audio/alsa: break state %s\n", Debug(4, "audio/alsa: break state %s\n",
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle))); snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
break; 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); n = RingBufferGetReadPointer(AlsaRingBuffer, &p);
if (!n) { // ring buffer empty if (!n) { // ring buffer empty
if (first) { // only error on first loop if (first) { // only error on first loop
@ -322,7 +341,6 @@ static int AlsaPlayRingbuffer(void)
} }
return 0; return 0;
} }
#endif
if (n < avail) { // not enough bytes in ring buffer if (n < avail) { // not enough bytes in ring buffer
avail = n; avail = n;
} }
@ -376,21 +394,47 @@ static void AlsaFlushBuffers(void)
int err; int err;
snd_pcm_state_t state; snd_pcm_state_t state;
RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer)); if (AlsaRingBuffer && AlsaPCMHandle) {
state = snd_pcm_state(AlsaPCMHandle); RingBufferReadAdvance(AlsaRingBuffer,
Debug(3, "audio/alsa: state %d - %s\n", state, snd_pcm_state_name(state)); RingBufferUsedBytes(AlsaRingBuffer));
if (state != SND_PCM_STATE_OPEN) { state = snd_pcm_state(AlsaPCMHandle);
if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) { Debug(3, "audio/alsa: state %d - %s\n", state,
Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err)); snd_pcm_state_name(state));
} if (state != SND_PCM_STATE_OPEN) {
// ****ing alsa crash, when in open state here if ((err = snd_pcm_drop(AlsaPCMHandle)) < 0) {
if ((err = snd_pcm_prepare(AlsaPCMHandle)) < 0) { Error(_("audio: snd_pcm_drop(): %s\n"), snd_strerror(err));
Error(_("audio: snd_pcm_prepare(): %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); 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 #if 0
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -520,8 +564,6 @@ static void AlsaEnqueue(const void *samples, int count)
#endif #endif
#if 0
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// direct playback // direct playback
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -536,39 +578,11 @@ static void AlsaEnqueue(const void *samples, int count)
*/ */
static void AlsaEnqueue(const void *samples, int count) static void AlsaEnqueue(const void *samples, int count)
{ {
snd_pcm_state_t state; if (AlsaAddToRingbuffer(samples, count)) {
int avail; AudioRunning = 1;
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);
} }
// 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 #ifdef USE_AUDIO_THREAD
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -622,7 +636,7 @@ static void AlsaThread(void)
break; break;
} }
pthread_yield(); 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 samples sample buffer
** @param count number of bytes in 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) { if (!AlsaRingBuffer || !AlsaPCMHandle || !AudioSampleRate) {
printf("%p %p %d\n", AlsaRingBuffer, AlsaPCMHandle, AudioSampleRate);
Debug(3, "audio/alsa: enqueue not ready\n"); Debug(3, "audio/alsa: enqueue not ready\n");
return; 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 #endif
//----------------------------------------------------------------------------
/** /**
** Open alsa pcm device. ** 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 #endif // USE_ALSA
#ifdef USE_OSS #ifdef USE_OSS
@ -1116,6 +1169,10 @@ static int OssMixerChannel; ///< mixer channel index
static RingBuffer *OssRingBuffer; ///< audio ring buffer static RingBuffer *OssRingBuffer; ///< audio ring buffer
static unsigned OssStartThreshold; ///< start play, if filled static unsigned OssStartThreshold; ///< start play, if filled
#ifdef USE_AUDIO_THREAD
static volatile char OssFlushBuffer; ///< flag empty buffer
#endif
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// OSS pcm // OSS pcm
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -1204,17 +1261,20 @@ static int OssPlayRingbuffer(void)
} }
/** /**
** Flush oss buffers. ** Flush OSS buffers.
*/ */
static void OssFlushBuffers(void) static void OssFlushBuffers(void)
{ {
RingBufferReadAdvance(OssRingBuffer, RingBufferUsedBytes(OssRingBuffer)); if (OssRingBuffer && OssPcmFildes != -1) {
// flush kernel buffers RingBufferReadAdvance(OssRingBuffer,
if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) < 0) { RingBufferUsedBytes(OssRingBuffer));
Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"), // flush kernel buffers
strerror(errno)); if (ioctl(OssPcmFildes, SNDCTL_DSP_HALT_OUTPUT, NULL) < 0) {
return; Error(_("audio/oss: ioctl(SNDCTL_DSP_HALT_OUTPUT): %s\n"),
strerror(errno));
}
} }
AudioRunning = 0;
AudioPTS = INT64_C(0x8000000000000000); AudioPTS = INT64_C(0x8000000000000000);
} }
@ -1256,15 +1316,108 @@ static void OssPoller(void)
if (OssPcmFildes == -1) { // setup failure if (OssPcmFildes == -1) { // setup failure
return; return;
} }
if (AudioRunning) { if (!AudioThread && AudioRunning) {
OssPlayRingbuffer(); 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 ** @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) ** @param volume volume (0 .. 100)
*/ */
@ -1317,7 +1470,7 @@ static const char *OssMixerChannelNames[SOUND_MIXER_NRDEVICES] =
SOUND_DEVICE_NAMES; SOUND_DEVICE_NAMES;
/** /**
** Initialize oss mixer. ** Initialize OSS mixer.
*/ */
static void OssInitMixer(void) 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. ** @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 freq sample frequency
** @param channels number of channels ** @param channels number of channels
@ -1427,14 +1580,11 @@ static int OssSetup(int *freq, int *channels)
int ret; int ret;
int tmp; int tmp;
if (OssPcmFildes == -1) { // oss not ready if (OssPcmFildes == -1) { // OSS not ready
return -1; return -1;
} }
// flush any buffered data // flush any buffered data
{ AudioFlushBuffers();
AudioRunning = 0;
OssFlushBuffers();
}
ret = 0; 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) 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) 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 #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 // thread playback
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
#ifdef USE_AUDIO_THREAD #ifdef USE_AUDIO_THREAD
static pthread_t AudioThread; ///< audio play thread
static pthread_mutex_t AudioMutex; ///< audio condition mutex
/** /**
** Audio play thread. ** Audio play thread.
*/ */
@ -1565,18 +1814,13 @@ static void *AudioPlayHandlerThread(void *dummy)
Debug(3, "audio: wait on start condition\n"); Debug(3, "audio: wait on start condition\n");
pthread_mutex_lock(&AudioMutex); pthread_mutex_lock(&AudioMutex);
AudioRunning = 0; AudioRunning = 0;
#ifndef SEARCH_HDMI_BUG
do { do {
pthread_cond_wait(&AudioStartCond, &AudioMutex); pthread_cond_wait(&AudioStartCond, &AudioMutex);
// cond_wait can return, without signal! // cond_wait can return, without signal!
} while (!AudioRunning); } while (!AudioRunning);
#else
usleep(1 * 1000);
AudioRunning = 1;
#endif
pthread_mutex_unlock(&AudioMutex); pthread_mutex_unlock(&AudioMutex);
#ifdef SEARCH_HDMI_BUG2 #ifdef USE_AUDIORING
if (atomic_read(&AudioRingFilled) > 1) { if (atomic_read(&AudioRingFilled) > 1) {
int sample_rate; int sample_rate;
int channels; int channels;
@ -1619,9 +1863,7 @@ static void *AudioPlayHandlerThread(void *dummy)
#endif #endif
Debug(3, "audio: play start\n"); Debug(3, "audio: play start\n");
#ifdef USE_ALSA UsedAudioModule->Thread();
AlsaThread();
#endif
} }
return dummy; return dummy;
@ -1636,15 +1878,9 @@ static void AudioInitThread(void)
pthread_cond_init(&AudioStartCond, NULL); pthread_cond_init(&AudioStartCond, NULL);
pthread_create(&AudioThread, NULL, AudioPlayHandlerThread, NULL); pthread_create(&AudioThread, NULL, AudioPlayHandlerThread, NULL);
pthread_setname_np(AudioThread, "softhddev audio"); 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(); 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. ** Place samples in audio output queue.
** **
@ -1679,14 +1928,7 @@ static void AudioExitThread(void)
*/ */
void AudioEnqueue(const void *samples, int count) void AudioEnqueue(const void *samples, int count)
{ {
#ifdef USE_ALSA UsedAudioModule->Enqueue(samples, count);
AlsaEnqueue(samples, count);
#endif
#ifdef USE_OSS
OssEnqueue(samples, count);
#endif
(void)samples;
(void)count;
} }
/** /**
@ -1694,24 +1936,7 @@ void AudioEnqueue(const void *samples, int count)
*/ */
void AudioFlushBuffers(void) void AudioFlushBuffers(void)
{ {
#ifdef USE_ALSA UsedAudioModule->FlushBuffers();
#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
} }
/** /**
@ -1719,14 +1944,7 @@ void AudioFlushBuffers(void)
*/ */
void AudioPoller(void) void AudioPoller(void)
{ {
#ifndef USE_AUDIO_THREAD UsedAudioModule->Poller();
#ifdef USE_ALSA
Error(_("audio/alsa: poller not implemented\n"));
#endif
#ifdef USE_OSS
OssPoller();
#endif
#endif
} }
/** /**
@ -1734,13 +1952,17 @@ void AudioPoller(void)
*/ */
int AudioFreeBytes(void) int AudioFreeBytes(void)
{ {
#ifdef USE_ALSA return UsedAudioModule->FreeBytes();
return AlsaRingBuffer ? RingBufferFreeBytes(AlsaRingBuffer) : INT32_MAX; }
#endif
#ifdef USE_OSS /**
return OssRingBuffer ? RingBufferFreeBytes(OssRingBuffer) : INT32_MAX; ** Get audio delay in time stamps.
#endif **
return INT32_MAX; // no driver, much space ** @returns audio delay in time stamps.
*/
uint64_t AudioGetDelay(void)
{
return UsedAudioModule->GetDelay();
} }
/** /**
@ -1760,22 +1982,6 @@ void AudioSetClock(int64_t pts)
AudioPTS = 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. ** Get current audio clock.
** **
@ -1783,11 +1989,12 @@ uint64_t AudioGetDelay(void)
*/ */
int64_t AudioGetClock(void) int64_t AudioGetClock(void)
{ {
int64_t delay; if ((uint64_t) AudioPTS != INT64_C(0x8000000000000000)) {
int64_t delay;
delay = AudioGetDelay(); if ((delay = AudioGetDelay())) {
if (delay && (uint64_t) AudioPTS != INT64_C(0x8000000000000000)) { return AudioPTS - delay;
return AudioPTS - delay; }
} }
return INT64_C(0x8000000000000000); return INT64_C(0x8000000000000000);
} }
@ -1830,53 +2037,81 @@ int AudioSetup(int *freq, int *channels)
// FIXME: set flag invalid setup // FIXME: set flag invalid setup
return -1; return -1;
} }
#if defined(SEARCH_HDMI_BUG) || defined(SEARCH_HDMI_BUG2) #ifdef USE_AUDIORING
// FIXME: need to store possible combination and report this // FIXME: need to store possible combination and report this
return AudioRingAdd(*freq, *channels); return AudioRingAdd(*freq, *channels);
#endif #endif
#ifdef USE_ALSA return UsedAudioModule->Setup(freq, channels);
return AlsaSetup(freq, channels);
#endif
#ifdef USE_OSS
return OssSetup(freq, channels);
#endif
return -1;
} }
/** /**
** Set pcm audio device. ** Set pcm audio device.
** **
** @param device name of pcm device (fe. "hw:0,9" or "/dev/dsp") ** @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) void AudioSetDevice(const char *device)
{ {
AudioModuleName = "alsa"; // detect alsa/OSS
if (!device[0]) {
AudioModuleName = "noop";
} else if (device[0] == '/') {
AudioModuleName = "oss";
}
AudioPCMDevice = device; AudioPCMDevice = device;
} }
/** /**
** Initialize audio output module. ** Initialize audio output module.
**
** @todo FIXME: make audio output module selectable.
*/ */
void AudioInit(void) void AudioInit(void)
{ {
int freq; int freq;
int chan; int chan;
unsigned u;
const char *name;
#ifdef SEARCH_HDMI_BUG2 name = "noop";
AudioRingInit(); #ifdef USE_OSS
name = "oss";
#endif #endif
#ifdef USE_ALSA #ifdef USE_ALSA
AlsaInit(); name = "alsa";
#endif #endif
#ifdef USE_OSS if (AudioModuleName) {
OssInit(); 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 #endif
UsedAudioModule->Init();
freq = 48000; freq = 48000;
chan = 2; chan = 2;
if (AudioSetup(&freq, &chan)) { // set default parameters if (AudioSetup(&freq, &chan)) { // set default parameters
Error(_("audio: can't do initial setup\n")); Error(_("audio: can't do initial setup\n"));
} }
#ifdef USE_AUDIO_THREAD #ifdef USE_AUDIO_THREAD
AudioInitThread(); if (UsedAudioModule->Thread) { // supports threads
AudioInitThread();
}
#endif #endif
AudioPaused = 1; AudioPaused = 1;
@ -1890,13 +2125,8 @@ void AudioExit(void)
#ifdef USE_AUDIO_THREAD #ifdef USE_AUDIO_THREAD
AudioExitThread(); AudioExitThread();
#endif #endif
#ifdef USE_ALSA UsedAudioModule->Exit();
AlsaExit(); #ifdef USE_AUDIORING
#endif
#ifdef USE_OSS
OssExit();
#endif
#ifdef SEARCH_HDMI_BUG2
AudioRingExit(); AudioRingExit();
#endif #endif
} }
@ -1942,7 +2172,7 @@ static void PrintVersion(void)
#ifdef GIT_REV #ifdef GIT_REV
"(GIT-" GIT_REV ")" "(GIT-" GIT_REV ")"
#endif #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"); "\tLicense AGPLv3: GNU Affero General Public License version 3\n");
} }

View File

@ -30,21 +30,19 @@
extern void AudioEnqueue(const void *, int); ///< buffer audio samples extern void AudioEnqueue(const void *, int); ///< buffer audio samples
extern void AudioFlushBuffers(void); ///< flush audio buffers extern void AudioFlushBuffers(void); ///< flush audio buffers
extern void AudioPoller(void); ///< poll audio events/handling extern void AudioPoller(void); ///< poll audio events/handling
extern int AudioFreeBytes(void); ///< free bytes in audio output extern int AudioFreeBytes(void); ///< free bytes in audio output
//extern int AudioUsedBytes(void); ///< used 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 void AudioSetClock(int64_t); ///< set audio clock base
extern int64_t AudioGetClock(); ///< get current audio clock 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 int AudioSetup(int *, int *); ///< setup audio output
//extern void AudioPlay(void); ///< play audio //extern void AudioPlay(void); ///< play audio
//extern void AudioPause(void); ///< pause 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 AudioInit(void); ///< setup audio module
extern void AudioExit(void); ///< cleanup and exit audio module extern void AudioExit(void); ///< cleanup and exit audio module