Mercurial > mplayer.hg
changeset 8572:46dbfa8c1999
new EsounD audio driver (-ao esd)
author | jkeil |
---|---|
date | Fri, 27 Dec 2002 16:02:57 +0000 |
parents | 3a12d4e79322 |
children | b2e4f9dab7ad |
files | ChangeLog Makefile configure libao2/ao_esd.c libao2/audio_out.c |
diffstat | 5 files changed, 510 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Fri Dec 27 01:19:41 2002 +0000 +++ b/ChangeLog Fri Dec 27 16:02:57 2002 +0000 @@ -1,6 +1,8 @@ mplayer (0.90) + * -ao esd: new EsounD audio driver + rc2: General: * DOCS/tech/encoding-tips.txt and tech/directfb.txt
--- a/Makefile Fri Dec 27 01:19:41 2002 +0000 +++ b/Makefile Fri Dec 27 16:02:57 2002 +0000 @@ -34,7 +34,7 @@ OBJS_MPLAYER = $(SRCS_MPLAYER:.c=.o) VO_LIBS = $(AA_LIB) $(X_LIB) $(SDL_LIB) $(GGI_LIB) $(MP1E_LIB) $(MLIB_LIB) $(SVGA_LIB) $(DIRECTFB_LIB) $(GIF_LIB) -AO_LIBS = $(ARTS_LIB) $(NAS_LIB) $(SGIAUDIO_LIB) +AO_LIBS = $(ARTS_LIB) $(ESD_LIB) $(NAS_LIB) $(SGIAUDIO_LIB) CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(XVID_LIB) $(DECORE_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB) COMMON_LIBS = libmpcodecs/libmpcodecs.a mp3lib/libMP3.a liba52/liba52.a libmpeg2/libmpeg2.a $(W32_LIB) $(DS_LIB) libaf/libaf.a libmpdemux/libmpdemux.a input/libinput.a $(PP_LIB) postproc/libswscale.a linux/libosdep.a $(CSS_LIB) $(CODEC_LIBS) $(FREETYPE_LIB) $(TERMCAP_LIB) $(CDPARANOIA_LIB) $(STREAMING_LIB) $(WIN32_LIB)
--- a/configure Fri Dec 27 01:19:41 2002 +0000 +++ b/configure Fri Dec 27 16:02:57 2002 +0000 @@ -211,6 +211,7 @@ Audio output: --disable-ossaudio disable OSS sound support [autodetect] --disable-arts disable aRts sound support [autodetect] + --disable-esd disable esd sound support [autodetect] --disable-alsa disable alsa sound support [autodetect] --disable-sunaudio disable Sun sound support [autodetect] --disable-win32waveout disable windows waveout sound support [autodetect] @@ -975,6 +976,7 @@ _rtc=auto _ossaudio=auto _arts=auto +_esd=auto _liblzo=auto _mad=auto _vorbis=auto @@ -1112,6 +1114,8 @@ --disable-ossaudio) _ossaudio=no ;; --enable-arts) _arts=yes ;; --disable-arts) _arts=no ;; + --enable-esd) _esd=yes ;; + --disable-esd) _esd=no ;; --enable-mad) _mad=yes ;; --disable-mad) _mad=no ;; --enable-liblzo) _liblzo=yes ;; @@ -3181,6 +3185,32 @@ echores "$_arts" +echocheck "EsounD" +if test "$_esd" = auto ; then + _esd=no + if ( esd-config --version ) >> "$TMPLOG" 2>&1 ; then + +cat > $TMPC << EOF +#include <esd.h> +int main(void) { return 0; } +EOF +cc_check `esd-config --libs` `esd-config --cflags` && ( "$TMPO" >> "$TMPLOG" 2>&1 ) && _esd=yes + + fi +fi + +if test "$_esd" = yes ; then + _def_esd='#define USE_ESD 1' + _aosrc="$_aosrc ao_esd.c" + _aomodules="esd $_aomodules" + _ld_esd=`esd-config --libs` + _inc_esd=`esd-config --cflags` +else + _noaomodules="esd $_noaomodules" +fi +echores "$_esd" + + echocheck "ALSA audio" if test "$_alsa" != no ; then _alsa=no @@ -4711,6 +4741,8 @@ NAS_LIB = $_ld_nas ARTS_LIB = $_ld_arts ARTS_INC = $_inc_arts +ESD_LIB = $_ld_esd +ESD_INC = $_inc_esd SGIAUDIO_LIB = $_ld_sgiaudio # input/demuxer/codecs @@ -5027,6 +5059,7 @@ $_def_alsa5 $_def_alsa9 $_def_arts +$_def_esd $_def_sys_asoundlib_h $_def_alsa_asoundlib_h $_def_sunaudio
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libao2/ao_esd.c Fri Dec 27 16:02:57 2002 +0000 @@ -0,0 +1,468 @@ +/* + * ao_esd - EsounD audio output driver for MPlayer + * + * Juergen Keil <jk@tools.de> + * + * This driver is distributed under the terms of the GPL + * + * TODO / known problems: + * - does not work well when the esd daemon has autostandby disabled + * (workaround: run esd with option "-as 2" - fortunatelly this is + * the default) + * - plays noise on a linux 2.4.4 kernel with a SB16PCI card, when using + * a local tcp connection to the esd daemon; there is no noise when using + * a unix domain socket connection. + * (there are EIO errors reported by the sound card driver, so this is + * most likely a linux sound card driver problem) + */ + +#include "../config.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#ifdef __svr4__ +#include <stropts.h> +#endif +#include <esd.h> + +#include "audio_out.h" +#include "audio_out_internal.h" +#include "afmt.h" +#include "../config.h" +#include "../mp_msg.h" + + +#undef ESD_DEBUG + +#if ESD_DEBUG +#define dprintf(...) printf(__VA_ARGS__) +#else +#define dprintf(...) /**/ +#endif + + +#define ESD_CLIENT_NAME "MPlayer" +#define ESD_MAX_DELAY (1.0f) /* max amount of data buffered in esd (#sec) */ + + +static ao_info_t info = +{ + "EsounD audio output", + "esd", + "Juergen Keil <jk@tools.de>", + "" +}; + +LIBAO_EXTERN(esd) + +static int esd_fd = -1; +static int esd_play_fd = -1; +static esd_server_info_t *esd_svinfo; +static int esd_latency; +static int esd_bytes_per_sample; +static unsigned long esd_samples_written; +static struct timeval esd_play_start; + + +/* + * to set/get/query special features/parameters + */ +static int control(int cmd, int arg) +{ + esd_player_info_t *esd_pi; + esd_info_t *esd_i; + time_t now; + static time_t vol_cache_time; + static ao_control_vol_t vol_cache; + + switch (cmd) { + case AOCONTROL_GET_VOLUME: + time(&now); + if (now == vol_cache_time) { + *(ao_control_vol_t *)arg = vol_cache; + return CONTROL_OK; + } + + dprintf("esd: get vol\n"); + if ((esd_i = esd_get_all_info(esd_fd)) == NULL) + return CONTROL_ERROR; + + for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next) + if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0) + break; + + if (esd_pi != NULL) { + ao_control_vol_t *vol = (ao_control_vol_t *)arg; + vol->left = esd_pi->left_vol_scale * 100 / ESD_VOLUME_BASE; + vol->right = esd_pi->right_vol_scale * 100 / ESD_VOLUME_BASE; + + vol_cache = *vol; + vol_cache_time = now; + } + esd_free_all_info(esd_i); + + return CONTROL_OK; + + case AOCONTROL_SET_VOLUME: + dprintf("esd: set vol\n"); + if ((esd_i = esd_get_all_info(esd_fd)) == NULL) + return CONTROL_ERROR; + + for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next) + if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0) + break; + + if (esd_pi != NULL) { + ao_control_vol_t *vol = (ao_control_vol_t *)arg; + esd_set_stream_pan(esd_fd, esd_pi->source_id, + vol->left * ESD_VOLUME_BASE / 100, + vol->right * ESD_VOLUME_BASE / 100); + + vol_cache = *vol; + time(&vol_cache_time); + } + esd_free_all_info(esd_i); + return CONTROL_OK; + + default: + return CONTROL_UNKNOWN; + } +} + + +/* + * open & setup audio device + * return: 1=success 0=fail + */ +static int init(int rate_hz, int channels, int format, int flags) +{ + esd_format_t esd_fmt; + int bytes_per_sample; + int fl; + + if (esd_fd < 0) { + esd_fd = esd_open_sound(NULL); + if (esd_fd < 0) { + mp_msg(MSGT_AO, MSGL_ERR, "AO: [esd] esd_open_sound failed: %s\n", + strerror(errno)); + return 0; + } + + esd_svinfo = esd_get_server_info(esd_fd); + /* + if (esd_svinfo) { + mp_msg(MSGT_AO, MSGL_INFO, "AO: [esd] server info:\n"); + esd_print_server_info(esd_svinfo); + } + */ + + esd_latency = esd_get_latency(esd_fd); + /* mp_msg(MSGT_AO, MSGL_INFO, "AO: [esd] latency: %d\n", esd_latency); */ + } + + esd_fmt = ESD_STREAM | ESD_PLAY; + +#if ESD_RESAMPLES + /* let the esd daemon convert sample rate */ +#else + /* let mplayer's audio filter convert the sample rate */ + if (esd_svinfo != NULL) + rate_hz = esd_svinfo->rate; +#endif + ao_data.samplerate = rate_hz; + + + /* EsounD can play mono or stereo */ + switch (channels) { + case 1: + esd_fmt |= ESD_MONO; + ao_data.channels = bytes_per_sample = 1; + break; + default: + esd_fmt |= ESD_STEREO; + ao_data.channels = bytes_per_sample = 2; + break; + } + + /* EsounD can play 8bit unsigned and 16bit signed native */ + switch (format) { + case AFMT_S8: + case AFMT_U8: + esd_fmt |= ESD_BITS8; + ao_data.format = AFMT_U8; + break; + default: + esd_fmt |= ESD_BITS16; + ao_data.format = AFMT_S16_NE; + bytes_per_sample *= 2; + break; + } + + esd_play_fd = esd_play_stream_fallback(esd_fmt, rate_hz, + NULL, ESD_CLIENT_NAME); + if (esd_play_fd < 0) { + mp_msg(MSGT_AO, MSGL_ERR, + "AO: [esd] failed to open esd playback stream: %s\n", + strerror(errno)); + return 0; + } + + /* enable non-blocking i/o on the socket connection to the esd server */ + if ((fl = fcntl(esd_play_fd, F_GETFL)) >= 0) + fcntl(esd_play_fd, F_SETFL, O_NDELAY|fl); + +#if ESD_DEBUG + { + int sbuf, rbuf, len; + len = sizeof(sbuf); + getsockopt(esd_play_fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &len); + len = sizeof(rbuf); + getsockopt(esd_play_fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &len); + dprintf("esd: send/receive socket buffer space %d/%d bytes\n", + sbuf, rbuf); + } +#endif + + ao_data.bps = bytes_per_sample * rate_hz; + ao_data.outburst = ao_data.bps > 100000 ? 4*ESD_BUF_SIZE : 2*ESD_BUF_SIZE; + + esd_play_start.tv_sec = 0; + esd_samples_written = 0; + esd_bytes_per_sample = bytes_per_sample; + + return 1; +} + + +/* + * close audio device + */ +static void uninit() +{ + if (esd_play_fd >= 0) { + esd_close(esd_play_fd); + esd_play_fd = -1; + } + + if (esd_svinfo) { + esd_free_server_info(esd_svinfo); + esd_svinfo = NULL; + } + + if (esd_fd >= 0) { + esd_close(esd_fd); + esd_fd = -1; + } +} + + +/* + * plays 'len' bytes of 'data' + * it should round it down to outburst*n + * return: number of bytes played + */ +static int play(void* data, int len, int flags) +{ + int offs; + int nwritten; + int nsamples; + int remainder, n; + int saved_fl; + + /* round down buffersize to a multiple of ESD_BUF_SIZE bytes */ + len = len / ESD_BUF_SIZE * ESD_BUF_SIZE; + if (len <= 0) + return 0; + +#define SINGLE_WRITE 0 +#if SINGLE_WRITE + nwritten = write(esd_play_fd, data, len); +#else + for (offs = 0; offs + ESD_BUF_SIZE <= len; offs += ESD_BUF_SIZE) { + /* + * note: we're writing to a non-blocking socket here. + * A partial write means, that the socket buffer is full. + */ + nwritten = write(esd_play_fd, (char*)data + offs, ESD_BUF_SIZE); + if (nwritten != ESD_BUF_SIZE) { + if (nwritten < 0 && errno != EAGAIN) { + dprintf("esd play: write failed: %s\n", strerror(errno)); + } + break; + } + } + nwritten = offs; +#endif + + if (nwritten > 0 && nwritten % ESD_BUF_SIZE != 0) { + + /* + * partial write of an audio block of ESD_BUF_SIZE bytes. + * + * Send the remainder of that block as well; this avoids a busy + * polling loop in the esd daemon, which waits for the rest of + * the incomplete block using reads from a non-blocking + * socket. This busy polling loop wastes CPU cycles on the + * esd server machine, and we're trying to avoid that. + * (esd 0.2.28+ has the busy polling read loop, 0.2.22 inserts + * 0 samples which is bad as well) + * + * Let's hope the blocking write does not consume too much time. + * + * (fortunatelly, this piece of code is not used when playing + * sound on the local machine - on solaris at least) + */ + remainder = ESD_BUF_SIZE - nwritten % ESD_BUF_SIZE; + dprintf("esd play: partial audio block written, remainder %d \n", + remainder); + + /* blocking write of remaining bytes for the partial audio block */ + saved_fl = fcntl(esd_play_fd, F_GETFL); + fcntl(esd_play_fd, F_SETFL, saved_fl & ~O_NDELAY); + n = write(esd_play_fd, (char *)data + nwritten, remainder); + fcntl(esd_play_fd, F_SETFL, saved_fl); + + if (n != remainder) { + mp_msg(MSGT_AO, MSGL_ERR, + "AO: [esd] send remainer of audio block failed, %d/%d\n", + n, remainder); + } else + nwritten += n; + } + + if (nwritten > 0) { + if (!esd_play_start.tv_sec) + gettimeofday(&esd_play_start, NULL); + nsamples = nwritten / esd_bytes_per_sample; + esd_samples_written += nsamples; + + dprintf("esd play: %d %lu\n", nsamples, esd_samples_written); + } else { + dprintf("esd play: blocked / %lu\n", esd_samples_written); + } + + return nwritten; +} + + +/* + * stop playing, keep buffers (for pause) + */ +static void audio_pause() +{ + /* + * not possible with esd. the esd daemom will continue playing + * buffered data (not more than ESD_MAX_DELAY seconds of samples) + */ +} + + +/* + * resume playing, after audio_pause() + */ +static void audio_resume() +{ + /* + * not possible with esd. + * + * Let's hope the pause was long enough that the esd ran out of + * buffered data; we restart our time based delay computation + * for an audio resume. + */ + esd_play_start.tv_sec = 0; + esd_samples_written = 0; +} + + +/* + * stop playing and empty buffers (for seeking/pause) + */ +static void reset() +{ +#ifdef __svr4__ + /* throw away data buffered in the esd connection */ + if (ioctl(esd_play_fd, I_FLUSH, FLUSHW)) + perror("I_FLUSH"); +#endif +} + + +/* + * return: how many bytes can be played without blocking + */ +static int get_space() +{ + struct timeval tmout; + fd_set wfds; + float current_delay; + int space; + + /* + * Don't buffer too much data in the esd daemon. + * + * If we send too much, esd will block in write()s to the sound + * device, and the consequence is a huge slow down for things like + * esd_get_all_info(). + */ + if ((current_delay = get_delay()) >= ESD_MAX_DELAY) { + dprintf("esd get_space: too much data buffered\n"); + return 0; + } + + FD_ZERO(&wfds); + FD_SET(esd_play_fd, &wfds); + tmout.tv_sec = 0; + tmout.tv_usec = 0; + + if (select(esd_play_fd + 1, NULL, &wfds, NULL, &tmout) != 1) + return 0; + + if (!FD_ISSET(esd_play_fd, &wfds)) + return 0; + + /* try to fill 50% of the remaining "free" buffer space */ + space = (ESD_MAX_DELAY - current_delay) * ao_data.bps * 0.5f; + + /* round up to next multiple of ESD_BUF_SIZE */ + space = (space + ESD_BUF_SIZE-1) / ESD_BUF_SIZE * ESD_BUF_SIZE; + + dprintf("esd get_space: %d\n", space); + return space; +} + + +/* + * return: delay in seconds between first and last sample in buffer + */ +static float get_delay() +{ + struct timeval now; + double buffered_samples_time; + double play_time; + + if (!esd_play_start.tv_sec) + return 0; + + buffered_samples_time = (float)esd_samples_written / ao_data.samplerate; + gettimeofday(&now, NULL); + play_time = now.tv_sec - esd_play_start.tv_sec; + play_time += (now.tv_usec - esd_play_start.tv_usec) / 1000000.; + + /* dprintf("esd delay: %f %f\n", play_time, buffered_samples_time); */ + + if (play_time > buffered_samples_time) { + dprintf("esd: underflow\n"); + esd_play_start.tv_sec = 0; + esd_samples_written = 0; + return 0; + } + + dprintf("esd: get_delay %f\n", buffered_samples_time - play_time); + return buffered_samples_time - play_time; +}
--- a/libao2/audio_out.c Fri Dec 27 01:19:41 2002 +0000 +++ b/libao2/audio_out.c Fri Dec 27 16:02:57 2002 +0000 @@ -19,6 +19,9 @@ #ifdef USE_ARTS extern ao_functions_t audio_out_arts; #endif +#ifdef USE_ESD +extern ao_functions_t audio_out_esd; +#endif extern ao_functions_t audio_out_null; #ifdef HAVE_ALSA5 extern ao_functions_t audio_out_alsa5; @@ -79,6 +82,9 @@ #ifdef USE_ARTS &audio_out_arts, #endif +#ifdef USE_ESD + &audio_out_esd, +#endif #ifdef HAVE_NAS &audio_out_nas, #endif