From d32f49a90ba69525884ee6fe4f2055792aa10430 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 24 Sep 2008 07:20:26 +0200 Subject: output: one thread per audio output To keep I/O nastiness and latencies away from the core, move the audio output code to a separate thread, one per output. The thread is created on demand, and currently runs until mpd exits. --- src/Makefile.am | 2 + src/output_api.h | 26 +++++++++++ src/output_control.c | 77 ++++++++++++++++---------------- src/output_init.c | 4 ++ src/output_thread.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/output_thread.h | 26 +++++++++++ 6 files changed, 218 insertions(+), 38 deletions(-) create mode 100644 src/output_thread.c create mode 100644 src/output_thread.h diff --git a/src/Makefile.am b/src/Makefile.am index 368e051fc..2069c45c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ mpd_headers = \ audioOutput.h \ output_api.h \ output_list.h \ + output_thread.h \ output_control.h \ audioOutputs/audioOutput_shout.h \ buffer2array.h \ @@ -114,6 +115,7 @@ mpd_SOURCES = \ audio.c \ audioOutput.c \ output_list.c \ + output_thread.c \ output_control.c \ output_init.c \ buffer2array.c \ diff --git a/src/output_api.h b/src/output_api.h index 887b57c7c..bea5c5b4c 100644 --- a/src/output_api.h +++ b/src/output_api.h @@ -26,6 +26,7 @@ #include "tag.h" #include "conf.h" #include "log.h" +#include "notify.h" #include "os_compat.h" #define DISABLED_AUDIO_OUTPUT_PLUGIN(plugin) const struct audio_output_plugin plugin; @@ -54,6 +55,16 @@ struct audio_output_plugin { const struct tag *tag); }; +enum audio_output_command { + AO_COMMAND_NONE = 0, + AO_COMMAND_OPEN, + AO_COMMAND_CLOSE, + AO_COMMAND_PLAY, + AO_COMMAND_CANCEL, + AO_COMMAND_SEND_TAG, + AO_COMMAND_KILL +}; + struct audio_output { int open; const char *name; @@ -67,7 +78,22 @@ struct audio_output { char *convBuffer; size_t convBufferLen; + pthread_t thread; + struct notify notify; + enum audio_output_command command; + union { + struct { + const char *data; + size_t size; + } play; + + const struct tag *tag; + } args; + int result; + void *data; }; +extern struct notify audio_output_client_notify; + #endif diff --git a/src/output_control.c b/src/output_control.c index 0818a459b..2fc692d6b 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -18,8 +18,27 @@ #include "output_control.h" #include "output_api.h" +#include "output_thread.h" #include "pcm_utils.h" -#include "utils.h" + +#include + +struct notify audio_output_client_notify = NOTIFY_INITIALIZER; + +static void ao_command_wait(struct audio_output *ao) +{ + while (ao->command != AO_COMMAND_NONE) { + notify_signal(&ao->notify); + notify_wait(&audio_output_client_notify); + } +} + +static void ao_command(struct audio_output *ao, enum audio_output_command cmd) +{ + assert(ao->command == AO_COMMAND_NONE); + ao->command = cmd; + ao_command_wait(ao); +} int audio_output_open(struct audio_output *audioOutput, const struct audio_format *audioFormat) @@ -47,67 +66,46 @@ int audio_output_open(struct audio_output *audioOutput, audio_output_close(audioOutput); } - if (!audioOutput->open) - ret = audioOutput->plugin->open(audioOutput); + if (audioOutput->thread == 0) + audio_output_thread_start(audioOutput); - return ret; -} - -static void convertAudioFormat(struct audio_output *audioOutput, - const char **chunkArgPtr, size_t *sizeArgPtr) -{ - size_t size = pcm_sizeOfConvBuffer(&(audioOutput->inAudioFormat), - *sizeArgPtr, - &(audioOutput->outAudioFormat)); - - if (size > audioOutput->convBufferLen) { - if (audioOutput->convBuffer != NULL) - free(audioOutput->convBuffer); - audioOutput->convBuffer = xmalloc(size); - audioOutput->convBufferLen = size; + if (!audioOutput->open) { + ao_command(audioOutput, AO_COMMAND_OPEN); + ret = audioOutput->result; } - *sizeArgPtr = pcm_convertAudioFormat(&(audioOutput->inAudioFormat), - *chunkArgPtr, *sizeArgPtr, - &(audioOutput->outAudioFormat), - audioOutput->convBuffer, - &audioOutput->convState); - - *chunkArgPtr = audioOutput->convBuffer; + return ret; } int audio_output_play(struct audio_output *audioOutput, const char *playChunk, size_t size) { - int ret; - if (!audioOutput->open) return -1; - if (!audio_format_equals(&audioOutput->inAudioFormat, - &audioOutput->outAudioFormat)) - convertAudioFormat(audioOutput, &playChunk, &size); - - ret = audioOutput->plugin->play(audioOutput, playChunk, size); + audioOutput->args.play.data = playChunk; + audioOutput->args.play.size = size; + ao_command(audioOutput, AO_COMMAND_PLAY); - return ret; + return audioOutput->result; } void audio_output_cancel(struct audio_output *audioOutput) { - if (audioOutput->open) - audioOutput->plugin->cancel(audioOutput); + ao_command(audioOutput, AO_COMMAND_CANCEL); } void audio_output_close(struct audio_output *audioOutput) { if (audioOutput->open) - audioOutput->plugin->close(audioOutput); + ao_command(audioOutput, AO_COMMAND_CLOSE); } void audio_output_finish(struct audio_output *audioOutput) { audio_output_close(audioOutput); + if (audioOutput->thread != 0) + ao_command(audioOutput, AO_COMMAND_KILL); if (audioOutput->plugin->finish) audioOutput->plugin->finish(audioOutput); if (audioOutput->convBuffer) @@ -117,6 +115,9 @@ void audio_output_finish(struct audio_output *audioOutput) void audio_output_send_tag(struct audio_output *audioOutput, const struct tag *tag) { - if (audioOutput->plugin->send_tag) - audioOutput->plugin->send_tag(audioOutput, tag); + if (audioOutput->plugin->send_tag == NULL) + return; + + audioOutput->args.tag = tag; + ao_command(audioOutput, AO_COMMAND_SEND_TAG); } diff --git a/src/output_init.c b/src/output_init.c index f6d3799c4..f57f39336 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -98,6 +98,10 @@ int audio_output_init(struct audio_output *ao, ConfigParam * param) } else audio_format_clear(&ao->reqAudioFormat); + ao->thread = 0; + notify_init(&ao->notify); + ao->command = AO_COMMAND_NONE; + if (plugin->init(ao, param) != 0) return 0; diff --git a/src/output_thread.c b/src/output_thread.c new file mode 100644 index 000000000..eb86f0688 --- /dev/null +++ b/src/output_thread.c @@ -0,0 +1,121 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: http://www.musicpd.org + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "output_thread.h" +#include "output_api.h" +#include "utils.h" + +static void ao_command_finished(struct audio_output *ao) +{ + assert(ao->command != AO_COMMAND_NONE); + ao->command = AO_COMMAND_NONE; + notify_signal(&audio_output_client_notify); +} + +static void convertAudioFormat(struct audio_output *audioOutput, + const char **chunkArgPtr, size_t *sizeArgPtr) +{ + size_t size = pcm_sizeOfConvBuffer(&(audioOutput->inAudioFormat), + *sizeArgPtr, + &(audioOutput->outAudioFormat)); + + if (size > audioOutput->convBufferLen) { + if (audioOutput->convBuffer != NULL) + free(audioOutput->convBuffer); + audioOutput->convBuffer = xmalloc(size); + audioOutput->convBufferLen = size; + } + + *sizeArgPtr = pcm_convertAudioFormat(&(audioOutput->inAudioFormat), + *chunkArgPtr, *sizeArgPtr, + &(audioOutput->outAudioFormat), + audioOutput->convBuffer, + &audioOutput->convState); + + *chunkArgPtr = audioOutput->convBuffer; +} + +static void ao_play(struct audio_output *ao) +{ + const char *data = ao->args.play.data; + size_t size = ao->args.play.size; + + if (!audio_format_equals(&ao->inAudioFormat, &ao->outAudioFormat)) + convertAudioFormat(ao, &data, &size); + + ao->result = ao->plugin->play(ao, data, size); + ao_command_finished(ao); +} + +static void *audio_output_task(void *arg) +{ + struct audio_output *ao = arg; + + notify_enter(&ao->notify); + + while (1) { + switch (ao->command) { + case AO_COMMAND_NONE: + break; + + case AO_COMMAND_OPEN: + assert(!ao->open); + ao->plugin->open(ao); + ao_command_finished(ao); + break; + + case AO_COMMAND_CLOSE: + assert(ao->open); + ao->plugin->close(ao); + ao_command_finished(ao); + break; + + case AO_COMMAND_PLAY: + ao_play(ao); + break; + + case AO_COMMAND_CANCEL: + ao->plugin->cancel(ao); + ao_command_finished(ao); + break; + + case AO_COMMAND_SEND_TAG: + ao->plugin->send_tag(ao, ao->args.tag); + ao_command_finished(ao); + break; + + case AO_COMMAND_KILL: + ao_command_finished(ao); + return NULL; + } + + notify_wait(&ao->notify); + } +} + +void audio_output_thread_start(struct audio_output *ao) +{ + pthread_attr_t attr; + + assert(ao->command == AO_COMMAND_NONE); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (pthread_create(&ao->thread, &attr, audio_output_task, ao)) + FATAL("Failed to spawn output task: %s\n", strerror(errno)); +} diff --git a/src/output_thread.h b/src/output_thread.h new file mode 100644 index 000000000..2b2f1fb3d --- /dev/null +++ b/src/output_thread.h @@ -0,0 +1,26 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: http://www.musicpd.org + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OUTPUT_THREAD_H +#define OUTPUT_THREAD_H + +struct audio_output; + +void audio_output_thread_start(struct audio_output *ao); + +#endif -- cgit v1.2.3