mirror of
https://projects.vdr-developer.org/git/vdr-plugin-softhddevice.git
synced 2023-10-10 19:16:51 +02:00
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:
parent
e619f5c836
commit
c0d0a4ae7c
@ -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
|
||||
|
6
Makefile
6
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) \
|
||||
|
26
Todo
26
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
|
||||
|
590
audio.c
590
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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libintl.h>
|
||||
#define _(str) gettext(str) ///< gettext shortcut
|
||||
@ -73,10 +71,10 @@
|
||||
# error "No valid SNDCTL_DSP_HALT_OUTPUT found."
|
||||
# endif
|
||||
#endif
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
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,9 +394,12 @@ static void AlsaFlushBuffers(void)
|
||||
int err;
|
||||
snd_pcm_state_t state;
|
||||
|
||||
RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer));
|
||||
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));
|
||||
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));
|
||||
@ -388,9 +409,32 @@ static void AlsaFlushBuffers(void)
|
||||
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));
|
||||
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));
|
||||
return;
|
||||
}
|
||||
}
|
||||
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,12 +1989,13 @@ uint64_t AudioGetDelay(void)
|
||||
*/
|
||||
int64_t AudioGetClock(void)
|
||||
{
|
||||
if ((uint64_t) AudioPTS != INT64_C(0x8000000000000000)) {
|
||||
int64_t delay;
|
||||
|
||||
delay = AudioGetDelay();
|
||||
if (delay && (uint64_t) AudioPTS != INT64_C(0x8000000000000000)) {
|
||||
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
|
||||
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");
|
||||
}
|
||||
|
||||
|
8
audio.h
8
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user