aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-02-23 09:29:56 +0100
committerMax Kellermann <max@duempel.org>2009-02-23 09:29:56 +0100
commit5a898c15e79ab87d2466e61549fcc20ce115c67e (patch)
treecb65b88718b0b8f3cf05221816b193833c41fe8a
parentd50a3d513eb0452e762f1e4eeb717318958cd83c (diff)
downloadmpd-5a898c15e79ab87d2466e61549fcc20ce115c67e.tar.gz
mpd-5a898c15e79ab87d2466e61549fcc20ce115c67e.tar.xz
mpd-5a898c15e79ab87d2466e61549fcc20ce115c67e.zip
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.
-rw-r--r--src/output/alsa_plugin.c31
-rw-r--r--src/output/ao_plugin.c20
-rw-r--r--src/output/fifo_plugin.c17
-rw-r--r--src/output/jack_plugin.c33
-rw-r--r--src/output/mvp_plugin.c12
-rw-r--r--src/output/null_plugin.c6
-rw-r--r--src/output/oss_plugin.c17
-rw-r--r--src/output/osx_plugin.c45
-rw-r--r--src/output/pulse_plugin.c6
-rw-r--r--src/output/shout_plugin.c7
-rw-r--r--src/output_plugin.h6
-rw-r--r--src/output_thread.c22
12 files changed, 103 insertions, 119 deletions
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index bbb03e80b..05e097513 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -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 == -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)
+ return ret * ad->frame_size;
+
+ 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 = {
diff --git a/src/output/ao_plugin.c b/src/output/ao_plugin.c
index b0662d7a1..c9d10174e 100644
--- a/src/output/ao_plugin.c
+++ b/src/output/ao_plugin.c
@@ -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 (ao_play_deconst(ad->device, playChunk, chunk_size) == 0) {
- audioOutputAo_error("Closing libao device due to play error");
- return false;
- }
+ if (size > ad->writeSize)
+ size = ad->writeSize;
- 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 = {
diff --git a/src/output/fifo_plugin.c b/src/output/fifo_plugin.c
index b0c6d12f4..1be0f2a7d 100644
--- a/src/output/fifo_plugin.c
+++ b/src/output/fifo_plugin.c
@@ -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 = {
diff --git a/src/output/jack_plugin.c b/src/output/jack_plugin.c
index 2bcdf874f..22841f8ef 100644
--- a/src/output/jack_plugin.c
+++ b/src/output/jack_plugin.c
@@ -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 = {
diff --git a/src/output/mvp_plugin.c b/src/output/mvp_plugin.c
index cb570c8ee..840661ea7 100644
--- a/src/output/mvp_plugin.c
+++ b/src/output/mvp_plugin.c
@@ -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 = {
diff --git a/src/output/null_plugin.c b/src/output/null_plugin.c
index 1ae55ca9b..2506feb74 100644
--- a/src/output/null_plugin.c
+++ b/src/output/null_plugin.c
@@ -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
diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c
index 007d5bc84..eca6f9101 100644
--- a/src/output/oss_plugin.c
+++ b/src/output/oss_plugin.c
@@ -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 = {
diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c
index a97f56982..ffeb8cd5e 100644
--- a/src/output/osx_plugin.c
+++ b/src/output/osx_plugin.c
@@ -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;
- }
-
- memcpy(od->buffer + curpos, playChunk, bytesToCopy);
- playChunk += bytesToCopy;
+ if (nbytes > size)
+ nbytes = size;
- }
+ memcpy(od->buffer + start, playChunk, nbytes);
+ od->len += nbytes;
g_mutex_unlock(od->mutex);
- return true;
+ return nbytes;
}
const struct audio_output_plugin osxPlugin = {
diff --git a/src/output/pulse_plugin.c b/src/output/pulse_plugin.c
index 559d2ee56..0203310e7 100644
--- a/src/output/pulse_plugin.c
+++ b/src/output/pulse_plugin.c
@@ -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 = {
diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c
index 7887e2922..2563e8092 100644
--- a/src/output/shout_plugin.c
+++ b/src/output/shout_plugin.c
@@ -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
diff --git a/src/output_plugin.h b/src/output_plugin.h
index b652dc44e..8a2bcadac 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -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)
{
diff --git a/src/output_thread.c b/src/output_thread.c
index 098e1b427..709d8d1a2 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -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);