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:
parent
d50a3d513e
commit
5a898c15e7
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user