output_api: play() returns a length

The old API required an output plugin to not return until all data
passed to the play() method is consumed.  Some output plugins have to
loop to fulfill that requirement, and may block during that.  Simplify
these, by letting them consume only part of the buffer: make play()
return the length of the consumed data.
This commit is contained in:
Max Kellermann 2009-02-23 09:29:56 +01:00
parent d50a3d513e
commit 5a898c15e7
12 changed files with 102 additions and 118 deletions

View File

@ -441,7 +441,7 @@ alsa_close(void *data)
mixer_close(ad->mixer);
}
static bool
static size_t
alsa_play(void *data, const char *chunk, size_t size)
{
struct alsa_data *ad = data;
@ -449,28 +449,19 @@ alsa_play(void *data, const char *chunk, size_t size)
size /= ad->frame_size;
while (size > 0) {
while (true) {
ret = ad->writei(ad->pcm, chunk, size);
if (ret > 0)
return ret * ad->frame_size;
if (ret == -EAGAIN || ret == -EINTR)
continue;
if (ret < 0) {
if (alsa_recover(ad, ret) < 0) {
g_warning("closing ALSA device \"%s\" due to write "
"error: %s\n",
alsa_device(ad), snd_strerror(-errno));
return false;
}
continue;
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
alsa_recover(ad, ret) < 0) {
g_warning("closing ALSA device \"%s\" due to write "
"error: %s\n",
alsa_device(ad), snd_strerror(-errno));
return 0;
}
chunk += ret * ad->frame_size;
size -= ret;
}
return true;
}
const struct audio_output_plugin alsaPlugin = {

View File

@ -208,29 +208,23 @@ static int ao_play_deconst(ao_device *device, const void *output_samples,
return ao_play(device, u.out, num_bytes);
}
static bool
static size_t
audioOutputAo_play(void *data, const char *playChunk, size_t size)
{
AoData *ad = (AoData *)data;
size_t chunk_size;
if (ad->device == NULL)
return false;
while (size > 0) {
chunk_size = ad->writeSize > size
? size : ad->writeSize;
if (size > ad->writeSize)
size = ad->writeSize;
if (ao_play_deconst(ad->device, playChunk, chunk_size) == 0) {
audioOutputAo_error("Closing libao device due to play error");
return false;
}
playChunk += chunk_size;
size -= chunk_size;
if (ao_play_deconst(ad->device, playChunk, size) == 0) {
audioOutputAo_error("Closing libao device due to play error");
return 0;
}
return true;
return size;
}
const struct audio_output_plugin aoPlugin = {

View File

@ -237,11 +237,10 @@ static void fifo_dropBufferedAudio(void *data)
}
}
static bool
static size_t
fifo_playAudio(void *data, const char *playChunk, size_t size)
{
FifoData *fd = (FifoData *)data;
size_t offset = 0;
ssize_t bytes;
if (!fd->timer->started)
@ -251,8 +250,11 @@ fifo_playAudio(void *data, const char *playChunk, size_t size)
timer_add(fd->timer, size);
while (size) {
bytes = write(fd->output, playChunk + offset, size);
while (true) {
bytes = write(fd->output, playChunk, size);
if (bytes > 0)
return (size_t)bytes;
if (bytes < 0) {
switch (errno) {
case EAGAIN:
@ -265,14 +267,9 @@ fifo_playAudio(void *data, const char *playChunk, size_t size)
g_warning("Closing FIFO output \"%s\" due to write error: %s",
fd->path, strerror(errno));
return false;
return 0;
}
size -= bytes;
offset += bytes;
}
return true;
}
const struct audio_output_plugin fifoPlugin = {

View File

@ -387,7 +387,7 @@ mpd_jack_write_samples(struct jack_data *jd, const void *src,
}
}
static bool
static size_t
mpd_jack_play(void *data, const char *buff, size_t size)
{
struct jack_data *jd = data;
@ -396,36 +396,33 @@ mpd_jack_play(void *data, const char *buff, size_t size)
if (jd->shutdown) {
g_warning("Refusing to play, because there is no client thread.");
return false;
return 0;
}
assert(size % frame_size == 0);
size /= frame_size;
while (size > 0 && !jd->shutdown) {
while (!jd->shutdown) {
space = jack_ringbuffer_write_space(jd->ringbuffer[0]);
space1 = jack_ringbuffer_write_space(jd->ringbuffer[1]);
if (space > space1)
/* send data symmetrically */
space = space1;
space /= sample_size;
if (space > 0) {
if (space > size)
space = size;
mpd_jack_write_samples(jd, buff, space);
buff += space * frame_size;
size -= space;
} else {
/* XXX do something more intelligent to
synchronize */
g_usleep(1000);
}
if (space >= frame_size)
break;
/* XXX do something more intelligent to
synchronize */
g_usleep(1000);
}
return true;
space /= sample_size;
if (space < size)
size = space;
mpd_jack_write_samples(jd, buff, size);
return size * frame_size;
}
const struct audio_output_plugin jackPlugin = {

View File

@ -248,7 +248,7 @@ static void mvp_dropBufferedAudio(void *data)
}
}
static bool
static size_t
mvp_playAudio(void *data, const char *playChunk, size_t size)
{
MvpData *md = data;
@ -258,19 +258,19 @@ mvp_playAudio(void *data, const char *playChunk, size_t size)
if (md->fd < 0)
mvp_openDevice(md, &md->audio_format);
while (size > 0) {
while (true) {
ret = write(md->fd, playChunk, size);
if (ret > 0)
return (size_t)ret;
if (ret < 0) {
if (errno == EINTR)
continue;
g_warning("closing mvp PCM device due to write error: "
"%s\n", strerror(errno));
return false;
return 0;
}
playChunk += ret;
size -= ret;
}
return true;
}
const struct audio_output_plugin mvpPlugin = {

View File

@ -74,14 +74,14 @@ null_close(void *data)
}
}
static bool
static size_t
null_play(void *data, G_GNUC_UNUSED const char *chunk, size_t size)
{
struct null_data *nd = data;
Timer *timer = nd->timer;
if (!nd->sync)
return true;
return size;
if (!timer->started)
timer_start(timer);
@ -90,7 +90,7 @@ null_play(void *data, G_GNUC_UNUSED const char *chunk, size_t size)
timer_add(timer, size);
return true;
return size;
}
static void

View File

@ -553,7 +553,7 @@ static void oss_dropBufferedAudio(void *data)
}
}
static bool
static size_t
oss_playAudio(void *data, const char *playChunk, size_t size)
{
OssData *od = data;
@ -563,20 +563,17 @@ oss_playAudio(void *data, const char *playChunk, size_t size)
if (od->fd < 0 && !oss_open(od))
return false;
while (size > 0) {
while (true) {
ret = write(od->fd, playChunk, size);
if (ret < 0) {
if (errno == EINTR)
continue;
if (ret > 0)
return (size_t)ret;
if (ret < 0 && errno != EINTR) {
g_warning("closing oss device \"%s\" due to write error: "
"%s\n", od->device, strerror(errno));
return false;
return 0;
}
playChunk += ret;
size -= ret;
}
return true;
}
const struct audio_output_plugin ossPlugin = {

View File

@ -256,13 +256,11 @@ osx_openDevice(void *data, struct audio_format *audioFormat)
return true;
}
static bool
static size_t
osx_play(void *data, const char *playChunk, size_t size)
{
OsxData *od = data;
size_t bytesToCopy;
size_t bytes;
size_t curpos;
size_t start, nbytes;
if (!od->started) {
int err;
@ -270,42 +268,35 @@ osx_play(void *data, const char *playChunk, size_t size)
err = AudioOutputUnitStart(od->au);
if (err) {
g_warning("unable to start audio output: %i\n", err);
return false;
return 0;
}
}
g_mutex_lock(od->mutex);
while (size) {
curpos = od->pos + od->len;
if (curpos >= od->bufferSize)
curpos -= od->bufferSize;
while (od->len >= od->bufferSize)
/* wait for some free space in the buffer */
g_cond_wait(od->condition, od->mutex);
bytesToCopy = MIN(od->bufferSize, size);
start = od->pos + od->len;
if (start >= od->bufferSize)
start -= od->bufferSize;
while (od->len > od->bufferSize - bytesToCopy) {
g_cond_wait(od->condition, od->mutex);
}
nbytes = start < od->pos
? od->pos - start
: od->bufferSize - start;
size -= bytesToCopy;
od->len += bytesToCopy;
assert(nbytes > 0);
bytes = od->bufferSize - curpos;
if (bytesToCopy > bytes) {
memcpy(od->buffer + curpos, playChunk, bytes);
curpos = 0;
playChunk += bytes;
bytesToCopy -= bytes;
}
if (nbytes > size)
nbytes = size;
memcpy(od->buffer + curpos, playChunk, bytesToCopy);
playChunk += bytesToCopy;
}
memcpy(od->buffer + start, playChunk, nbytes);
od->len += nbytes;
g_mutex_unlock(od->mutex);
return true;
return nbytes;
}
const struct audio_output_plugin osxPlugin = {

View File

@ -160,7 +160,7 @@ static void pulse_close(void *data)
}
}
static bool
static size_t
pulse_play(void *data, const char *playChunk, size_t size)
{
struct pulse_data *pd = data;
@ -172,10 +172,10 @@ pulse_play(void *data, const char *playChunk, size_t size)
audio_output_get_name(pd->ao),
pa_strerror(error));
pulse_close(pd);
return false;
return 0;
}
return true;
return size;
}
const struct audio_output_plugin pulse_plugin = {

View File

@ -413,7 +413,7 @@ my_shout_open_device(void *data, struct audio_format *audio_format)
return true;
}
static bool
static size_t
my_shout_play(void *data, const char *chunk, size_t size)
{
struct shout_data *sd = (struct shout_data *)data;
@ -427,7 +427,10 @@ my_shout_play(void *data, const char *chunk, size_t size)
return false;
}
return write_page(sd);
if (!write_page(sd))
return 0;
return size;
}
static bool

View File

@ -92,8 +92,10 @@ struct audio_output_plugin {
/**
* Play a chunk of audio data.
*
* @return the number of bytes played, or 0 on error
*/
bool (*play)(void *data, const char *playChunk, size_t size);
size_t (*play)(void *data, const char *chunk, size_t size);
/**
* Try to cancel data which may still be in the device's
@ -167,7 +169,7 @@ ao_plugin_send_tag(const struct audio_output_plugin *plugin,
plugin->send_tag(data, tag);
}
static inline bool
static inline size_t
ao_plugin_play(const struct audio_output_plugin *plugin,
void *data, const void *chunk, size_t size)
{

View File

@ -52,9 +52,9 @@ static void ao_play(struct audio_output *ao)
{
const char *data = ao->args.play.data;
size_t size = ao->args.play.size;
bool ret;
assert(size > 0);
assert(size % audio_format_frame_size(&ao->out_audio_format) == 0);
if (!audio_format_equals(&ao->in_audio_format, &ao->out_audio_format)) {
data = pcm_convert(&ao->convert_state,
@ -69,10 +69,22 @@ static void ao_play(struct audio_output *ao)
return;
}
ret = ao_plugin_play(ao->plugin, ao->data, data, size);
if (!ret) {
ao_plugin_cancel(ao->plugin, ao->data);
ao_close(ao);
while (size > 0) {
size_t nbytes;
nbytes = ao_plugin_play(ao->plugin, ao->data, data, size);
if (nbytes == 0) {
/* play()==0 means failure */
ao_plugin_cancel(ao->plugin, ao->data);
ao_close(ao);
break;
}
assert(nbytes <= size);
assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0);
data += nbytes;
size -= nbytes;
}
ao_command_finished(ao);