diff options
Diffstat (limited to 'libao2')
-rw-r--r-- | libao2/ao_arts.c | 148 | ||||
-rw-r--r-- | libao2/ao_esd.c | 477 | ||||
-rw-r--r-- | libao2/ao_portaudio.c | 431 | ||||
-rw-r--r-- | libao2/ao_pulse.c | 2 | ||||
-rw-r--r-- | libao2/ao_sgi.c | 301 | ||||
-rw-r--r-- | libao2/audio_out.c | 11 |
6 files changed, 435 insertions, 935 deletions
diff --git a/libao2/ao_arts.c b/libao2/ao_arts.c deleted file mode 100644 index d828e7953e..0000000000 --- a/libao2/ao_arts.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * aRts audio output driver for MPlayer - * - * copyright (c) 2002 Michele Balistreri <brain87@gmx.net> - * - * 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 <artsc.h> -#include <stdio.h> - -#include "config.h" -#include "audio_out.h" -#include "audio_out_internal.h" -#include "libaf/af_format.h" -#include "mp_msg.h" - -#define OBTAIN_BITRATE(a) (((a != AF_FORMAT_U8) && (a != AF_FORMAT_S8)) ? 16 : 8) - -/* Feel free to experiment with the following values: */ -#define ARTS_PACKETS 10 /* Number of audio packets */ -#define ARTS_PACKET_SIZE_LOG2 11 /* Log2 of audio packet size */ - -static arts_stream_t stream; - -static const ao_info_t info = -{ - "aRts audio output", - "arts", - "Michele Balistreri <brain87@gmx.net>", - "" -}; - -LIBAO_EXTERN(arts) - -static int control(int cmd, void *arg) -{ - return CONTROL_UNKNOWN; -} - -static int init(int rate_hz, int channels, int format, int flags) -{ - int err; - int frag_spec; - - if( (err=arts_init()) ) { - mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] %s\n", arts_error_text(err)); - return 0; - } - mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Connected to sound server.\n"); - - /* - * arts supports 8bit unsigned and 16bit signed sample formats - * (16bit apparently in little endian format, even in the case - * when artsd runs on a big endian cpu). - * - * Unsupported formats are translated to one of these two formats - * using mplayer's audio filters. - */ - switch (format) { - case AF_FORMAT_U8: - case AF_FORMAT_S8: - format = AF_FORMAT_U8; - break; - default: - format = AF_FORMAT_S16_LE; /* artsd always expects little endian?*/ - break; - } - - ao_data.format = format; - ao_data.channels = channels; - ao_data.samplerate = rate_hz; - ao_data.bps = (rate_hz*channels); - - if(format != AF_FORMAT_U8 && format != AF_FORMAT_S8) - ao_data.bps*=2; - - stream=arts_play_stream(rate_hz, OBTAIN_BITRATE(format), channels, "MPlayer"); - - if(stream == NULL) { - mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] Unable to open a stream.\n"); - arts_free(); - return 0; - } - - /* Set the stream to blocking: it will not block anyway, but it seems */ - /* to be working better */ - arts_stream_set(stream, ARTS_P_BLOCKING, 1); - frag_spec = ARTS_PACKET_SIZE_LOG2 | ARTS_PACKETS << 16; - arts_stream_set(stream, ARTS_P_PACKET_SETTINGS, frag_spec); - ao_data.buffersize = arts_stream_get(stream, ARTS_P_BUFFER_SIZE); - mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Stream opened.\n"); - - mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n", - ao_data.buffersize); - mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n", - arts_stream_get(stream, ARTS_P_PACKET_SIZE)); - - return 1; -} - -static void uninit(int immed) -{ - arts_close_stream(stream); - arts_free(); -} - -static int play(void* data,int len,int flags) -{ - return arts_write(stream, data, len); -} - -static void audio_pause(void) -{ -} - -static void audio_resume(void) -{ -} - -static void reset(void) -{ -} - -static int get_space(void) -{ - return arts_stream_get(stream, ARTS_P_BUFFER_SPACE); -} - -static float get_delay(void) -{ - return ((float) (ao_data.buffersize - arts_stream_get(stream, - ARTS_P_BUFFER_SPACE))) / ((float) ao_data.bps); -} diff --git a/libao2/ao_esd.c b/libao2/ao_esd.c deleted file mode 100644 index e7c6701aa0..0000000000 --- a/libao2/ao_esd.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * EsounD audio output driver for MPlayer - * - * copyright (c) 2002 Juergen Keil <jk@tools.de> - * - * 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. - */ - - /* - * 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 <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> -#include <time.h> -#ifdef __svr4__ -#include <stropts.h> -#endif -#include <esd.h> - -#include "config.h" -#include "audio_out.h" -#include "audio_out_internal.h" -#include "libaf/af_format.h" -#include "mp_msg.h" - - -#define ESD_RESAMPLES 0 -#define ESD_DEBUG 0 - -#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 const 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; -extern float audio_delay; - -/* - * to set/get/query special features/parameters - */ -static int control(int cmd, void *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; - char *server = ao_subdevice; /* NULL for localhost */ - float lag_seconds, lag_net = 0., lag_serv; - struct timeval proto_start, proto_end; - - global_ao->no_persistent_volume = true; - - if (esd_fd < 0) { - esd_fd = esd_open_sound(server); - if (esd_fd < 0) { - mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ESD] esd_open_sound failed: %s\n", - strerror(errno)); - return 0; - } - - /* get server info, and measure network latency */ - gettimeofday(&proto_start, NULL); - esd_svinfo = esd_get_server_info(esd_fd); - if(server) { - gettimeofday(&proto_end, NULL); - lag_net = (proto_end.tv_sec - proto_start.tv_sec) + - (proto_end.tv_usec - proto_start.tv_usec) / 1000000.0; - lag_net /= 2.0; /* round trip -> one way */ - } else - lag_net = 0.0; /* no network lag */ - - /* - if (esd_svinfo) { - mp_msg(MSGT_AO, MSGL_INFO, "AO: [esd] server info:\n"); - esd_print_server_info(esd_svinfo); - } - */ - } - - 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 AF_FORMAT_S8: - case AF_FORMAT_U8: - esd_fmt |= ESD_BITS8; - ao_data.format = AF_FORMAT_U8; - break; - default: - esd_fmt |= ESD_BITS16; - ao_data.format = AF_FORMAT_S16_NE; - bytes_per_sample *= 2; - break; - } - - /* modify audio_delay depending on esd_latency - * latency is number of samples @ 44.1khz stereo 16 bit - * adjust according to rate_hz & bytes_per_sample - */ -#ifdef CONFIG_ESD_LATENCY - esd_latency = esd_get_latency(esd_fd); -#else - esd_latency = ((channels == 1 ? 2 : 1) * ESD_DEFAULT_RATE * - (ESD_BUF_SIZE + 64 * (4.0f / bytes_per_sample)) - ) / rate_hz; - esd_latency += ESD_BUF_SIZE * 2; -#endif - if(esd_latency > 0) { - lag_serv = (esd_latency * 4.0f) / (bytes_per_sample * rate_hz); - lag_seconds = lag_net + lag_serv; - audio_delay += lag_seconds; - mp_tmsg(MSGT_AO, MSGL_INFO,"[AO ESD] latency: [server: %0.2fs, net: %0.2fs] (adjust %0.2fs)\n", - lag_serv, lag_net, lag_seconds); - } - - esd_play_fd = esd_play_stream_fallback(esd_fmt, rate_hz, - server, ESD_CLIENT_NAME); - if (esd_play_fd < 0) { - mp_tmsg(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(int immed) -{ - 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 n; - - /* 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, nwritten=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. - */ - n = write(esd_play_fd, (char*)data + offs, ESD_BUF_SIZE); - if ( n < 0 ) { - if ( errno != EAGAIN ) - dprintf("esd play: write failed: %s\n", strerror(errno)); - break; - } else if ( n != ESD_BUF_SIZE ) { - nwritten += n; - break; - } else - nwritten += n; - } -#endif - - 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(void) -{ - /* - * 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(void) -{ - /* - * 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(void) -{ -#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(void) -{ - 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(void) -{ - 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; -} diff --git a/libao2/ao_portaudio.c b/libao2/ao_portaudio.c new file mode 100644 index 0000000000..c8275f0b38 --- /dev/null +++ b/libao2/ao_portaudio.c @@ -0,0 +1,431 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <pthread.h> + +#include <libavutil/avutil.h> +#include <portaudio.h> + +#include "config.h" +#include "subopt-helper.h" +#include "libaf/af_format.h" +#include "mp_msg.h" +#include "audio_out.h" + +struct priv { + PaStream *stream; + int framelen; + + pthread_mutex_t ring_mutex; + + // protected by ring_mutex + unsigned char *ring; + int ring_size; // max size of the ring + int read_pos; // points to first byte that can be read + int read_len; // number of bytes that can be read + double play_time; // time when last packet returned to PA is on speaker + // 0 is N/A (0 is not a valid PA time value) + int play_silence; // play this many bytes of silence, before real data + bool play_remaining;// play what's left in the buffer, then stop stream +}; + +struct format_map { + int mp_format; + PaSampleFormat pa_format; +}; + +static const struct format_map format_maps[] = { + // first entry is the default format + {AF_FORMAT_S16_NE, paInt16}, + {AF_FORMAT_S24_NE, paInt24}, + {AF_FORMAT_S32_NE, paInt32}, + {AF_FORMAT_S8, paInt8}, + {AF_FORMAT_U8, paUInt8}, + {AF_FORMAT_FLOAT_NE, paFloat32}, + {AF_FORMAT_UNKNOWN, 0} +}; + +static void print_help(void) +{ + mp_msg(MSGT_AO, MSGL_FATAL, + "\n-ao portaudio commandline help:\n" + "Example: mplayer -ao portaudio:device=subdevice\n" + "\nOptions:\n" + " device=subdevice\n" + " Audio device PortAudio should use. Devices can be listed\n" + " with -ao portaudio:device=help\n" + " The subdevice can be passed as index, or as complete name.\n"); +} + +static bool check_pa_ret(int ret) +{ + if (ret < 0) { + mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] %s\n", + Pa_GetErrorText(ret)); + if (ret == paUnanticipatedHostError) { + const PaHostErrorInfo* hosterr = Pa_GetLastHostErrorInfo(); + mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Host error: %s\n", + hosterr->errorText); + } + return false; + } + return true; +} + +// Amount of bytes that contain audio of the given duration, aligned to frames. +static int seconds_to_bytes(struct ao *ao, double duration_seconds) +{ + struct priv *priv = ao->priv; + + int bytes = duration_seconds * ao->bps; + if (bytes % priv->framelen) + bytes += priv->framelen - (bytes % priv->framelen); + return bytes; +} + +static int to_int(const char *s, int return_on_error) +{ + char *endptr; + int res = strtol(s, &endptr, 10); + return (s[0] && !endptr[0]) ? res : return_on_error; +} + +static int find_device(struct ao *ao, const char *name) +{ + int help = strcmp(name, "help") == 0; + int count = Pa_GetDeviceCount(); + check_pa_ret(count); + int found = paNoDevice; + int index = to_int(name, -1); + if (help) + mp_msg(MSGT_AO, MSGL_INFO, "PortAudio devices:\n"); + for (int n = 0; n < count; n++) { + const PaDeviceInfo* info = Pa_GetDeviceInfo(n); + if (help) { + if (info->maxOutputChannels < 1) + continue; + mp_msg(MSGT_AO, MSGL_INFO, " %d '%s', %d channels, latency: %.2f " + "ms, sample rate: %.0f\n", n, info->name, + info->maxOutputChannels, + info->defaultHighOutputLatency * 1000, + info->defaultSampleRate); + } + if (strcmp(name, info->name) == 0 || n == index) { + found = n; + break; + } + } + if (found == paNoDevice && !help) + mp_msg(MSGT_AO, MSGL_FATAL, "[portaudio] Device '%s' not found!\n", + name); + return found; +} + +static int ring_write(struct ao *ao, unsigned char *data, int len) +{ + struct priv *priv = ao->priv; + + int free = priv->ring_size - priv->read_len; + int write_pos = (priv->read_pos + priv->read_len) % priv->ring_size; + int write_len = FFMIN(len, free); + int len1 = FFMIN(priv->ring_size - write_pos, write_len); + int len2 = write_len - len1; + + memcpy(priv->ring + write_pos, data, len1); + memcpy(priv->ring, data + len1, len2); + + priv->read_len += write_len; + + return write_len; +} + +static int ring_read(struct ao *ao, unsigned char *data, int len) +{ + struct priv *priv = ao->priv; + + int read_len = FFMIN(len, priv->read_len); + int len1 = FFMIN(priv->ring_size - priv->read_pos, read_len); + int len2 = read_len - len1; + + memcpy(data, priv->ring + priv->read_pos, len1); + memcpy(data + len1, priv->ring, len2); + + priv->read_len -= read_len; + priv->read_pos = (priv->read_pos + read_len) % priv->ring_size; + + return read_len; +} + +static void fill_silence(unsigned char *ptr, int len) +{ + memset(ptr, 0, len); +} + +static int stream_callback(const void *input, + void *output_v, + unsigned long frameCount, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData) +{ + struct ao *ao = userData; + struct priv *priv = ao->priv; + int res = paContinue; + unsigned char *output = output_v; + int len_bytes = frameCount * priv->framelen; + + pthread_mutex_lock(&priv->ring_mutex); + + // NOTE: PA + ALSA in dmix mode seems to pretend that there is no latency + // (outputBufferDacTime == currentTime) + priv->play_time = timeInfo->outputBufferDacTime + + len_bytes / (float)ao->bps; + + if (priv->play_silence > 0) { + int bytes = FFMIN(priv->play_silence, len_bytes); + fill_silence(output, bytes); + priv->play_silence -= bytes; + len_bytes -= bytes; + output += bytes; + } + int read = ring_read(ao, output, len_bytes); + len_bytes -= read; + output += read; + + if (len_bytes > 0) { + if (priv->play_remaining) { + res = paComplete; + priv->play_remaining = false; + } else { + mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Buffer underflow!\n"); + } + fill_silence(output, len_bytes); + } + + pthread_mutex_unlock(&priv->ring_mutex); + + return res; +} + +static void uninit(struct ao *ao, bool cut_audio) +{ + struct priv *priv = ao->priv; + + if (priv->stream) { + if (!cut_audio && Pa_IsStreamActive(priv->stream) == 1) { + pthread_mutex_lock(&priv->ring_mutex); + + priv->play_remaining = true; + + pthread_mutex_unlock(&priv->ring_mutex); + + check_pa_ret(Pa_StopStream(priv->stream)); + } + check_pa_ret(Pa_CloseStream(priv->stream)); + } + + pthread_mutex_destroy(&priv->ring_mutex); + Pa_Terminate(); +} + +static int init(struct ao *ao, char *params) +{ + struct priv *priv = talloc_zero(ao, struct priv); + ao->priv = priv; + + if (!check_pa_ret(Pa_Initialize())) + return -1; + + pthread_mutex_init(&priv->ring_mutex, NULL); + + char *device = NULL; + const opt_t subopts[] = { + {"device", OPT_ARG_MSTRZ, &device, NULL}, + {NULL} + }; + if (subopt_parse(params, subopts) != 0) { + print_help(); + goto error_exit; + } + + int pa_device = Pa_GetDefaultOutputDevice(); + if (device) + pa_device = find_device(ao, device); + if (pa_device == paNoDevice) + goto error_exit; + + PaStreamParameters sp = { + .device = pa_device, + .channelCount = ao->channels, + .suggestedLatency + = Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency, + }; + + const struct format_map *fmt = format_maps; + while (fmt->pa_format) { + if (fmt->mp_format == ao->format) { + PaStreamParameters test = sp; + test.sampleFormat = fmt->pa_format; + if (Pa_IsFormatSupported(NULL, &test, ao->samplerate) == paNoError) + break; + } + fmt++; + } + if (!fmt->pa_format) { + mp_msg(MSGT_AO, MSGL_V, + "[portaudio] Unsupported format, using default.\n"); + fmt = format_maps; + } + + ao->format = fmt->mp_format; + sp.sampleFormat = fmt->pa_format; + priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8); + ao->bps = ao->samplerate * priv->framelen; + + if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate))) + goto error_exit; + if (!check_pa_ret(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate, + paFramesPerBufferUnspecified, paNoFlag, + stream_callback, ao))) + goto error_exit; + + priv->ring_size = seconds_to_bytes(ao, 0.5); + priv->ring = talloc_zero_size(priv, priv->ring_size); + + free(device); + return 0; + +error_exit: + uninit(ao, true); + free(device); + return -1; +} + +static int play(struct ao *ao, void *data, int len, int flags) +{ + struct priv *priv = ao->priv; + + pthread_mutex_lock(&priv->ring_mutex); + + int write_len = ring_write(ao, data, len); + if (flags & AOPLAY_FINAL_CHUNK) + priv->play_remaining = true; + + pthread_mutex_unlock(&priv->ring_mutex); + + if (Pa_IsStreamStopped(priv->stream) == 1) + check_pa_ret(Pa_StartStream(priv->stream)); + + return write_len; +} + +static int get_space(struct ao *ao) +{ + struct priv *priv = ao->priv; + + pthread_mutex_lock(&priv->ring_mutex); + + int free = priv->ring_size - priv->read_len; + + pthread_mutex_unlock(&priv->ring_mutex); + + return free; +} + +static float get_delay(struct ao *ao) +{ + struct priv *priv = ao->priv; + + double stream_time = Pa_GetStreamTime(priv->stream); + + pthread_mutex_lock(&priv->ring_mutex); + + float frame_time = priv->play_time ? priv->play_time - stream_time : 0; + float buffer_latency = (priv->read_len + priv->play_silence) + / (float)ao->bps; + + pthread_mutex_unlock(&priv->ring_mutex); + + return buffer_latency + frame_time; +} + +static void reset(struct ao *ao) +{ + struct priv *priv = ao->priv; + + if (Pa_IsStreamStopped(priv->stream) != 1) + check_pa_ret(Pa_AbortStream(priv->stream)); + + pthread_mutex_lock(&priv->ring_mutex); + + priv->read_len = 0; + priv->read_pos = 0; + priv->play_remaining = false; + priv->play_time = 0; + priv->play_silence = 0; + + pthread_mutex_unlock(&priv->ring_mutex); +} + +static void pause(struct ao *ao) +{ + struct priv *priv = ao->priv; + + check_pa_ret(Pa_AbortStream(priv->stream)); + + double stream_time = Pa_GetStreamTime(priv->stream); + + pthread_mutex_lock(&priv->ring_mutex); + + // When playback resumes, replace the lost audio (due to dropping the + // portaudio/driver/hardware internal buffers) with silence. + float frame_time = priv->play_time ? priv->play_time - stream_time : 0; + priv->play_silence += seconds_to_bytes(ao, FFMAX(frame_time, 0)); + priv->play_time = 0; + + pthread_mutex_unlock(&priv->ring_mutex); +} + +static void resume(struct ao *ao) +{ + struct priv *priv = ao->priv; + + check_pa_ret(Pa_StartStream(priv->stream)); +} + +const struct ao_driver audio_out_portaudio = { + .is_new = true, + .info = &(const struct ao_info) { + "PortAudio", + "portaudio", + "wm4", + "", + }, + .init = init, + .uninit = uninit, + .reset = reset, + .get_space = get_space, + .play = play, + .get_delay = get_delay, + .pause = pause, + .resume = resume, +}; diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index ed6e08286a..e48c402fa6 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -186,7 +186,7 @@ static int init(struct ao *ao, char *params) ao->priv = priv; if (params) { - devarg = strdup(ao_subdevice); + devarg = strdup(params); sink = strchr(devarg, ':'); if (sink) *sink++ = 0; diff --git a/libao2/ao_sgi.c b/libao2/ao_sgi.c deleted file mode 100644 index 492c8ff3ba..0000000000 --- a/libao2/ao_sgi.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * SGI/IRIX audio output driver - * - * copyright (c) 2001 oliver.schoenbrunner@jku.at - * - * 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 <unistd.h> -#include <errno.h> -#include <dm |