summaryrefslogtreecommitdiffstats
path: root/libao2
diff options
context:
space:
mode:
Diffstat (limited to 'libao2')
-rw-r--r--libao2/ao_coreaudio.c3
-rw-r--r--libao2/ao_dsound.c70
-rw-r--r--libao2/ao_ivtv.c161
-rw-r--r--libao2/ao_mpegpes.c336
-rw-r--r--libao2/ao_nas.c646
-rw-r--r--libao2/ao_openal.c30
-rw-r--r--libao2/ao_oss.c3
-rw-r--r--libao2/ao_pulse.c7
-rw-r--r--libao2/ao_sdl.c319
-rw-r--r--libao2/ao_sun.c692
-rw-r--r--libao2/ao_win32.c326
-rw-r--r--libao2/audio_out.c35
-rw-r--r--libao2/audio_out.h1
13 files changed, 81 insertions, 2548 deletions
diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c
index d1a93c85e2..ff077b81b6 100644
--- a/libao2/ao_coreaudio.c
+++ b/libao2/ao_coreaudio.c
@@ -44,6 +44,7 @@
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
+#include <endian.h>
#include "config.h"
#include "mp_msg.h"
@@ -820,7 +821,7 @@ static int OpenSPDIF(void)
/* FIXME: If output stream is not native byte-order, we need change endian somewhere. */
/* Although there's no such case reported. */
-#if HAVE_BIGENDIAN
+#if BYTE_ORDER == BIG_ENDIAN
if (!(ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian))
#else
/* tell mplayer that we need a byteswap on AC3 streams, */
diff --git a/libao2/ao_dsound.c b/libao2/ao_dsound.c
index b33f2949fd..f1fc0dd00a 100644
--- a/libao2/ao_dsound.c
+++ b/libao2/ao_dsound.c
@@ -121,8 +121,10 @@ static int min_free_space = 0; ///if the free space is below this val
///there will always be at least this amout of free space to prevent
///get_space() from returning wrong values when buffer is 100% full.
///will be replaced with nBlockAlign in init()
+static int underrun_check = 0; ///0 or last reported free space (underrun detection)
static int device_num = 0; ///wanted device number
static GUID device; ///guid of the device
+static int audio_volume;
/***************************************************************************************/
@@ -314,6 +316,8 @@ static int write_buffer(unsigned char *data, int len)
LPVOID lpvPtr2;
DWORD dwBytes2;
+ underrun_check = 0;
+
// Lock the buffer
res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
// If the buffer was lost, restore and retry lock.
@@ -391,16 +395,16 @@ static int control(int cmd, void *arg)
switch (cmd) {
case AOCONTROL_GET_VOLUME: {
ao_control_vol_t* vol = (ao_control_vol_t*)arg;
- IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
- vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0);
- //printf("ao_dsound: volume: %f\n",vol->left);
+ vol->left = vol->right = audio_volume;
return CONTROL_OK;
}
case AOCONTROL_SET_VOLUME: {
ao_control_vol_t* vol = (ao_control_vol_t*)arg;
- volume = (DWORD)(log10(vol->right) * 5000.0) - 10000;
+ volume = audio_volume = vol->right;
+ if (volume < 1)
+ volume = 1;
+ volume = (DWORD)(log10(volume) * 5000.0) - 10000;
IDirectSoundBuffer_SetVolume(hdsbuf, volume);
- //printf("ao_dsound: volume: %f\n",vol->left);
return CONTROL_OK;
}
}
@@ -421,6 +425,7 @@ static int init(int rate, int channels, int format, int flags)
if (!InitDirectSound()) return 0;
global_ao->no_persistent_volume = true;
+ audio_volume = 100;
// ok, now create the buffers
WAVEFORMATEXTENSIBLE wformat;
@@ -544,6 +549,7 @@ static void reset(void)
// reset directsound buffer
IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
write_offset=0;
+ underrun_check=0;
}
/**
@@ -568,22 +574,16 @@ static void audio_resume(void)
*/
static void uninit(int immed)
{
- if(immed)reset();
- else{
- DWORD status;
- IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
- while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
- usec_sleep(20000);
- }
+ if (!immed)
+ usec_sleep(get_delay() * 1000000);
+ reset();
+
DestroyBuffer();
UninitDirectSound();
}
-/**
-\brief find out how many bytes can be written into the audio buffer without
-\return free space in bytes, has to return 0 if the buffer is almost full
-*/
-static int get_space(void)
+// return exact number of free (safe to write) bytes
+static int check_free_buffer_size(void)
{
int space;
DWORD play_offset;
@@ -595,6 +595,28 @@ static int get_space(void)
// write_cursor is the position after which it is assumed to be save to write data
// write_offset is the postion where we actually write the data to
if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+ // Check for buffer underruns. An underrun happens if DirectSound
+ // started to play old data beyond the current write_offset. Detect this
+ // by checking whether the free space shrinks, even though no data was
+ // written (i.e. no write_buffer). Doesn't always work, but the only
+ // reason we need this is to deal with the situation when playback ends,
+ // and the buffer is only half-filled.
+ if (space < underrun_check) {
+ // there's no useful data in the buffers
+ space = buffer_size;
+ reset();
+ }
+ underrun_check = space;
+ return space;
+}
+
+/**
+\brief find out how many bytes can be written into the audio buffer without
+\return free space in bytes, has to return 0 if the buffer is almost full
+*/
+static int get_space(void)
+{
+ int space = check_free_buffer_size();
if(space < min_free_space)return 0;
return space-min_free_space;
}
@@ -608,13 +630,7 @@ static int get_space(void)
*/
static int play(void* data, int len, int flags)
{
- DWORD play_offset;
- int space;
-
- // make sure we have enough space to write data
- IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
- space=buffer_size-(write_offset-play_offset);
- if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+ int space = check_free_buffer_size();
if(space < len) len = space;
if (!(flags & AOPLAY_FINAL_CHUNK))
@@ -628,10 +644,6 @@ static int play(void* data, int len, int flags)
*/
static float get_delay(void)
{
- DWORD play_offset;
- int space;
- IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
- space=play_offset-write_offset;
- if(space <= 0)space += buffer_size;
+ int space = check_free_buffer_size();
return (float)(buffer_size - space) / (float)ao_data.bps;
}
diff --git a/libao2/ao_ivtv.c b/libao2/ao_ivtv.c
deleted file mode 100644
index e05537cbd8..0000000000
--- a/libao2/ao_ivtv.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * audio output for WinTV PVR-150/250/350 (a.k.a IVTV) cards
- * through Connexant hardware MPEG decoder
- * See http://ivtvdriver.org/index.php/Main_Page for more details on the
- * cards supported by the ivtv driver.
- *
- * WARNING: You need to force -ac hwmpa for audio output to work.
- *
- * Copyright (C) 2006 Benjamin Zores
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <inttypes.h>
-
-#include "config.h"
-
-#include "mp_msg.h"
-
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "libaf/af_format.h"
-#include "libmpdemux/mpeg_packetizer.h"
-#include "libvo/vo_ivtv.h"
-
-#define MPEG_AUDIO_ID 0x1C0
-
-static int freq = 0;
-
-static const ao_info_t info =
-{
- "IVTV MPEG Audio Decoder output",
- "ivtv",
- "Benjamin Zores",
- ""
-};
-
-LIBAO_EXTERN(ivtv)
-
-/* to set/get/query special features/parameters */
-static int
-control (int cmd,void *arg)
-{
- return CONTROL_UNKNOWN;
-}
-
-/* open & setup audio device */
-static int
-init (int rate, int channels, int format, int flags)
-{
- if (ivtv_fd < 0)
- return 0;
-
- if (format != AF_FORMAT_MPEG2)
- {
- mp_msg (MSGT_AO, MSGL_FATAL,
- "AO: [ivtv] can only handle MPEG audio streams.\n");
- return 0;
- }
-
- ao_data.outburst = 2048;
- ao_data.samplerate = rate;
- ao_data.channels = channels;
- ao_data.format = AF_FORMAT_MPEG2;
- ao_data.buffersize = 2048;
- ao_data.bps = rate * 2 * 2;
- ao_data.brokenpts = 0;
- freq = rate;
-
- /* check for supported audio rate */
- if (rate != 32000 || rate != 41000 || rate != 48000)
- {
- mp_tmsg (MSGT_AO, MSGL_ERR, "[AO MPEGPES] %d Hz not supported, try to resample.\n", rate);
- rate = 48000;
- }
-
- return 1;
-}
-
-/* close audio device */
-static void
-uninit (int immed)
-{
- /* nothing to do */
-}
-
-/* stop playing and empty buffers (for seeking/pause) */
-static void
-reset (void)
-{
- /* nothing to do */
-}
-
-/* stop playing, keep buffers (for pause) */
-static void
-audio_pause (void)
-{
- reset ();
-}
-
-/* resume playing, after audio_pause() */
-static void
-audio_resume (void)
-{
- /* nothing to do */
-}
-
-/* how many bytes can be played without blocking */
-static int
-get_space (void)
-{
- extern int vo_pts;
- float x;
- int y;
-
- x = (float) (vo_pts - ao_data.brokenpts) / 90000.0;
- if (x <= 0)
- return 0;
-
- y = freq * 4 * x;
- y /= ao_data.outburst;
- y *= ao_data.outburst;
-
- if (y > 32000)
- y = 32000;
-
- return y;
-}
-
-/* number of bytes played */
-static int
-play (void *data, int len, int flags)
-{
- if (ao_data.format != AF_FORMAT_MPEG2)
- return 0;
-
- send_mpeg_pes_packet (data, len, MPEG_AUDIO_ID, ao_data.brokenpts, 2, ivtv_write);
-
- return len;
-}
-
-/* delay in seconds between first and last sample in buffer */
-static float
-get_delay (void)
-{
- return 0.0;
-}
diff --git a/libao2/ao_mpegpes.c b/libao2/ao_mpegpes.c
deleted file mode 100644
index fe3f20fc85..0000000000
--- a/libao2/ao_mpegpes.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * MPEG-PES audio output driver
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <errno.h>
-
-#include "config.h"
-
-#include "audio_out.h"
-#include "audio_out_internal.h"
-
-#include "libaf/af_format.h"
-#include "libmpdemux/mpeg_packetizer.h"
-#include "subopt-helper.h"
-
-#include "mp_msg.h"
-
-#ifdef CONFIG_DVB
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <linux/dvb/audio.h>
-audio_mixer_t dvb_mixer={255,255};
-#endif
-
-#define true 1
-#define false 0
-
-extern int vo_mpegpes_fd;
-int ao_mpegpes_fd = -1;
-
-#include <errno.h>
-
-static const ao_info_t info =
-{
-#ifdef CONFIG_DVB
- "DVB audio output",
-#else
- "MPEG-PES audio output",
-#endif
- "mpegpes",
- "A'rpi",
- ""
-};
-
-LIBAO_EXTERN(mpegpes)
-
-
-// to set/get/query special features/parameters
-static int control(int cmd,void *arg){
-#ifdef CONFIG_DVB
- switch(cmd){
- case AOCONTROL_GET_VOLUME:
- if(ao_mpegpes_fd >= 0){
- ((ao_control_vol_t*)(arg))->left=dvb_mixer.volume_left/2.56;
- ((ao_control_vol_t*)(arg))->right=dvb_mixer.volume_right/2.56;
- return CONTROL_OK;
- }
- return CONTROL_ERROR;
- case AOCONTROL_SET_VOLUME:
- if(ao_mpegpes_fd >= 0){
- dvb_mixer.volume_left=((ao_control_vol_t*)(arg))->left*2.56;
- dvb_mixer.volume_right=((ao_control_vol_t*)(arg))->right*2.56;
- if(dvb_mixer.volume_left>255) dvb_mixer.volume_left=255;
- if(dvb_mixer.volume_right>255) dvb_mixer.volume_right=255;
- // printf("Setting DVB volume: %d ; %d \n",dvb_mixer.volume_left,dvb_mixer.volume_right);
- if ( (ioctl(vo_mpegpes_fd,AUDIO_SET_MIXER, &dvb_mixer) < 0)){
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] DVB audio set mixer failed: %s.\n",
- strerror(errno));
- return CONTROL_ERROR;
- }
- return CONTROL_OK;
- }
- return CONTROL_ERROR;
- }
-#endif
- return CONTROL_UNKNOWN;
-}
-
-static int freq=0;
-static int freq_id=0;
-
-#ifdef CONFIG_DVB
-static int init_device(int card)
-{
- char ao_file[30];
- sprintf(ao_file, "/dev/dvb/adapter%d/audio0", card);
- mp_msg(MSGT_VO,MSGL_INFO, "Opening %s\n", ao_file);
- if((ao_mpegpes_fd = open(ao_file,O_RDWR|O_NONBLOCK)) < 0)
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO DEVICE: %s\n", strerror(errno));
- return -1;
- }
- if( (ioctl(ao_mpegpes_fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY) < 0))
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SELECT SOURCE: %s\n", strerror(errno));
- goto fail;
- }
- if((ioctl(ao_mpegpes_fd, AUDIO_PLAY) < 0))
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO PLAY: %s\n", strerror(errno));
- goto fail;
- }
- if((ioctl(ao_mpegpes_fd, AUDIO_SET_AV_SYNC, true) < 0))
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET AV SYNC: %s\n", strerror(errno));
- goto fail;
- }
- //FIXME: in vo_mpegpes audio was initialized as MUTEd
- if((ioctl(ao_mpegpes_fd, AUDIO_SET_MUTE, false) < 0))
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET MUTE: %s\n", strerror(errno));
- goto fail;
- }
- return ao_mpegpes_fd;
-fail:
- close(ao_mpegpes_fd);
- ao_mpegpes_fd = -1;
- return -1;
-}
-#endif
-
-static int preinit(const char *arg)
-{
- int card = -1;
- char *ao_file = NULL;
-
- const opt_t subopts[] = {
- {"card", OPT_ARG_INT, &card, NULL},
- {"file", OPT_ARG_MSTRZ, &ao_file, NULL},
- {NULL}
- };
-
- if(subopt_parse(ao_subdevice, subopts) != 0)
- {
- mp_msg(MSGT_VO, MSGL_ERR, "AO_MPEGPES, Unrecognized options\n");
- return -1;
- }
- if(card==-1)
- {
- //search the first usable card
- int n;
- char file[30];
- for(n=0; n<4; n++)
- {
- sprintf(file, "/dev/dvb/adapter%d/audio0", n);
- if(access(file, F_OK | W_OK)==0)
- {
- card = n+1;
- break;
- }
- }
- }
- if((card < 1) || (card > 4))
- {
- mp_msg(MSGT_VO, MSGL_ERR, "DVB card number must be between 1 and 4\n");
- return -1;
- }
- card--;
-
-#ifdef CONFIG_DVB
- if(!ao_file)
- return init_device(card);
-#else
- if(!ao_file)
- return vo_mpegpes_fd; //video fd
-#endif
-
- ao_mpegpes_fd = open(ao_file, O_WRONLY | O_CREAT, 0666);
- if(ao_mpegpes_fd < 0)
- {
- mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes: %s\n", strerror(errno));
- return -1;
- }
- return ao_mpegpes_fd;
-}
-
-static int my_ao_write(const unsigned char* data,int len){
- int orig_len = len;
-#ifdef CONFIG_DVB
-#define NFD 1
- struct pollfd pfd[NFD];
-
- pfd[0].fd = ao_mpegpes_fd;
- pfd[0].events = POLLOUT;
-
- while(len>0){
- if(poll(pfd,NFD,1)){
- if(pfd[0].revents & POLLOUT){
- int ret = write(ao_mpegpes_fd, data, len);
- if(ret<=0){
- mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes write: %s\n", strerror(errno));
- usleep(0);
- } else {
- len-=ret;
- data+=ret;
- }
- } else usleep(1000);
- }
- }
-
-#else
- if(ao_mpegpes_fd < 0) return 0; // no file
- write(ao_mpegpes_fd, data, len); // write to file
-#endif
- return orig_len;
-}
-
-
-// open & setup audio device
-// return: 1=success 0=fail
-static int init(int rate,int channels,int format,int flags){
- if(preinit(NULL)<0) return 0;
-
- ao_data.channels=2;
- ao_data.outburst=2000;
- switch(format){
- case AF_FORMAT_S16_BE:
- case AF_FORMAT_MPEG2:
- case AF_FORMAT_AC3_BE:
- ao_data.format=format;
- break;
- case AF_FORMAT_AC3_LE:
- ao_data.format=AF_FORMAT_AC3_BE;
- break;
- default:
- ao_data.format=AF_FORMAT_S16_BE;
- }
-
- switch(rate){
- case 48000: freq_id=0;break;
- case 96000: freq_id=1;break;
- case 44100: freq_id=2;break;
- case 32000: freq_id=3;break;
- default:
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] %d Hz not supported, try to resample.\n", rate);
-#if 0
- if(rate>48000) rate=96000; else
- if(rate>44100) rate=48000; else
- if(rate>32000) rate=44100; else
- rate=32000;
- goto retry;
-#else
- rate=48000; freq_id=0;
-#endif
- }
-
- ao_data.bps=rate*2*2;
- freq=ao_data.samplerate=rate;
-
- return 1;
-}
-
-// close audio device
-static void uninit(int immed){
- if (ao_mpegpes_fd >= 0)
- close(ao_mpegpes_fd);
- ao_mpegpes_fd = -1;
-}
-
-// stop playing and empty buffers (for seeking/pause)
-static void reset(void){
-
-}
-
-// stop playing, keep buffers (for pause)
-static void audio_pause(void)
-{
- // for now, just call reset();
- reset();
-}
-
-// resume playing, after audio_pause()
-static void audio_resume(void)
-{
-}
-
-extern int vo_pts;
-
-// return: how many bytes can be played without blocking
-static int get_space(void){
- float x=(float)(vo_pts-ao_data.brokenpts)/90000.0;
- int y;
- //FIXME: is it correct?
- if(vo_mpegpes_fd < 0) return 32000; //not using -vo mpegpes
-// printf("vo_pts: %5.3f ao_pts: %5.3f\n",vo_pts/90000.0,ao_data.brokenpts/90000.0);
- if(x<=0) return 0;
- y=freq*4*x;y/=ao_data.outburst;y*=ao_data.outburst;
- if(y>32000) y=32000;
-// printf("diff: %5.3f -> %d \n",x,y);
- return y;
-}
-
-// 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){
-// printf("\nao_mpegpes: play(%d) freq=%d\n",len,freq_id);
- if(ao_data.format==AF_FORMAT_MPEG2)
- send_mpeg_pes_packet (data, len, 0x1C0, ao_data.brokenpts, 1, my_ao_write);
- else {
-// if(len>2000) len=2000;
-// printf("ao_mpegpes: len=%d \n",len);
- send_mpeg_lpcm_packet(data, len, 0xA0, ao_data.brokenpts, freq_id, my_ao_write);
- }
- return len;
-}
-
-// return: delay in seconds between first and last sample in buffer
-static float get_delay(void){
-
- return 0.0;
-}
diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c
deleted file mode 100644
index d3274df9a5..0000000000
--- a/libao2/ao_nas.c
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * NAS audio output driver
- *
- * copyright (c) 2001 Tobias Diedrich <ranma@gmx.at>
- *
- * Based on the libaudiooss parts rewritten by me, which were
- * originally based on the NAS output plugin for XMMS.
- *
- * XMMS plugin by Willem Monsuwe
- * adapted for libaudiooss by Jon Trulson
- * further modified by Erik Inge Bolsø
- * largely rewritten and used for this ao driver by Tobias Diedrich
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Theory of operation:
- *
- * The NAS consists of two parts, a server daemon and a client.
- * We setup the server to use a buffer of size bytes_per_second
- * with a low watermark of buffer_size - NAS_FRAG_SIZE.
- * Upon starting the flow the server will generate a buffer underrun
- * event and the event handler will fill the buffer for the first time.
- * Now the server will generate a lowwater event when the server buffer
- * falls below the low watermark value. The event handler gets called
- * again and refills the buffer by the number of bytes requested by the
- * server (usually a multiple of 4096). To prevent stuttering on
- * startup (start of playing, seeks, unpausing) the client buffer should
- * be bigger than the server buffer. (For debugging we also do some
- * accounting of what we think how much of the server buffer is filled)
- */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <limits.h>
-#include <audio/audiolib.h>
-
-#include "config.h"
-#include "mp_msg.h"
-
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "libaf/af_format.h"
-
-/* NAS_FRAG_SIZE must be a power-of-two value */
-#define NAS_FRAG_SIZE 4096
-
-static const char * const nas_event_types[] = {
- "Undefined",
- "Undefined",
- "ElementNotify",
- "GrabNotify",
- "MonitorNotify",
- "BucketNotify",
- "DeviceNotify"
-};
-
-static const char * const nas_elementnotify_kinds[] = {
- "LowWater",
- "HighWater",
- "State",
- "Unknown"
-};
-
-static const char * const nas_states[] = {
- "Stop",
- "Start",
- "Pause",
- "Any"
-};
-
-static const char * const nas_reasons[] = {
- "User",
- "Underrun",
- "Overrun",
- "EOF",
- "Watermark",
- "Hardware",
- "Any"
-};
-
-static const char* nas_reason(unsigned int reason)
-{
- if (reason > 6) reason = 6;
- return nas_reasons[reason];
-}
-
-static const char* nas_elementnotify_kind(unsigned int kind)
-{
- if (kind > 2) kind = 3;
- return nas_elementnotify_kinds[kind];
-}
-
-static const char* nas_event_type(unsigned int type) {
- if (type > 6) type = 0;
- return nas_event_types[type];
-}
-
-static const char* nas_state(unsigned int state) {
- if (state>3) state = 3;
- return nas_states[state];
-}
-
-static const ao_info_t info =
-{
- "NAS audio output",
- "nas",
- "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
- ""
-};
-
-struct ao_nas_data {
- AuServer *aud;
- AuFlowID flow;
- AuDeviceID dev;
- AuFixedPoint gain;
-
- unsigned int state;
- int expect_underrun;
-
- char *client_buffer;
- char *server_buffer;
- unsigned int client_buffer_size;
- unsigned int client_buffer_used;
- unsigned int server_buffer_size;
- unsigned int server_buffer_used;
- pthread_mutex_t buffer_mutex;
-
- pthread_t event_thread;
- int stop_thread;
-};
-
-static struct ao_nas_data *nas_data;
-
-LIBAO_EXTERN(nas)
-
-static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as)
-{
- char s[100];
- AuGetErrorText(aud, as, s, 100);
- mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
-}
-
-static int nas_readBuffer(struct ao_nas_data *nas_data, unsigned int num)
-{
- AuStatus as;
-
- pthread_mutex_lock(&nas_data->buffer_mutex);
- mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
- num,
- nas_data->client_buffer_used, nas_data->client_buffer_size,
- nas_data->server_buffer_used, nas_data->server_buffer_size);
-
- if (nas_data->client_buffer_used == 0) {
- mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
- pthread_mutex_unlock(&nas_data->buffer_mutex);
- return 0;
- }
- if (num > nas_data->client_buffer_used)
- num = nas_data->client_buffer_used;
-
- /*
- * It is not appropriate to call AuWriteElement() here because the
- * buffer is locked and delays writing to the network will cause
- * other threads to block waiting for buffer_mutex. Instead the
- * data is copied to "server_buffer" and written to the network
- * outside of the locked section of code.
- *
- * (Note: Rather than these two buffers, a single circular buffer
- * could eliminate the memcpy/memmove steps.)
- */
- /* make sure we don't overflow the buffer */
- if (num > nas_data->server_buffer_size)
- num = nas_data->server_buffer_size;
- memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
-
- nas_data->client_buffer_used -= num;
- nas_data->server_buffer_used += num;
- memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
- pthread_mutex_unlock(&nas_data->buffer_mutex);
-
- /*
- * Now write the new buffer to the network.
- */
- AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
- if (as != AuSuccess)
- nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
-
- return num;
-}
-
-static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, unsigned int len)
-{
- pthread_mutex_lock(&nas_data->buffer_mutex);
- mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
- len, nas_data->client_buffer_used, nas_data->client_buffer_size,
- nas_data->server_buffer_used, nas_data->server_buffer_size);
-
- /* make sure we don't overflow the buffer */
- if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)
- len = nas_data->client_buffer_size - nas_data->client_buffer_used;
- memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
- nas_data->client_buffer_used += len;
-
- pthread_mutex_unlock(&nas_data->buffer_mutex);
-
- return len;
-}
-
-static int nas_empty_event_queue(struct ao_nas_data *nas_data)
-{
- AuEvent ev;
- int result = 0;
-
- while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
- AuTrue, AuEventTypeElementNotify, &ev)) {
- AuDispatchEvent(nas_data->aud, &ev);
- result = 1;
- }
- return result;
-}
-
-static void *nas_event_thread_start(void *data)
-{
- struct ao_nas_data *nas_data = data;
-
- do {
- mp_msg(MSGT_AO, MSGL_DBG2,
- "ao_nas: event thread heartbeat (state=%s)\n",
- nas_state(nas_data->state));
- nas_empty_event_queue(nas_data);
- usleep(1000);
- } while (!nas_data->stop_thread);
-
- return NULL;
-}
-
-static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
-{
- char s[100];
- AuGetErrorText(aud, ev->error_code, s, 100);
- mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
- "error_code: %d\n"
- "request_code: %d\n"
- "minor_code: %d\n",
- s,
- ev->error_code,
- ev->request_code,
- ev->minor_code);
-
- return AuTrue;
-}
-
-static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
-{
- AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
- struct ao_nas_data *nas_data = hnd->data;
-
- mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: event_handler(): type %s kind %s state %s->%s reason %s numbytes %d expect_underrun %d\n",
- nas_event_type(event->type),
- nas_elementnotify_kind(event->kind),
- nas_state(event->prev_state),
- nas_state(event->cur_state),
- nas_reason(event->reason),
- (int)event->num_bytes,
- nas_data->expect_underrun);
-
- if (event->num_bytes > INT_MAX) {
- mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");
- }
-
- if (event->num_bytes > nas_data->server_buffer_used)
- event->num_bytes = nas_data->server_buffer_used;
- nas_data->server_buffer_used -= event->num_bytes;
-
- switch (event->reason) {
- case AuReasonWatermark:
- nas_readBuffer(nas_data, event->num_bytes);
- break;
- case AuReasonUnderrun:
- // buffer underrun -> refill buffer
- nas_data->server_buffer_used = 0;
- if (nas_data->expect_underrun) {
- nas_data->expect_underrun = 0;
- } else {
- static int hint = 1;
- mp_msg(MSGT_AO, MSGL_WARN,
- "ao_nas: Buffer underrun.\n");
- if (hint) {
- hint = 0;
- mp_msg(MSGT_AO, MSGL_HINT,
- "Possible reasons are:\n"
- "1) Network congestion.\n"
- "2) Your NAS server is too slow.\n"
- "Try renicing your nasd to e.g. -15.\n");
- }
- }
- if (nas_readBuffer(nas_data,
- nas_data->server_buffer_size -
- nas_data->server_buffer_used) != 0) {
- event->cur_state = AuStateStart;
- break;
- }
- mp_msg(MSGT_AO, MSGL_DBG2,
- "ao_nas: Can't refill buffer, stopping flow.\n");
- AuStopFlow(aud, nas_data->flow, NULL);
- break;
- default:
- break;
- }
- nas_data->state=event->cur_state;
- return AuTrue;
-}
-
-static AuDeviceID nas_find_device(AuServer *aud, int nch)
-{
- int i;
- for (i = 0; i < AuServerNumDevices(aud); i++) {
- AuDeviceAttributes *dev = AuServerDevice(aud, i);
- if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
- AuDeviceNumTracks(dev) == nch) {
- return AuDeviceIdentifier(dev);
- }
- }
- return AuNone;
-}
-
-static unsigned int nas_aformat_to_auformat(unsigned int *format)
-{
- switch (*format) {
- case AF_FORMAT_U8:
- return AuFormatLinearUnsigned8;
- case AF_FORMAT_S8:
- return AuFormatLinearSigned8;
- case AF_FORMAT_U16_LE:
- return AuFormatLinearUnsigned16LSB;
- case AF_FORMAT_U16_BE:
- return AuFormatLinearUnsigned16MSB;