aboutsummaryrefslogtreecommitdiff
path: root/src/output
diff options
context:
space:
mode:
Diffstat (limited to 'src/output')
-rw-r--r--src/output/AlsaOutputPlugin.cxx (renamed from src/output/alsa_output_plugin.c)210
-rw-r--r--src/output/AlsaOutputPlugin.hxx (renamed from src/output/alsa_output_plugin.h)6
-rw-r--r--src/output/HttpdClient.cxx444
-rw-r--r--src/output/HttpdClient.hxx186
-rw-r--r--src/output/HttpdInternal.hxx (renamed from src/output/httpd_internal.h)122
-rw-r--r--src/output/HttpdOutputPlugin.cxx570
-rw-r--r--src/output/HttpdOutputPlugin.hxx (renamed from src/output/httpd_output_plugin.h)6
-rw-r--r--src/output/NullOutputPlugin.cxx (renamed from src/output/null_output_plugin.c)43
-rw-r--r--src/output/NullOutputPlugin.hxx (renamed from src/output/null_output_plugin.h)6
-rw-r--r--src/output/OSXOutputPlugin.cxx (renamed from src/output/osx_output_plugin.c)89
-rw-r--r--src/output/OSXOutputPlugin.hxx (renamed from src/output/osx_output_plugin.h)6
-rw-r--r--src/output/OssOutputPlugin.cxx (renamed from src/output/oss_output_plugin.c)65
-rw-r--r--src/output/OssOutputPlugin.hxx (renamed from src/output/oss_output_plugin.h)6
-rw-r--r--src/output/RoarOutputPlugin.cxx (renamed from src/output/roar_output_plugin.c)148
-rw-r--r--src/output/RoarOutputPlugin.hxx (renamed from src/output/roar_output_plugin.h)10
-rw-r--r--src/output/fifo_output_plugin.c1
-rw-r--r--src/output/httpd_client.c764
-rw-r--r--src/output/httpd_client.h71
-rw-r--r--src/output/httpd_output_plugin.c623
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.h12
-rw-r--r--src/output/shout_output_plugin.c104
-rw-r--r--src/output/winmm_output_plugin.c1
-rw-r--r--src/output/winmm_output_plugin.h1
24 files changed, 1677 insertions, 1819 deletions
diff --git a/src/output/alsa_output_plugin.c b/src/output/AlsaOutputPlugin.cxx
index d8b18427..1badeb63 100644
--- a/src/output/alsa_output_plugin.c
+++ b/src/output/AlsaOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "alsa_output_plugin.h"
+#include "AlsaOutputPlugin.hxx"
#include "output_api.h"
#include "mixer_list.h"
#include "pcm_export.h"
@@ -26,6 +26,8 @@
#include <glib.h>
#include <alsa/asoundlib.h>
+#include <string>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "alsa"
@@ -43,14 +45,16 @@ enum {
typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
snd_pcm_uframes_t size);
-struct alsa_data {
+struct AlsaOutput {
struct audio_output base;
- struct pcm_export_state export;
+ struct pcm_export_state pcm_export;
- /** the configured name of the ALSA device; NULL for the
- default device */
- char *device;
+ /**
+ * The configured name of the ALSA device; empty for the
+ * default device
+ */
+ std::string device;
/** use memory mapped I/O? */
bool use_mmap;
@@ -101,6 +105,25 @@ struct alsa_data {
* The number of frames written in the current period.
*/
snd_pcm_uframes_t period_position;
+
+ /**
+ * This buffer gets allocated after opening the ALSA device.
+ * It contains silence samples, enough to fill one period (see
+ * #period_frames).
+ */
+ void *silence;
+
+ AlsaOutput():mode(0), writei(snd_pcm_writei) {
+ }
+
+ bool Init(const config_param *param, GError **error_r) {
+ return ao_base_init(&base, &alsa_output_plugin,
+ param, error_r);
+ }
+
+ void Deinit() {
+ ao_base_finish(&base);
+ }
};
/**
@@ -113,26 +136,15 @@ alsa_output_quark(void)
}
static const char *
-alsa_device(const struct alsa_data *ad)
+alsa_device(const AlsaOutput *ad)
{
- return ad->device != NULL ? ad->device : default_device;
-}
-
-static struct alsa_data *
-alsa_data_new(void)
-{
- struct alsa_data *ret = g_new(struct alsa_data, 1);
-
- ret->mode = 0;
- ret->writei = snd_pcm_writei;
-
- return ret;
+ return ad->device.empty() ? default_device : ad->device.c_str();
}
static void
-alsa_configure(struct alsa_data *ad, const struct config_param *param)
+alsa_configure(AlsaOutput *ad, const struct config_param *param)
{
- ad->device = config_dup_block_string(param, "device", NULL);
+ ad->device = config_get_block_string(param, "device", "");
ad->use_mmap = config_get_block_bool(param, "use_mmap", false);
@@ -161,10 +173,10 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param)
static struct audio_output *
alsa_init(const struct config_param *param, GError **error_r)
{
- struct alsa_data *ad = alsa_data_new();
+ AlsaOutput *ad = new AlsaOutput();
- if (!ao_base_init(&ad->base, &alsa_output_plugin, param, error_r)) {
- g_free(ad);
+ if (!ad->Init(param, error_r)) {
+ delete ad;
return NULL;
}
@@ -176,12 +188,10 @@ alsa_init(const struct config_param *param, GError **error_r)
static void
alsa_finish(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- ao_base_finish(&ad->base);
-
- g_free(ad->device);
- g_free(ad);
+ ad->Deinit();
+ delete ad;
/* free libasound's config cache */
snd_config_update_free_global();
@@ -190,18 +200,18 @@ alsa_finish(struct audio_output *ao)
static bool
alsa_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- pcm_export_init(&ad->export);
+ pcm_export_init(&ad->pcm_export);
return true;
}
static void
alsa_output_disable(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- pcm_export_deinit(&ad->export);
+ pcm_export_deinit(&ad->pcm_export);
}
static bool
@@ -349,7 +359,8 @@ alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
{
/* try the input format first */
- int err = alsa_output_try_format(pcm, hwparams, audio_format->format,
+ int err = alsa_output_try_format(pcm, hwparams,
+ sample_format(audio_format->format),
packed_r, reverse_endian_r);
/* if unsupported by the hardware, try other formats */
@@ -383,15 +394,11 @@ alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
* the configured settings and the audio format.
*/
static bool
-alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup(AlsaOutput *ad, struct audio_format *audio_format,
bool *packed_r, bool *reverse_endian_r, GError **error)
{
- snd_pcm_hw_params_t *hwparams;
- snd_pcm_sw_params_t *swparams;
unsigned int sample_rate = audio_format->sample_rate;
unsigned int channels = audio_format->channels;
- snd_pcm_uframes_t alsa_buffer_size;
- snd_pcm_uframes_t alsa_period_size;
int err;
const char *cmd = NULL;
int retry = MPD_ALSA_RETRY_NR;
@@ -401,6 +408,7 @@ alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
period_time_ro = period_time = ad->period_time;
configure_hw:
/* configure HW params */
+ snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
cmd = "snd_pcm_hw_params_any";
err = snd_pcm_hw_params_any(ad->pcm, hwparams);
@@ -434,7 +442,7 @@ configure_hw:
g_set_error(error, alsa_output_quark(), err,
"ALSA device \"%s\" does not support format %s: %s",
alsa_device(ad),
- sample_format_to_string(audio_format->format),
+ sample_format_to_string(sample_format(audio_format->format)),
snd_strerror(-err));
return false;
}
@@ -525,11 +533,13 @@ configure_hw:
if (retry != MPD_ALSA_RETRY_NR)
g_debug("ALSA period_time set to %d\n", period_time);
+ snd_pcm_uframes_t alsa_buffer_size;
cmd = "snd_pcm_hw_params_get_buffer_size";
err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
if (err < 0)
goto error;
+ snd_pcm_uframes_t alsa_period_size;
cmd = "snd_pcm_hw_params_get_period_size";
err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
NULL);
@@ -537,6 +547,7 @@ configure_hw:
goto error;
/* configure SW params */
+ snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
cmd = "snd_pcm_sw_params_current";
@@ -576,6 +587,11 @@ configure_hw:
ad->period_frames = alsa_period_size;
ad->period_position = 0;
+ ad->silence = g_malloc(snd_pcm_frames_to_bytes(ad->pcm,
+ alsa_period_size));
+ snd_pcm_format_set_silence(format, ad->silence,
+ alsa_period_size * channels);
+
return true;
error:
@@ -586,7 +602,7 @@ error:
}
static bool
-alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup_dsd(AlsaOutput *ad, struct audio_format *audio_format,
bool *shift8_r, bool *packed_r, bool *reverse_endian_r,
GError **error_r)
{
@@ -619,6 +635,7 @@ alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
g_set_error(error_r, alsa_output_quark(), 0,
"Failed to configure DSD-over-USB on ALSA device \"%s\"",
alsa_device(ad));
+ g_free(ad->silence);
return false;
}
@@ -626,7 +643,7 @@ alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
}
static bool
-alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup_or_dsd(AlsaOutput *ad, struct audio_format *audio_format,
GError **error_r)
{
bool shift8 = false, packed, reverse_endian;
@@ -642,8 +659,9 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
if (!success)
return false;
- pcm_export_open(&ad->export,
- audio_format->format, audio_format->channels,
+ pcm_export_open(&ad->pcm_export,
+ sample_format(audio_format->format),
+ audio_format->channels,
dsd_usb, shift8, packed, reverse_endian);
return true;
}
@@ -651,12 +669,10 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
static bool
alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
- int err;
- bool success;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- err = snd_pcm_open(&ad->pcm, alsa_device(ad),
- SND_PCM_STREAM_PLAYBACK, ad->mode);
+ int err = snd_pcm_open(&ad->pcm, alsa_device(ad),
+ SND_PCM_STREAM_PLAYBACK, ad->mode);
if (err < 0) {
g_set_error(error, alsa_output_quark(), err,
"Failed to open ALSA device \"%s\": %s",
@@ -667,20 +683,29 @@ alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **e
g_debug("opened %s type=%s", snd_pcm_name(ad->pcm),
snd_pcm_type_name(snd_pcm_type(ad->pcm)));
- success = alsa_setup_or_dsd(ad, audio_format, error);
- if (!success) {
+ if (!alsa_setup_or_dsd(ad, audio_format, error)) {
snd_pcm_close(ad->pcm);
return false;
}
ad->in_frame_size = audio_format_frame_size(audio_format);
- ad->out_frame_size = pcm_export_frame_size(&ad->export, audio_format);
+ ad->out_frame_size = pcm_export_frame_size(&ad->pcm_export,
+ audio_format);
return true;
}
+/**
+ * Write silence to the ALSA device.
+ */
+static void
+alsa_write_silence(AlsaOutput *ad, snd_pcm_uframes_t nframes)
+{
+ ad->writei(ad->pcm, ad->silence, nframes);
+}
+
static int
-alsa_recover(struct alsa_data *ad, int err)
+alsa_recover(AlsaOutput *ad, int err)
{
if (err == -EPIPE) {
g_debug("Underrun on ALSA device \"%s\"\n", alsa_device(ad));
@@ -701,6 +726,26 @@ alsa_recover(struct alsa_data *ad, int err)
case SND_PCM_STATE_XRUN:
ad->period_position = 0;
err = snd_pcm_prepare(ad->pcm);
+
+ if (err == 0) {
+ /* this works around a driver bug observed on
+ the Raspberry Pi: after snd_pcm_drop(), the
+ whole ring buffer must be invalidated, but
+ the snd_pcm_prepare() call above makes the
+ driver play random data that just happens
+ to be still in the buffer; by adding and
+ cancelling some silence, this bug does not
+ occur */
+ alsa_write_silence(ad, ad->period_frames);
+
+ /* cancel the silence data right away to avoid
+ increasing latency; even though this
+ function call invalidates the portion of
+ silence, the driver seems to avoid the
+ bug */
+ snd_pcm_reset(ad->pcm);
+ }
+
break;
case SND_PCM_STATE_DISCONNECTED:
break;
@@ -719,7 +764,7 @@ alsa_recover(struct alsa_data *ad, int err)
static void
alsa_drain(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
return;
@@ -729,20 +774,7 @@ alsa_drain(struct audio_output *ao)
period */
snd_pcm_uframes_t nframes =
ad->period_frames - ad->period_position;
- size_t nbytes = nframes * ad->out_frame_size;
- void *buffer = g_malloc(nbytes);
- snd_pcm_hw_params_t *params;
- snd_pcm_format_t format;
- unsigned channels;
-
- snd_pcm_hw_params_alloca(&params);
- snd_pcm_hw_params_current(ad->pcm, params);
- snd_pcm_hw_params_get_format(params, &format);
- snd_pcm_hw_params_get_channels(params, &channels);
-
- snd_pcm_format_set_silence(format, buffer, nframes * channels);
- ad->writei(ad->pcm, buffer, nframes);
- g_free(buffer);
+ alsa_write_silence(ad, nframes);
}
snd_pcm_drain(ad->pcm);
@@ -753,7 +785,7 @@ alsa_drain(struct audio_output *ao)
static void
alsa_cancel(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
ad->period_position = 0;
@@ -763,20 +795,21 @@ alsa_cancel(struct audio_output *ao)
static void
alsa_close(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
snd_pcm_close(ad->pcm);
+ g_free(ad->silence);
}
static size_t
alsa_play(struct audio_output *ao, const void *chunk, size_t size,
GError **error)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
assert(size % ad->in_frame_size == 0);
- chunk = pcm_export(&ad->export, chunk, size, &size);
+ chunk = pcm_export(&ad->pcm_export, chunk, size, &size);
assert(size % ad->out_frame_size == 0);
@@ -789,7 +822,7 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
% ad->period_frames;
size_t bytes_written = ret * ad->out_frame_size;
- return pcm_export_source_size(&ad->export,
+ return pcm_export_source_size(&ad->pcm_export,
bytes_written);
}
@@ -803,17 +836,20 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
}
const struct audio_output_plugin alsa_output_plugin = {
- .name = "alsa",
- .test_default_device = alsa_test_default_device,
- .init = alsa_init,
- .finish = alsa_finish,
- .enable = alsa_output_enable,
- .disable = alsa_output_disable,
- .open = alsa_open,
- .play = alsa_play,
- .drain = alsa_drain,
- .cancel = alsa_cancel,
- .close = alsa_close,
-
- .mixer_plugin = &alsa_mixer_plugin,
+ "alsa",
+ alsa_test_default_device,
+ alsa_init,
+ alsa_finish,
+ alsa_output_enable,
+ alsa_output_disable,
+ alsa_open,
+ alsa_close,
+ nullptr,
+ nullptr,
+ alsa_play,
+ alsa_drain,
+ alsa_cancel,
+ nullptr,
+
+ &alsa_mixer_plugin,
};
diff --git a/src/output/alsa_output_plugin.h b/src/output/AlsaOutputPlugin.hxx
index daa1f361..dc7e639a 100644
--- a/src/output/alsa_output_plugin.h
+++ b/src/output/AlsaOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ALSA_OUTPUT_PLUGIN_H
-#define MPD_ALSA_OUTPUT_PLUGIN_H
+#ifndef MPD_ALSA_OUTPUT_PLUGIN_HXX
+#define MPD_ALSA_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin alsa_output_plugin;
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx
new file mode 100644
index 00000000..0a00ee2f
--- /dev/null
+++ b/src/output/HttpdClient.cxx
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "HttpdClient.hxx"
+#include "HttpdInternal.hxx"
+#include "util/fifo_buffer.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "SocketError.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+HttpdClient::~HttpdClient()
+{
+ if (state == RESPONSE) {
+ if (current_page != nullptr)
+ current_page->Unref();
+
+ for (auto page : pages)
+ page->Unref();
+ }
+
+ if (metadata)
+ metadata->Unref();
+}
+
+void
+HttpdClient::Close()
+{
+ httpd->RemoveClient(*this);
+}
+
+void
+HttpdClient::LockClose()
+{
+ const ScopeLock protect(httpd->mutex);
+ Close();
+}
+
+void
+HttpdClient::BeginResponse()
+{
+ assert(state != RESPONSE);
+
+ state = RESPONSE;
+ current_page = nullptr;
+
+ httpd->SendHeader(*this);
+}
+
+/**
+ * Handle a line of the HTTP request.
+ */
+bool
+HttpdClient::HandleLine(const char *line)
+{
+ assert(state != RESPONSE);
+
+ if (state == REQUEST) {
+ if (strncmp(line, "GET /", 5) != 0) {
+ /* only GET is supported */
+ g_warning("malformed request line from client");
+ return false;
+ }
+
+ line = strchr(line + 5, ' ');
+ if (line == nullptr || strncmp(line + 1, "HTTP/", 5) != 0) {
+ /* HTTP/0.9 without request headers */
+ BeginResponse();
+ return true;
+ }
+
+ /* after the request line, request headers follow */
+ state = HEADERS;
+ return true;
+ } else {
+ if (*line == 0) {
+ /* empty line: request is finished */
+ BeginResponse();
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
+ /* Send icy metadata */
+ metadata_requested = metadata_supported;
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
+ /* Send as dlna */
+ dlna_streaming_requested = true;
+ /* metadata is not supported by dlna streaming, so disable it */
+ metadata_supported = false;
+ metadata_requested = false;
+ return true;
+ }
+
+ /* expect more request headers */
+ return true;
+ }
+}
+
+/**
+ * Sends the status line and response headers to the client.
+ */
+bool
+HttpdClient::SendResponse()
+{
+ char buffer[1024];
+ assert(state == RESPONSE);
+
+ if (dlna_streaming_requested) {
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 206 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: 10000\r\n"
+ "Content-RangeX: 0-1000000/1000000\r\n"
+ "transferMode.dlna.org: Streaming\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Connection: close\r\n"
+ "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
+ "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
+ "\r\n",
+ httpd->content_type);
+
+ } else if (metadata_requested) {
+ gchar *metadata_header;
+
+ metadata_header =
+ icy_server_metadata_header(httpd->name, httpd->genre,
+ httpd->website,
+ httpd->content_type,
+ metaint);
+
+ g_strlcpy(buffer, metadata_header, sizeof(buffer));
+
+ g_free(metadata_header);
+
+ } else { /* revert to a normal HTTP request */
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: close\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "\r\n",
+ httpd->content_type);
+ }
+
+ ssize_t nbytes = SocketMonitor::Write(buffer, strlen(buffer));
+ if (gcc_unlikely(nbytes < 0)) {
+ const SocketErrorMessage msg;
+ g_warning("failed to write to client: %s", (const char *)msg);
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+HttpdClient::HttpdClient(HttpdOutput *_httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported)
+ :BufferedSocket(_fd, _loop),
+ httpd(_httpd),
+ state(REQUEST),
+ dlna_streaming_requested(false),
+ metadata_supported(_metadata_supported),
+ metadata_requested(false), metadata_sent(true),
+ metaint(8192), /*TODO: just a std value */
+ metadata(nullptr),
+ metadata_current_position(0), metadata_fill(0)
+{
+}
+
+size_t
+HttpdClient::GetQueueSize() const
+{
+ if (state != RESPONSE)
+ return 0;
+
+ size_t size = 0;
+ for (auto page : pages)
+ size += page->size;
+ return size;
+}
+
+void
+HttpdClient::CancelQueue()
+{
+ if (state != RESPONSE)
+ return;
+
+ for (auto page : pages)
+ page->Unref();
+ pages.clear();
+
+ if (current_page == nullptr)
+ CancelWrite();
+}
+
+ssize_t
+HttpdClient::TryWritePage(const Page &page, size_t position)
+{
+ assert(position < page.size);
+
+ return Write(page.data + position, page.size - position);
+}
+
+ssize_t
+HttpdClient::TryWritePageN(const Page &page, size_t position, ssize_t n)
+{
+ return n >= 0
+ ? Write(page.data + position, n)
+ : TryWritePage(page, position);
+}
+
+ssize_t
+HttpdClient::GetBytesTillMetaData() const
+{
+ if (metadata_requested &&
+ current_page->size - current_position > metaint - metadata_fill)
+ return metaint - metadata_fill;
+
+ return -1;
+}
+
+inline bool
+HttpdClient::TryWrite()
+{
+ const ScopeLock protect(httpd->mutex);
+
+ assert(state == RESPONSE);
+
+ if (current_page == nullptr) {
+ if (pages.empty()) {
+ /* another thread has removed the event source
+ while this thread was waiting for
+ httpd->mutex */
+ CancelWrite();
+ return true;
+ }
+
+ current_page = pages.front();
+ pages.pop_front();
+ current_position = 0;
+ }
+
+ const ssize_t bytes_to_write = GetBytesTillMetaData();
+ if (bytes_to_write == 0) {
+ if (!metadata_sent) {
+ ssize_t nbytes = TryWritePage(*metadata,
+ metadata_current_position);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_current_position += nbytes;
+
+ if (metadata->size - metadata_current_position == 0) {
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ metadata_sent = true;
+ }
+ } else {
+ guchar empty_data = 0;
+
+ ssize_t nbytes = Write(&empty_data, 1);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ }
+ } else {
+ ssize_t nbytes =
+ TryWritePageN(*current_page, current_position,
+ bytes_to_write);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ current_position += nbytes;
+ assert(current_position <= current_page->size);
+
+ if (metadata_requested)
+ metadata_fill += nbytes;
+
+ if (current_position >= current_page->size) {
+ current_page->Unref();
+ current_page = nullptr;
+
+ if (pages.empty())
+ /* all pages are sent: remove the
+ event source */
+ CancelWrite();
+ }
+ }
+
+ return true;
+}
+
+void
+HttpdClient::PushPage(Page *page)
+{
+ if (state != RESPONSE)
+ /* the client is still writing the HTTP request */
+ return;
+
+ page->Ref();
+ pages.push_back(page);
+
+ ScheduleWrite();
+}
+
+void
+HttpdClient::PushMetaData(Page *page)
+{
+ if (metadata) {
+ metadata->Unref();
+ metadata = nullptr;
+ }
+
+ g_return_if_fail (page);
+
+ page->Ref();
+ metadata = page;
+ metadata_sent = false;
+}
+
+bool
+HttpdClient::OnSocketReady(unsigned flags)
+{
+ if (!BufferedSocket::OnSocketReady(flags))
+ return false;
+
+ if (flags & WRITE)
+ if (!TryWrite())
+ return false;
+
+ return true;
+}
+
+BufferedSocket::InputResult
+HttpdClient::OnSocketInput(const void *data, size_t length)
+{
+ if (state == RESPONSE) {
+ g_warning("unexpected input from client");
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ const char *line = (const char *)data;
+ const char *newline = (const char *)memchr(line, '\n', length);
+ if (newline == nullptr)
+ return InputResult::MORE;
+
+ ConsumeInput(newline + 1 - line);
+
+ if (newline > line && newline[-1] == '\r')
+ --newline;
+
+ /* terminate the string at the end of the line; the const_cast
+ is a dirty hack */
+ *const_cast<char *>(newline) = 0;
+
+ if (!HandleLine(line)) {
+ assert(state == RESPONSE);
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ if (state == RESPONSE && !SendResponse())
+ return InputResult::CLOSED;
+
+ return InputResult::AGAIN;
+}
+
+void
+HttpdClient::OnSocketError(GError *error)
+{
+ g_warning("error on HTTP client: %s", error->message);
+ g_error_free(error);
+}
+
+void
+HttpdClient::OnSocketClosed()
+{
+ LockClose();
+}
diff --git a/src/output/HttpdClient.hxx b/src/output/HttpdClient.hxx
new file mode 100644
index 00000000..46196d2e
--- /dev/null
+++ b/src/output/HttpdClient.hxx
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OUTPUT_HTTPD_CLIENT_HXX
+#define MPD_OUTPUT_HTTPD_CLIENT_HXX
+
+#include "event/BufferedSocket.hxx"
+#include "gcc.h"
+
+#include <list>
+
+#include <stddef.h>
+
+struct HttpdOutput;
+class Page;
+
+class HttpdClient final : public BufferedSocket {
+ /**
+ * The httpd output object this client is connected to.
+ */
+ HttpdOutput *const httpd;
+
+ /**
+ * The current state of the client.
+ */
+ enum {
+ /** reading the request line */
+ REQUEST,
+
+ /** reading the request headers */
+ HEADERS,
+
+ /** sending the HTTP response */
+ RESPONSE,
+ } state;
+
+ /**
+ * A queue of #Page objects to be sent to the client.
+ */
+ std::list<Page *> pages;
+
+ /**
+ * The #page which is currently being sent to the client.
+ */
+ Page *current_page;
+
+ /**
+ * The amount of bytes which were already sent from
+ * #current_page.
+ */
+ size_t current_position;
+
+ /**
+ * If DLNA streaming was an option.
+ */
+ bool dlna_streaming_requested;
+
+ /* ICY */
+
+ /**
+ * Do we support sending Icy-Metadata to the client? This is
+ * disabled if the httpd audio output uses encoder tags.
+ */
+ bool metadata_supported;
+
+ /**
+ * If we should sent icy metadata.
+ */
+ bool metadata_requested;
+
+ /**
+ * If the current metadata was already sent to the client.
+ */
+ bool metadata_sent;
+
+ /**
+ * The amount of streaming data between each metadata block
+ */
+ guint metaint;
+
+ /**
+ * The metadata as #Page which is currently being sent to the client.
+ */
+ Page *metadata;
+
+ /*
+ * The amount of bytes which were already sent from the metadata.
+ */
+ size_t metadata_current_position;
+
+ /**
+ * The amount of streaming data sent to the client
+ * since the last icy information was sent.
+ */
+ guint metadata_fill;
+
+public:
+ /**
+ * @param httpd the HTTP output device
+ * @param fd the socket file descriptor
+ */
+ HttpdClient(HttpdOutput *httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported);
+
+ /**
+ * Note: this does not remove the client from the
+ * #HttpdOutput object.
+ */
+ ~HttpdClient();
+
+ /**
+ * Frees the client and removes it from the server's client list.
+ */
+ void Close();
+
+ void LockClose();
+
+ /**
+ * Returns the total size of this client's page queue.
+ */
+ gcc_pure
+ size_t GetQueueSize() const;
+
+ /**
+ * Clears the page queue.
+ */
+ void CancelQueue();
+
+ /**
+ * Handle a line of the HTTP request.
+ */
+ bool HandleLine(const char *line);
+
+ /**
+ * Switch the client to the "RESPONSE" state.
+ */
+ void BeginResponse();
+
+ /**
+ * Sends the status line and response headers to the client.
+ */
+ bool SendResponse();
+
+ gcc_pure
+ ssize_t GetBytesTillMetaData() const;
+
+ ssize_t TryWritePage(const Page &page, size_t position);
+ ssize_t TryWritePageN(const Page &page, size_t position, ssize_t n);
+
+ bool TryWrite();
+
+ /**
+ * Appends a page to the client's queue.
+ */
+ void PushPage(Page *page);
+
+ /**
+ * Sends the passed metadata.
+ */
+ void PushMetaData(Page *page);
+
+protected:
+ virtual bool OnSocketReady(unsigned flags) override;
+ virtual InputResult OnSocketInput(const void *data,
+ size_t length) override;
+ virtual void OnSocketError(GError *error) override;
+ virtual void OnSocketClosed() override;
+};
+
+#endif
diff --git a/src/output/httpd_internal.h b/src/output/HttpdInternal.hxx
index 5dcb8ab9..4b526bcd 100644
--- a/src/output/httpd_internal.h
+++ b/src/output/HttpdInternal.hxx
@@ -27,14 +27,18 @@
#include "output_internal.h"
#include "timer.h"
+#include "thread/Mutex.hxx"
+#include "event/ServerSocket.hxx"
-#include <glib.h>
+#include <forward_list>
-#include <stdbool.h>
+struct config_param;
+class EventLoop;
+class ServerSocket;
+class HttpdClient;
+class Page;
-struct httpd_client;
-
-struct httpd_output {
+struct HttpdOutput final : private ServerSocket {
struct audio_output base;
/**
@@ -65,7 +69,7 @@ struct httpd_output {
* This mutex protects the listener socket and the client
* list.
*/
- GMutex *mutex;
+ mutable Mutex mutex;
/**
* A #timer object to synchronize this output with the
@@ -74,19 +78,14 @@ struct httpd_output {
struct timer *timer;
/**
- * The listener socket.
- */
- struct server_socket *server_socket;
-
- /**
* The header page, which is sent to every client on connect.
*/
- struct page *header;
+ Page *header;
/**
* The metadata, which is sent to every client.
*/
- struct page *metadata;
+ Page *metadata;
/**
* The configured name.
@@ -105,7 +104,7 @@ struct httpd_output {
* A linked list containing all clients which are currently
* connected.
*/
- GList *clients;
+ std::forward_list<HttpdClient> clients;
/**
* A temporary buffer for the httpd_output_read_page()
@@ -118,21 +117,88 @@ struct httpd_output {
* at the same time.
*/
guint clients_max, clients_cnt;
-};
-/**
- * Removes a client from the httpd_output.clients linked list.
- */
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client);
+ HttpdOutput(EventLoop &_loop);
+ ~HttpdOutput();
-/**
- * Sends the encoder header to the client. This is called right after
- * the response headers have been sent.
- */
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client);
+ bool Configure(const config_param *param, GError **error_r);
+
+ bool Bind(GError **error_r);
+ void Unbind();
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool OpenEncoder(struct audio_format *audio_format,
+ GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool Open(struct audio_format *audio_format, GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ void Close();
+
+ /**
+ * Check whether there is at least one client.
+ *
+ * Caller must lock the mutex.
+ */
+ gcc_pure
+ bool HasClients() const {
+ return !clients.empty();
+ }
+
+ /**
+ * Check whether there is at least one client.
+ */
+ gcc_pure
+ bool LockHasClients() const {
+ const ScopeLock protect(mutex);
+ return HasClients();
+ }
+
+ void AddClient(int fd);
+
+ /**
+ * Removes a client from the httpd_output.clients linked list.
+ */
+ void RemoveClient(HttpdClient &client);
+
+ /**
+ * Sends the encoder header to the client. This is called
+ * right after the response headers have been sent.
+ */
+ void SendHeader(HttpdClient &client) const;
+
+ /**
+ * Reads data from the encoder (as much as available) and
+ * returns it as a new #page object.
+ */
+ Page *ReadPage();
+
+ /**
+ * Broadcasts a page struct to all clients.
+ *
+ * Mutext must not be locked.
+ */
+ void BroadcastPage(Page *page);
+
+ /**
+ * Broadcasts data from the encoder to all clients.
+ */
+ void BroadcastFromEncoder();
+
+ bool EncodeAndPlay(const void *chunk, size_t size, GError **error_r);
+
+ void SendTag(const struct tag *tag);
+
+private:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) override;
+};
#endif
diff --git a/src/output/HttpdOutputPlugin.cxx b/src/output/HttpdOutputPlugin.cxx
new file mode 100644
index 00000000..cb515e65
--- /dev/null
+++ b/src/output/HttpdOutputPlugin.cxx
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "HttpdOutputPlugin.hxx"
+#include "HttpdInternal.hxx"
+#include "HttpdClient.hxx"
+#include "output_api.h"
+#include "encoder_plugin.h"
+#include "encoder_list.h"
+#include "resolver.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "fd_util.h"
+#include "Main.hxx"
+
+#include <assert.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBWRAP
+#include <sys/socket.h> /* needed for AF_UNIX */
+#include <tcpd.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+httpd_output_quark(void)
+{
+ return g_quark_from_static_string("httpd_output");
+}
+
+inline
+HttpdOutput::HttpdOutput(EventLoop &_loop)
+ :ServerSocket(_loop),
+ encoder(nullptr), unflushed_input(0),
+ metadata(nullptr)
+{
+}
+
+HttpdOutput::~HttpdOutput()
+{
+ if (metadata != nullptr)
+ metadata->Unref();
+
+ if (encoder != nullptr)
+ encoder_finish(encoder);
+
+}
+
+inline bool
+HttpdOutput::Bind(GError **error_r)
+{
+ open = false;
+
+ const ScopeLock protect(mutex);
+ return ServerSocket::Open(error_r);
+}
+
+inline void
+HttpdOutput::Unbind()
+{
+ assert(!open);
+
+ const ScopeLock protect(mutex);
+ ServerSocket::Close();
+}
+
+inline bool
+HttpdOutput::Configure(const config_param *param, GError **error_r)
+{
+ /* read configuration */
+ name = config_get_block_string(param, "name", "Set name in config");
+ genre = config_get_block_string(param, "genre", "Set genre in config");
+ website = config_get_block_string(param, "website",
+ "Set website in config");
+
+ guint port = config_get_block_unsigned(param, "port", 8000);
+
+ const char *encoder_name =
+ config_get_block_string(param, "encoder", "vorbis");
+ const struct encoder_plugin *encoder_plugin =
+ encoder_plugin_get(encoder_name);
+ if (encoder_plugin == NULL) {
+ g_set_error(error_r, httpd_output_quark(), 0,
+ "No such encoder: %s", encoder_name);
+ return false;
+ }
+
+ clients_max = config_get_block_unsigned(param,"max_clients", 0);
+
+ /* set up bind_to_address */
+
+ const char *bind_to_address =
+ config_get_block_string(param, "bind_to_address", NULL);
+ bool success = bind_to_address != NULL &&
+ strcmp(bind_to_address, "any") != 0
+ ? AddHost(bind_to_address, port, error_r)
+ : AddPort(port, error_r);
+ if (!success)
+ return false;
+
+ /* initialize encoder */
+
+ encoder = encoder_init(encoder_plugin, param, error_r);
+ if (encoder == nullptr)
+ return false;
+
+ /* determine content type */
+ content_type = encoder_get_mime_type(encoder);
+ if (content_type == nullptr)
+ content_type = "application/octet-stream";
+
+ return true;
+}
+
+static struct audio_output *
+httpd_output_init(const struct config_param *param,
+ GError **error_r)
+{
+ HttpdOutput *httpd = new HttpdOutput(*main_loop);
+
+ if (!ao_base_init(&httpd->base, &httpd_output_plugin, param,
+ error_r)) {
+ delete httpd;
+ return nullptr;
+ }
+
+ if (!httpd->Configure(param, error_r)) {
+ ao_base_finish(&httpd->base);
+ delete httpd;
+ return nullptr;
+ }
+
+ return &httpd->base;
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
+
+static inline constexpr HttpdOutput *
+Cast(audio_output *ao)
+{
+ return (HttpdOutput *)((char *)ao - offsetof(HttpdOutput, base));
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+static void
+httpd_output_finish(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ ao_base_finish(&httpd->base);
+ delete httpd;
+}
+
+/**
+ * Creates a new #HttpdClient object and adds it into the
+ * HttpdOutput.clients linked list.
+ */
+inline void
+HttpdOutput::AddClient(int fd)
+{
+ clients.emplace_front(this, fd, GetEventLoop(),
+ encoder->plugin->tag == NULL);
+ ++clients_cnt;
+
+ /* pass metadata to client */
+ if (metadata != nullptr)
+ clients.front().PushMetaData(metadata);
+}
+
+void
+HttpdOutput::OnAccept(int fd, const sockaddr &address,
+ size_t address_length, gcc_unused int uid)
+{
+ /* the listener socket has become readable - a client has
+ connected */
+
+#ifdef HAVE_LIBWRAP
+ if (address.sa_family != AF_UNIX) {
+ char *hostaddr = sockaddr_to_string(&address, address_length, NULL);
+ const char *progname = g_get_prgname();
+
+ struct request_info req;
+ request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
+
+ fromhost(&req);
+
+ if (!hosts_access(&req)) {
+ /* tcp wrappers says no */
+ g_warning("libwrap refused connection (libwrap=%s) from %s",
+ progname, hostaddr);
+ g_free(hostaddr);
+ close_socket(fd);
+ return;
+ }
+
+ g_free(hostaddr);
+ }
+#else
+ (void)address;
+ (void)address_length;
+#endif /* HAVE_WRAP */
+
+ const ScopeLock protect(mutex);
+
+ if (fd >= 0) {
+ /* can we allow additional client */
+ if (open && (clients_max == 0 || clients_cnt < clients_max))
+ AddClient(fd);
+ else
+ close_socket(fd);
+ } else if (fd < 0 && errno != EINTR) {
+ g_warning("accept() failed: %s", g_strerror(errno));
+ }
+}
+
+Page *
+HttpdOutput::ReadPage()
+{
+ if (unflushed_input >= 65536) {
+ /* we have fed a lot of input into the encoder, but it
+ didn't give anything back yet - flush now to avoid
+ buffer underruns */
+ encoder_flush(encoder, NULL);
+ unflushed_input = 0;
+ }
+
+ size_t size = 0;
+ do {
+ size_t nbytes = encoder_read(encoder,
+ buffer + size,
+ sizeof(buffer) - size);
+ if (nbytes == 0)
+ break;
+
+ unflushed_input = 0;
+
+ size += nbytes;
+ } while (size < sizeof(buffer));
+
+ if (size == 0)
+ return NULL;
+
+ return Page::Copy(buffer, size);
+}
+
+static bool
+httpd_output_enable(struct audio_output *ao, GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ return httpd->Bind(error_r);
+}
+
+static void
+httpd_output_disable(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->Unbind();
+}
+
+inline bool
+HttpdOutput::OpenEncoder(struct audio_format *audio_format, GError **error)
+{
+ if (!encoder_open(encoder, audio_format, error))
+ return false;
+
+ /* we have to remember the encoder header, i.e. the first
+ bytes of encoder output after opening it, because it has to
+ be sent to every new client */
+ header = ReadPage();
+
+ unflushed_input = 0;
+
+ return true;
+}
+
+inline bool
+HttpdOutput::Open(struct audio_format *audio_format, GError **error_r)
+{
+ assert(!open);
+ assert(clients.empty());
+
+ /* open the encoder */
+
+ if (!OpenEncoder(audio_format, error_r))
+ return false;
+
+ /* initialize other attributes */
+
+ clients_cnt = 0;
+ timer = timer_new(audio_format);
+
+ open = true;
+
+ return true;
+}
+
+static bool
+httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
+ GError **error)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ assert(httpd->clients.empty());
+
+ const ScopeLock protect(httpd->mutex);
+ return httpd->Open(audio_format, error);
+}
+
+inline void
+HttpdOutput::Close()
+{
+ assert(open);
+
+ open = false;
+
+ timer_free(timer);
+
+ clients.clear();
+
+ if (header != NULL)
+ header->Unref();
+
+ encoder_close(encoder);
+}
+
+static void
+httpd_output_close(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ httpd->Close();
+}
+
+void
+HttpdOutput::RemoveClient(HttpdClient &client)
+{
+ assert(clients_cnt > 0);
+
+ for (auto prev = clients.before_begin(), i = std::next(prev);;
+ prev = i, i = std::next(prev)) {
+ assert(i != clients.end());
+ if (&*i == &client) {
+ clients.erase_after(prev);
+ clients_cnt--;
+ break;
+ }
+ }
+}
+
+void
+HttpdOutput::SendHeader(HttpdClient &client) const
+{
+ if (header != NULL)
+ client.PushPage(header);
+}
+
+static unsigned
+httpd_output_delay(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (!httpd->LockHasClients() && httpd->base.pause) {
+ /* if there's no client and this output is paused,
+ then httpd_output_pause() will not do anything, it
+ will not fill the buffer and it will not update the
+ timer; therefore, we reset the timer here */
+ timer_reset(httpd->timer);
+
+ /* some arbitrary delay that is long enough to avoid
+ consuming too much CPU, and short enough to notice
+ new clients quickly enough */
+ return 1000;
+ }
+
+ return httpd->timer->started
+ ? timer_delay(httpd->timer)
+ : 0;
+}
+
+void
+HttpdOutput::BroadcastPage(Page *page)
+{
+ assert(page != NULL);
+
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushPage(page);
+}
+
+void
+HttpdOutput::BroadcastFromEncoder()
+{
+ mutex.lock();
+ for (auto &client : clients) {
+ if (client.GetQueueSize() > 256 * 1024) {
+ g_debug("client is too slow, flushing its queue");
+ client.CancelQueue();
+ }
+ }
+ mutex.unlock();
+
+ Page *page;
+ while ((page = ReadPage()) != nullptr) {
+ BroadcastPage(page);
+ page->Unref();
+ }
+}
+
+inline bool
+HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, GError **error_r)
+{
+ if (!encoder_write(encoder, chunk, size, error_r))
+ return false;
+
+ unflushed_input += size;
+
+ BroadcastFromEncoder();
+ return true;
+}
+
+static size_t
+httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
+ GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ if (!httpd->EncodeAndPlay(chunk, size, error_r))
+ return 0;
+ }
+
+ if (!httpd->timer->started)
+ timer_start(httpd->timer);
+ timer_add(httpd->timer, size);
+
+ return size;
+}
+
+static bool
+httpd_output_pause(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ static const char silence[1020] = { 0 };
+ return httpd_output_play(ao, silence, sizeof(silence),
+ NULL) > 0;
+ } else {
+ return true;
+ }
+}
+
+inline void
+HttpdOutput::SendTag(const struct tag *tag)
+{
+ assert(tag != NULL);
+
+ if (encoder->plugin->tag != NULL) {
+ /* embed encoder tags */
+
+ /* flush the current stream, and end it */
+
+ encoder_pre_tag(encoder, NULL);
+ BroadcastFromEncoder();
+
+ /* send the tag to the encoder - which starts a new
+ stream now */
+
+ encoder_tag(encoder, tag, NULL);
+
+ /* the first page generated by the encoder will now be
+ used as the new "header" page, which is sent to all
+ new clients */
+
+ Page *page = ReadPage();
+ if (page != NULL) {
+ if (header != NULL)
+ header->Unref();
+ header = page;
+ BroadcastPage(page);
+ }
+ } else {
+ /* use Icy-Metadata */
+
+ if (metadata != NULL)
+ metadata->Unref();
+
+ static constexpr tag_type types[] = {
+ TAG_ALBUM, TAG_ARTIST, TAG_TITLE,
+ TAG_NUM_OF_ITEM_TYPES
+ };
+
+ metadata = icy_server_metadata_page(tag, &types[0]);
+ if (metadata != NULL) {
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushMetaData(metadata);
+ }
+ }
+}
+
+static void
+httpd_output_tag(struct audio_output *ao, const struct tag *tag)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->SendTag(tag);
+}
+
+static void
+httpd_output_cancel(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.CancelQueue();
+}
+
+const struct audio_output_plugin httpd_output_plugin = {
+ "httpd",
+ nullptr,
+ httpd_output_init,
+ httpd_output_finish,
+ httpd_output_enable,
+ httpd_output_disable,
+ httpd_output_open,
+ httpd_output_close,
+ httpd_output_delay,
+ httpd_output_tag,
+ httpd_output_play,
+ nullptr,
+ httpd_output_cancel,
+ httpd_output_pause,
+ nullptr,
+};
diff --git a/src/output/httpd_output_plugin.h b/src/output/HttpdOutputPlugin.hxx
index d0eb1533..c74d2bd4 100644
--- a/src/output/httpd_output_plugin.h
+++ b/src/output/HttpdOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_HTTPD_OUTPUT_PLUGIN_H
-#define MPD_HTTPD_OUTPUT_PLUGIN_H
+#ifndef MPD_HTTPD_OUTPUT_PLUGIN_HXX
+#define MPD_HTTPD_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin httpd_output_plugin;
diff --git a/src/output/null_output_plugin.c b/src/output/NullOutputPlugin.cxx
index 9d7588ff..3596cbdc 100644
--- a/src/output/null_output_plugin.c
+++ b/src/output/NullOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "null_output_plugin.h"
+#include "NullOutputPlugin.hxx"
#include "output_api.h"
#include "timer.h"
@@ -26,7 +26,7 @@
#include <assert.h>
-struct null_data {
+struct NullOutput {
struct audio_output base;
bool sync;
@@ -37,7 +37,7 @@ struct null_data {
static struct audio_output *
null_init(const struct config_param *param, GError **error_r)
{
- struct null_data *nd = g_new(struct null_data, 1);
+ NullOutput *nd = g_new(NullOutput, 1);
if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) {
g_free(nd);
@@ -52,7 +52,7 @@ null_init(const struct config_param *param, GError **error_r)
static void
null_finish(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
ao_base_finish(&nd->base);
g_free(nd);
@@ -62,7 +62,7 @@ static bool
null_open(struct audio_output *ao, struct audio_format *audio_format,
G_GNUC_UNUSED GError **error)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (nd->sync)
nd->timer = timer_new(audio_format);
@@ -73,7 +73,7 @@ null_open(struct audio_output *ao, struct audio_format *audio_format,
static void
null_close(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (nd->sync)
timer_free(nd->timer);
@@ -82,7 +82,7 @@ null_close(struct audio_output *ao)
static unsigned
null_delay(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
return nd->sync && nd->timer->started
? timer_delay(nd->timer)
@@ -93,7 +93,7 @@ static size_t
null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
struct timer *timer = nd->timer;
if (!nd->sync)
@@ -109,7 +109,7 @@ null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
static void
null_cancel(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (!nd->sync)
return;
@@ -118,12 +118,19 @@ null_cancel(struct audio_output *ao)
}
const struct audio_output_plugin null_output_plugin = {
- .name = "null",
- .init = null_init,
- .finish = null_finish,
- .open = null_open,
- .close = null_close,
- .delay = null_delay,
- .play = null_play,
- .cancel = null_cancel,
+ "null",
+ nullptr,
+ null_init,
+ null_finish,
+ nullptr,
+ nullptr,
+ null_open,
+ null_close,
+ null_delay,
+ nullptr,
+ null_play,
+ nullptr,
+ null_cancel,
+ nullptr,
+ nullptr,
};
diff --git a/src/output/null_output_plugin.h b/src/output/NullOutputPlugin.hxx
index 392bf0aa..a58f1cb1 100644
--- a/src/output/null_output_plugin.h
+++ b/src/output/NullOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_NULL_OUTPUT_PLUGIN_H
-#define MPD_NULL_OUTPUT_PLUGIN_H
+#ifndef MPD_NULL_OUTPUT_PLUGIN_HXX
+#define MPD_NULL_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin null_output_plugin;
diff --git a/src/output/osx_output_plugin.c b/src/output/OSXOutputPlugin.cxx
index cd51fca2..5a04fe1d 100644
--- a/src/output/osx_output_plugin.c
+++ b/src/output/OSXOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,11 @@
*/
#include "config.h"
-#include "osx_output_plugin.h"
+#include "OSXOutputPlugin.hxx"
#include "output_api.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
#include <glib.h>
#include <CoreAudio/AudioHardware.h>
@@ -30,7 +32,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "osx"
-struct osx_output {
+struct OSXOutput {
struct audio_output base;
/* configuration settings */
@@ -39,8 +41,8 @@ struct osx_output {
const char *device_name;
AudioUnit au;
- GMutex *mutex;
- GCond *condition;
+ Mutex mutex;
+ Cond condition;
struct fifo_buffer *buffer;
};
@@ -63,7 +65,7 @@ osx_output_test_default_device(void)
}
static void
-osx_output_configure(struct osx_output *oo, const struct config_param *param)
+osx_output_configure(OSXOutput *oo, const struct config_param *param)
{
const char *device = config_get_block_string(param, "device", NULL);
@@ -85,15 +87,13 @@ osx_output_configure(struct osx_output *oo, const struct config_param *param)
static struct audio_output *
osx_output_init(const struct config_param *param, GError **error_r)
{
- struct osx_output *oo = g_new(struct osx_output, 1);
+ OSXOutput *oo = new OSXOutput();
if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) {
- g_free(oo);
+ delete oo;
return NULL;
}
osx_output_configure(oo, param);
- oo->mutex = g_mutex_new();
- oo->condition = g_cond_new();
return &oo->base;
}
@@ -101,15 +101,13 @@ osx_output_init(const struct config_param *param, GError **error_r)
static void
osx_output_finish(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
- g_mutex_free(od->mutex);
- g_cond_free(od->condition);
- g_free(od);
+ delete oo;
}
static bool
-osx_output_set_device(struct osx_output *oo, GError **error)
+osx_output_set_device(OSXOutput *oo, GError **error)
{
bool ret = true;
OSStatus status;
@@ -135,7 +133,7 @@ osx_output_set_device(struct osx_output *oo, GError **error)
/* what are the available audio device IDs? */
numdevices = size / sizeof(AudioDeviceID);
- deviceids = g_malloc(size);
+ deviceids = new AudioDeviceID[numdevices];
status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
&size,
deviceids);
@@ -192,8 +190,7 @@ osx_output_set_device(struct osx_output *oo, GError **error)
(unsigned int) deviceids[i], name);
done:
- if (deviceids != NULL)
- g_free(deviceids);
+ delete[] deviceids;
return ret;
}
@@ -205,13 +202,13 @@ osx_render(void *vdata,
G_GNUC_UNUSED UInt32 in_number_frames,
AudioBufferList *buffer_list)
{
- struct osx_output *od = (struct osx_output *) vdata;
+ OSXOutput *od = (OSXOutput *) vdata;
AudioBuffer *buffer = &buffer_list->mBuffers[0];
size_t buffer_size = buffer->mDataByteSize;
assert(od->buffer != NULL);
- g_mutex_lock(od->mutex);
+ od->mutex.lock();
size_t nbytes;
const void *src = fifo_buffer_read(od->buffer, &nbytes);
@@ -225,8 +222,8 @@ osx_render(void *vdata,
} else
nbytes = 0;
- g_cond_signal(od->condition);
- g_mutex_unlock(od->mutex);
+ od->condition.signal();
+ od->mutex.unlock();
buffer->mDataByteSize = nbytes;
@@ -242,7 +239,7 @@ osx_render(void *vdata,
static bool
osx_output_enable(struct audio_output *ao, GError **error_r)
{
- struct osx_output *oo = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
@@ -293,7 +290,7 @@ osx_output_enable(struct audio_output *ao, GError **error_r)
static void
osx_output_disable(struct audio_output *ao)
{
- struct osx_output *oo = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
CloseComponent(oo->au);
}
@@ -301,17 +298,16 @@ osx_output_disable(struct audio_output *ao)
static void
osx_output_cancel(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
- g_mutex_lock(od->mutex);
+ const ScopeLock protect(od->mutex);
fifo_buffer_clear(od->buffer);
- g_mutex_unlock(od->mutex);
}
static void
osx_output_close(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
AudioOutputUnitStop(od->au);
AudioUnitUninitialize(od->au);
@@ -322,7 +318,7 @@ osx_output_close(struct audio_output *ao)
static bool
osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
AudioStreamBasicDescription stream_description;
stream_description.mSampleRate = audio_format->sample_rate;
@@ -397,9 +393,9 @@ static size_t
osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
- g_mutex_lock(od->mutex);
+ const ScopeLock protect(od->mutex);
void *dest;
size_t max_length;
@@ -410,7 +406,7 @@ osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
break;
/* wait for some free space in the buffer */
- g_cond_wait(od->condition, od->mutex);
+ od->condition.wait(od->mutex);
}
if (size > max_length)
@@ -419,20 +415,23 @@ osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
memcpy(dest, chunk, size);
fifo_buffer_append(od->buffer, size);
- g_mutex_unlock(od->mutex);
-
return size;
}
const struct audio_output_plugin osx_output_plugin = {
- .name = "osx",
- .test_default_device = osx_output_test_default_device,
- .init = osx_output_init,
- .finish = osx_output_finish,
- .enable = osx_output_enable,
- .disable = osx_output_disable,
- .open = osx_output_open,
- .close = osx_output_close,
- .play = osx_output_play,
- .cancel = osx_output_cancel,
+ "osx",
+ osx_output_test_default_device,
+ osx_output_init,
+ osx_output_finish,
+ osx_output_enable,
+ osx_output_disable,
+ osx_output_open,
+ osx_output_close,
+ nullptr,
+ nullptr,
+ osx_output_play,
+ nullptr,
+ osx_output_cancel,
+ nullptr,
+ nullptr,
};
diff --git a/src/output/osx_output_plugin.h b/src/output/OSXOutputPlugin.hxx
index 814702d4..2a417288 100644
--- a/src/output/osx_output_plugin.h
+++ b/src/output/OSXOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OSX_OUTPUT_PLUGIN_H
-#define MPD_OSX_OUTPUT_PLUGIN_H
+#ifndef MPD_OSX_OUTPUT_PLUGIN_HXX
+#define MPD_OSX_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin osx_output_plugin;
diff --git a/src/output/oss_output_plugin.c b/src/output/OssOutputPlugin.cxx
index e366a453..ace88b6f 100644
--- a/src/output/oss_output_plugin.c
+++ b/src/output/OssOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,10 @@
*/
#include "config.h"
-#include "oss_output_plugin.h"
+#include "OssOutputPlugin.hxx"
#include "output_api.h"
#include "mixer_list.h"
#include "fd_util.h"
-#include "glib_compat.h"
#include <glib.h>
@@ -60,7 +59,7 @@ struct oss_data {
struct audio_output base;
#ifdef AFMT_S24_PACKED
- struct pcm_export_state export;
+ struct pcm_export_state pcm_export;
#endif
int fd;
@@ -163,11 +162,10 @@ oss_output_test_default_device(void)
static struct audio_output *
oss_open_default(GError **error)
{
- int i;
int err[G_N_ELEMENTS(default_devices)];
enum oss_stat ret[G_N_ELEMENTS(default_devices)];
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
ret[i] = oss_stat_device(default_devices[i], &err[i]);
if (ret[i] == OSS_STAT_NO_ERROR) {
struct oss_data *od = oss_data_new();
@@ -182,7 +180,7 @@ oss_open_default(GError **error)
}
}
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
const char *dev = default_devices[i];
switch(ret[i]) {
case OSS_STAT_NO_ERROR:
@@ -243,7 +241,7 @@ oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
{
struct oss_data *od = (struct oss_data *)ao;
- pcm_export_init(&od->export);
+ pcm_export_init(&od->pcm_export);
return true;
}
@@ -252,7 +250,7 @@ oss_output_disable(struct audio_output *ao)
{
struct oss_data *od = (struct oss_data *)ao;
- pcm_export_deinit(&od->export);
+ pcm_export_deinit(&od->pcm_export);
}
#endif
@@ -504,7 +502,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
enum sample_format *sample_format_r,
int *oss_format_r,
#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
+ struct pcm_export_state *pcm_export,
#endif
GError **error_r)
{
@@ -539,7 +537,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
*oss_format_r = oss_format;
#ifdef AFMT_S24_PACKED
- pcm_export_open(export, sample_format, 0, false, false,
+ pcm_export_open(pcm_export, sample_format, 0, false, false,
oss_format == AFMT_S24_PACKED,
oss_format == AFMT_S24_PACKED &&
G_BYTE_ORDER != G_LITTLE_ENDIAN);
@@ -556,16 +554,16 @@ static bool
oss_setup_sample_format(int fd, struct audio_format *audio_format,
int *oss_format_r,
#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
+ struct pcm_export_state *pcm_export,
#endif
GError **error_r)
{
enum sample_format mpd_format;
enum oss_setup_result result =
- oss_probe_sample_format(fd, audio_format->format,
+ oss_probe_sample_format(fd, sample_format(audio_format->format),
&mpd_format, oss_format_r,
#ifdef AFMT_S24_PACKED
- export,
+ pcm_export,
#endif
error_r);
switch (result) {
@@ -603,7 +601,7 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
result = oss_probe_sample_format(fd, mpd_format,
&mpd_format, oss_format_r,
#ifdef AFMT_S24_PACKED
- export,
+ pcm_export,
#endif
error_r);
switch (result) {
@@ -635,7 +633,7 @@ oss_setup(struct oss_data *od, struct audio_format *audio_format,
oss_setup_sample_rate(od->fd, audio_format, error_r) &&
oss_setup_sample_format(od->fd, audio_format, &od->oss_format,
#ifdef AFMT_S24_PACKED
- &od->export,
+ &od->pcm_export,
#endif
error_r);
}
@@ -749,14 +747,14 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
return 0;
#ifdef AFMT_S24_PACKED
- chunk = pcm_export(&od->export, chunk, size, &size);
+ chunk = pcm_export(&od->pcm_export, chunk, size, &size);
#endif
while (true) {
ret = write(od->fd, chunk, size);
if (ret > 0) {
#ifdef AFMT_S24_PACKED
- ret = pcm_export_source_size(&od->export, ret);
+ ret = pcm_export_source_size(&od->pcm_export, ret);
#endif
return ret;
}
@@ -771,18 +769,25 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
}
const struct audio_output_plugin oss_output_plugin = {
- .name = "oss",
- .test_default_device = oss_output_test_default_device,
- .init = oss_output_init,
- .finish = oss_output_finish,
+ "oss",
+ oss_output_test_default_device,
+ oss_output_init,
+ oss_output_finish,
#ifdef AFMT_S24_PACKED
- .enable = oss_output_enable,
- .disable = oss_output_disable,
+ oss_output_enable,
+ oss_output_disable,
+#else
+ nullptr,
+ nullptr,
#endif
- .open = oss_output_open,
- .close = oss_output_close,
- .play = oss_output_play,
- .cancel = oss_output_cancel,
-
- .mixer_plugin = &oss_mixer_plugin,
+ oss_output_open,
+ oss_output_close,
+ nullptr,
+ nullptr,
+ oss_output_play,
+ nullptr,
+ oss_output_cancel,
+ nullptr,
+
+ &oss_mixer_plugin,
};
diff --git a/src/output/oss_output_plugin.h b/src/output/OssOutputPlugin.hxx
index 2aecc2b3..6c5c9530 100644
--- a/src/output/oss_output_plugin.h
+++ b/src/output/OssOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OSS_OUTPUT_PLUGIN_H
-#define MPD_OSS_OUTPUT_PLUGIN_H
+#ifndef MPD_OSS_OUTPUT_PLUGIN_HXX
+#define MPD_OSS_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin oss_output_plugin;
diff --git a/src/output/roar_output_plugin.c b/src/output/RoarOutputPlugin.cxx
index 1c2c4832..43aeb09a 100644
--- a/src/output/roar_output_plugin.c
+++ b/src/output/RoarOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
* Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
*
@@ -19,25 +19,19 @@
*/
#include "config.h"
-#include "roar_output_plugin.h"
+#include "RoarOutputPlugin.hxx"
#include "output_api.h"
#include "mixer_list.h"
-#include "roar_output_plugin.h"
+#include "thread/Mutex.hxx"
#include <glib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
#include <roaraudio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "roaraudio"
-typedef struct roar
-{
+struct RoarOutput {
struct audio_output base;
roar_vs_t * vss;
@@ -47,9 +41,18 @@ typedef struct roar
int role;
struct roar_connection con;
struct roar_audio_info info;
- GMutex *lock;
+ Mutex mutex;
volatile bool alive;
-} roar_t;
+
+ RoarOutput()
+ :err(ROAR_ERROR_NONE),
+ host(nullptr), name(nullptr) {}
+
+ ~RoarOutput() {
+ g_free(host);
+ g_free(name);
+ }
+};
static inline GQuark
roar_output_quark(void)
@@ -58,9 +61,9 @@ roar_output_quark(void)
}
static int
-roar_output_get_volume_locked(struct roar *roar)
+roar_output_get_volume_locked(RoarOutput *roar)
{
- if (roar->vss == NULL || !roar->alive)
+ if (roar->vss == nullptr || !roar->alive)
return -1;
float l, r;
@@ -72,20 +75,18 @@ roar_output_get_volume_locked(struct roar *roar)
}
int
-roar_output_get_volume(struct roar *roar)
+roar_output_get_volume(RoarOutput *roar)
{
- g_mutex_lock(roar->lock);
- int volume = roar_output_get_volume_locked(roar);
- g_mutex_unlock(roar->lock);
- return volume;
+ const ScopeLock protect(roar->mutex);
+ return roar_output_get_volume_locked(roar);
}
static bool
-roar_output_set_volume_locked(struct roar *roar, unsigned volume)
+roar_output_set_volume_locked(RoarOutput *roar, unsigned volume)
{
assert(volume <= 100);
- if (roar->vss == NULL || !roar->alive)
+ if (roar->vss == nullptr || !roar->alive)
return false;
int error;
@@ -96,22 +97,20 @@ roar_output_set_volume_locked(struct roar *roar, unsigned volume)
}
bool
-roar_output_set_volume(struct roar *roar, unsigned volume)
+roar_output_set_volume(RoarOutput *roar, unsigned volume)
{
- g_mutex_lock(roar->lock);
- bool success = roar_output_set_volume_locked(roar, volume);
- g_mutex_unlock(roar->lock);
- return success;
+ const ScopeLock protect(roar->mutex);
+ return roar_output_set_volume_locked(roar, volume);
}
static void
-roar_configure(struct roar * self, const struct config_param *param)
+roar_configure(RoarOutput *self, const struct config_param *param)
{
- self->host = config_dup_block_string(param, "server", NULL);
+ self->host = config_dup_block_string(param, "server", nullptr);
self->name = config_dup_block_string(param, "name", "MPD");
const char *role = config_get_block_string(param, "role", "music");
- self->role = role != NULL
+ self->role = role != nullptr
? roar_str2role(role)
: ROAR_ROLE_MUSIC;
}
@@ -119,15 +118,13 @@ roar_configure(struct roar * self, const struct config_param *param)
static struct audio_output *
roar_init(const struct config_param *param, GError **error_r)
{
- struct roar *self = g_new0(struct roar, 1);
+ RoarOutput *self = new RoarOutput();
if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) {
- g_free(self);
- return NULL;
+ delete self;
+ return nullptr;
}
- self->lock = g_mutex_new();
- self->err = ROAR_ERROR_NONE;
roar_configure(self, param);
return &self->base;
}
@@ -135,14 +132,10 @@ roar_init(const struct config_param *param, GError **error_r)
static void
roar_finish(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
-
- g_free(self->host);
- g_free(self->name);
- g_mutex_free(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
ao_base_finish(&self->base);
- g_free(self);
+ delete self;
}
static void
@@ -181,24 +174,22 @@ roar_use_audio_format(struct roar_audio_info *info,
static bool
roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
{
g_set_error(error, roar_output_quark(), 0,
"Failed to connect to Roar server");
- g_mutex_unlock(self->lock);
return false;
}
self->vss = roar_vs_new_from_con(&(self->con), &(self->err));
- if (self->vss == NULL || self->err != ROAR_ERROR_NONE)
+ if (self->vss == nullptr || self->err != ROAR_ERROR_NONE)
{
g_set_error(error, roar_output_quark(), 0,
"Failed to connect to server");
- g_mutex_unlock(self->lock);
return false;
}
@@ -208,43 +199,41 @@ roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **e
&(self->err)) < 0)
{
g_set_error(error, roar_output_quark(), 0, "Failed to start stream");
- g_mutex_unlock(self->lock);
return false;
}
roar_vs_role(self->vss, self->role, &(self->err));
self->alive = true;
- g_mutex_unlock(self->lock);
return true;
}
static void
roar_close(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
+
self->alive = false;
- if (self->vss != NULL)
+ if (self->vss != nullptr)
roar_vs_close(self->vss, ROAR_VS_TRUE, &(self->err));
- self->vss = NULL;
+ self->vss = nullptr;
roar_disconnect(&(self->con));
- g_mutex_unlock(self->lock);
}
static void
-roar_cancel_locked(struct roar *self)
+roar_cancel_locked(RoarOutput *self)
{
- if (self->vss == NULL)
+ if (self->vss == nullptr)
return;
roar_vs_t *vss = self->vss;
- self->vss = NULL;
+ self->vss = nullptr;
roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
self->alive = false;
vss = roar_vs_new_from_con(&(self->con), &(self->err));
- if (vss == NULL)
+ if (vss == nullptr)
return;
if (roar_vs_stream(vss, &(self->info), ROAR_DIR_PLAY,
@@ -262,20 +251,19 @@ roar_cancel_locked(struct roar *self)
static void
roar_cancel(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
- g_mutex_lock(self->lock);
+ const ScopeLock protect(self->mutex);
roar_cancel_locked(self);
- g_mutex_unlock(self->lock);
}
static size_t
roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
ssize_t rc;
- if (self->vss == NULL)
+ if (self->vss == nullptr)
{
g_set_error(error, roar_output_quark(), 0, "Connection is invalid");
return 0;
@@ -332,19 +320,20 @@ roar_tag_convert(enum tag_type type, bool *is_uuid)
return "HASH";
default:
- return NULL;
+ return nullptr;
}
}
static void
roar_send_tag(struct audio_output *ao, const struct tag *meta)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
- if (self->vss == NULL)
+ if (self->vss == nullptr)
return;
- g_mutex_lock(self->lock);
+ const ScopeLock protect(self->mutex);
+
size_t cnt = 1;
struct roar_keyval vals[32];
memset(vals, 0, sizeof(vals));
@@ -361,7 +350,7 @@ roar_send_tag(struct audio_output *ao, const struct tag *meta)
{
bool is_uuid = false;
const char *key = roar_tag_convert(meta->items[i]->type, &is_uuid);
- if (key != NULL)
+ if (key != nullptr)
{
if (is_uuid)
{
@@ -383,19 +372,22 @@ roar_send_tag(struct audio_output *ao, const struct tag *meta)
for (unsigned i = 0; i < 32; i++)
g_free(vals[i].key);
-
- g_mutex_unlock(self->lock);
}
const struct audio_output_plugin roar_output_plugin = {
- .name = "roar",
- .init = roar_init,
- .finish = roar_finish,
- .open = roar_open,
- .play = roar_play,
- .cancel = roar_cancel,
- .close = roar_close,
- .send_tag = roar_send_tag,
-
- .mixer_plugin = &roar_mixer_plugin
+ "roar",
+ nullptr,
+ roar_init,
+ roar_finish,
+ nullptr,
+ nullptr,
+ roar_open,
+ roar_close,
+ nullptr,
+ roar_send_tag,
+ roar_play,
+ nullptr,
+ roar_cancel,
+ nullptr,
+ &roar_mixer_plugin,
};
diff --git a/src/output/roar_output_plugin.h b/src/output/RoarOutputPlugin.hxx
index 78b628cc..faa4b4d5 100644
--- a/src/output/roar_output_plugin.h
+++ b/src/output/RoarOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,16 +20,14 @@
#ifndef MPD_ROAR_OUTPUT_PLUGIN_H
#define MPD_ROAR_OUTPUT_PLUGIN_H
-#include <stdbool.h>
-
-struct roar;
+struct RoarOutput;
extern const struct audio_output_plugin roar_output_plugin;
int
-roar_output_get_volume(struct roar *roar);
+roar_output_get_volume(RoarOutput *roar);
bool
-roar_output_set_volume(struct roar *roar, unsigned volume);
+roar_output_set_volume(RoarOutput *roar, unsigned volume);
#endif
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index 022be0b4..3d6171ae 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -20,7 +20,6 @@
#include "config.h"
#include "fifo_output_plugin.h"
#include "output_api.h"
-#include "utils.h"
#include "timer.h"
#include "fd_util.h"
#include "open.h"
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
deleted file mode 100644
index 72de9045..00000000
--- a/src/output/httpd_client.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "httpd_client.h"
-#include "httpd_internal.h"
-#include "fifo_buffer.h"
-#include "page.h"
-#include "icy_server.h"
-#include "glib_socket.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-struct httpd_client {
- /**
- * The httpd output object this client is connected to.
- */
- struct httpd_output *httpd;
-
- /**
- * The TCP socket.
- */
- GIOChannel *channel;
-
- /**
- * The GLib main loop source id for reading from the socket,
- * and to detect errors.
- */
- guint read_source_id;
-
- /**
- * The GLib main loop source id for writing to the socket. If
- * 0, then there is no event source currently (because there
- * are no queued pages).
- */
- guint write_source_id;
-
- /**
- * For buffered reading. This pointer is only valid while the
- * HTTP request is read.
- */
- struct fifo_buffer *input;
-
- /**
- * The current state of the client.
- */
- enum {
- /** reading the request line */
- REQUEST,
-
- /** reading the request headers */
- HEADERS,
-
- /** sending the HTTP response */
- RESPONSE,
- } state;
-
- /**
- * A queue of #page objects to be sent to the client.
- */
- GQueue *pages;
-
- /**
- * The #page which is currently being sent to the client.
- */
- struct page *current_page;
-
- /**
- * The amount of bytes which were already sent from
- * #current_page.
- */
- size_t current_position;
-
- /**
- * If DLNA streaming was an option.
- */
- bool dlna_streaming_requested;
-
- /* ICY */
-
- /**
- * Do we support sending Icy-Metadata to the client? This is
- * disabled if the httpd audio output uses encoder tags.
- */
- bool metadata_supported;
-
- /**
- * If we should sent icy metadata.
- */
- bool metadata_requested;
-
- /**
- * If the current metadata was already sent to the client.
- */
- bool metadata_sent;
-
- /**
- * The amount of streaming data between each metadata block
- */
- guint metaint;
-
- /**
- * The metadata as #page which is currently being sent to the client.
- */
- struct page *metadata;
-
- /*
- * The amount of bytes which were already sent from the metadata.
- */
- size_t metadata_current_position;
-
- /**
- * The amount of streaming data sent to the client
- * since the last icy information was sent.
- */
- guint metadata_fill;
-};
-
-static void
-httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct page *page = data;
-
- page_unref(page);
-}
-
-void
-httpd_client_free(struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->state == RESPONSE) {
- if (client->write_source_id != 0)
- g_source_remove(client->write_source_id);
-
- if (client->current_page != NULL)
- page_unref(client->current_page);
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_free(client->pages);
- } else
- fifo_buffer_free(client->input);
-
- if (client->metadata)
- page_unref (client->metadata);
-
- g_source_remove(client->read_source_id);
- g_io_channel_unref(client->channel);
- g_free(client);
-}
-
-/**
- * Frees the client and removes it from the server's client list.
- */
-static void
-httpd_client_close(struct httpd_client *client)
-{
- assert(client != NULL);
-
- httpd_output_remove_client(client->httpd, client);
- httpd_client_free(client);
-}
-
-/**
- * Switch the client to the "RESPONSE" state.
- */
-static void
-httpd_client_begin_response(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- client->state = RESPONSE;
- client->write_source_id = 0;
- client->pages = g_queue_new();
- client->current_page = NULL;
-
- httpd_output_send_header(client->httpd, client);
-}
-
-/**
- * Handle a line of the HTTP request.
- */
-static bool
-httpd_client_handle_line(struct httpd_client *client, const char *line)
-{
- assert(client->state != RESPONSE);
-
- if (client->state == REQUEST) {
- if (strncmp(line, "GET /", 5) != 0) {
- /* only GET is supported */
- g_warning("malformed request line from client");
- return false;
- }
-
- line = strchr(line + 5, ' ');
- if (line == NULL || strncmp(line + 1, "HTTP/", 5) != 0) {
- /* HTTP/0.9 without request headers */
- httpd_client_begin_response(client);
- return true;
- }
-
- /* after the request line, request headers follow */
- client->state = HEADERS;
- return true;
- } else {
- if (*line == 0) {
- /* empty line: request is finished */
- httpd_client_begin_response(client);
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
- /* Send icy metadata */
- client->metadata_requested =
- client->metadata_supported;
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
- /* Send as dlna */
- client->dlna_streaming_requested = true;
- /* metadata is not supported by dlna streaming, so disable it */
- client->metadata_supported = false;
- client->metadata_requested = false;
- return true;
- }
-
- /* expect more request headers */
- return true;
- }
-}
-
-/**
- * Check if a complete line of input is present in the input buffer,
- * and duplicates it. It is removed from the input buffer. The
- * return value has to be freed with g_free().
- */
-static char *
-httpd_client_read_line(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- /* empty input buffer */
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- /* incomplete line */
- return NULL;
-
- line = g_strndup(p, newline - p);
- fifo_buffer_consume(client->input, newline - p + 1);
-
- /* remove trailing whitespace (e.g. '\r') */
- return g_strchomp(line);
-}
-
-/**
- * Sends the status line and response headers to the client.
- */
-static bool
-httpd_client_send_response(struct httpd_client *client)
-{
- char buffer[1024];
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->state == RESPONSE);
-
- if (client->dlna_streaming_requested) {
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 206 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: 10000\r\n"
- "Content-RangeX: 0-1000000/1000000\r\n"
- "transferMode.dlna.org: Streaming\r\n"
- "Accept-Ranges: bytes\r\n"
- "Connection: close\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
- "\r\n",
- client->httpd->content_type);
-
- } else if (client->metadata_requested) {
- gchar *metadata_header;
-
- metadata_header = icy_server_metadata_header(
- client->httpd->name,
- client->httpd->genre,
- client->httpd->website,
- client->httpd->content_type,
- client->metaint);
-
- g_strlcpy(buffer, metadata_header, sizeof(buffer));
-
- g_free(metadata_header);
-
- } else { /* revert to a normal HTTP request */
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Connection: close\r\n"
- "Pragma: no-cache\r\n"
- "Cache-Control: no-cache, no-store\r\n"
- "\r\n",
- client->httpd->content_type);
- }
-
- status = g_io_channel_write_chars(client->channel,
- buffer, strlen(buffer),
- &bytes_written, &error);
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- return false;
-}
-
-/**
- * Data has been received from the client and it is appended to the
- * input buffer.
- */
-static bool
-httpd_client_received(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- char *line;
- bool success;
-
- while ((line = httpd_client_read_line(client)) != NULL) {
- success = httpd_client_handle_line(client, line);
- g_free(line);
- if (!success) {
- assert(client->state != RESPONSE);
- return false;
- }
-
- if (client->state == RESPONSE) {
- if (!fifo_buffer_is_empty(client->input)) {
- g_warning("unexpected input from client");
- return false;
- }
-
- fifo_buffer_free(client->input);
-
- return httpd_client_send_response(client);
- }
- }
-
- return true;
-}
-
-static bool
-httpd_client_read(struct httpd_client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- if (client->state == RESPONSE) {
- /* the client has already sent the request, and he
- must not send more */
- char buffer[1];
-
- status = g_io_channel_read_chars(client->channel, buffer,
- sizeof(buffer), &bytes_read,
- NULL);
- if (status == G_IO_STATUS_NORMAL)
- g_warning("unexpected input from client");
-
- return false;
- }
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("buffer overflow");
- return false;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- fifo_buffer_append(client->input, bytes_read);
- return httpd_client_received(client);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return true;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client: %s",
- error->message);
- g_error_free(error);
- return false;
- }
-
- /* unreachable */
- return false;
-}
-
-static gboolean
-httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- bool ret;
-
- g_mutex_lock(httpd->mutex);
-
- if (condition == G_IO_IN && httpd_client_read(client)) {
- ret = true;
- } else {
- httpd_client_close(client);
- ret = false;
- }
-
- g_mutex_unlock(httpd->mutex);
-
- return ret;
-}
-
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
-{
- struct httpd_client *client = g_new(struct httpd_client, 1);
-
- client->httpd = httpd;
-
- client->channel = g_io_channel_new_socket(fd);
-
- /* GLib is responsible for closing the file descriptor */
- g_io_channel_set_close_on_unref(client->channel, true);
- /* NULL encoding means the stream is binary safe */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->read_source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- httpd_client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
- client->state = REQUEST;
-
- client->dlna_streaming_requested = false;
- client->metadata_supported = metadata_supported;
- client->metadata_requested = false;
- client->metadata_sent = true;
- client->metaint = 8192; /*TODO: just a std value */
- client->metadata = NULL;
- client->metadata_current_position = 0;
- client->metadata_fill = 0;
-
- return client;
-}
-
-static void
-httpd_client_add_page_size(gpointer data, gpointer user_data)
-{
- struct page *page = data;
- size_t *size = user_data;
-
- *size += page->size;
-}
-
-size_t
-httpd_client_queue_size(const struct httpd_client *client)
-{
- size_t size = 0;
-
- if (client->state != RESPONSE)
- return 0;
-
- g_queue_foreach(client->pages, httpd_client_add_page_size, &size);
- return size;
-}
-
-void
-httpd_client_cancel(struct httpd_client *client)
-{
- if (client->state != RESPONSE)
- return;
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_clear(client->pages);
-
- if (client->write_source_id != 0 && client->current_page == NULL) {
- g_source_remove(client->write_source_id);
- client->write_source_id = 0;
- }
-}
-
-static GIOStatus
-write_page_to_channel(GIOChannel *channel,
- const struct page *page, size_t position,
- gsize *bytes_written_r, GError **error)
-{
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- return g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- page->size - position,
- bytes_written_r, error);
-}
-
-static GIOStatus
-write_n_bytes_to_channel(GIOChannel *channel, const struct page *page,
- size_t position, gint n,
- gsize *bytes_written_r, GError **error)
-{
- GIOStatus status;
-
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- if (n == -1) {
- status = write_page_to_channel (channel, page, position,
- bytes_written_r, error);
- } else {
- status = g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- n, bytes_written_r, error);
- }
-
- return status;
-}
-
-static gint
-bytes_left_till_metadata (struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->metadata_requested &&
- client->current_page->size - client->current_position
- > client->metaint - client->metadata_fill)
- return client->metaint - client->metadata_fill;
-
- return -1;
-}
-
-static gboolean
-httpd_client_out_event(GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition, gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
- gint bytes_to_write;
-
- g_mutex_lock(httpd->mutex);
-
- assert(condition == G_IO_OUT);
- assert(client->state == RESPONSE);
-
- if (client->write_source_id == 0) {
- /* another thread has removed the event source while
- this thread was waiting for httpd->mutex */
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- if (client->current_page == NULL) {
- client->current_page = g_queue_pop_head(client->pages);
- client->current_position = 0;
- }
-
- bytes_to_write = bytes_left_till_metadata(client);
-
- if (bytes_to_write == 0) {
- gint metadata_to_write;
-
- metadata_to_write = client->metadata_current_position;
-
- if (!client->metadata_sent) {
- status = write_page_to_channel(source,
- client->metadata,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (client->metadata->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- client->metadata_sent = true;
- }
- } else {
- struct page *empty_meta;
- guchar empty_data = 0;
-
- empty_meta = page_new_copy(&empty_data, 1);
-
- status = write_page_to_channel(source,
- empty_meta,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (empty_meta->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- }
- }
-
- bytes_written = 0;
- } else {
- status = write_n_bytes_to_channel(source, client->current_page,
- client->current_position, bytes_to_write,
- &bytes_written, &error);
- }
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- client->current_position += bytes_written;
- assert(client->current_position <= client->current_page->size);
-
- if (client->metadata_requested)
- client->metadata_fill += bytes_written;
-
- if (client->current_position >= client->current_page->size) {
- page_unref(client->current_page);
- client->current_page = NULL;
-
- if (g_queue_is_empty(client->pages)) {
- /* all pages are sent: remove the
- event source */
- client->write_source_id = 0;
-
- g_mutex_unlock(httpd->mutex);
- return false;
- }
- }
-
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_AGAIN:
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-}
-
-void
-httpd_client_send(struct httpd_client *client, struct page *page)
-{
- if (client->state != RESPONSE)
- /* the client is still writing the HTTP request */
- return;
-
- page_ref(page);
- g_queue_push_tail(client->pages, page);
-
- if (client->write_source_id == 0)
- client->write_source_id =
- g_io_add_watch(client->channel, G_IO_OUT,
- httpd_client_out_event, client);
-}
-
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page)
-{
- if (client->metadata) {
- page_unref(client->metadata);
- client->metadata = NULL;
- }
-
- g_return_if_fail (page);
-
- page_ref(page);
- client->metadata = page;
- client->metadata_sent = false;
-}
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
deleted file mode 100644
index 739163f4..00000000
--- a/src/output/httpd_client.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_OUTPUT_HTTPD_CLIENT_H
-#define MPD_OUTPUT_HTTPD_CLIENT_H
-
-#include <glib.h>
-
-#include <stdbool.h>
-
-struct httpd_client;
-struct httpd_output;
-struct page;
-
-/**
- * Creates a new #httpd_client object
- *
- * @param httpd the HTTP output device
- * @param fd the socket file descriptor
- */
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported);
-
-/**
- * Frees memory and resources allocated by the #httpd_client object.
- * This does not remove it from the #httpd_output object.
- */
-void
-httpd_client_free(struct httpd_client *client);
-
-/**
- * Returns the total size of this client's page queue.
- */
-size_t
-httpd_client_queue_size(const struct httpd_client *client);
-
-/**
- * Clears the page queue.
- */
-void
-httpd_client_cancel(struct httpd_client *client);
-
-/**
- * Appends a page to the client's queue.
- */
-void
-httpd_client_send(struct httpd_client *client, struct page *page);
-
-/**
- * Sends the passed metadata.
- */
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page);
-
-#endif
diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c
deleted file mode 100644
index 1d730df7..00000000
--- a/src/output/httpd_output_plugin.c
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "httpd_output_plugin.h"
-#include "httpd_internal.h"
-#include "httpd_client.h"
-#include "output_api.h"
-#include "encoder_plugin.h"
-#include "encoder_list.h"
-#include "resolver.h"
-#include "page.h"
-#include "icy_server.h"
-#include "fd_util.h"
-#include "server_socket.h"
-
-#include <assert.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#ifdef HAVE_LIBWRAP
-#include <sys/socket.h> /* needed for AF_UNIX */
-#include <tcpd.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-httpd_output_quark(void)
-{
- return g_quark_from_static_string("httpd_output");
-}
-
-/**
- * Check whether there is at least one client.
- *
- * Caller must lock the mutex.
- */
-G_GNUC_PURE
-static bool
-httpd_output_has_clients(const struct httpd_output *httpd)
-{
- return httpd->clients != NULL;
-}
-
-/**
- * Check whether there is at least one client.
- */
-G_GNUC_PURE
-static bool
-httpd_output_lock_has_clients(const struct httpd_output *httpd)
-{
- g_mutex_lock(httpd->mutex);
- bool result = httpd_output_has_clients(httpd);
- g_mutex_unlock(httpd->mutex);
- return result;
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, int uid, void *ctx);
-
-static bool
-httpd_output_bind(struct httpd_output *httpd, GError **error_r)
-{
- httpd->open = false;
-
- g_mutex_lock(httpd->mutex);
- bool success = server_socket_open(httpd->server_socket, error_r);
- g_mutex_unlock(httpd->mutex);
-
- return success;
-}
-
-static void
-httpd_output_unbind(struct httpd_output *httpd)
-{
- assert(!httpd->open);
-
- g_mutex_lock(httpd->mutex);
- server_socket_close(httpd->server_socket);
- g_mutex_unlock(httpd->mutex);
-}
-
-static struct audio_output *
-httpd_output_init(const struct config_param *param,
- GError **error)
-{
- struct httpd_output *httpd = g_new(struct httpd_output, 1);
- if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) {
- g_free(httpd);
- return NULL;
- }
-
- /* read configuration */
- httpd->name =
- config_get_block_string(param, "name", "Set name in config");
- httpd->genre =
- config_get_block_string(param, "genre", "Set genre in config");
- httpd->website =
- config_get_block_string(param, "website", "Set website in config");
-
- guint port = config_get_block_unsigned(param, "port", 8000);
-
- const char *encoder_name =
- config_get_block_string(param, "encoder", "vorbis");
- const struct encoder_plugin *encoder_plugin =
- encoder_plugin_get(encoder_name);
- if (encoder_plugin == NULL) {
- g_set_error(error, httpd_output_quark(), 0,
- "No such encoder: %s", encoder_name);
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- httpd->clients_max = config_get_block_unsigned(param,"max_clients", 0);
-
- /* set up bind_to_address */
-
- httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
-
- const char *bind_to_address =
- config_get_block_string(param, "bind_to_address", NULL);
- bool success = bind_to_address != NULL &&
- strcmp(bind_to_address, "any") != 0
- ? server_socket_add_host(httpd->server_socket, bind_to_address,
- port, error)
- : server_socket_add_port(httpd->server_socket, port, error);
- if (!success) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* initialize metadata */
- httpd->metadata = NULL;
- httpd->unflushed_input = 0;
-
- /* initialize encoder */
-
- httpd->encoder = encoder_init(encoder_plugin, param, error);
- if (httpd->encoder == NULL) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* determine content type */
- httpd->content_type = encoder_get_mime_type(httpd->encoder);
- if (httpd->content_type == NULL) {
- httpd->content_type = "application/octet-stream";
- }
-
- httpd->mutex = g_mutex_new();
-
- return &httpd->base;
-}
-
-static void
-httpd_output_finish(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd->metadata)
- page_unref(httpd->metadata);
-
- encoder_finish(httpd->encoder);
- server_socket_free(httpd->server_socket);
- g_mutex_free(httpd->mutex);
- ao_base_finish(&httpd->base);
- g_free(httpd);
-}
-
-/**
- * Creates a new #httpd_client object and adds it into the
- * httpd_output.clients linked list.
- */
-static void
-httpd_client_add(struct httpd_output *httpd, int fd)
-{
- struct httpd_client *client =
- httpd_client_new(httpd, fd,
- httpd->encoder->plugin->tag == NULL);
-
- httpd->clients = g_list_prepend(httpd->clients, client);
- httpd->clients_cnt++;
-
- /* pass metadata to client */
- if (httpd->metadata)
- httpd_client_send_metadata(client, httpd->metadata);
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, G_GNUC_UNUSED int uid, void *ctx)
-{
- struct httpd_output *httpd = ctx;
-
- /* the listener socket has become readable - a client has
- connected */
-
-#ifdef HAVE_LIBWRAP
- if (address->sa_family != AF_UNIX) {
- char *hostaddr = sockaddr_to_string(address, address_length, NULL);
- const char *progname = g_get_prgname();
-
- struct request_info req;
- request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
-
- fromhost(&req);
-
- if (!hosts_access(&req)) {
- /* tcp wrappers says no */
- g_warning("libwrap refused connection (libwrap=%s) from %s",
- progname, hostaddr);
- g_free(hostaddr);
- close_socket(fd);
- g_mutex_unlock(httpd->mutex);
- return;
- }
-
- g_free(hostaddr);
- }
-#else
- (void)address;
- (void)address_length;
-#endif /* HAVE_WRAP */
-
- g_mutex_lock(httpd->mutex);
-
- if (fd >= 0) {
- /* can we allow additional client */
- if (httpd->open &&
- (httpd->clients_max == 0 ||
- httpd->clients_cnt < httpd->clients_max))
- httpd_client_add(httpd, fd);
- else
- close_socket(fd);
- } else if (fd < 0 && errno != EINTR) {
- g_warning("accept() failed: %s", g_strerror(errno));
- }
-
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Reads data from the encoder (as much as available) and returns it
- * as a new #page object.
- */
-static struct page *
-httpd_output_read_page(struct httpd_output *httpd)
-{
- if (httpd->unflushed_input >= 65536) {
- /* we have fed a lot of input into the encoder, but it
- didn't give anything back yet - flush now to avoid
- buffer underruns */
- encoder_flush(httpd->encoder, NULL);
- httpd->unflushed_input = 0;
- }
-
- size_t size = 0;
- do {
- size_t nbytes = encoder_read(httpd->encoder,
- httpd->buffer + size,
- sizeof(httpd->buffer) - size);
- if (nbytes == 0)
- break;
-
- httpd->unflushed_input = 0;
-
- size += nbytes;
- } while (size < sizeof(httpd->buffer));
-
- if (size == 0)
- return NULL;
-
- return page_new_copy(httpd->buffer, size);
-}
-
-static bool
-httpd_output_encoder_open(struct httpd_output *httpd,
- struct audio_format *audio_format,
- GError **error)
-{
- if (!encoder_open(httpd->encoder, audio_format, error))
- return false;
-
- /* we have to remember the encoder header, i.e. the first
- bytes of encoder output after opening it, because it has to
- be sent to every new client */
- httpd->header = httpd_output_read_page(httpd);
-
- httpd->unflushed_input = 0;
-
- return true;
-}
-
-static bool
-httpd_output_enable(struct audio_output *ao, GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- return httpd_output_bind(httpd, error_r);
-}
-
-static void
-httpd_output_disable(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- httpd_output_unbind(httpd);
-}
-
-static bool
-httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
- GError **error)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- /* open the encoder */
-
- if (!httpd_output_encoder_open(httpd, audio_format, error)) {
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* initialize other attributes */
-
- httpd->clients = NULL;
- httpd->clients_cnt = 0;
- httpd->timer = timer_new(audio_format);
-
- httpd->open = true;
-
- g_mutex_unlock(httpd->mutex);
- return true;
-}
-
-static void
-httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_free(client);
-}
-
-static void
-httpd_output_close(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- httpd->open = false;
-
- timer_free(httpd->timer);
-
- g_list_foreach(httpd->clients, httpd_client_delete, NULL);
- g_list_free(httpd->clients);
-
- if (httpd->header != NULL)
- page_unref(httpd->header);
-
- encoder_close(httpd->encoder);
-
- g_mutex_unlock(httpd->mutex);
-}
-
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- assert(httpd != NULL);
- assert(client != NULL);
-
- httpd->clients = g_list_remove(httpd->clients, client);
- httpd->clients_cnt--;
-}
-
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- if (httpd->header != NULL)
- httpd_client_send(client, httpd->header);
-}
-
-static unsigned
-httpd_output_delay(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
- /* if there's no client and this output is paused,
- then httpd_output_pause() will not do anything, it
- will not fill the buffer and it will not update the
- timer; therefore, we reset the timer here */
- timer_reset(httpd->timer);
-
- /* some arbitrary delay that is long enough to avoid
- consuming too much CPU, and short enough to notice
- new clients quickly enough */
- return 1000;
- }
-
- return httpd->timer->started
- ? timer_delay(httpd->timer)
- : 0;
-}
-
-static void
-httpd_client_check_queue(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- if (httpd_client_queue_size(client) > 256 * 1024) {
- g_debug("client is too slow, flushing its queue");
- httpd_client_cancel(client);
- }
-}
-
-static void
-httpd_client_send_page(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *page = user_data;
-
- httpd_client_send(client, page);
-}
-
-/**
- * Broadcasts a page struct to all clients.
- */
-static void
-httpd_output_broadcast_page(struct httpd_output *httpd, struct page *page)
-{
- assert(page != NULL);
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_send_page, page);
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Broadcasts data from the encoder to all clients.
- */
-static void
-httpd_output_encoder_to_clients(struct httpd_output *httpd)
-{
- struct page *page;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_check_queue, NULL);
- g_mutex_unlock(httpd->mutex);
-
- while ((page = httpd_output_read_page(httpd)) != NULL) {
- httpd_output_broadcast_page(httpd, page);
- page_unref(page);
- }
-}
-
-static bool
-httpd_output_encode_and_play(struct httpd_output *httpd,
- const void *chunk, size_t size, GError **error)
-{
- if (!encoder_write(httpd->encoder, chunk, size, error))
- return false;
-
- httpd->unflushed_input += size;
-
- httpd_output_encoder_to_clients(httpd);
-
- return true;
-}
-
-static size_t
-httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
- return 0;
- }
-
- if (!httpd->timer->started)
- timer_start(httpd->timer);
- timer_add(httpd->timer, size);
-
- return size;
-}
-
-static bool
-httpd_output_pause(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- static const char silence[1020];
- return httpd_output_play(ao, silence, sizeof(silence),
- NULL) > 0;
- } else {
- return true;
- }
-}
-
-static void
-httpd_send_metadata(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *icy_metadata = user_data;
-
- httpd_client_send_metadata(client, icy_metadata);
-}
-
-static void
-httpd_output_tag(struct audio_output *ao, const struct tag *tag)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- assert(tag != NULL);
-
- if (httpd->encoder->plugin->tag != NULL) {
- /* embed encoder tags */
-
- /* flush the current stream, and end it */
-
- encoder_pre_tag(httpd->encoder, NULL);
- httpd_output_encoder_to_clients(httpd);
-
- /* send the tag to the encoder - which starts a new
- stream now */
-
- encoder_tag(httpd->encoder, tag, NULL);
-
- /* the first page generated by the encoder will now be
- used as the new "header" page, which is sent to all
- new clients */
-
- struct page *page = httpd_output_read_page(httpd);
- if (page != NULL) {
- if (httpd->header != NULL)
- page_unref(httpd->header);
- httpd->header = page;
- httpd_output_broadcast_page(httpd, page);
- }
- } else {
- /* use Icy-Metadata */
-
- if (httpd->metadata != NULL)
- page_unref (httpd->metadata);
-
- httpd->metadata =
- icy_server_metadata_page(tag, TAG_ALBUM,
- TAG_ARTIST, TAG_TITLE,
- TAG_NUM_OF_ITEM_TYPES);
- if (httpd->metadata != NULL) {
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients,
- httpd_send_metadata, httpd->metadata);
- g_mutex_unlock(httpd->mutex);
- }
- }
-}
-
-static void
-httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_cancel(client);
-}
-
-static void
-httpd_output_cancel(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL);
- g_mutex_unlock(httpd->mutex);
-}
-
-const struct audio_output_plugin httpd_output_plugin = {
- .name = "httpd",
- .init = httpd_output_init,
- .finish = httpd_output_finish,
- .enable = httpd_output_enable,
- .disable = httpd_output_disable,
- .open = httpd_output_open,
- .close = httpd_output_close,
- .delay = httpd_output_delay,
- .send_tag = httpd_output_tag,
- .play = httpd_output_play,
- .pause = httpd_output_pause,
- .cancel = httpd_output_cancel,
-};
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index e267427d..457fa9f0 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -21,7 +21,7 @@
#include "pulse_output_plugin.h"
#include "output_api.h"
#include "mixer_list.h"
-#include "mixer/pulse_mixer_plugin.h"
+#include "mixer/PulseMixerPlugin.h"
#include <glib.h>
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 02a51f27..bcc8004a 100644
--- a/src/output/pulse_output_plugin.h
+++ b/src/output/pulse_output_plugin.h
@@ -20,9 +20,9 @@
#ifndef MPD_PULSE_OUTPUT_PLUGIN_H
#define MPD_PULSE_OUTPUT_PLUGIN_H
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stdbool.h>
struct pulse_output;
struct pulse_mixer;
@@ -30,6 +30,10 @@ struct pa_cvolume;
extern const struct audio_output_plugin pulse_output_plugin;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_output_lock(struct pulse_output *po);
@@ -46,4 +50,8 @@ bool
pulse_output_set_volume(struct pulse_output *po,
const struct pa_cvolume *volume, GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 56456a0e..56d7a88b 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -97,23 +97,22 @@ static void free_shout_data(struct shout_data *sd)
g_free(sd);
}
-#define check_block_param(name) { \
- block_param = config_get_block_param(param, name); \
- if (!block_param) { \
- MPD_ERROR("no \"%s\" defined for shout device defined at line " \
- "%i\n", name, param->line); \
- } \
- }
+gcc_pure
+static const char *
+require_block_string(const struct config_param *param, const char *name)
+{
+ const char *value = config_get_block_string(param, name, NULL);
+ if (value == NULL)
+ MPD_ERROR("no \"%s\" defined for shout device defined at line " \
+ "%i\n", name, param->line); \
-static struct audio_output *
-my_shout_init_driver(const struct config_param *param,
- GError **error)
+ return value;
+}
+
+static bool
+my_shout_configure(struct shout_data *sd, const struct config_param *param,
+ GError **error)
{
- struct shout_data *sd = new_shout_data();
- if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
- free_shout_data(sd);
- return NULL;
- }
const struct audio_format *audio_format =
&sd->base.config_audio_format;
@@ -125,30 +124,18 @@ my_shout_init_driver(const struct config_param *param,
return NULL;
}
- if (shout_init_count == 0)
- shout_init();
-
- shout_init_count++;
-
- const struct block_param *block_param;
- check_block_param("host");
- char *host = block_param->value;
-
- check_block_param("mount");
- char *mount = block_param->value;
+ const char *host = require_block_string(param, "host");
+ const char *mount = require_block_string(param, "mount");
unsigned port = config_get_block_unsigned(param, "port", 0);
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
- goto failure;
+ return false;
}
- check_block_param("password");
- const char *passwd = block_param->value;
-
- check_block_param("name");
- const char *name = block_param->value;
+ const char *passwd = require_block_string(param, "password");
+ const char *name = require_block_string(param, "name");
bool public = config_get_block_bool(param, "public", false);
@@ -164,21 +151,21 @@ my_shout_init_driver(const struct config_param *param,
"shout quality \"%s\" is not a number in the "
"range -1 to 10, line %i",
value, param->line);
- goto failure;
+ return false;
}
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
g_set_error(error, shout_output_quark(), 0,
"quality and bitrate are "
"both defined");
- goto failure;
+ return false;
}
} else {
value = config_get_block_string(param, "bitrate", NULL);
if (value == NULL) {
g_set_error(error, shout_output_quark(), 0,
"neither bitrate nor quality defined");
- goto failure;
+ return false;
}
char *test;
@@ -187,7 +174,7 @@ my_shout_init_driver(const struct config_param *param,
if (*test != '\0' || sd->bitrate <= 0) {
g_set_error(error, shout_output_quark(), 0,
"bitrate must be a positive integer");
- goto failure;
+ return false;
}
}
@@ -199,12 +186,12 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"couldn't find shout encoder plugin \"%s\"",
encoding);
- goto failure;
+ return false;
}
sd->encoder = encoder_init(encoder_plugin, param, error);
if (sd->encoder == NULL)
- goto failure;
+ return false;
unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
@@ -220,7 +207,7 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"you cannot stream \"%s\" to shoutcast, use mp3",
encoding);
- goto failure;
+ return false;
} else if (0 == strcmp(value, "shoutcast"))
protocol = SHOUT_PROTOCOL_ICY;
else if (0 == strcmp(value, "icecast1"))
@@ -232,7 +219,7 @@ my_shout_init_driver(const struct config_param *param,
"shout protocol \"%s\" is not \"shoutcast\" or "
"\"icecast1\"or \"icecast2\"",
value);
- goto failure;
+ return false;
}
} else {
protocol = SHOUT_PROTOCOL_HTTP;
@@ -251,7 +238,7 @@ my_shout_init_driver(const struct config_param *param,
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
/* optional paramters */
@@ -262,21 +249,21 @@ my_shout_init_driver(const struct config_param *param,
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "description", NULL);
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "url", NULL);
if (value != NULL && shout_set_url(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
{
@@ -301,12 +288,31 @@ my_shout_init_driver(const struct config_param *param,
}
}
- return &sd->base;
+ return true;
+}
-failure:
- ao_base_finish(&sd->base);
- free_shout_data(sd);
- return NULL;
+static struct audio_output *
+my_shout_init_driver(const struct config_param *param,
+ GError **error)
+{
+ struct shout_data *sd = new_shout_data();
+ if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (!my_shout_configure(sd, param, error)) {
+ ao_base_finish(&sd->base);
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (shout_init_count == 0)
+ shout_init();
+
+ shout_init_count++;
+
+ return &sd->base;
}
static bool
diff --git a/src/output/winmm_output_plugin.c b/src/output/winmm_output_plugin.c
index 4d95834b..c1b3af12 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/winmm_output_plugin.c
@@ -26,7 +26,6 @@
#include <stdlib.h>
#include <string.h>
-#include <windows.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "winmm_output"
diff --git a/src/output/winmm_output_plugin.h b/src/output/winmm_output_plugin.h
index 0605530e..36435648 100644
--- a/src/output/winmm_output_plugin.h
+++ b/src/output/winmm_output_plugin.h
@@ -25,6 +25,7 @@
#ifdef ENABLE_WINMM_OUTPUT
#include <windows.h>
+#include <mmsystem.h>
struct winmm_output;