diff options
Diffstat (limited to 'src/output')
-rw-r--r-- | src/output/AlsaOutputPlugin.cxx (renamed from src/output/alsa_output_plugin.c) | 318 | ||||
-rw-r--r-- | src/output/AlsaOutputPlugin.hxx (renamed from src/output/alsa_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/AoOutputPlugin.cxx (renamed from src/output/ao_output_plugin.c) | 161 | ||||
-rw-r--r-- | src/output/AoOutputPlugin.hxx (renamed from src/output/ao_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/FifoOutputPlugin.cxx (renamed from src/output/fifo_output_plugin.c) | 235 | ||||
-rw-r--r-- | src/output/FifoOutputPlugin.hxx (renamed from src/output/fifo_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/HttpdClient.cxx | 444 | ||||
-rw-r--r-- | src/output/HttpdClient.hxx | 186 | ||||
-rw-r--r-- | src/output/HttpdInternal.hxx (renamed from src/output/httpd_internal.h) | 140 | ||||
-rw-r--r-- | src/output/HttpdOutputPlugin.cxx | 568 | ||||
-rw-r--r-- | src/output/HttpdOutputPlugin.hxx (renamed from src/output/httpd_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/JackOutputPlugin.cxx (renamed from src/output/jack_output_plugin.c) | 245 | ||||
-rw-r--r-- | src/output/JackOutputPlugin.hxx (renamed from src/output/jack_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/NullOutputPlugin.cxx | 143 | ||||
-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) | 118 | ||||
-rw-r--r-- | src/output/OSXOutputPlugin.hxx (renamed from src/output/osx_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/OpenALOutputPlugin.cxx (renamed from src/output/openal_output_plugin.c) | 108 | ||||
-rw-r--r-- | src/output/OpenALOutputPlugin.hxx (renamed from src/output/openal_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/OssOutputPlugin.cxx (renamed from src/output/oss_output_plugin.c) | 237 | ||||
-rw-r--r-- | src/output/OssOutputPlugin.hxx (renamed from src/output/oss_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/PipeOutputPlugin.cxx (renamed from src/output/pipe_output_plugin.c) | 89 | ||||
-rw-r--r-- | src/output/PipeOutputPlugin.hxx (renamed from src/output/pipe_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/PulseOutputPlugin.cxx (renamed from src/output/pulse_output_plugin.c) | 315 | ||||
-rw-r--r-- | src/output/PulseOutputPlugin.hxx (renamed from src/output/pulse_output_plugin.h) | 32 | ||||
-rw-r--r-- | src/output/RecorderOutputPlugin.cxx (renamed from src/output/recorder_output_plugin.c) | 157 | ||||
-rw-r--r-- | src/output/RecorderOutputPlugin.hxx (renamed from src/output/recorder_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/RoarOutputPlugin.cxx (renamed from src/output/roar_output_plugin.c) | 190 | ||||
-rw-r--r-- | src/output/RoarOutputPlugin.hxx (renamed from src/output/roar_output_plugin.h) | 10 | ||||
-rw-r--r-- | src/output/ShoutOutputPlugin.cxx (renamed from src/output/shout_output_plugin.c) | 379 | ||||
-rw-r--r-- | src/output/ShoutOutputPlugin.hxx (renamed from src/output/shout_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/SolarisOutputPlugin.cxx (renamed from src/output/solaris_output_plugin.c) | 75 | ||||
-rw-r--r-- | src/output/SolarisOutputPlugin.hxx (renamed from src/output/solaris_output_plugin.h) | 6 | ||||
-rw-r--r-- | src/output/WinmmOutputPlugin.cxx (renamed from src/output/winmm_output_plugin.c) | 130 | ||||
-rw-r--r-- | src/output/WinmmOutputPlugin.hxx (renamed from src/output/winmm_output_plugin.h) | 15 | ||||
-rw-r--r-- | src/output/ffado_output_plugin.c | 359 | ||||
-rw-r--r-- | src/output/ffado_output_plugin.h | 25 | ||||
-rw-r--r-- | src/output/httpd_client.c | 764 | ||||
-rw-r--r-- | src/output/httpd_client.h | 71 | ||||
-rw-r--r-- | src/output/httpd_output_plugin.c | 623 | ||||
-rw-r--r-- | src/output/mvp_output_plugin.c | 344 | ||||
-rw-r--r-- | src/output/mvp_output_plugin.h | 25 | ||||
-rw-r--r-- | src/output/null_output_plugin.c | 129 |
43 files changed, 2985 insertions, 3728 deletions
diff --git a/src/output/alsa_output_plugin.c b/src/output/AlsaOutputPlugin.cxx index d8b184273..b26a3e1df 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,14 +18,17 @@ */ #include "config.h" -#include "alsa_output_plugin.h" -#include "output_api.h" -#include "mixer_list.h" -#include "pcm_export.h" +#include "AlsaOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "MixerList.hxx" +#include "pcm/PcmExport.hxx" +#include "util/Manual.hxx" #include <glib.h> #include <alsa/asoundlib.h> +#include <string> + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "alsa" @@ -34,23 +37,23 @@ static const char default_device[] = "default"; -enum { - MPD_ALSA_BUFFER_TIME_US = 500000, -}; +static constexpr unsigned MPD_ALSA_BUFFER_TIME_US = 500000; #define MPD_ALSA_RETRY_NR 5 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; + Manual<PcmExport> 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 +104,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 ¶m, GError **error_r) { + return ao_base_init(&base, &alsa_output_plugin, + param, error_r); + } + + void Deinit() { + ao_base_finish(&base); + } }; /** @@ -113,58 +135,47 @@ 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 config_param ¶m) { - ad->device = config_dup_block_string(param, "device", NULL); + ad->device = param.GetBlockValue("device", ""); - ad->use_mmap = config_get_block_bool(param, "use_mmap", false); + ad->use_mmap = param.GetBlockValue("use_mmap", false); - ad->dsd_usb = config_get_block_bool(param, "dsd_usb", false); + ad->dsd_usb = param.GetBlockValue("dsd_usb", false); - ad->buffer_time = config_get_block_unsigned(param, "buffer_time", - MPD_ALSA_BUFFER_TIME_US); - ad->period_time = config_get_block_unsigned(param, "period_time", 0); + ad->buffer_time = param.GetBlockValue("buffer_time", + MPD_ALSA_BUFFER_TIME_US); + ad->period_time = param.GetBlockValue("period_time", 0u); #ifdef SND_PCM_NO_AUTO_RESAMPLE - if (!config_get_block_bool(param, "auto_resample", true)) + if (!param.GetBlockValue("auto_resample", true)) ad->mode |= SND_PCM_NO_AUTO_RESAMPLE; #endif #ifdef SND_PCM_NO_AUTO_CHANNELS - if (!config_get_block_bool(param, "auto_channels", true)) + if (!param.GetBlockValue("auto_channels", true)) ad->mode |= SND_PCM_NO_AUTO_CHANNELS; #endif #ifdef SND_PCM_NO_AUTO_FORMAT - if (!config_get_block_bool(param, "auto_format", true)) + if (!param.GetBlockValue("auto_format", true)) ad->mode |= SND_PCM_NO_AUTO_FORMAT; #endif } static struct audio_output * -alsa_init(const struct config_param *param, GError **error_r) +alsa_init(const config_param ¶m, 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 +187,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; - - ao_base_finish(&ad->base); + AlsaOutput *ad = (AlsaOutput *)ao; - g_free(ad->device); - g_free(ad); + ad->Deinit(); + delete ad; /* free libasound's config cache */ snd_config_update_free_global(); @@ -190,18 +199,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); + ad->pcm_export.Construct(); 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); + ad->pcm_export.Destruct(); } static bool @@ -222,31 +231,31 @@ alsa_test_default_device(void) } static snd_pcm_format_t -get_bitformat(enum sample_format sample_format) +get_bitformat(SampleFormat sample_format) { switch (sample_format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: + case SampleFormat::UNDEFINED: + case SampleFormat::DSD: return SND_PCM_FORMAT_UNKNOWN; - case SAMPLE_FORMAT_S8: + case SampleFormat::S8: return SND_PCM_FORMAT_S8; - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: return SND_PCM_FORMAT_S16; - case SAMPLE_FORMAT_S24_P32: + case SampleFormat::S24_P32: return SND_PCM_FORMAT_S24; - case SAMPLE_FORMAT_S32: + case SampleFormat::S32: return SND_PCM_FORMAT_S32; - case SAMPLE_FORMAT_FLOAT: + case SampleFormat::FLOAT: return SND_PCM_FORMAT_FLOAT; } assert(false); - return SND_PCM_FORMAT_UNKNOWN; + gcc_unreachable(); } static snd_pcm_format_t @@ -313,7 +322,7 @@ alsa_try_format_or_packed(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, */ static int alsa_output_try_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, - enum sample_format sample_format, + SampleFormat sample_format, bool *packed_r, bool *reverse_endian_r) { snd_pcm_format_t alsa_format = get_bitformat(sample_format); @@ -344,35 +353,36 @@ alsa_output_try_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, */ static int alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, - struct audio_format *audio_format, + AudioFormat &audio_format, bool *packed_r, bool *reverse_endian_r) { /* try the input format first */ - int err = alsa_output_try_format(pcm, hwparams, audio_format->format, + int err = alsa_output_try_format(pcm, hwparams, + audio_format.format, packed_r, reverse_endian_r); /* if unsupported by the hardware, try other formats */ - static const enum sample_format probe_formats[] = { - SAMPLE_FORMAT_S24_P32, - SAMPLE_FORMAT_S32, - SAMPLE_FORMAT_S16, - SAMPLE_FORMAT_S8, - SAMPLE_FORMAT_UNDEFINED, + static const SampleFormat probe_formats[] = { + SampleFormat::S24_P32, + SampleFormat::S32, + SampleFormat::S16, + SampleFormat::S8, + SampleFormat::UNDEFINED, }; for (unsigned i = 0; - err == -EINVAL && probe_formats[i] != SAMPLE_FORMAT_UNDEFINED; + err == -EINVAL && probe_formats[i] != SampleFormat::UNDEFINED; ++i) { - const enum sample_format mpd_format = probe_formats[i]; - if (mpd_format == audio_format->format) + const SampleFormat mpd_format = probe_formats[i]; + if (mpd_format == audio_format.format) continue; err = alsa_output_try_format(pcm, hwparams, mpd_format, packed_r, reverse_endian_r); if (err == 0) - audio_format->format = mpd_format; + audio_format.format = mpd_format; } return err; @@ -383,15 +393,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, AudioFormat &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; + unsigned int sample_rate = audio_format.sample_rate; + unsigned int channels = audio_format.channels; int err; const char *cmd = NULL; int retry = MPD_ALSA_RETRY_NR; @@ -401,6 +407,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 +441,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(audio_format.format), snd_strerror(-err)); return false; } @@ -449,21 +456,21 @@ configure_hw: if (err < 0) { g_set_error(error, alsa_output_quark(), err, "ALSA device \"%s\" does not support %i channels: %s", - alsa_device(ad), (int)audio_format->channels, + alsa_device(ad), (int)audio_format.channels, snd_strerror(-err)); return false; } - audio_format->channels = (int8_t)channels; + audio_format.channels = (int8_t)channels; err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams, &sample_rate, NULL); if (err < 0 || sample_rate == 0) { g_set_error(error, alsa_output_quark(), err, "ALSA device \"%s\" does not support %u Hz audio", - alsa_device(ad), audio_format->sample_rate); + alsa_device(ad), audio_format.sample_rate); return false; } - audio_format->sample_rate = sample_rate; + audio_format.sample_rate = sample_rate; snd_pcm_uframes_t buffer_size_min, buffer_size_max; snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min); @@ -525,11 +532,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 +546,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 +586,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,22 +601,22 @@ error: } static bool -alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format, +alsa_setup_dsd(AlsaOutput *ad, const AudioFormat audio_format, bool *shift8_r, bool *packed_r, bool *reverse_endian_r, GError **error_r) { assert(ad->dsd_usb); - assert(audio_format->format == SAMPLE_FORMAT_DSD); + assert(audio_format.format == SampleFormat::DSD); /* pass 24 bit to alsa_setup() */ - struct audio_format usb_format = *audio_format; - usb_format.format = SAMPLE_FORMAT_S24_P32; + AudioFormat usb_format = audio_format; + usb_format.format = SampleFormat::S24_P32; usb_format.sample_rate /= 2; - const struct audio_format check = usb_format; + const AudioFormat check = usb_format; - if (!alsa_setup(ad, &usb_format, packed_r, reverse_endian_r, error_r)) + if (!alsa_setup(ad, usb_format, packed_r, reverse_endian_r, error_r)) return false; /* if the device allows only 32 bit, shift all DSD-over-USB @@ -609,16 +624,17 @@ alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format, the DSD-over-USB documentation does not specify whether this is legal, but there is anecdotical evidence that this is possible (and the only option for some devices) */ - *shift8_r = usb_format.format == SAMPLE_FORMAT_S32; - if (usb_format.format == SAMPLE_FORMAT_S32) - usb_format.format = SAMPLE_FORMAT_S24_P32; + *shift8_r = usb_format.format == SampleFormat::S32; + if (usb_format.format == SampleFormat::S32) + usb_format.format = SampleFormat::S24_P32; - if (!audio_format_equals(&usb_format, &check)) { + if (usb_format != check) { /* no bit-perfect playback, which is required for DSD over USB */ 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,13 +642,13 @@ 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, AudioFormat &audio_format, GError **error_r) { bool shift8 = false, packed, reverse_endian; const bool dsd_usb = ad->dsd_usb && - audio_format->format == SAMPLE_FORMAT_DSD; + audio_format.format == SampleFormat::DSD; const bool success = dsd_usb ? alsa_setup_dsd(ad, audio_format, &shift8, &packed, &reverse_endian, @@ -642,21 +658,19 @@ 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, - dsd_usb, shift8, packed, reverse_endian); + ad->pcm_export->Open(audio_format.format, + audio_format.channels, + dsd_usb, shift8, packed, reverse_endian); return true; } static bool -alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) +alsa_open(struct audio_output *ao, AudioFormat &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 +681,28 @@ 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->in_frame_size = audio_format.GetFrameSize(); + ad->out_frame_size = ad->pcm_export->GetFrameSize(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 +723,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 +761,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 +771,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(¶ms); - 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 +782,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 +792,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 = ad->pcm_export->Export(chunk, size, size); assert(size % ad->out_frame_size == 0); @@ -789,8 +819,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, - bytes_written); + return ad->pcm_export->CalcSourceSize(bytes_written); } if (ret < 0 && ret != -EAGAIN && ret != -EINTR && @@ -803,17 +832,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 daa1f3615..dc7e639a8 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/ao_output_plugin.c b/src/output/AoOutputPlugin.cxx index d7e577fa4..db7b9a360 100644 --- a/src/output/ao_output_plugin.c +++ b/src/output/AoOutputPlugin.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,28 +18,41 @@ */ #include "config.h" -#include "ao_output_plugin.h" -#include "output_api.h" +#include "AoOutputPlugin.hxx" +#include "OutputAPI.hxx" #include <ao/ao.h> #include <glib.h> +#include <string.h> + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "ao" /* An ao_sample_format, with all fields set to zero: */ -static const ao_sample_format OUR_AO_FORMAT_INITIALIZER; +static ao_sample_format OUR_AO_FORMAT_INITIALIZER; static unsigned ao_output_ref; -struct ao_data { +struct AoOutput { struct audio_output base; size_t write_size; int driver; ao_option *options; ao_device *device; -} AoData; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &ao_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } + + bool Configure(const config_param ¶m, GError **error_r); +}; static inline GQuark ao_output_quark(void) @@ -81,78 +94,83 @@ ao_output_error(GError **error_r) "%s", error); } -static struct audio_output * -ao_output_init(const struct config_param *param, - GError **error) +inline bool +AoOutput::Configure(const config_param ¶m, GError **error_r) { - struct ao_data *ad = g_new(struct ao_data, 1); - - if (!ao_base_init(&ad->base, &ao_output_plugin, param, error)) { - g_free(ad); - return NULL; - } - - ao_info *ai; const char *value; - ad->options = NULL; + options = nullptr; - ad->write_size = config_get_block_unsigned(param, "write_size", 1024); + write_size = param.GetBlockValue("write_size", 1024u); if (ao_output_ref == 0) { ao_initialize(); } ao_output_ref++; - value = config_get_block_string(param, "driver", "default"); + value = param.GetBlockValue("driver", "default"); if (0 == strcmp(value, "default")) - ad->driver = ao_default_driver_id(); + driver = ao_default_driver_id(); else - ad->driver = ao_driver_id(value); + driver = ao_driver_id(value); - if (ad->driver < 0) { - g_set_error(error, ao_output_quark(), 0, + if (driver < 0) { + g_set_error(error_r, ao_output_quark(), 0, "\"%s\" is not a valid ao driver", value); - ao_base_finish(&ad->base); - g_free(ad); - return NULL; + return false; } - if ((ai = ao_driver_info(ad->driver)) == NULL) { - g_set_error(error, ao_output_quark(), 0, + ao_info *ai = ao_driver_info(driver); + if (ai == nullptr) { + g_set_error(error_r, ao_output_quark(), 0, "problems getting driver info"); - ao_base_finish(&ad->base); - g_free(ad); - return NULL; + return false; } g_debug("using ao driver \"%s\" for \"%s\"\n", ai->short_name, - config_get_block_string(param, "name", NULL)); + param.GetBlockValue("name", nullptr)); - value = config_get_block_string(param, "options", NULL); - if (value != NULL) { - gchar **options = g_strsplit(value, ";", 0); + value = param.GetBlockValue("options", nullptr); + if (value != nullptr) { + gchar **_options = g_strsplit(value, ";", 0); - for (unsigned i = 0; options[i] != NULL; ++i) { - gchar **key_value = g_strsplit(options[i], "=", 2); + for (unsigned i = 0; _options[i] != nullptr; ++i) { + gchar **key_value = g_strsplit(_options[i], "=", 2); - if (key_value[0] == NULL || key_value[1] == NULL) { - g_set_error(error, ao_output_quark(), 0, + if (key_value[0] == nullptr || key_value[1] == nullptr) { + g_set_error(error_r, ao_output_quark(), 0, "problems parsing options \"%s\"", - options[i]); - ao_base_finish(&ad->base); - g_free(ad); - return NULL; + _options[i]); + return false; } - ao_append_option(&ad->options, key_value[0], + ao_append_option(&options, key_value[0], key_value[1]); g_strfreev(key_value); } - g_strfreev(options); + g_strfreev(_options); + } + + return true; +} + +static struct audio_output * +ao_output_init(const config_param ¶m, GError **error_r) +{ + AoOutput *ad = new AoOutput(); + + if (!ad->Initialize(param, error_r)) { + delete ad; + return nullptr; + } + + if (!ad->Configure(param, error_r)) { + ad->Deinitialize(); + delete ad; + return nullptr; } return &ad->base; @@ -161,11 +179,11 @@ ao_output_init(const struct config_param *param, static void ao_output_finish(struct audio_output *ao) { - struct ao_data *ad = (struct ao_data *)ao; + AoOutput *ad = (AoOutput *)ao; ao_free_options(ad->options); - ao_base_finish(&ad->base); - g_free(ad); + ad->Deinitialize(); + delete ad; ao_output_ref--; @@ -176,24 +194,24 @@ ao_output_finish(struct audio_output *ao) static void ao_output_close(struct audio_output *ao) { - struct ao_data *ad = (struct ao_data *)ao; + AoOutput *ad = (AoOutput *)ao; ao_close(ad->device); } static bool -ao_output_open(struct audio_output *ao, struct audio_format *audio_format, +ao_output_open(struct audio_output *ao, AudioFormat &audio_format, GError **error) { ao_sample_format format = OUR_AO_FORMAT_INITIALIZER; - struct ao_data *ad = (struct ao_data *)ao; + AoOutput *ad = (AoOutput *)ao; - switch (audio_format->format) { - case SAMPLE_FORMAT_S8: + switch (audio_format.format) { + case SampleFormat::S8: format.bits = 8; break; - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: format.bits = 16; break; @@ -201,18 +219,18 @@ ao_output_open(struct audio_output *ao, struct audio_format *audio_format, /* support for 24 bit samples in libao is currently dubious, and until we have sorted that out, convert everything to 16 bit */ - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; format.bits = 16; break; } - format.rate = audio_format->sample_rate; + format.rate = audio_format.sample_rate; format.byte_format = AO_FMT_NATIVE; - format.channels = audio_format->channels; + format.channels = audio_format.channels; ad->device = ao_open_live(ad->driver, &format, ad->options); - if (ad->device == NULL) { + if (ad->device == nullptr) { ao_output_error(error); return false; } @@ -230,7 +248,7 @@ static int ao_play_deconst(ao_device *device, const void *output_samples, { union { const void *in; - void *out; + char *out; } u; u.in = output_samples; @@ -241,7 +259,7 @@ static size_t ao_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct ao_data *ad = (struct ao_data *)ao; + AoOutput *ad = (AoOutput *)ao; if (size > ad->write_size) size = ad->write_size; @@ -255,10 +273,19 @@ ao_output_play(struct audio_output *ao, const void *chunk, size_t size, } const struct audio_output_plugin ao_output_plugin = { - .name = "ao", - .init = ao_output_init, - .finish = ao_output_finish, - .open = ao_output_open, - .close = ao_output_close, - .play = ao_output_play, + "ao", + nullptr, + ao_output_init, + ao_output_finish, + nullptr, + nullptr, + ao_output_open, + ao_output_close, + nullptr, + nullptr, + ao_output_play, + nullptr, + nullptr, + nullptr, + nullptr, }; diff --git a/src/output/ao_output_plugin.h b/src/output/AoOutputPlugin.hxx index 9a3a47c05..a44885e56 100644 --- a/src/output/ao_output_plugin.h +++ b/src/output/AoOutputPlugin.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_AO_OUTPUT_PLUGIN_H -#define MPD_AO_OUTPUT_PLUGIN_H +#ifndef MPD_AO_OUTPUT_PLUGIN_HXX +#define MPD_AO_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin ao_output_plugin; diff --git a/src/output/fifo_output_plugin.c b/src/output/FifoOutputPlugin.cxx index 022be0b4a..50062988c 100644 --- a/src/output/fifo_output_plugin.c +++ b/src/output/FifoOutputPlugin.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,10 +18,9 @@ */ #include "config.h" -#include "fifo_output_plugin.h" -#include "output_api.h" -#include "utils.h" -#include "timer.h" +#include "FifoOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "Timer.hxx" #include "fd_util.h" #include "open.h" @@ -38,14 +37,37 @@ #define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */ -struct fifo_data { +struct FifoOutput { struct audio_output base; char *path; int input; int output; bool created; - struct timer *timer; + Timer *timer; + + FifoOutput() + :path(nullptr), input(-1), output(-1), created(false) {} + + ~FifoOutput() { + g_free(path); + } + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &fifo_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } + + bool Create(GError **error_r); + bool Check(GError **error_r); + void Delete(); + + bool Open(GError **error_r); + void Close(); }; /** @@ -57,156 +79,138 @@ fifo_output_quark(void) return g_quark_from_static_string("fifo_output"); } -static struct fifo_data *fifo_data_new(void) -{ - struct fifo_data *ret; - - ret = g_new(struct fifo_data, 1); - - ret->path = NULL; - ret->input = -1; - ret->output = -1; - ret->created = false; - - return ret; -} - -static void fifo_data_free(struct fifo_data *fd) +inline void +FifoOutput::Delete() { - g_free(fd->path); - g_free(fd); -} - -static void fifo_delete(struct fifo_data *fd) -{ - g_debug("Removing FIFO \"%s\"", fd->path); + g_debug("Removing FIFO \"%s\"", path); - if (unlink(fd->path) < 0) { + if (unlink(path) < 0) { g_warning("Could not remove FIFO \"%s\": %s", - fd->path, g_strerror(errno)); + path, g_strerror(errno)); return; } - fd->created = false; + created = false; } -static void -fifo_close(struct fifo_data *fd) +void +FifoOutput::Close() { struct stat st; - if (fd->input >= 0) { - close(fd->input); - fd->input = -1; + if (input >= 0) { + close(input); + input = -1; } - if (fd->output >= 0) { - close(fd->output); - fd->output = -1; + if (output >= 0) { + close(output); + output = -1; } - if (fd->created && (stat(fd->path, &st) == 0)) - fifo_delete(fd); + if (created && (stat(path, &st) == 0)) + Delete(); } -static bool -fifo_make(struct fifo_data *fd, GError **error) +inline bool +FifoOutput::Create(GError **error_r) { - if (mkfifo(fd->path, 0666) < 0) { - g_set_error(error, fifo_output_quark(), errno, + if (mkfifo(path, 0666) < 0) { + g_set_error(error_r, fifo_output_quark(), errno, "Couldn't create FIFO \"%s\": %s", - fd->path, g_strerror(errno)); + path, g_strerror(errno)); return false; } - fd->created = true; - + created = true; return true; } -static bool -fifo_check(struct fifo_data *fd, GError **error) +inline bool +FifoOutput::Check(GError **error_r) { struct stat st; - - if (stat(fd->path, &st) < 0) { + if (stat(path, &st) < 0) { if (errno == ENOENT) { /* Path doesn't exist */ - return fifo_make(fd, error); + return Create(error_r); } - g_set_error(error, fifo_output_quark(), errno, + g_set_error(error_r, fifo_output_quark(), errno, "Failed to stat FIFO \"%s\": %s", - fd->path, g_strerror(errno)); + path, g_strerror(errno)); return false; } if (!S_ISFIFO(st.st_mode)) { - g_set_error(error, fifo_output_quark(), 0, + g_set_error(error_r, fifo_output_quark(), 0, "\"%s\" already exists, but is not a FIFO", - fd->path); + path); return false; } return true; } -static bool -fifo_open(struct fifo_data *fd, GError **error) +inline bool +FifoOutput::Open(GError **error_r) { - if (!fifo_check(fd, error)) + if (!Check(error_r)) return false; - fd->input = open_cloexec(fd->path, O_RDONLY|O_NONBLOCK|O_BINARY, 0); - if (fd->input < 0) { - g_set_error(error, fifo_output_quark(), errno, + input = open_cloexec(path, O_RDONLY|O_NONBLOCK|O_BINARY, 0); + if (input < 0) { + g_set_error(error_r, fifo_output_quark(), errno, "Could not open FIFO \"%s\" for reading: %s", - fd->path, g_strerror(errno)); - fifo_close(fd); + path, g_strerror(errno)); + Close(); return false; } - fd->output = open_cloexec(fd->path, O_WRONLY|O_NONBLOCK|O_BINARY, 0); - if (fd->output < 0) { - g_set_error(error, fifo_output_quark(), errno, + output = open_cloexec(path, O_WRONLY|O_NONBLOCK|O_BINARY, 0); + if (output < 0) { + g_set_error(error_r, fifo_output_quark(), errno, "Could not open FIFO \"%s\" for writing: %s", - fd->path, g_strerror(errno)); - fifo_close(fd); + path, g_strerror(errno)); + Close(); return false; } return true; } -static struct audio_output * -fifo_output_init(const struct config_param *param, - GError **error_r) +static bool +fifo_open(FifoOutput *fd, GError **error_r) { - struct fifo_data *fd; + return fd->Open(error_r); +} - GError *error = NULL; - char *path = config_dup_block_path(param, "path", &error); +static struct audio_output * +fifo_output_init(const config_param ¶m, GError **error_r) +{ + GError *error = nullptr; + char *path = param.DupBlockPath("path", &error); if (!path) { - if (error != NULL) + if (error != nullptr) g_propagate_error(error_r, error); else g_set_error(error_r, fifo_output_quark(), 0, "No \"path\" parameter specified"); - return NULL; + return nullptr; } - fd = fifo_data_new(); + FifoOutput *fd = new FifoOutput(); fd->path = path; - if (!ao_base_init(&fd->base, &fifo_output_plugin, param, error_r)) { - fifo_data_free(fd); - return NULL; + if (!fd->Initialize(param, error_r)) { + delete fd; + return nullptr; } if (!fifo_open(fd, error_r)) { - ao_base_finish(&fd->base); - fifo_data_free(fd); - return NULL; + fd->Deinitialize(); + delete fd; + return nullptr; } return &fd->base; @@ -215,20 +219,20 @@ fifo_output_init(const struct config_param *param, static void fifo_output_finish(struct audio_output *ao) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; - fifo_close(fd); - ao_base_finish(&fd->base); - fifo_data_free(fd); + fd->Close(); + fd->Deinitialize(); + delete fd; } static bool -fifo_output_open(struct audio_output *ao, struct audio_format *audio_format, +fifo_output_open(struct audio_output *ao, AudioFormat &audio_format, G_GNUC_UNUSED GError **error) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; - fd->timer = timer_new(audio_format); + fd->timer = new Timer(audio_format); return true; } @@ -236,19 +240,19 @@ fifo_output_open(struct audio_output *ao, struct audio_format *audio_format, static void fifo_output_close(struct audio_output *ao) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; - timer_free(fd->timer); + delete fd->timer; } static void fifo_output_cancel(struct audio_output *ao) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; char buf[FIFO_BUFFER_SIZE]; int bytes = 1; - timer_reset(fd->timer); + fd->timer->Reset(); while (bytes > 0 && errno != EINTR) bytes = read(fd->input, buf, FIFO_BUFFER_SIZE); @@ -262,10 +266,10 @@ fifo_output_cancel(struct audio_output *ao) static unsigned fifo_output_delay(struct audio_output *ao) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; - return fd->timer->started - ? timer_delay(fd->timer) + return fd->timer->IsStarted() + ? fd->timer->GetDelay() : 0; } @@ -273,12 +277,12 @@ static size_t fifo_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct fifo_data *fd = (struct fifo_data *)ao; + FifoOutput *fd = (FifoOutput *)ao; ssize_t bytes; - if (!fd->timer->started) - timer_start(fd->timer); - timer_add(fd->timer, size); + if (!fd->timer->IsStarted()) + fd->timer->Start(); + fd->timer->Add(size); while (true) { bytes = write(fd->output, chunk, size); @@ -304,12 +308,19 @@ fifo_output_play(struct audio_output *ao, const void *chunk, size_t size, } const struct audio_output_plugin fifo_output_plugin = { - .name = "fifo", - .init = fifo_output_init, - .finish = fifo_output_finish, - .open = fifo_output_open, - .close = fifo_output_close, - .delay = fifo_output_delay, - .play = fifo_output_play, - .cancel = fifo_output_cancel, + "fifo", + nullptr, + fifo_output_init, + fifo_output_finish, + nullptr, + nullptr, + fifo_output_open, + fifo_output_close, + fifo_output_delay, + nullptr, + fifo_output_play, + nullptr, + fifo_output_cancel, + nullptr, + nullptr, }; diff --git a/src/output/fifo_output_plugin.h b/src/output/FifoOutputPlugin.hxx index 85f7985e1..dca2886d8 100644 --- a/src/output/fifo_output_plugin.h +++ b/src/output/FifoOutputPlugin.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_FIFO_OUTPUT_PLUGIN_H -#define MPD_FIFO_OUTPUT_PLUGIN_H +#ifndef MPD_FIFO_OUTPUT_PLUGIN_HXX +#define MPD_FIFO_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin fifo_output_plugin; diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx new file mode 100644 index 000000000..0a00ee2f9 --- /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 000000000..46196d2e3 --- /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 5dcb8ab9b..e8a37e033 100644 --- a/src/output/httpd_internal.h +++ b/src/output/HttpdInternal.hxx @@ -25,16 +25,22 @@ #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H #define MPD_OUTPUT_HTTPD_INTERNAL_H -#include "output_internal.h" -#include "timer.h" - -#include <glib.h> - -#include <stdbool.h> - -struct httpd_client; - -struct httpd_output { +#include "OutputInternal.hxx" +#include "Timer.hxx" +#include "thread/Mutex.hxx" +#include "event/ServerSocket.hxx" + +#include <forward_list> + +struct config_param; +class EventLoop; +class ServerSocket; +class HttpdClient; +class Page; +struct Encoder; +struct Tag; + +struct HttpdOutput final : private ServerSocket { struct audio_output base; /** @@ -46,7 +52,7 @@ struct httpd_output { /** * The configured encoder plugin. */ - struct encoder *encoder; + Encoder *encoder; /** * Number of bytes which were fed into the encoder, without @@ -65,28 +71,23 @@ 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 + * A #Timer object to synchronize this output with the * wallclock. */ - struct timer *timer; - - /** - * The listener socket. - */ - struct server_socket *server_socket; + Timer *timer; /** * 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 +106,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 +119,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 ¶m, GError **error_r); + + bool Bind(GError **error_r); + void Unbind(); + + /** + * Caller must lock the mutex. + */ + bool OpenEncoder(AudioFormat &audio_format, + GError **error_r); + + /** + * Caller must lock the mutex. + */ + bool Open(AudioFormat &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 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 000000000..4169aabfa --- /dev/null +++ b/src/output/HttpdOutputPlugin.cxx @@ -0,0 +1,568 @@ +/* + * 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 "OutputAPI.hxx" +#include "EncoderPlugin.hxx" +#include "EncoderList.hxx" +#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 <string.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 ¶m, GError **error_r) +{ + /* read configuration */ + name = param.GetBlockValue("name", "Set name in config"); + genre = param.GetBlockValue("genre", "Set genre in config"); + website = param.GetBlockValue("website", "Set website in config"); + + guint port = param.GetBlockValue("port", 8000u); + + const char *encoder_name = + param.GetBlockValue("encoder", "vorbis"); + const auto 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 = param.GetBlockValue("max_clients", 0u); + + /* set up bind_to_address */ + + const char *bind_to_address = param.GetBlockValue("bind_to_address"); + 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 ¶m, + 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 == nullptr); + ++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(AudioFormat &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(AudioFormat &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 = new Timer(audio_format); + + open = true; + + return true; +} + +static bool +httpd_output_open(struct audio_output *ao, AudioFormat &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; + + delete 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 */ + httpd->timer->Reset(); + + /* 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->IsStarted() + ? httpd->timer->GetDelay() + : 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->IsStarted()) + httpd->timer->Start(); + httpd->timer->Add(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 Tag *tag) +{ + assert(tag != NULL); + + if (encoder->plugin.tag != nullptr) { + /* 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 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 d0eb1533f..c74d2bd4a 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/jack_output_plugin.c b/src/output/JackOutputPlugin.cxx index d5c8ca412..241857d82 100644 --- a/src/output/jack_output_plugin.c +++ b/src/output/JackOutputPlugin.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,8 +18,8 @@ */ #include "config.h" -#include "jack_output_plugin.h" -#include "output_api.h" +#include "JackOutputPlugin.hxx" +#include "OutputAPI.hxx" #include <assert.h> @@ -29,6 +29,7 @@ #include <jack/ringbuffer.h> #include <stdlib.h> +#include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> @@ -43,7 +44,7 @@ enum { static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t); -struct jack_data { +struct JackOutput { struct audio_output base; /** @@ -66,7 +67,7 @@ struct jack_data { size_t ringbuffer_size; /* the current audio format */ - struct audio_format audio_format; + AudioFormat audio_format; /* jack library stuff */ jack_port_t *ports[MAX_PORTS]; @@ -80,6 +81,15 @@ struct jack_data { * silence. */ bool pause; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &jack_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } }; /** @@ -96,7 +106,7 @@ jack_output_quark(void) * channels. */ static jack_nframes_t -mpd_jack_available(const struct jack_data *jd) +mpd_jack_available(const JackOutput *jd) { size_t min = jack_ringbuffer_read_space(jd->ringbuffer[0]); @@ -114,8 +124,7 @@ mpd_jack_available(const struct jack_data *jd) static int mpd_jack_process(jack_nframes_t nframes, void *arg) { - struct jack_data *jd = (struct jack_data *) arg; - jack_default_audio_sample_t *out; + JackOutput *jd = (JackOutput *) arg; if (nframes <= 0) return 0; @@ -131,7 +140,9 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) /* generate silence while MPD is paused */ for (unsigned i = 0; i < jd->audio_format.channels; ++i) { - out = jack_port_get_buffer(jd->ports[i], nframes); + jack_default_audio_sample_t *out = + (jack_default_audio_sample_t *) + jack_port_get_buffer(jd->ports[i], nframes); for (jack_nframes_t f = 0; f < nframes; ++f) out[f] = 0.0; @@ -145,8 +156,10 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) available = nframes; for (unsigned i = 0; i < jd->audio_format.channels; ++i) { - out = jack_port_get_buffer(jd->ports[i], nframes); - if (out == NULL) + jack_default_audio_sample_t *out = + (jack_default_audio_sample_t *) + jack_port_get_buffer(jd->ports[i], nframes); + if (out == nullptr) /* workaround for libjack1 bug: if the server connection fails, the process callback is invoked anyway, but unable to get a @@ -165,8 +178,10 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) for (unsigned i = jd->audio_format.channels; i < jd->num_source_ports; ++i) { - out = jack_port_get_buffer(jd->ports[i], nframes); - if (out == NULL) + jack_default_audio_sample_t *out = + (jack_default_audio_sample_t *) + jack_port_get_buffer(jd->ports[i], nframes); + if (out == nullptr) /* workaround for libjack1 bug: if the server connection fails, the process callback is invoked anyway, but unable to get a @@ -183,23 +198,23 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) static void mpd_jack_shutdown(void *arg) { - struct jack_data *jd = (struct jack_data *) arg; + JackOutput *jd = (JackOutput *) arg; jd->shutdown = true; } static void -set_audioformat(struct jack_data *jd, struct audio_format *audio_format) +set_audioformat(JackOutput *jd, AudioFormat &audio_format) { - audio_format->sample_rate = jack_get_sample_rate(jd->client); + audio_format.sample_rate = jack_get_sample_rate(jd->client); if (jd->num_source_ports == 1) - audio_format->channels = 1; - else if (audio_format->channels > jd->num_source_ports) - audio_format->channels = 2; + audio_format.channels = 1; + else if (audio_format.channels > jd->num_source_ports) + audio_format.channels = 2; - if (audio_format->format != SAMPLE_FORMAT_S16 && - audio_format->format != SAMPLE_FORMAT_S24_P32) - audio_format->format = SAMPLE_FORMAT_S24_P32; + if (audio_format.format != SampleFormat::S16 && + audio_format.format != SampleFormat::S24_P32) + audio_format.format = SampleFormat::S24_P32; } static void @@ -220,14 +235,14 @@ mpd_jack_info(const char *msg) * Disconnect the JACK client. */ static void -mpd_jack_disconnect(struct jack_data *jd) +mpd_jack_disconnect(JackOutput *jd) { - assert(jd != NULL); - assert(jd->client != NULL); + assert(jd != nullptr); + assert(jd->client != nullptr); jack_deactivate(jd->client); jack_client_close(jd->client); - jd->client = NULL; + jd->client = nullptr; } /** @@ -235,17 +250,17 @@ mpd_jack_disconnect(struct jack_data *jd) * (e.g. register callbacks). */ static bool -mpd_jack_connect(struct jack_data *jd, GError **error_r) +mpd_jack_connect(JackOutput *jd, GError **error_r) { jack_status_t status; - assert(jd != NULL); + assert(jd != nullptr); jd->shutdown = false; jd->client = jack_client_open(jd->name, jd->options, &status, jd->server_name); - if (jd->client == NULL) { + if (jd->client == nullptr) { g_set_error(error_r, jack_output_quark(), 0, "Failed to connect to JACK server, status=%d", status); @@ -260,7 +275,7 @@ mpd_jack_connect(struct jack_data *jd, GError **error_r) jd->source_ports[i], JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - if (jd->ports[i] == NULL) { + if (jd->ports[i] == nullptr) { g_set_error(error_r, jack_output_quark(), 0, "Cannot register output port \"%s\"", jd->source_ports[i]); @@ -284,7 +299,7 @@ parse_port_list(int line, const char *source, char **dest, GError **error_r) char **list = g_strsplit(source, ",", 0); unsigned n = 0; - for (n = 0; list[n] != NULL; ++n) { + for (n = 0; list[n] != nullptr; ++n) { if (n >= MAX_PORTS) { g_set_error(error_r, jack_output_quark(), 0, "too many port names in line %d", @@ -308,59 +323,59 @@ parse_port_list(int line, const char *source, char **dest, GError **error_r) } static struct audio_output * -mpd_jack_init(const struct config_param *param, GError **error_r) +mpd_jack_init(const config_param ¶m, GError **error_r) { - struct jack_data *jd = g_new(struct jack_data, 1); + JackOutput *jd = new JackOutput(); - if (!ao_base_init(&jd->base, &jack_output_plugin, param, error_r)) { - g_free(jd); - return NULL; + if (!jd->Initialize(param, error_r)) { + delete jd; + return nullptr; } const char *value; jd->options = JackNullOption; - jd->name = config_get_block_string(param, "client_name", NULL); - if (jd->name != NULL) - jd->options |= JackUseExactName; + jd->name = param.GetBlockValue("client_name", nullptr); + if (jd->name != nullptr) + jd->options = jack_options_t(jd->options | JackUseExactName); else /* if there's a no configured client name, we don't care about the JackUseExactName option */ jd->name = "Music Player Daemon"; - jd->server_name = config_get_block_string(param, "server_name", NULL); - if (jd->server_name != NULL) - jd->options |= JackServerName; + jd->server_name = param.GetBlockValue("server_name", nullptr); + if (jd->server_name != nullptr) + jd->options = jack_options_t(jd->options | JackServerName); - if (!config_get_block_bool(param, "autostart", false)) - jd->options |= JackNoStartServer; + if (!param.GetBlockValue("autostart", false)) + jd->options = jack_options_t(jd->options | JackNoStartServer); /* configure the source ports */ - value = config_get_block_string(param, "source_ports", "left,right"); - jd->num_source_ports = parse_port_list(param->line, value, + value = param.GetBlockValue("source_ports", "left,right"); + jd->num_source_ports = parse_port_list(param.line, value, jd->source_ports, error_r); if (jd->num_source_ports == 0) - return NULL; + return nullptr; /* configure the destination ports */ - value = config_get_block_string(param, "destination_ports", NULL); - if (value == NULL) { + value = param.GetBlockValue("destination_ports", nullptr); + if (value == nullptr) { /* compatibility with MPD < 0.16 */ - value = config_get_block_string(param, "ports", NULL); - if (value != NULL) + value = param.GetBlockValue("ports", nullptr); + if (value != nullptr) g_warning("deprecated option 'ports' in line %d", - param->line); + param.line); } - if (value != NULL) { + if (value != nullptr) { jd->num_destination_ports = - parse_port_list(param->line, value, + parse_port_list(param.line, value, jd->destination_ports, error_r); if (jd->num_destination_ports == 0) - return NULL; + return nullptr; } else { jd->num_destination_ports = 0; } @@ -370,10 +385,9 @@ mpd_jack_init(const struct config_param *param, GError **error_r) g_warning("number of source ports (%u) mismatches the " "number of destination ports (%u) in line %d", jd->num_source_ports, jd->num_destination_ports, - param->line); + param.line); - jd->ringbuffer_size = - config_get_block_unsigned(param, "ringbuffer_size", 32768); + jd->ringbuffer_size = param.GetBlockValue("ringbuffer_size", 32768u); jack_set_error_function(mpd_jack_error); @@ -387,7 +401,7 @@ mpd_jack_init(const struct config_param *param, GError **error_r) static void mpd_jack_finish(struct audio_output *ao) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; for (unsigned i = 0; i < jd->num_source_ports; ++i) g_free(jd->source_ports[i]); @@ -395,17 +409,17 @@ mpd_jack_finish(struct audio_output *ao) for (unsigned i = 0; i < jd->num_destination_ports; ++i) g_free(jd->destination_ports[i]); - ao_base_finish(&jd->base); - g_free(jd); + jd->Deinitialize(); + delete jd; } static bool mpd_jack_enable(struct audio_output *ao, GError **error_r) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; for (unsigned i = 0; i < jd->num_source_ports; ++i) - jd->ringbuffer[i] = NULL; + jd->ringbuffer[i] = nullptr; return mpd_jack_connect(jd, error_r); } @@ -413,15 +427,15 @@ mpd_jack_enable(struct audio_output *ao, GError **error_r) static void mpd_jack_disable(struct audio_output *ao) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; - if (jd->client != NULL) + if (jd->client != nullptr) mpd_jack_disconnect(jd); for (unsigned i = 0; i < jd->num_source_ports; ++i) { - if (jd->ringbuffer[i] != NULL) { + if (jd->ringbuffer[i] != nullptr) { jack_ringbuffer_free(jd->ringbuffer[i]); - jd->ringbuffer[i] = NULL; + jd->ringbuffer[i] = nullptr; } } } @@ -430,11 +444,11 @@ mpd_jack_disable(struct audio_output *ao) * Stops the playback on the JACK connection. */ static void -mpd_jack_stop(struct jack_data *jd) +mpd_jack_stop(JackOutput *jd) { - assert(jd != NULL); + assert(jd != nullptr); - if (jd->client == NULL) + if (jd->client == nullptr) return; if (jd->shutdown) @@ -446,13 +460,13 @@ mpd_jack_stop(struct jack_data *jd) } static bool -mpd_jack_start(struct jack_data *jd, GError **error_r) +mpd_jack_start(JackOutput *jd, GError **error_r) { const char *destination_ports[MAX_PORTS], **jports; - const char *duplicate_port = NULL; + const char *duplicate_port = nullptr; unsigned num_destination_ports; - assert(jd->client != NULL); + assert(jd->client != nullptr); assert(jd->audio_format.channels <= jd->num_source_ports); /* allocate the ring buffers on the first open(); these @@ -460,7 +474,7 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) because we can never know when mpd_jack_process() gets called */ for (unsigned i = 0; i < jd->num_source_ports; ++i) { - if (jd->ringbuffer[i] == NULL) + if (jd->ringbuffer[i] == nullptr) jd->ringbuffer[i] = jack_ringbuffer_create(jd->ringbuffer_size); @@ -479,20 +493,20 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) if (jd->num_destination_ports == 0) { /* no output ports were configured - ask libjack for defaults */ - jports = jack_get_ports(jd->client, NULL, NULL, + jports = jack_get_ports(jd->client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput); - if (jports == NULL) { + if (jports == nullptr) { g_set_error(error_r, jack_output_quark(), 0, "no ports found"); mpd_jack_stop(jd); return false; } - assert(*jports != NULL); + assert(*jports != nullptr); for (num_destination_ports = 0; num_destination_ports < MAX_PORTS && - jports[num_destination_ports] != NULL; + jports[num_destination_ports] != nullptr; ++num_destination_ports) { g_debug("destination_port[%u] = '%s'\n", num_destination_ports, @@ -507,7 +521,7 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) memcpy(destination_ports, jd->destination_ports, num_destination_ports * sizeof(*destination_ports)); - jports = NULL; + jports = nullptr; } assert(num_destination_ports > 0); @@ -541,7 +555,7 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) "Not a valid JACK port: %s", destination_ports[i]); - if (jports != NULL) + if (jports != nullptr) free(jports); mpd_jack_stop(jd); @@ -549,7 +563,7 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) } } - if (duplicate_port != NULL) { + if (duplicate_port != nullptr) { /* mono input file: connect the one source channel to the both destination channels */ int ret; @@ -561,7 +575,7 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) "Not a valid JACK port: %s", duplicate_port); - if (jports != NULL) + if (jports != nullptr) free(jports); mpd_jack_stop(jd); @@ -569,30 +583,30 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) } } - if (jports != NULL) + if (jports != nullptr) free(jports); return true; } static bool -mpd_jack_open(struct audio_output *ao, struct audio_format *audio_format, +mpd_jack_open(struct audio_output *ao, AudioFormat &audio_format, GError **error_r) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; - assert(jd != NULL); + assert(jd != nullptr); jd->pause = false; - if (jd->client != NULL && jd->shutdown) + if (jd->client != nullptr && jd->shutdown) mpd_jack_disconnect(jd); - if (jd->client == NULL && !mpd_jack_connect(jd, error_r)) + if (jd->client == nullptr && !mpd_jack_connect(jd, error_r)) return false; set_audioformat(jd, audio_format); - jd->audio_format = *audio_format; + jd->audio_format = audio_format; if (!mpd_jack_start(jd, error_r)) return false; @@ -603,7 +617,7 @@ mpd_jack_open(struct audio_output *ao, struct audio_format *audio_format, static void mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; mpd_jack_stop(jd); } @@ -611,7 +625,7 @@ mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao) static unsigned mpd_jack_delay(struct audio_output *ao) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; return jd->base.pause && jd->pause && !jd->shutdown ? 1000 @@ -625,7 +639,7 @@ sample_16_to_jack(int16_t sample) } static void -mpd_jack_write_samples_16(struct jack_data *jd, const int16_t *src, +mpd_jack_write_samples_16(JackOutput *jd, const int16_t *src, unsigned num_samples) { jack_default_audio_sample_t sample; @@ -634,7 +648,8 @@ mpd_jack_write_samples_16(struct jack_data *jd, const int16_t *src, while (num_samples-- > 0) { for (i = 0; i < jd->audio_format.channels; ++i) { sample = sample_16_to_jack(*src++); - jack_ringbuffer_write(jd->ringbuffer[i], (void*)&sample, + jack_ringbuffer_write(jd->ringbuffer[i], + (const char *)&sample, sizeof(sample)); } } @@ -647,7 +662,7 @@ sample_24_to_jack(int32_t sample) } static void -mpd_jack_write_samples_24(struct jack_data *jd, const int32_t *src, +mpd_jack_write_samples_24(JackOutput *jd, const int32_t *src, unsigned num_samples) { jack_default_audio_sample_t sample; @@ -656,29 +671,31 @@ mpd_jack_write_samples_24(struct jack_data *jd, const int32_t *src, while (num_samples-- > 0) { for (i = 0; i < jd->audio_format.channels; ++i) { sample = sample_24_to_jack(*src++); - jack_ringbuffer_write(jd->ringbuffer[i], (void*)&sample, + jack_ringbuffer_write(jd->ringbuffer[i], + (const char *)&sample, sizeof(sample)); } } } static void -mpd_jack_write_samples(struct jack_data *jd, const void *src, +mpd_jack_write_samples(JackOutput *jd, const void *src, unsigned num_samples) { switch (jd->audio_format.format) { - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: mpd_jack_write_samples_16(jd, (const int16_t*)src, num_samples); break; - case SAMPLE_FORMAT_S24_P32: + case SampleFormat::S24_P32: mpd_jack_write_samples_24(jd, (const int32_t*)src, num_samples); break; default: assert(false); + gcc_unreachable(); } } @@ -686,8 +703,8 @@ static size_t mpd_jack_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) { - struct jack_data *jd = (struct jack_data *)ao; - const size_t frame_size = audio_format_frame_size(&jd->audio_format); + JackOutput *jd = (JackOutput *)ao; + const size_t frame_size = jd->audio_format.GetFrameSize(); size_t space = 0, space1; jd->pause = false; @@ -730,7 +747,7 @@ mpd_jack_play(struct audio_output *ao, const void *chunk, size_t size, static bool mpd_jack_pause(struct audio_output *ao) { - struct jack_data *jd = (struct jack_data *)ao; + JackOutput *jd = (JackOutput *)ao; if (jd->shutdown) return false; @@ -741,15 +758,19 @@ mpd_jack_pause(struct audio_output *ao) } const struct audio_output_plugin jack_output_plugin = { - .name = "jack", - .test_default_device = mpd_jack_test_default_device, - .init = mpd_jack_init, - .finish = mpd_jack_finish, - .enable = mpd_jack_enable, - .disable = mpd_jack_disable, - .open = mpd_jack_open, - .delay = mpd_jack_delay, - .play = mpd_jack_play, - .pause = mpd_jack_pause, - .close = mpd_jack_close, + "jack", + mpd_jack_test_default_device, + mpd_jack_init, + mpd_jack_finish, + mpd_jack_enable, + mpd_jack_disable, + mpd_jack_open, + mpd_jack_close, + mpd_jack_delay, + nullptr, + mpd_jack_play, + nullptr, + nullptr, + mpd_jack_pause, + nullptr, }; diff --git a/src/output/jack_output_plugin.h b/src/output/JackOutputPlugin.hxx index 2f94ae7dc..908105ad2 100644 --- a/src/output/jack_output_plugin.h +++ b/src/output/JackOutputPlugin.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_JACK_OUTPUT_PLUGIN_H -#define MPD_JACK_OUTPUT_PLUGIN_H +#ifndef MPD_JACK_OUTPUT_PLUGIN_HXX +#define MPD_JACK_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin jack_output_plugin; diff --git a/src/output/NullOutputPlugin.cxx b/src/output/NullOutputPlugin.cxx new file mode 100644 index 000000000..0ce32fbda --- /dev/null +++ b/src/output/NullOutputPlugin.cxx @@ -0,0 +1,143 @@ +/* + * 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 "NullOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "Timer.hxx" + +#include <assert.h> + +struct NullOutput { + struct audio_output base; + + bool sync; + + Timer *timer; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &null_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } +}; + +static struct audio_output * +null_init(const config_param ¶m, GError **error_r) +{ + NullOutput *nd = new NullOutput(); + + if (!nd->Initialize(param, error_r)) { + delete nd; + return nullptr; + } + + nd->sync = param.GetBlockValue("sync", true); + + return &nd->base; +} + +static void +null_finish(struct audio_output *ao) +{ + NullOutput *nd = (NullOutput *)ao; + + nd->Deinitialize(); + delete nd; +} + +static bool +null_open(struct audio_output *ao, AudioFormat &audio_format, + gcc_unused GError **error) +{ + NullOutput *nd = (NullOutput *)ao; + + if (nd->sync) + nd->timer = new Timer(audio_format); + + return true; +} + +static void +null_close(struct audio_output *ao) +{ + NullOutput *nd = (NullOutput *)ao; + + if (nd->sync) + delete nd->timer; +} + +static unsigned +null_delay(struct audio_output *ao) +{ + NullOutput *nd = (NullOutput *)ao; + + return nd->sync && nd->timer->IsStarted() + ? nd->timer->GetDelay() + : 0; +} + +static size_t +null_play(struct audio_output *ao, gcc_unused const void *chunk, size_t size, + gcc_unused GError **error) +{ + NullOutput *nd = (NullOutput *)ao; + Timer *timer = nd->timer; + + if (!nd->sync) + return size; + + if (!timer->IsStarted()) + timer->Start(); + timer->Add(size); + + return size; +} + +static void +null_cancel(struct audio_output *ao) +{ + NullOutput *nd = (NullOutput *)ao; + + if (!nd->sync) + return; + + nd->timer->Reset(); +} + +const struct audio_output_plugin null_output_plugin = { + "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 392bf0aa3..a58f1cb13 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 cd51fca20..6e42b2518 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 "output_api.h" -#include "fifo_buffer.h" +#include "OSXOutputPlugin.hxx" +#include "OutputAPI.hxx" +#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,9 +65,9 @@ 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 config_param ¶m) { - const char *device = config_get_block_string(param, "device", NULL); + const char *device = param.GetBlockValue("device"); if (device == NULL || 0 == strcmp(device, "default")) { oo->component_subtype = kAudioUnitSubType_DefaultOutput; @@ -83,17 +85,15 @@ 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) +osx_output_init(const config_param ¶m, 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); @@ -320,30 +316,30 @@ osx_output_close(struct audio_output *ao) } static bool -osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) +osx_output_open(struct audio_output *ao, AudioFormat &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; + stream_description.mSampleRate = audio_format.sample_rate; stream_description.mFormatID = kAudioFormatLinearPCM; stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; - switch (audio_format->format) { - case SAMPLE_FORMAT_S8: + switch (audio_format.format) { + case SampleFormat::S8: stream_description.mBitsPerChannel = 8; break; - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: stream_description.mBitsPerChannel = 16; break; - case SAMPLE_FORMAT_S32: + case SampleFormat::S32: stream_description.mBitsPerChannel = 32; break; default: - audio_format->format = SAMPLE_FORMAT_S32; + audio_format.format = SampleFormat::S32; stream_description.mBitsPerChannel = 32; break; } @@ -352,11 +348,10 @@ osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GErr stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif - stream_description.mBytesPerPacket = - audio_format_frame_size(audio_format); + stream_description.mBytesPerPacket = audio_format.GetFrameSize(); stream_description.mFramesPerPacket = 1; stream_description.mBytesPerFrame = stream_description.mBytesPerPacket; - stream_description.mChannelsPerFrame = audio_format->channels; + stream_description.mChannelsPerFrame = audio_format.channels; ComponentResult result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, @@ -378,8 +373,8 @@ osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GErr } /* create a buffer of 1s */ - od->buffer = fifo_buffer_new(audio_format->sample_rate * - audio_format_frame_size(audio_format)); + od->buffer = fifo_buffer_new(audio_format.sample_rate * + audio_format.GetFrameSize()); status = AudioOutputUnitStart(od->au); if (status != 0) { @@ -397,9 +392,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 +405,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 +414,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 814702d4f..2a4172880 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/openal_output_plugin.c b/src/output/OpenALOutputPlugin.cxx index ebd35ef12..1864052fa 100644 --- a/src/output/openal_output_plugin.c +++ b/src/output/OpenALOutputPlugin.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,8 +18,8 @@ */ #include "config.h" -#include "openal_output_plugin.h" -#include "output_api.h" +#include "OpenALOutputPlugin.hxx" +#include "OutputAPI.hxx" #include <glib.h> @@ -37,7 +37,7 @@ /* should be enough for buffer size = 2048 */ #define NUM_BUFFERS 16 -struct openal_data { +struct OpenALOutput { struct audio_output base; const char *device_name; @@ -48,6 +48,15 @@ struct openal_data { ALuint source; ALenum format; ALuint frequency; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &openal_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } }; static inline GQuark @@ -57,33 +66,33 @@ openal_output_quark(void) } static ALenum -openal_audio_format(struct audio_format *audio_format) +openal_audio_format(AudioFormat &audio_format) { - /* note: cannot map SAMPLE_FORMAT_S8 to AL_FORMAT_STEREO8 or + /* note: cannot map SampleFormat::S8 to AL_FORMAT_STEREO8 or AL_FORMAT_MONO8 since OpenAL expects unsigned 8 bit samples, while MPD uses signed samples */ - switch (audio_format->format) { - case SAMPLE_FORMAT_S16: - if (audio_format->channels == 2) + switch (audio_format.format) { + case SampleFormat::S16: + if (audio_format.channels == 2) return AL_FORMAT_STEREO16; - if (audio_format->channels == 1) + if (audio_format.channels == 1) return AL_FORMAT_MONO16; /* fall back to mono */ - audio_format->channels = 1; + audio_format.channels = 1; return openal_audio_format(audio_format); default: /* fall back to 16 bit */ - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; return openal_audio_format(audio_format); } } G_GNUC_PURE static inline ALint -openal_get_source_i(const struct openal_data *od, ALenum param) +openal_get_source_i(const OpenALOutput *od, ALenum param) { ALint value; alGetSourcei(od->source, param, &value); @@ -92,34 +101,34 @@ openal_get_source_i(const struct openal_data *od, ALenum param) G_GNUC_PURE static inline bool -openal_has_processed(const struct openal_data *od) +openal_has_processed(const OpenALOutput *od) { return openal_get_source_i(od, AL_BUFFERS_PROCESSED) > 0; } G_GNUC_PURE static inline ALint -openal_is_playing(const struct openal_data *od) +openal_is_playing(const OpenALOutput *od) { return openal_get_source_i(od, AL_SOURCE_STATE) == AL_PLAYING; } static bool -openal_setup_context(struct openal_data *od, +openal_setup_context(OpenALOutput *od, GError **error) { od->device = alcOpenDevice(od->device_name); - if (od->device == NULL) { + if (od->device == nullptr) { g_set_error(error, openal_output_quark(), 0, "Error opening OpenAL device \"%s\"\n", od->device_name); return false; } - od->context = alcCreateContext(od->device, NULL); + od->context = alcCreateContext(od->device, nullptr); - if (od->context == NULL) { + if (od->context == nullptr) { g_set_error(error, openal_output_quark(), 0, "Error creating context for \"%s\"\n", od->device_name); @@ -131,19 +140,17 @@ openal_setup_context(struct openal_data *od, } static struct audio_output * -openal_init(const struct config_param *param, GError **error_r) +openal_init(const config_param ¶m, GError **error_r) { - const char *device_name = config_get_block_string(param, "device", NULL); - struct openal_data *od; - - if (device_name == NULL) { - device_name = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + const char *device_name = param.GetBlockValue("device"); + if (device_name == nullptr) { + device_name = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); } - od = g_new(struct openal_data, 1); - if (!ao_base_init(&od->base, &openal_output_plugin, param, error_r)) { - g_free(od); - return NULL; + OpenALOutput *od = new OpenALOutput(); + if (!od->Initialize(param, error_r)) { + delete od; + return nullptr; } od->device_name = device_name; @@ -154,17 +161,17 @@ openal_init(const struct config_param *param, GError **error_r) static void openal_finish(struct audio_output *ao) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; - ao_base_finish(&od->base); - g_free(od); + od->Deinitialize(); + delete od; } static bool -openal_open(struct audio_output *ao, struct audio_format *audio_format, +openal_open(struct audio_output *ao, AudioFormat &audio_format, GError **error) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; od->format = openal_audio_format(audio_format); @@ -191,7 +198,7 @@ openal_open(struct audio_output *ao, struct audio_format *audio_format, } od->filled = 0; - od->frequency = audio_format->sample_rate; + od->frequency = audio_format.sample_rate; return true; } @@ -199,7 +206,7 @@ openal_open(struct audio_output *ao, struct audio_format *audio_format, static void openal_close(struct audio_output *ao) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; alcMakeContextCurrent(od->context); alDeleteSources(1, &od->source); @@ -211,7 +218,7 @@ openal_close(struct audio_output *ao) static unsigned openal_delay(struct audio_output *ao) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; return od->filled < NUM_BUFFERS || openal_has_processed(od) ? 0 @@ -225,7 +232,7 @@ static size_t openal_play(struct audio_output *ao, const void *chunk, size_t size, G_GNUC_UNUSED GError **error) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; ALuint buffer; if (alcGetCurrentContext() != od->context) { @@ -256,7 +263,7 @@ openal_play(struct audio_output *ao, const void *chunk, size_t size, static void openal_cancel(struct audio_output *ao) { - struct openal_data *od = (struct openal_data *)ao; + OpenALOutput *od = (OpenALOutput *)ao; od->filled = 0; alcMakeContextCurrent(od->context); @@ -268,12 +275,19 @@ openal_cancel(struct audio_output *ao) } const struct audio_output_plugin openal_output_plugin = { - .name = "openal", - .init = openal_init, - .finish = openal_finish, - .open = openal_open, - .close = openal_close, - .delay = openal_delay, - .play = openal_play, - .cancel = openal_cancel, + "openal", + nullptr, + openal_init, + openal_finish, + nullptr, + nullptr, + openal_open, + openal_close, + openal_delay, + nullptr, + openal_play, + nullptr, + openal_cancel, + nullptr, + nullptr, }; diff --git a/src/output/openal_output_plugin.h b/src/output/OpenALOutputPlugin.hxx index 25f6ccf46..e1ebf3d4f 100644 --- a/src/output/openal_output_plugin.h +++ b/src/output/OpenALOutputPlugin.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_OPENAL_OUTPUT_PLUGIN_H -#define MPD_OPENAL_OUTPUT_PLUGIN_H +#ifndef MPD_OPENAL_OUTPUT_PLUGIN_HXX +#define MPD_OPENAL_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin openal_output_plugin; diff --git a/src/output/oss_output_plugin.c b/src/output/OssOutputPlugin.cxx index e366a4537..2ef0edd67 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 "output_api.h" -#include "mixer_list.h" +#include "OssOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "MixerList.hxx" #include "fd_util.h" -#include "glib_compat.h" #include <glib.h> @@ -53,14 +52,15 @@ #endif #ifdef AFMT_S24_PACKED -#include "pcm_export.h" +#include "pcm/PcmExport.hxx" +#include "util/Manual.hxx" #endif -struct oss_data { +struct OssOutput { struct audio_output base; #ifdef AFMT_S24_PACKED - struct pcm_export_state export; + Manual<PcmExport> pcm_export; #endif int fd; @@ -70,13 +70,24 @@ struct oss_data { * The current input audio format. This is needed to reopen * the device after cancel(). */ - struct audio_format audio_format; + AudioFormat audio_format; /** * The current OSS audio format. This is needed to reopen the * device after cancel(). */ int oss_format; + + OssOutput():fd(-1), device(nullptr) {} + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &oss_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } }; /** @@ -88,23 +99,6 @@ oss_output_quark(void) return g_quark_from_static_string("oss_output"); } -static struct oss_data * -oss_data_new(void) -{ - struct oss_data *ret = g_new(struct oss_data, 1); - - ret->device = NULL; - ret->fd = -1; - - return ret; -} - -static void -oss_data_free(struct oss_data *od) -{ - g_free(od); -} - enum oss_stat { OSS_STAT_NO_ERROR = 0, OSS_STAT_NOT_CHAR_DEV = -1, @@ -163,17 +157,16 @@ 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; ) { + const config_param empty; + 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(); - if (!ao_base_init(&od->base, &oss_output_plugin, NULL, - error)) { - g_free(od); + OssOutput *od = new OssOutput(); + if (!od->Initialize(empty, error)) { + delete od; return NULL; } @@ -182,7 +175,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: @@ -209,14 +202,13 @@ oss_open_default(GError **error) } static struct audio_output * -oss_output_init(const struct config_param *param, GError **error) +oss_output_init(const config_param ¶m, GError **error_r) { - const char *device = config_get_block_string(param, "device", NULL); + const char *device = param.GetBlockValue("device"); if (device != NULL) { - struct oss_data *od = oss_data_new(); - if (!ao_base_init(&od->base, &oss_output_plugin, param, - error)) { - g_free(od); + OssOutput *od = new OssOutput(); + if (!od->Initialize(param, error_r)) { + delete od; return NULL; } @@ -224,16 +216,16 @@ oss_output_init(const struct config_param *param, GError **error) return &od->base; } - return oss_open_default(error); + return oss_open_default(error_r); } static void oss_output_finish(struct audio_output *ao) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; ao_base_finish(&od->base); - oss_data_free(od); + delete od; } #ifdef AFMT_S24_PACKED @@ -241,24 +233,24 @@ oss_output_finish(struct audio_output *ao) static bool oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; - pcm_export_init(&od->export); + od->pcm_export.Construct(); return true; } static void oss_output_disable(struct audio_output *ao) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; - pcm_export_deinit(&od->export); + od->pcm_export.Destruct(); } #endif static void -oss_close(struct oss_data *od) +oss_close(OssOutput *od) { if (od->fd >= 0) close(od->fd); @@ -317,10 +309,10 @@ oss_try_ioctl(int fd, unsigned long request, int value, * specified number is not supported. */ static bool -oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r) +oss_setup_channels(int fd, AudioFormat &audio_format, GError **error_r) { const char *const msg = "Failed to set channel count"; - int channels = audio_format->channels; + int channels = audio_format.channels; enum oss_setup_result result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels, msg, error_r); switch (result) { @@ -328,7 +320,7 @@ oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r) if (!audio_valid_channel_count(channels)) break; - audio_format->channels = channels; + audio_format.channels = channels; return true; case ERROR: @@ -339,7 +331,7 @@ oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r) } for (unsigned i = 1; i < 2; ++i) { - if (i == audio_format->channels) + if (i == audio_format.channels) /* don't try that again */ continue; @@ -351,7 +343,7 @@ oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r) if (!audio_valid_channel_count(channels)) break; - audio_format->channels = channels; + audio_format.channels = channels; return true; case ERROR: @@ -371,11 +363,11 @@ oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r) * specified sample rate is not supported. */ static bool -oss_setup_sample_rate(int fd, struct audio_format *audio_format, +oss_setup_sample_rate(int fd, AudioFormat &audio_format, GError **error_r) { const char *const msg = "Failed to set sample rate"; - int sample_rate = audio_format->sample_rate; + int sample_rate = audio_format.sample_rate; enum oss_setup_result result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate, msg, error_r); @@ -384,7 +376,7 @@ oss_setup_sample_rate(int fd, struct audio_format *audio_format, if (!audio_valid_sample_rate(sample_rate)) break; - audio_format->sample_rate = sample_rate; + audio_format.sample_rate = sample_rate; return true; case ERROR: @@ -397,7 +389,7 @@ oss_setup_sample_rate(int fd, struct audio_format *audio_format, static const int sample_rates[] = { 48000, 44100, 0 }; for (unsigned i = 0; sample_rates[i] != 0; ++i) { sample_rate = sample_rates[i]; - if (sample_rate == (int)audio_format->sample_rate) + if (sample_rate == (int)audio_format.sample_rate) continue; result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate, @@ -407,7 +399,7 @@ oss_setup_sample_rate(int fd, struct audio_format *audio_format, if (!audio_valid_sample_rate(sample_rate)) break; - audio_format->sample_rate = sample_rate; + audio_format.sample_rate = sample_rate; return true; case ERROR: @@ -427,28 +419,28 @@ oss_setup_sample_rate(int fd, struct audio_format *audio_format, * AFMT_QUERY if there is no direct counterpart. */ static int -sample_format_to_oss(enum sample_format format) +sample_format_to_oss(SampleFormat format) { switch (format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_FLOAT: - case SAMPLE_FORMAT_DSD: + case SampleFormat::UNDEFINED: + case SampleFormat::FLOAT: + case SampleFormat::DSD: return AFMT_QUERY; - case SAMPLE_FORMAT_S8: + case SampleFormat::S8: return AFMT_S8; - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: return AFMT_S16_NE; - case SAMPLE_FORMAT_S24_P32: + case SampleFormat::S24_P32: #ifdef AFMT_S24_NE return AFMT_S24_NE; #else return AFMT_QUERY; #endif - case SAMPLE_FORMAT_S32: + case SampleFormat::S32: #ifdef AFMT_S32_NE return AFMT_S32_NE; #else @@ -461,50 +453,50 @@ sample_format_to_oss(enum sample_format format) /** * Convert an OSS sample format to its MPD counterpart. Returns - * SAMPLE_FORMAT_UNDEFINED if there is no direct counterpart. + * SampleFormat::UNDEFINED if there is no direct counterpart. */ -static enum sample_format +static SampleFormat sample_format_from_oss(int format) { switch (format) { case AFMT_S8: - return SAMPLE_FORMAT_S8; + return SampleFormat::S8; case AFMT_S16_NE: - return SAMPLE_FORMAT_S16; + return SampleFormat::S16; #ifdef AFMT_S24_PACKED case AFMT_S24_PACKED: - return SAMPLE_FORMAT_S24_P32; + return SampleFormat::S24_P32; #endif #ifdef AFMT_S24_NE case AFMT_S24_NE: - return SAMPLE_FORMAT_S24_P32; + return SampleFormat::S24_P32; #endif #ifdef AFMT_S32_NE case AFMT_S32_NE: - return SAMPLE_FORMAT_S32; + return SampleFormat::S32; #endif default: - return SAMPLE_FORMAT_UNDEFINED; + return SampleFormat::UNDEFINED; } } /** * Probe one sample format. * - * @return the selected sample format or SAMPLE_FORMAT_UNDEFINED on + * @return the selected sample format or SampleFormat::UNDEFINED on * error */ static enum oss_setup_result -oss_probe_sample_format(int fd, enum sample_format sample_format, - enum sample_format *sample_format_r, +oss_probe_sample_format(int fd, SampleFormat sample_format, + SampleFormat *sample_format_r, int *oss_format_r, #ifdef AFMT_S24_PACKED - struct pcm_export_state *export, + PcmExport &pcm_export, #endif GError **error_r) { @@ -518,7 +510,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format, "Failed to set sample format", error_r); #ifdef AFMT_S24_PACKED - if (result == UNSUPPORTED && sample_format == SAMPLE_FORMAT_S24_P32) { + if (result == UNSUPPORTED && sample_format == SampleFormat::S24_P32) { /* if the driver doesn't support padded 24 bit, try packed 24 bit */ oss_format = AFMT_S24_PACKED; @@ -532,14 +524,14 @@ oss_probe_sample_format(int fd, enum sample_format sample_format, return result; sample_format = sample_format_from_oss(oss_format); - if (sample_format == SAMPLE_FORMAT_UNDEFINED) + if (sample_format == SampleFormat::UNDEFINED) return UNSUPPORTED; *sample_format_r = sample_format; *oss_format_r = oss_format; #ifdef AFMT_S24_PACKED - pcm_export_open(export, sample_format, 0, false, false, + pcm_export.Open(sample_format, 0, false, false, oss_format == AFMT_S24_PACKED, oss_format == AFMT_S24_PACKED && G_BYTE_ORDER != G_LITTLE_ENDIAN); @@ -553,24 +545,24 @@ oss_probe_sample_format(int fd, enum sample_format sample_format, * specified format is not supported. */ static bool -oss_setup_sample_format(int fd, struct audio_format *audio_format, +oss_setup_sample_format(int fd, AudioFormat &audio_format, int *oss_format_r, #ifdef AFMT_S24_PACKED - struct pcm_export_state *export, + PcmExport &pcm_export, #endif GError **error_r) { - enum sample_format mpd_format; + SampleFormat mpd_format; enum oss_setup_result result = - oss_probe_sample_format(fd, audio_format->format, + oss_probe_sample_format(fd, audio_format.format, &mpd_format, oss_format_r, #ifdef AFMT_S24_PACKED - export, + pcm_export, #endif error_r); switch (result) { case SUCCESS: - audio_format->format = mpd_format; + audio_format.format = mpd_format; return true; case ERROR: @@ -586,29 +578,29 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format, /* the requested sample format is not available - probe for other formats supported by MPD */ - static const enum sample_format sample_formats[] = { - SAMPLE_FORMAT_S24_P32, - SAMPLE_FORMAT_S32, - SAMPLE_FORMAT_S16, - SAMPLE_FORMAT_S8, - SAMPLE_FORMAT_UNDEFINED /* sentinel */ + static const SampleFormat sample_formats[] = { + SampleFormat::S24_P32, + SampleFormat::S32, + SampleFormat::S16, + SampleFormat::S8, + SampleFormat::UNDEFINED /* sentinel */ }; - for (unsigned i = 0; sample_formats[i] != SAMPLE_FORMAT_UNDEFINED; ++i) { + for (unsigned i = 0; sample_formats[i] != SampleFormat::UNDEFINED; ++i) { mpd_format = sample_formats[i]; - if (mpd_format == audio_format->format) + if (mpd_format == audio_format.format) /* don't try that again */ continue; 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) { case SUCCESS: - audio_format->format = mpd_format; + audio_format.format = mpd_format; return true; case ERROR: @@ -628,14 +620,14 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format, * Sets up the OSS device which was opened before. */ static bool -oss_setup(struct oss_data *od, struct audio_format *audio_format, +oss_setup(OssOutput *od, AudioFormat &audio_format, GError **error_r) { return oss_setup_channels(od->fd, audio_format, error_r) && 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); } @@ -644,7 +636,7 @@ oss_setup(struct oss_data *od, struct audio_format *audio_format, * Reopen the device with the saved audio_format, without any probing. */ static bool -oss_reopen(struct oss_data *od, GError **error_r) +oss_reopen(OssOutput *od, GError **error_r) { assert(od->fd < 0); @@ -696,10 +688,10 @@ oss_reopen(struct oss_data *od, GError **error_r) } static bool -oss_output_open(struct audio_output *ao, struct audio_format *audio_format, +oss_output_open(struct audio_output *ao, AudioFormat &audio_format, GError **error) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; od->fd = open_cloexec(od->device, O_WRONLY, 0); if (od->fd < 0) { @@ -714,14 +706,14 @@ oss_output_open(struct audio_output *ao, struct audio_format *audio_format, return false; } - od->audio_format = *audio_format; + od->audio_format = audio_format; return true; } static void oss_output_close(struct audio_output *ao) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; oss_close(od); } @@ -729,7 +721,7 @@ oss_output_close(struct audio_output *ao) static void oss_output_cancel(struct audio_output *ao) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; if (od->fd >= 0) { ioctl(od->fd, SNDCTL_DSP_RESET, 0); @@ -741,7 +733,7 @@ static size_t oss_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct oss_data *od = (struct oss_data *)ao; + OssOutput *od = (OssOutput *)ao; ssize_t ret; /* reopen the device since it was closed by dropBufferedAudio */ @@ -749,14 +741,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 = od->pcm_export->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 = od->pcm_export->CalcSourceSize(ret); #endif return ret; } @@ -771,18 +763,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 2aecc2b3a..6c5c9530b 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/pipe_output_plugin.c b/src/output/PipeOutputPlugin.cxx index 90c5a5331..f485f1554 100644 --- a/src/output/pipe_output_plugin.c +++ b/src/output/PipeOutputPlugin.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,17 +18,28 @@ */ #include "config.h" -#include "pipe_output_plugin.h" -#include "output_api.h" +#include "PipeOutputPlugin.hxx" +#include "OutputAPI.hxx" #include <stdio.h> #include <errno.h> -struct pipe_output { +struct PipeOutput { struct audio_output base; char *cmd; FILE *fh; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &pipe_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } + + bool Configure(const config_param ¶m, GError **error_r); }; /** @@ -40,22 +51,33 @@ pipe_output_quark(void) return g_quark_from_static_string("pipe_output"); } +inline bool +PipeOutput::Configure(const config_param ¶m, GError **error_r) +{ + cmd = param.DupBlockString("command"); + if (cmd == nullptr) { + g_set_error(error_r, pipe_output_quark(), 0, + "No \"command\" parameter specified"); + return false; + } + + return true; +} + static struct audio_output * -pipe_output_init(const struct config_param *param, - GError **error) +pipe_output_init(const config_param ¶m, GError **error_r) { - struct pipe_output *pd = g_new(struct pipe_output, 1); + PipeOutput *pd = new PipeOutput(); - if (!ao_base_init(&pd->base, &pipe_output_plugin, param, error)) { - g_free(pd); - return NULL; + if (!pd->Initialize(param, error_r)) { + delete pd; + return nullptr; } - pd->cmd = config_dup_block_string(param, "command", NULL); - if (pd->cmd == NULL) { - g_set_error(error, pipe_output_quark(), 0, - "No \"command\" parameter specified"); - return NULL; + if (!pd->Configure(param, error_r)) { + pd->Deinitialize(); + delete pd; + return nullptr; } return &pd->base; @@ -64,22 +86,22 @@ pipe_output_init(const struct config_param *param, static void pipe_output_finish(struct audio_output *ao) { - struct pipe_output *pd = (struct pipe_output *)ao; + PipeOutput *pd = (PipeOutput *)ao; g_free(pd->cmd); - ao_base_finish(&pd->base); - g_free(pd); + pd->Deinitialize(); + delete pd; } static bool pipe_output_open(struct audio_output *ao, - G_GNUC_UNUSED struct audio_format *audio_format, + G_GNUC_UNUSED AudioFormat &audio_format, G_GNUC_UNUSED GError **error) { - struct pipe_output *pd = (struct pipe_output *)ao; + PipeOutput *pd = (PipeOutput *)ao; pd->fh = popen(pd->cmd, "w"); - if (pd->fh == NULL) { + if (pd->fh == nullptr) { g_set_error(error, pipe_output_quark(), errno, "Error opening pipe \"%s\": %s", pd->cmd, g_strerror(errno)); @@ -92,7 +114,7 @@ pipe_output_open(struct audio_output *ao, static void pipe_output_close(struct audio_output *ao) { - struct pipe_output *pd = (struct pipe_output *)ao; + PipeOutput *pd = (PipeOutput *)ao; pclose(pd->fh); } @@ -100,7 +122,7 @@ pipe_output_close(struct audio_output *ao) static size_t pipe_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct pipe_output *pd = (struct pipe_output *)ao; + PipeOutput *pd = (PipeOutput *)ao; size_t ret; ret = fwrite(chunk, 1, size, pd->fh); @@ -112,10 +134,19 @@ pipe_output_play(struct audio_output *ao, const void *chunk, size_t size, GError } const struct audio_output_plugin pipe_output_plugin = { - .name = "pipe", - .init = pipe_output_init, - .finish = pipe_output_finish, - .open = pipe_output_open, - .close = pipe_output_close, - .play = pipe_output_play, + "pipe", + nullptr, + pipe_output_init, + pipe_output_finish, + nullptr, + nullptr, + pipe_output_open, + pipe_output_close, + nullptr, + nullptr, + pipe_output_play, + nullptr, + nullptr, + nullptr, + nullptr, }; diff --git a/src/output/pipe_output_plugin.h b/src/output/PipeOutputPlugin.hxx index 9f014f829..f0c29706b 100644 --- a/src/output/pipe_output_plugin.h +++ b/src/output/PipeOutputPlugin.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_PIPE_OUTPUT_PLUGIN_H -#define MPD_PIPE_OUTPUT_PLUGIN_H +#ifndef MPD_PIPE_OUTPUT_PLUGIN_HXX +#define MPD_PIPE_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin pipe_output_plugin; diff --git a/src/output/pulse_output_plugin.c b/src/output/PulseOutputPlugin.cxx index e267427df..f59c8d76e 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/PulseOutputPlugin.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,10 +18,10 @@ */ #include "config.h" -#include "pulse_output_plugin.h" -#include "output_api.h" -#include "mixer_list.h" -#include "mixer/pulse_mixer_plugin.h" +#include "PulseOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "MixerList.hxx" +#include "mixer/PulseMixerPlugin.hxx" #include <glib.h> @@ -45,14 +45,14 @@ #define PA_CHECK_VERSION(a,b,c) false #endif -struct pulse_output { +struct PulseOutput { struct audio_output base; const char *name; const char *server; const char *sink; - struct pulse_mixer *mixer; + PulseMixer *mixer; struct pa_threaded_mainloop *mainloop; struct pa_context *context; @@ -79,36 +79,36 @@ pulse_output_quark(void) } void -pulse_output_lock(struct pulse_output *po) +pulse_output_lock(PulseOutput *po) { pa_threaded_mainloop_lock(po->mainloop); } void -pulse_output_unlock(struct pulse_output *po) +pulse_output_unlock(PulseOutput *po) { pa_threaded_mainloop_unlock(po->mainloop); } void -pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm) +pulse_output_set_mixer(PulseOutput *po, PulseMixer *pm) { - assert(po != NULL); - assert(po->mixer == NULL); - assert(pm != NULL); + assert(po != nullptr); + assert(po->mixer == nullptr); + assert(pm != nullptr); po->mixer = pm; - if (po->mainloop == NULL) + if (po->mainloop == nullptr) return; pa_threaded_mainloop_lock(po->mainloop); - if (po->context != NULL && + if (po->context != nullptr && pa_context_get_state(po->context) == PA_CONTEXT_READY) { pulse_mixer_on_connect(pm, po->context); - if (po->stream != NULL && + if (po->stream != nullptr && pa_stream_get_state(po->stream) == PA_STREAM_READY) pulse_mixer_on_change(pm, po->context, po->stream); } @@ -117,23 +117,22 @@ pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm) } void -pulse_output_clear_mixer(struct pulse_output *po, - G_GNUC_UNUSED struct pulse_mixer *pm) +pulse_output_clear_mixer(PulseOutput *po, gcc_unused PulseMixer *pm) { - assert(po != NULL); - assert(pm != NULL); + assert(po != nullptr); + assert(pm != nullptr); assert(po->mixer == pm); - po->mixer = NULL; + po->mixer = nullptr; } bool -pulse_output_set_volume(struct pulse_output *po, +pulse_output_set_volume(PulseOutput *po, const struct pa_cvolume *volume, GError **error_r) { pa_operation *o; - if (po->context == NULL || po->stream == NULL || + if (po->context == nullptr || po->stream == nullptr || pa_stream_get_state(po->stream) != PA_STREAM_READY) { g_set_error(error_r, pulse_output_quark(), 0, "disconnected"); return false; @@ -141,8 +140,8 @@ pulse_output_set_volume(struct pulse_output *po, o = pa_context_set_sink_input_volume(po->context, pa_stream_get_index(po->stream), - volume, NULL, NULL); - if (o == NULL) { + volume, nullptr, nullptr); + if (o == nullptr) { g_set_error(error_r, pulse_output_quark(), 0, "failed to set PulseAudio volume: %s", pa_strerror(pa_context_errno(po->context))); @@ -166,8 +165,8 @@ pulse_wait_for_operation(struct pa_threaded_mainloop *mainloop, { pa_operation_state_t state; - assert(mainloop != NULL); - assert(operation != NULL); + assert(mainloop != nullptr); + assert(operation != nullptr); state = pa_operation_get_state(operation); while (state == PA_OPERATION_RUNNING) { @@ -188,7 +187,7 @@ static void pulse_output_stream_success_cb(G_GNUC_UNUSED pa_stream *s, G_GNUC_UNUSED int success, void *userdata) { - struct pulse_output *po = userdata; + PulseOutput *po = (PulseOutput *)userdata; pa_threaded_mainloop_signal(po->mainloop, 0); } @@ -196,11 +195,11 @@ pulse_output_stream_success_cb(G_GNUC_UNUSED pa_stream *s, static void pulse_output_context_state_cb(struct pa_context *context, void *userdata) { - struct pulse_output *po = userdata; + PulseOutput *po = (PulseOutput *)userdata; switch (pa_context_get_state(context)) { case PA_CONTEXT_READY: - if (po->mixer != NULL) + if (po->mixer != nullptr) pulse_mixer_on_connect(po->mixer, context); pa_threaded_mainloop_signal(po->mainloop, 0); @@ -208,7 +207,7 @@ pulse_output_context_state_cb(struct pa_context *context, void *userdata) case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: - if (po->mixer != NULL) + if (po->mixer != nullptr) pulse_mixer_on_disconnect(po->mixer); /* the caller thread might be waiting for these @@ -229,15 +228,15 @@ pulse_output_subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { - struct pulse_output *po = userdata; - pa_subscription_event_type_t facility - = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - pa_subscription_event_type_t type - = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK; + PulseOutput *po = (PulseOutput *)userdata; + pa_subscription_event_type_t facility = + pa_subscription_event_type_t(t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK); + pa_subscription_event_type_t type = + pa_subscription_event_type_t(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK); - if (po->mixer != NULL && + if (po->mixer != nullptr && facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT && - po->stream != NULL && + po->stream != nullptr && pa_stream_get_state(po->stream) == PA_STREAM_READY && idx == pa_stream_get_index(po->stream) && (type == PA_SUBSCRIPTION_EVENT_NEW || @@ -251,15 +250,15 @@ pulse_output_subscribe_cb(pa_context *context, * @return true on success, false on error */ static bool -pulse_output_connect(struct pulse_output *po, GError **error_r) +pulse_output_connect(PulseOutput *po, GError **error_r) { - assert(po != NULL); - assert(po->context != NULL); + assert(po != nullptr); + assert(po->context != nullptr); int error; error = pa_context_connect(po->context, po->server, - (pa_context_flags_t)0, NULL); + (pa_context_flags_t)0, nullptr); if (error < 0) { g_set_error(error_r, pulse_output_quark(), 0, "pa_context_connect() has failed: %s", @@ -274,21 +273,21 @@ pulse_output_connect(struct pulse_output *po, GError **error_r) * Frees and clears the stream. */ static void -pulse_output_delete_stream(struct pulse_output *po) +pulse_output_delete_stream(PulseOutput *po) { - assert(po != NULL); - assert(po->stream != NULL); + assert(po != nullptr); + assert(po->stream != nullptr); #if PA_CHECK_VERSION(0,9,8) - pa_stream_set_suspended_callback(po->stream, NULL, NULL); + pa_stream_set_suspended_callback(po->stream, nullptr, nullptr); #endif - pa_stream_set_state_callback(po->stream, NULL, NULL); - pa_stream_set_write_callback(po->stream, NULL, NULL); + pa_stream_set_state_callback(po->stream, nullptr, nullptr); + pa_stream_set_write_callback(po->stream, nullptr, nullptr); pa_stream_disconnect(po->stream); pa_stream_unref(po->stream); - po->stream = NULL; + po->stream = nullptr; } /** @@ -297,17 +296,17 @@ pulse_output_delete_stream(struct pulse_output *po) * Caller must lock the main loop. */ static void -pulse_output_delete_context(struct pulse_output *po) +pulse_output_delete_context(PulseOutput *po) { - assert(po != NULL); - assert(po->context != NULL); + assert(po != nullptr); + assert(po->context != nullptr); - pa_context_set_state_callback(po->context, NULL, NULL); - pa_context_set_subscribe_callback(po->context, NULL, NULL); + pa_context_set_state_callback(po->context, nullptr, nullptr); + pa_context_set_subscribe_callback(po->context, nullptr, nullptr); pa_context_disconnect(po->context); pa_context_unref(po->context); - po->context = NULL; + po->context = nullptr; } /** @@ -318,14 +317,14 @@ pulse_output_delete_context(struct pulse_output *po) * @return true on success, false on error */ static bool -pulse_output_setup_context(struct pulse_output *po, GError **error_r) +pulse_output_setup_context(PulseOutput *po, GError **error_r) { - assert(po != NULL); - assert(po->mainloop != NULL); + assert(po != nullptr); + assert(po->mainloop != nullptr); po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop), MPD_PULSE_NAME); - if (po->context == NULL) { + if (po->context == nullptr) { g_set_error(error_r, pulse_output_quark(), 0, "pa_context_new() has failed"); return false; @@ -345,26 +344,26 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r) } static struct audio_output * -pulse_output_init(const struct config_param *param, GError **error_r) +pulse_output_init(const config_param ¶m, GError **error_r) { - struct pulse_output *po; + PulseOutput *po; g_setenv("PULSE_PROP_media.role", "music", true); - po = g_new(struct pulse_output, 1); + po = new PulseOutput(); if (!ao_base_init(&po->base, &pulse_output_plugin, param, error_r)) { - g_free(po); - return NULL; + delete po; + return nullptr; } - po->name = config_get_block_string(param, "name", "mpd_pulse"); - po->server = config_get_block_string(param, "server", NULL); - po->sink = config_get_block_string(param, "sink", NULL); + po->name = param.GetBlockValue("name", "mpd_pulse"); + po->server = param.GetBlockValue("server"); + po->sink = param.GetBlockValue("sink"); - po->mixer = NULL; - po->mainloop = NULL; - po->context = NULL; - po->stream = NULL; + po->mixer = nullptr; + po->mainloop = nullptr; + po->context = nullptr; + po->stream = nullptr; return &po->base; } @@ -372,24 +371,24 @@ pulse_output_init(const struct config_param *param, GError **error_r) static void pulse_output_finish(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; ao_base_finish(&po->base); - g_free(po); + delete po; } static bool pulse_output_enable(struct audio_output *ao, GError **error_r) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; - assert(po->mainloop == NULL); - assert(po->context == NULL); + assert(po->mainloop == nullptr); + assert(po->context == nullptr); /* create the libpulse mainloop and start the thread */ po->mainloop = pa_threaded_mainloop_new(); - if (po->mainloop == NULL) { + if (po->mainloop == nullptr) { g_free(po); g_set_error(error_r, pulse_output_quark(), 0, @@ -402,7 +401,7 @@ pulse_output_enable(struct audio_output *ao, GError **error_r) if (pa_threaded_mainloop_start(po->mainloop) < 0) { pa_threaded_mainloop_unlock(po->mainloop); pa_threaded_mainloop_free(po->mainloop); - po->mainloop = NULL; + po->mainloop = nullptr; g_set_error(error_r, pulse_output_quark(), 0, "pa_threaded_mainloop_start() has failed"); @@ -415,7 +414,7 @@ pulse_output_enable(struct audio_output *ao, GError **error_r) pa_threaded_mainloop_unlock(po->mainloop); pa_threaded_mainloop_stop(po->mainloop); pa_threaded_mainloop_free(po->mainloop); - po->mainloop = NULL; + po->mainloop = nullptr; return false; } @@ -427,15 +426,15 @@ pulse_output_enable(struct audio_output *ao, GError **error_r) static void pulse_output_disable(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; - assert(po->mainloop != NULL); + assert(po->mainloop != nullptr); pa_threaded_mainloop_stop(po->mainloop); - if (po->context != NULL) + if (po->context != nullptr) pulse_output_delete_context(po); pa_threaded_mainloop_free(po->mainloop); - po->mainloop = NULL; + po->mainloop = nullptr; } /** @@ -447,13 +446,13 @@ pulse_output_disable(struct audio_output *ao) * @return true on success, false on error */ static bool -pulse_output_wait_connection(struct pulse_output *po, GError **error_r) +pulse_output_wait_connection(PulseOutput *po, GError **error_r) { - assert(po->mainloop != NULL); + assert(po->mainloop != nullptr); pa_context_state_t state; - if (po->context == NULL && !pulse_output_setup_context(po, error_r)) + if (po->context == nullptr && !pulse_output_setup_context(po, error_r)) return false; while (true) { @@ -488,10 +487,10 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r) static void pulse_output_stream_suspended_cb(G_GNUC_UNUSED pa_stream *stream, void *userdata) { - struct pulse_output *po = userdata; + PulseOutput *po = (PulseOutput *)userdata; - assert(stream == po->stream || po->stream == NULL); - assert(po->mainloop != NULL); + assert(stream == po->stream || po->stream == nullptr); + assert(po->mainloop != nullptr); /* wake up the main loop to break out of the loop in pulse_output_play() */ @@ -503,15 +502,15 @@ pulse_output_stream_suspended_cb(G_GNUC_UNUSED pa_stream *stream, void *userdata static void pulse_output_stream_state_cb(pa_stream *stream, void *userdata) { - struct pulse_output *po = userdata; + PulseOutput *po = (PulseOutput *)userdata; - assert(stream == po->stream || po->stream == NULL); - assert(po->mainloop != NULL); - assert(po->context != NULL); + assert(stream == po->stream || po->stream == nullptr); + assert(po->mainloop != nullptr); + assert(po->context != nullptr); switch (pa_stream_get_state(stream)) { case PA_STREAM_READY: - if (po->mixer != NULL) + if (po->mixer != nullptr) pulse_mixer_on_change(po->mixer, po->context, stream); pa_threaded_mainloop_signal(po->mainloop, 0); @@ -519,7 +518,7 @@ pulse_output_stream_state_cb(pa_stream *stream, void *userdata) case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: - if (po->mixer != NULL) + if (po->mixer != nullptr) pulse_mixer_on_disconnect(po->mixer); pa_threaded_mainloop_signal(po->mainloop, 0); @@ -535,9 +534,9 @@ static void pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes, void *userdata) { - struct pulse_output *po = userdata; + PulseOutput *po = (PulseOutput *)userdata; - assert(po->mainloop != NULL); + assert(po->mainloop != nullptr); po->writable = nbytes; pa_threaded_mainloop_signal(po->mainloop, 0); @@ -551,14 +550,14 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes, * @return true on success, false on error */ static bool -pulse_output_setup_stream(struct pulse_output *po, const pa_sample_spec *ss, +pulse_output_setup_stream(PulseOutput *po, const pa_sample_spec *ss, GError **error_r) { - assert(po != NULL); - assert(po->context != NULL); + assert(po != nullptr); + assert(po->context != nullptr); - po->stream = pa_stream_new(po->context, po->name, ss, NULL); - if (po->stream == NULL) { + po->stream = pa_stream_new(po->context, po->name, ss, nullptr); + if (po->stream == nullptr) { g_set_error(error_r, pulse_output_quark(), 0, "pa_stream_new() has failed: %s", pa_strerror(pa_context_errno(po->context))); @@ -579,18 +578,18 @@ pulse_output_setup_stream(struct pulse_output *po, const pa_sample_spec *ss, } static bool -pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, +pulse_output_open(struct audio_output *ao, AudioFormat &audio_format, GError **error_r) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; pa_sample_spec ss; int error; - assert(po->mainloop != NULL); + assert(po->mainloop != nullptr); pa_threaded_mainloop_lock(po->mainloop); - if (po->context != NULL) { + if (po->context != nullptr) { switch (pa_context_get_state(po->context)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_TERMINATED: @@ -616,11 +615,11 @@ pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, /* MPD doesn't support the other pulseaudio sample formats, so we just force MPD to send us everything as 16 bit */ - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; ss.format = PA_SAMPLE_S16NE; - ss.rate = audio_format->sample_rate; - ss.channels = audio_format->channels; + ss.rate = audio_format.sample_rate; + ss.channels = audio_format.channels; /* create a stream .. */ @@ -632,7 +631,8 @@ pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, /* .. and connect it (asynchronously) */ error = pa_stream_connect_playback(po->stream, po->sink, - NULL, 0, NULL, NULL); + nullptr, pa_stream_flags_t(0), + nullptr, nullptr); if (error < 0) { pulse_output_delete_stream(po); @@ -655,17 +655,17 @@ pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, static void pulse_output_close(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; pa_operation *o; - assert(po->mainloop != NULL); + assert(po->mainloop != nullptr); pa_threaded_mainloop_lock(po->mainloop); if (pa_stream_get_state(po->stream) == PA_STREAM_READY) { o = pa_stream_drain(po->stream, pulse_output_stream_success_cb, po); - if (o == NULL) { + if (o == nullptr) { g_warning("pa_stream_drain() has failed: %s", pa_strerror(pa_context_errno(po->context))); } else @@ -674,7 +674,7 @@ pulse_output_close(struct audio_output *ao) pulse_output_delete_stream(po); - if (po->context != NULL && + if (po->context != nullptr && pa_context_get_state(po->context) != PA_CONTEXT_READY) pulse_output_delete_context(po); @@ -688,7 +688,7 @@ pulse_output_close(struct audio_output *ao) * @return true on success, false on error */ static bool -pulse_output_wait_stream(struct pulse_output *po, GError **error_r) +pulse_output_wait_stream(PulseOutput *po, GError **error_r) { while (true) { switch (pa_stream_get_state(po->stream)) { @@ -716,9 +716,9 @@ pulse_output_wait_stream(struct pulse_output *po, GError **error_r) * 0.9.11, it uses a custom pause flag. */ static bool -pulse_output_stream_is_paused(struct pulse_output *po) +pulse_output_stream_is_paused(PulseOutput *po) { - assert(po->stream != NULL); + assert(po->stream != nullptr); #if !defined(PA_CHECK_VERSION) || !PA_CHECK_VERSION(0,9,11) return po->pause; @@ -731,18 +731,18 @@ pulse_output_stream_is_paused(struct pulse_output *po) * Sets cork mode on the stream. */ static bool -pulse_output_stream_pause(struct pulse_output *po, bool pause, +pulse_output_stream_pause(PulseOutput *po, bool pause, GError **error_r) { pa_operation *o; - assert(po->mainloop != NULL); - assert(po->context != NULL); - assert(po->stream != NULL); + assert(po->mainloop != nullptr); + assert(po->context != nullptr); + assert(po->stream != nullptr); o = pa_stream_cork(po->stream, pause, pulse_output_stream_success_cb, po); - if (o == NULL) { + if (o == nullptr) { g_set_error(error_r, pulse_output_quark(), 0, "pa_stream_cork() has failed: %s", pa_strerror(pa_context_errno(po->context))); @@ -765,7 +765,7 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause, static unsigned pulse_output_delay(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; unsigned result = 0; pa_threaded_mainloop_lock(po->mainloop); @@ -784,11 +784,11 @@ static size_t pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; int error; - assert(po->mainloop != NULL); - assert(po->stream != NULL); + assert(po->mainloop != nullptr); + assert(po->stream != nullptr); pa_threaded_mainloop_lock(po->mainloop); @@ -799,7 +799,7 @@ pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, return 0; } - assert(po->context != NULL); + assert(po->context != nullptr); /* unpause if previously paused */ @@ -839,7 +839,7 @@ pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, po->writable -= size; - error = pa_stream_write(po->stream, chunk, size, NULL, + error = pa_stream_write(po->stream, chunk, size, nullptr, 0, PA_SEEK_RELATIVE); pa_threaded_mainloop_unlock(po->mainloop); if (error < 0) { @@ -854,11 +854,11 @@ pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, static void pulse_output_cancel(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; + PulseOutput *po = (PulseOutput *)ao; pa_operation *o; - assert(po->mainloop != NULL); - assert(po->stream != NULL); + assert(po->mainloop != nullptr); + assert(po->stream != nullptr); pa_threaded_mainloop_lock(po->mainloop); @@ -869,10 +869,10 @@ pulse_output_cancel(struct audio_output *ao) return; } - assert(po->context != NULL); + assert(po->context != nullptr); o = pa_stream_flush(po->stream, pulse_output_stream_success_cb, po); - if (o == NULL) { + if (o == nullptr) { g_warning("pa_stream_flush() has failed: %s", pa_strerror(pa_context_errno(po->context))); pa_threaded_mainloop_unlock(po->mainloop); @@ -886,11 +886,11 @@ pulse_output_cancel(struct audio_output *ao) static bool pulse_output_pause(struct audio_output *ao) { - struct pulse_output *po = (struct pulse_output *)ao; - GError *error = NULL; + PulseOutput *po = (PulseOutput *)ao; + GError *error = nullptr; - assert(po->mainloop != NULL); - assert(po->stream != NULL); + assert(po->mainloop != nullptr); + assert(po->stream != nullptr); pa_threaded_mainloop_lock(po->mainloop); @@ -903,7 +903,7 @@ pulse_output_pause(struct audio_output *ao) return false; } - assert(po->context != NULL); + assert(po->context != nullptr); /* cork the stream */ @@ -923,33 +923,34 @@ pulse_output_pause(struct audio_output *ao) static bool pulse_output_test_default_device(void) { - struct pulse_output *po; bool success; - po = (struct pulse_output *)pulse_output_init(NULL, NULL); - if (po == NULL) + const config_param empty; + PulseOutput *po = (PulseOutput *)pulse_output_init(empty, nullptr); + if (po == nullptr) return false; - success = pulse_output_wait_connection(po, NULL); + success = pulse_output_wait_connection(po, nullptr); pulse_output_finish(&po->base); return success; } const struct audio_output_plugin pulse_output_plugin = { - .name = "pulse", - - .test_default_device = pulse_output_test_default_device, - .init = pulse_output_init, - .finish = pulse_output_finish, - .enable = pulse_output_enable, - .disable = pulse_output_disable, - .open = pulse_output_open, - .delay = pulse_output_delay, - .play = pulse_output_play, - .cancel = pulse_output_cancel, - .pause = pulse_output_pause, - .close = pulse_output_close, - - .mixer_plugin = &pulse_mixer_plugin, + "pulse", + pulse_output_test_default_device, + pulse_output_init, + pulse_output_finish, + pulse_output_enable, + pulse_output_disable, + pulse_output_open, + pulse_output_close, + pulse_output_delay, + nullptr, + pulse_output_play, + nullptr, + pulse_output_cancel, + pulse_output_pause, + + &pulse_mixer_plugin, }; diff --git a/src/output/pulse_output_plugin.h b/src/output/PulseOutputPlugin.hxx index 02a51f27b..58dc6703c 100644 --- a/src/output/pulse_output_plugin.h +++ b/src/output/PulseOutputPlugin.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,33 +17,39 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_PULSE_OUTPUT_PLUGIN_H -#define MPD_PULSE_OUTPUT_PLUGIN_H +#ifndef MPD_PULSE_OUTPUT_PLUGIN_HXX +#define MPD_PULSE_OUTPUT_PLUGIN_HXX -#include <stdbool.h> +#include "gerror.h" -#include <glib.h> - -struct pulse_output; -struct pulse_mixer; +struct PulseOutput; +struct PulseMixer; 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); +pulse_output_lock(PulseOutput *po); void -pulse_output_unlock(struct pulse_output *po); +pulse_output_unlock(PulseOutput *po); void -pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm); +pulse_output_set_mixer(PulseOutput *po, PulseMixer *pm); void -pulse_output_clear_mixer(struct pulse_output *po, struct pulse_mixer *pm); +pulse_output_clear_mixer(PulseOutput *po, PulseMixer *pm); bool -pulse_output_set_volume(struct pulse_output *po, +pulse_output_set_volume(PulseOutput *po, const struct pa_cvolume *volume, GError **error_r); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/output/recorder_output_plugin.c b/src/output/RecorderOutputPlugin.cxx index b84cb244c..afae17e84 100644 --- a/src/output/recorder_output_plugin.c +++ b/src/output/RecorderOutputPlugin.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,10 +18,10 @@ */ #include "config.h" -#include "recorder_output_plugin.h" -#include "output_api.h" -#include "encoder_plugin.h" -#include "encoder_list.h" +#include "RecorderOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "EncoderPlugin.hxx" +#include "EncoderList.hxx" #include "fd_util.h" #include "open.h" @@ -34,13 +34,13 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "recorder" -struct recorder_output { +struct RecorderOutput { struct audio_output base; /** * The configured encoder plugin. */ - struct encoder *encoder; + Encoder *encoder; /** * The destination file name. @@ -56,6 +56,24 @@ struct recorder_output { * The buffer for encoder_read(). */ char buffer[32768]; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &recorder_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } + + bool Configure(const config_param ¶m, GError **error_r); + + bool WriteToFile(const void *data, size_t length, GError **error_r); + + /** + * Writes pending data from the encoder to the output file. + */ + bool EncoderToFile(GError **error_r); }; /** @@ -67,68 +85,70 @@ recorder_output_quark(void) return g_quark_from_static_string("recorder_output"); } -static struct audio_output * -recorder_output_init(const struct config_param *param, GError **error_r) +inline bool +RecorderOutput::Configure(const config_param ¶m, GError **error_r) { - struct recorder_output *recorder = g_new(struct recorder_output, 1); - if (!ao_base_init(&recorder->base, &recorder_output_plugin, param, - error_r)) { - g_free(recorder); - return NULL; - } - /* read configuration */ 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) { + param.GetBlockValue("encoder", "vorbis"); + const auto encoder_plugin = encoder_plugin_get(encoder_name); + if (encoder_plugin == nullptr) { g_set_error(error_r, recorder_output_quark(), 0, "No such encoder: %s", encoder_name); - goto failure; + return false; } - recorder->path = config_get_block_string(param, "path", NULL); - if (recorder->path == NULL) { + path = param.GetBlockValue("path"); + if (path == nullptr) { g_set_error(error_r, recorder_output_quark(), 0, "'path' not configured"); - goto failure; + return false; } /* initialize encoder */ - recorder->encoder = encoder_init(encoder_plugin, param, error_r); - if (recorder->encoder == NULL) - goto failure; + encoder = encoder_init(*encoder_plugin, param, error_r); + if (encoder == nullptr) + return false; - return &recorder->base; + return true; +} + +static audio_output * +recorder_output_init(const config_param ¶m, GError **error_r) +{ + RecorderOutput *recorder = new RecorderOutput(); + + if (!recorder->Initialize(param, error_r)) { + delete recorder; + return nullptr; + } + + if (!recorder->Configure(param, error_r)) { + recorder->Deinitialize(); + delete recorder; + return nullptr; + } -failure: - ao_base_finish(&recorder->base); - g_free(recorder); - return NULL; + return &recorder->base; } static void recorder_output_finish(struct audio_output *ao) { - struct recorder_output *recorder = (struct recorder_output *)ao; + RecorderOutput *recorder = (RecorderOutput *)ao; encoder_finish(recorder->encoder); - ao_base_finish(&recorder->base); - g_free(recorder); + recorder->Deinitialize(); + delete recorder; } -static bool -recorder_write_to_file(struct recorder_output *recorder, - const void *_data, size_t length, - GError **error_r) +inline bool +RecorderOutput::WriteToFile(const void *_data, size_t length, GError **error_r) { assert(length > 0); - const int fd = recorder->fd; - const uint8_t *data = (const uint8_t *)_data, *end = data + length; while (true) { @@ -145,43 +165,37 @@ recorder_write_to_file(struct recorder_output *recorder, } else if (errno != EINTR) { g_set_error(error_r, recorder_output_quark(), 0, "Failed to write to '%s': %s", - recorder->path, g_strerror(errno)); + path, g_strerror(errno)); return false; } } } -/** - * Writes pending data from the encoder to the output file. - */ -static bool -recorder_output_encoder_to_file(struct recorder_output *recorder, - GError **error_r) +inline bool +RecorderOutput::EncoderToFile(GError **error_r) { - assert(recorder->fd >= 0); + assert(fd >= 0); while (true) { /* read from the encoder */ - size_t size = encoder_read(recorder->encoder, recorder->buffer, - sizeof(recorder->buffer)); + size_t size = encoder_read(encoder, buffer, sizeof(buffer)); if (size == 0) return true; /* write everything into the file */ - if (!recorder_write_to_file(recorder, recorder->buffer, size, - error_r)) + if (!WriteToFile(buffer, size, error_r)) return false; } } static bool recorder_output_open(struct audio_output *ao, - struct audio_format *audio_format, + AudioFormat &audio_format, GError **error_r) { - struct recorder_output *recorder = (struct recorder_output *)ao; + RecorderOutput *recorder = (RecorderOutput *)ao; /* create the output file */ @@ -203,7 +217,7 @@ recorder_output_open(struct audio_output *ao, return false; } - if (!recorder_output_encoder_to_file(recorder, error_r)) { + if (!recorder->EncoderToFile(error_r)) { encoder_close(recorder->encoder); close(recorder->fd); unlink(recorder->path); @@ -216,12 +230,12 @@ recorder_output_open(struct audio_output *ao, static void recorder_output_close(struct audio_output *ao) { - struct recorder_output *recorder = (struct recorder_output *)ao; + RecorderOutput *recorder = (RecorderOutput *)ao; /* flush the encoder and write the rest to the file */ - if (encoder_end(recorder->encoder, NULL)) - recorder_output_encoder_to_file(recorder, NULL); + if (encoder_end(recorder->encoder, nullptr)) + recorder->EncoderToFile(nullptr); /* now really close everything */ @@ -234,18 +248,27 @@ static size_t recorder_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) { - struct recorder_output *recorder = (struct recorder_output *)ao; + RecorderOutput *recorder = (RecorderOutput *)ao; return encoder_write(recorder->encoder, chunk, size, error_r) && - recorder_output_encoder_to_file(recorder, error_r) + recorder->EncoderToFile(error_r) ? size : 0; } const struct audio_output_plugin recorder_output_plugin = { - .name = "recorder", - .init = recorder_output_init, - .finish = recorder_output_finish, - .open = recorder_output_open, - .close = recorder_output_close, - .play = recorder_output_play, + "recorder", + nullptr, + recorder_output_init, + recorder_output_finish, + nullptr, + nullptr, + recorder_output_open, + recorder_output_close, + nullptr, + nullptr, + recorder_output_play, + nullptr, + nullptr, + nullptr, + nullptr, }; diff --git a/src/output/recorder_output_plugin.h b/src/output/RecorderOutputPlugin.hxx index a9bf755bd..a27f51e23 100644 --- a/src/output/recorder_output_plugin.h +++ b/src/output/RecorderOutputPlugin.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_RECORDER_OUTPUT_PLUGIN_H -#define MPD_RECORDER_OUTPUT_PLUGIN_H +#ifndef MPD_RECORDER_OUTPUT_PLUGIN_HXX +#define MPD_RECORDER_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin recorder_output_plugin; diff --git a/src/output/roar_output_plugin.c b/src/output/RoarOutputPlugin.cxx index 1c2c48321..36f7c395b 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,23 @@ */ #include "config.h" -#include "roar_output_plugin.h" -#include "output_api.h" -#include "mixer_list.h" -#include "roar_output_plugin.h" +#include "RoarOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "MixerList.hxx" +#include "thread/Mutex.hxx" #include <glib.h> -#include <stdint.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> +/* libroar/services.h declares roar_service_stream::new - work around + this C++ problem */ +#define new _new #include <roaraudio.h> +#undef new #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "roaraudio" -typedef struct roar -{ +struct RoarOutput { struct audio_output base; roar_vs_t * vss; @@ -47,9 +45,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 +65,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 +79,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,38 +101,34 @@ 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 config_param ¶m) { - self->host = config_dup_block_string(param, "server", NULL); - self->name = config_dup_block_string(param, "name", "MPD"); + self->host = param.DupBlockString("server", nullptr); + self->name = param.DupBlockString("name", "MPD"); - const char *role = config_get_block_string(param, "role", "music"); - self->role = role != NULL + const char *role = param.GetBlockValue("role", "music"); + self->role = role != nullptr ? roar_str2role(role) : ROAR_ROLE_MUSIC; } static struct audio_output * -roar_init(const struct config_param *param, GError **error_r) +roar_init(const config_param ¶m, 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,70 +136,66 @@ 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 roar_use_audio_format(struct roar_audio_info *info, - struct audio_format *audio_format) + AudioFormat &audio_format) { - info->rate = audio_format->sample_rate; - info->channels = audio_format->channels; + info->rate = audio_format.sample_rate; + info->channels = audio_format.channels; info->codec = ROAR_CODEC_PCM_S; - switch (audio_format->format) { - case SAMPLE_FORMAT_UNDEFINED: + switch (audio_format.format) { + case SampleFormat::UNDEFINED: + case SampleFormat::FLOAT: + case SampleFormat::DSD: info->bits = 16; - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; break; - case SAMPLE_FORMAT_S8: + case SampleFormat::S8: info->bits = 8; break; - case SAMPLE_FORMAT_S16: + case SampleFormat::S16: info->bits = 16; break; - case SAMPLE_FORMAT_S24_P32: + case SampleFormat::S24_P32: info->bits = 32; - audio_format->format = SAMPLE_FORMAT_S32; + audio_format.format = SampleFormat::S32; break; - case SAMPLE_FORMAT_S32: + case SampleFormat::S32: info->bits = 32; break; } } static bool -roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) +roar_open(struct audio_output *ao, AudioFormat &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 +205,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 +257,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 +326,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) +roar_send_tag(struct audio_output *ao, const 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 +356,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 +378,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 78b628cc4..faa4b4d5c 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/shout_output_plugin.c b/src/output/ShoutOutputPlugin.cxx index 56456a0ea..2d2c0afd0 100644 --- a/src/output/shout_output_plugin.c +++ b/src/output/ShoutOutputPlugin.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,10 +18,10 @@ */ #include "config.h" -#include "shout_output_plugin.h" -#include "output_api.h" -#include "encoder_plugin.h" -#include "encoder_list.h" +#include "ShoutOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "EncoderPlugin.hxx" +#include "EncoderList.hxx" #include "mpd_error.h" #include <shout/shout.h> @@ -29,20 +29,21 @@ #include <assert.h> #include <stdlib.h> +#include <string.h> #include <stdio.h> #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "shout" -#define DEFAULT_CONN_TIMEOUT 2 +static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2; -struct shout_data { +struct ShoutOutput final { struct audio_output base; shout_t *shout_conn; shout_metadata_t *shout_meta; - struct encoder *encoder; + Encoder *encoder; float quality; int bitrate; @@ -50,6 +51,31 @@ struct shout_data { int timeout; uint8_t buffer[32768]; + + ShoutOutput() + :shout_conn(shout_new()), + shout_meta(shout_metadata_new()), + quality(-2.0), + bitrate(-1), + timeout(DEFAULT_CONN_TIMEOUT) {} + + ~ShoutOutput() { + if (shout_meta != nullptr) + shout_metadata_free(shout_meta); + if (shout_conn != nullptr) + shout_free(shout_conn); + } + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &shout_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } + + bool Configure(const config_param ¶m, GError **error_r); }; static int shout_init_count; @@ -63,7 +89,7 @@ shout_output_quark(void) return g_quark_from_static_string("shout_output"); } -static const struct encoder_plugin * +static const EncoderPlugin * shout_encoder_plugin_get(const char *name) { if (strcmp(name, "ogg") == 0) @@ -74,137 +100,94 @@ shout_encoder_plugin_get(const char *name) return encoder_plugin_get(name); } -static struct shout_data *new_shout_data(void) +gcc_pure +static const char * +require_block_string(const config_param ¶m, const char *name) { - struct shout_data *ret = g_new(struct shout_data, 1); - - ret->shout_conn = shout_new(); - ret->shout_meta = shout_metadata_new(); - ret->bitrate = -1; - ret->quality = -2.0; - ret->timeout = DEFAULT_CONN_TIMEOUT; + const char *value = param.GetBlockValue(name); + if (value == nullptr) + MPD_ERROR("no \"%s\" defined for shout device defined at line " \ + "%i\n", name, param.line); - return ret; + return value; } -static void free_shout_data(struct shout_data *sd) -{ - if (sd->shout_meta) - shout_metadata_free(sd->shout_meta); - if (sd->shout_conn) - shout_free(sd->shout_conn); - - 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); \ - } \ - } - -static struct audio_output * -my_shout_init_driver(const struct config_param *param, - GError **error) +inline bool +ShoutOutput::Configure(const config_param ¶m, GError **error_r) { - 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; - if (!audio_format_fully_defined(audio_format)) { - g_set_error(error, shout_output_quark(), 0, + const AudioFormat audio_format = base.config_audio_format; + if (!audio_format.IsFullyDefined()) { + g_set_error(error_r, shout_output_quark(), 0, "Need full audio format specification"); - ao_base_finish(&sd->base); - free_shout_data(sd); - return NULL; + return nullptr; } - 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; - - unsigned port = config_get_block_unsigned(param, "port", 0); + const char *host = require_block_string(param, "host"); + const char *mount = require_block_string(param, "mount"); + unsigned port = param.GetBlockValue("port", 0u); if (port == 0) { - g_set_error(error, shout_output_quark(), 0, + g_set_error(error_r, shout_output_quark(), 0, "shout port must be configured"); - goto failure; + return false; } - check_block_param("password"); - const char *passwd = block_param->value; + const char *passwd = require_block_string(param, "password"); + const char *name = require_block_string(param, "name"); - check_block_param("name"); - const char *name = block_param->value; + bool is_public = param.GetBlockValue("public", false); - bool public = config_get_block_bool(param, "public", false); + const char *user = param.GetBlockValue("user", "source"); - const char *user = config_get_block_string(param, "user", "source"); - - const char *value = config_get_block_string(param, "quality", NULL); - if (value != NULL) { + const char *value = param.GetBlockValue("quality"); + if (value != nullptr) { char *test; - sd->quality = strtod(value, &test); + quality = strtod(value, &test); - if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) { - g_set_error(error, shout_output_quark(), 0, + if (*test != '\0' || quality < -1.0 || quality > 10.0) { + g_set_error(error_r, shout_output_quark(), 0, "shout quality \"%s\" is not a number in the " "range -1 to 10, line %i", - value, param->line); - goto failure; + value, param.line); + return false; } - if (config_get_block_string(param, "bitrate", NULL) != NULL) { - g_set_error(error, shout_output_quark(), 0, + if (param.GetBlockValue("bitrate") != nullptr) { + g_set_error(error_r, 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, + value = param.GetBlockValue("bitrate"); + if (value == nullptr) { + g_set_error(error_r, shout_output_quark(), 0, "neither bitrate nor quality defined"); - goto failure; + return false; } char *test; - sd->bitrate = strtol(value, &test, 10); + bitrate = strtol(value, &test, 10); - if (*test != '\0' || sd->bitrate <= 0) { - g_set_error(error, shout_output_quark(), 0, + if (*test != '\0' || bitrate <= 0) { + g_set_error(error_r, shout_output_quark(), 0, "bitrate must be a positive integer"); - goto failure; + return false; } } - const char *encoding = config_get_block_string(param, "encoding", - "ogg"); - const struct encoder_plugin *encoder_plugin = - shout_encoder_plugin_get(encoding); - if (encoder_plugin == NULL) { - g_set_error(error, shout_output_quark(), 0, + const char *encoding = param.GetBlockValue("encoding", "ogg"); + const auto encoder_plugin = shout_encoder_plugin_get(encoding); + if (encoder_plugin == nullptr) { + g_set_error(error_r, 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; + encoder = encoder_init(*encoder_plugin, param, error_r); + if (encoder == nullptr) + return false; unsigned shout_format; if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0) @@ -213,14 +196,14 @@ my_shout_init_driver(const struct config_param *param, shout_format = SHOUT_FORMAT_OGG; unsigned protocol; - value = config_get_block_string(param, "protocol", NULL); - if (value != NULL) { + value = param.GetBlockValue("protocol"); + if (value != nullptr) { if (0 == strcmp(value, "shoutcast") && 0 != strcmp(encoding, "mp3")) { - g_set_error(error, shout_output_quark(), 0, + g_set_error(error_r, 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")) @@ -228,89 +211,106 @@ my_shout_init_driver(const struct config_param *param, else if (0 == strcmp(value, "icecast2")) protocol = SHOUT_PROTOCOL_HTTP; else { - g_set_error(error, shout_output_quark(), 0, + g_set_error(error_r, shout_output_quark(), 0, "shout protocol \"%s\" is not \"shoutcast\" or " "\"icecast1\"or \"icecast2\"", value); - goto failure; + return false; } } else { protocol = SHOUT_PROTOCOL_HTTP; } - if (shout_set_host(sd->shout_conn, host) != SHOUTERR_SUCCESS || - shout_set_port(sd->shout_conn, port) != SHOUTERR_SUCCESS || - shout_set_password(sd->shout_conn, passwd) != SHOUTERR_SUCCESS || - shout_set_mount(sd->shout_conn, mount) != SHOUTERR_SUCCESS || - shout_set_name(sd->shout_conn, name) != SHOUTERR_SUCCESS || - shout_set_user(sd->shout_conn, user) != SHOUTERR_SUCCESS || - shout_set_public(sd->shout_conn, public) != SHOUTERR_SUCCESS || - shout_set_format(sd->shout_conn, shout_format) + if (shout_set_host(shout_conn, host) != SHOUTERR_SUCCESS || + shout_set_port(shout_conn, port) != SHOUTERR_SUCCESS || + shout_set_password(shout_conn, passwd) != SHOUTERR_SUCCESS || + shout_set_mount(shout_conn, mount) != SHOUTERR_SUCCESS || + shout_set_name(shout_conn, name) != SHOUTERR_SUCCESS || + shout_set_user(shout_conn, user) != SHOUTERR_SUCCESS || + shout_set_public(shout_conn, is_public) != SHOUTERR_SUCCESS || + shout_set_format(shout_conn, shout_format) != SHOUTERR_SUCCESS || - shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS || - 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; + shout_set_protocol(shout_conn, protocol) != SHOUTERR_SUCCESS || + shout_set_agent(shout_conn, "MPD") != SHOUTERR_SUCCESS) { + g_set_error(error_r, shout_output_quark(), 0, + "%s", shout_get_error(shout_conn)); + return false; } /* optional paramters */ - sd->timeout = config_get_block_unsigned(param, "timeout", - DEFAULT_CONN_TIMEOUT); + timeout = param.GetBlockValue("timeout", DEFAULT_CONN_TIMEOUT); - value = config_get_block_string(param, "genre", NULL); - 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; + value = param.GetBlockValue("genre"); + if (value != nullptr && shout_set_genre(shout_conn, value)) { + g_set_error(error_r, shout_output_quark(), 0, + "%s", shout_get_error(shout_conn)); + 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; + value = param.GetBlockValue("description"); + if (value != nullptr && shout_set_description(shout_conn, value)) { + g_set_error(error_r, shout_output_quark(), 0, + "%s", shout_get_error(shout_conn)); + 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; + value = param.GetBlockValue("url"); + if (value != nullptr && shout_set_url(shout_conn, value)) { + g_set_error(error_r, shout_output_quark(), 0, + "%s", shout_get_error(shout_conn)); + return false; } { char temp[11]; memset(temp, 0, sizeof(temp)); - snprintf(temp, sizeof(temp), "%u", audio_format->channels); - shout_set_audio_info(sd->shout_conn, SHOUT_AI_CHANNELS, temp); + snprintf(temp, sizeof(temp), "%u", audio_format.channels); + shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp); - snprintf(temp, sizeof(temp), "%u", audio_format->sample_rate); + snprintf(temp, sizeof(temp), "%u", audio_format.sample_rate); - shout_set_audio_info(sd->shout_conn, SHOUT_AI_SAMPLERATE, temp); + shout_set_audio_info(shout_conn, SHOUT_AI_SAMPLERATE, temp); - if (sd->quality >= -1.0) { - snprintf(temp, sizeof(temp), "%2.2f", sd->quality); - shout_set_audio_info(sd->shout_conn, SHOUT_AI_QUALITY, + if (quality >= -1.0) { + snprintf(temp, sizeof(temp), "%2.2f", quality); + shout_set_audio_info(shout_conn, SHOUT_AI_QUALITY, temp); } else { - snprintf(temp, sizeof(temp), "%d", sd->bitrate); - shout_set_audio_info(sd->shout_conn, SHOUT_AI_BITRATE, + snprintf(temp, sizeof(temp), "%d", bitrate); + shout_set_audio_info(shout_conn, SHOUT_AI_BITRATE, temp); } } - return &sd->base; + return true; +} + +static struct audio_output * +my_shout_init_driver(const config_param ¶m, GError **error_r) +{ + ShoutOutput *sd = new ShoutOutput(); + if (!sd->Initialize(param, error_r)) { + delete sd; + return nullptr; + } -failure: - ao_base_finish(&sd->base); - free_shout_data(sd); - return NULL; + if (!sd->Configure(param, error_r)) { + sd->Deinitialize(); + delete sd; + return nullptr; + } + + if (shout_init_count == 0) + shout_init(); + + shout_init_count++; + + return &sd->base; } static bool -handle_shout_error(struct shout_data *sd, int err, GError **error) +handle_shout_error(ShoutOutput *sd, int err, GError **error) { switch (err) { case SHOUTERR_SUCCESS: @@ -338,9 +338,9 @@ handle_shout_error(struct shout_data *sd, int err, GError **error) } static bool -write_page(struct shout_data *sd, GError **error) +write_page(ShoutOutput *sd, GError **error) { - assert(sd->encoder != NULL); + assert(sd->encoder != nullptr); while (true) { size_t nbytes = encoder_read(sd->encoder, @@ -356,11 +356,11 @@ write_page(struct shout_data *sd, GError **error) return true; } -static void close_shout_conn(struct shout_data * sd) +static void close_shout_conn(ShoutOutput * sd) { - if (sd->encoder != NULL) { - if (encoder_end(sd->encoder, NULL)) - write_page(sd, NULL); + if (sd->encoder != nullptr) { + if (encoder_end(sd->encoder, nullptr)) + write_page(sd, nullptr); encoder_close(sd->encoder); } @@ -375,12 +375,12 @@ static void close_shout_conn(struct shout_data * sd) static void my_shout_finish_driver(struct audio_output *ao) { - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; encoder_finish(sd->encoder); - ao_base_finish(&sd->base); - free_shout_data(sd); + sd->Deinitialize(); + delete sd; shout_init_count--; @@ -392,7 +392,7 @@ static void my_shout_drop_buffered_audio(struct audio_output *ao) { G_GNUC_UNUSED - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; /* needs to be implemented for shout */ } @@ -400,13 +400,13 @@ my_shout_drop_buffered_audio(struct audio_output *ao) static void my_shout_close_device(struct audio_output *ao) { - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; close_shout_conn(sd); } static bool -shout_connect(struct shout_data *sd, GError **error) +shout_connect(ShoutOutput *sd, GError **error) { switch (shout_open(sd->shout_conn)) { case SHOUTERR_SUCCESS: @@ -424,10 +424,10 @@ shout_connect(struct shout_data *sd, GError **error) } static bool -my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format, +my_shout_open_device(struct audio_output *ao, AudioFormat &audio_format, GError **error) { - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; if (!shout_connect(sd, error)) return false; @@ -449,7 +449,7 @@ my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format, static unsigned my_shout_delay(struct audio_output *ao) { - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; int delay = shout_delay(sd->shout_conn); if (delay < 0) @@ -462,7 +462,7 @@ static size_t my_shout_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct shout_data *sd = (struct shout_data *)ao; + ShoutOutput *sd = (ShoutOutput *)ao; return encoder_write(sd->encoder, chunk, size, error) && write_page(sd, error) @@ -473,13 +473,13 @@ my_shout_play(struct audio_output *ao, const void *chunk, size_t size, static bool my_shout_pause(struct audio_output *ao) { - static const char silence[1020]; + static char silence[1020]; - return my_shout_play(ao, silence, sizeof(silence), NULL); + return my_shout_play(ao, silence, sizeof(silence), nullptr); } static void -shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size) +shout_tag_to_metadata(const Tag *tag, char *dest, size_t size) { char artist[size]; char title[size]; @@ -505,12 +505,12 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size) } static void my_shout_set_tag(struct audio_output *ao, - const struct tag *tag) + const Tag *tag) { - struct shout_data *sd = (struct shout_data *)ao; - GError *error = NULL; + ShoutOutput *sd = (ShoutOutput *)ao; + GError *error = nullptr; - if (sd->encoder->plugin->tag != NULL) { + if (sd->encoder->plugin.tag != nullptr) { /* encoder plugin supports stream tags */ if (!encoder_pre_tag(sd->encoder, &error)) { @@ -519,7 +519,7 @@ static void my_shout_set_tag(struct audio_output *ao, return; } - if (!write_page(sd, NULL)) + if (!write_page(sd, nullptr)) return; if (!encoder_tag(sd->encoder, tag, &error)) { @@ -538,18 +538,23 @@ static void my_shout_set_tag(struct audio_output *ao, } } - write_page(sd, NULL); + write_page(sd, nullptr); } const struct audio_output_plugin shout_output_plugin = { - .name = "shout", - .init = my_shout_init_driver, - .finish = my_shout_finish_driver, - .open = my_shout_open_device, - .delay = my_shout_delay, - .play = my_shout_play, - .pause = my_shout_pause, - .cancel = my_shout_drop_buffered_audio, - .close = my_shout_close_device, - .send_tag = my_shout_set_tag, + "shout", + nullptr, + my_shout_init_driver, + my_shout_finish_driver, + nullptr, + nullptr, + my_shout_open_device, + my_shout_close_device, + my_shout_delay, + my_shout_set_tag, + my_shout_play, + nullptr, + my_shout_drop_buffered_audio, + my_shout_pause, + nullptr, }; diff --git a/src/output/shout_output_plugin.h b/src/output/ShoutOutputPlugin.hxx index 9a7378803..496b77975 100644 --- a/src/output/shout_output_plugin.h +++ b/src/output/ShoutOutputPlugin.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_SHOUT_OUTPUT_PLUGIN_H -#define MPD_SHOUT_OUTPUT_PLUGIN_H +#ifndef MPD_SHOUT_OUTPUT_PLUGIN_HXX +#define MPD_SHOUT_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin shout_output_plugin; diff --git a/src/output/solaris_output_plugin.c b/src/output/SolarisOutputPlugin.cxx index ce726009a..074eae728 100644 --- a/src/output/solaris_output_plugin.c +++ b/src/output/SolarisOutputPlugin.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,8 +18,8 @@ */ #include "config.h" -#include "solaris_output_plugin.h" -#include "output_api.h" +#include "SolarisOutputPlugin.hxx" +#include "OutputAPI.hxx" #include "fd_util.h" #include <glib.h> @@ -53,13 +53,22 @@ struct audio_info { #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "solaris_output" -struct solaris_output { +struct SolarisOutput { struct audio_output base; /* configuration */ const char *device; int fd; + + bool Initialize(const config_param ¶m, GError **error_r) { + return ao_base_init(&base, &solaris_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } }; /** @@ -81,16 +90,15 @@ solaris_output_test_default_device(void) } static struct audio_output * -solaris_output_init(const struct config_param *param, GError **error_r) +solaris_output_init(const config_param ¶m, GError **error_r) { - struct solaris_output *so = g_new(struct solaris_output, 1); - - if (!ao_base_init(&so->base, &solaris_output_plugin, param, error_r)) { - g_free(so); - return NULL; + SolarisOutput *so = new SolarisOutput(); + if (!so->Initialize(param, error_r)) { + delete so; + return nullptr; } - so->device = config_get_block_string(param, "device", "/dev/audio"); + so->device = param.GetBlockValue("device", "/dev/audio"); return &so->base; } @@ -98,23 +106,23 @@ solaris_output_init(const struct config_param *param, GError **error_r) static void solaris_output_finish(struct audio_output *ao) { - struct solaris_output *so = (struct solaris_output *)ao; + SolarisOutput *so = (SolarisOutput *)ao; - ao_base_finish(&so->base); - g_free(so); + so->Deinitialize(); + delete so; } static bool -solaris_output_open(struct audio_output *ao, struct audio_format *audio_format, +solaris_output_open(struct audio_output *ao, AudioFormat &audio_format, GError **error) { - struct solaris_output *so = (struct solaris_output *)ao; + SolarisOutput *so = (SolarisOutput *)ao; struct audio_info info; int ret, flags; /* support only 16 bit mono/stereo for now; nothing else has been tested */ - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; /* open the device in non-blocking mode */ @@ -142,8 +150,8 @@ solaris_output_open(struct audio_output *ao, struct audio_format *audio_format, return false; } - info.play.sample_rate = audio_format->sample_rate; - info.play.channels = audio_format->channels; + info.play.sample_rate = audio_format.sample_rate; + info.play.channels = audio_format.channels; info.play.precision = 16; info.play.encoding = AUDIO_ENCODING_LINEAR; @@ -161,7 +169,7 @@ solaris_output_open(struct audio_output *ao, struct audio_format *audio_format, static void solaris_output_close(struct audio_output *ao) { - struct solaris_output *so = (struct solaris_output *)ao; + SolarisOutput *so = (SolarisOutput *)ao; close(so->fd); } @@ -170,7 +178,7 @@ static size_t solaris_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { - struct solaris_output *so = (struct solaris_output *)ao; + SolarisOutput *so = (SolarisOutput *)ao; ssize_t nbytes; nbytes = write(so->fd, chunk, size); @@ -186,18 +194,25 @@ solaris_output_play(struct audio_output *ao, const void *chunk, size_t size, static void solaris_output_cancel(struct audio_output *ao) { - struct solaris_output *so = (struct solaris_output *)ao; + SolarisOutput *so = (SolarisOutput *)ao; ioctl(so->fd, I_FLUSH); } const struct audio_output_plugin solaris_output_plugin = { - .name = "solaris", - .test_default_device = solaris_output_test_default_device, - .init = solaris_output_init, - .finish = solaris_output_finish, - .open = solaris_output_open, - .close = solaris_output_close, - .play = solaris_output_play, - .cancel = solaris_output_cancel, + "solaris", + solaris_output_test_default_device, + solaris_output_init, + solaris_output_finish, + nullptr, + nullptr, + solaris_output_open, + solaris_output_close, + nullptr, + nullptr, + solaris_output_play, + nullptr, + solaris_output_cancel, + nullptr, + nullptr, }; diff --git a/src/output/solaris_output_plugin.h b/src/output/SolarisOutputPlugin.hxx index 600aea8c2..d0fbd32c8 100644 --- a/src/output/solaris_output_plugin.h +++ b/src/output/SolarisOutputPlugin.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_SOLARIS_OUTPUT_PLUGIN_H -#define MPD_SOLARIS_OUTPUT_PLUGIN_H +#ifndef MPD_SOLARIS_OUTPUT_PLUGIN_HXX +#define MPD_SOLARIS_OUTPUT_PLUGIN_HXX extern const struct audio_output_plugin solaris_output_plugin; diff --git a/src/output/winmm_output_plugin.c b/src/output/WinmmOutputPlugin.cxx index 4d95834b9..d02b52c58 100644 --- a/src/output/winmm_output_plugin.c +++ b/src/output/WinmmOutputPlugin.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,26 +18,24 @@ */ #include "config.h" -#include "winmm_output_plugin.h" -#include "output_api.h" -#include "pcm_buffer.h" -#include "mixer_list.h" -#include "winmm_output_plugin.h" +#include "WinmmOutputPlugin.hxx" +#include "OutputAPI.hxx" +#include "pcm/PcmBuffer.hxx" +#include "MixerList.hxx" #include <stdlib.h> #include <string.h> -#include <windows.h> #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "winmm_output" -struct winmm_buffer { - struct pcm_buffer buffer; +struct WinmmBuffer { + PcmBuffer buffer; WAVEHDR hdr; }; -struct winmm_output { +struct WinmmOutput { struct audio_output base; UINT device_id; @@ -49,7 +47,7 @@ struct winmm_output { */ HANDLE event; - struct winmm_buffer buffers[8]; + WinmmBuffer buffers[8]; unsigned next_buffer; }; @@ -63,7 +61,7 @@ winmm_output_quark(void) } HWAVEOUT -winmm_output_get_handle(struct winmm_output* output) +winmm_output_get_handle(WinmmOutput *output) { return output->handle; } @@ -78,7 +76,7 @@ static bool get_device_id(const char *device_name, UINT *device_id, GError **error_r) { /* if device is not specified use wave mapper */ - if (device_name == NULL) { + if (device_name == nullptr) { *device_id = WAVE_MAPPER; return true; } @@ -116,19 +114,19 @@ fail: } static struct audio_output * -winmm_output_init(const struct config_param *param, GError **error_r) +winmm_output_init(const config_param ¶m, GError **error_r) { - struct winmm_output *wo = g_new(struct winmm_output, 1); + WinmmOutput *wo = new WinmmOutput(); if (!ao_base_init(&wo->base, &winmm_output_plugin, param, error_r)) { g_free(wo); - return NULL; + return nullptr; } - const char *device = config_get_block_string(param, "device", NULL); + const char *device = param.GetBlockValue("device"); if (!get_device_id(device, &wo->device_id, error_r)) { ao_base_finish(&wo->base); g_free(wo); - return NULL; + return nullptr; } return &wo->base; @@ -137,49 +135,51 @@ winmm_output_init(const struct config_param *param, GError **error_r) static void winmm_output_finish(struct audio_output *ao) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; ao_base_finish(&wo->base); - g_free(wo); + delete wo; } static bool -winmm_output_open(struct audio_output *ao, struct audio_format *audio_format, +winmm_output_open(struct audio_output *ao, AudioFormat &audio_format, GError **error_r) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; - wo->event = CreateEvent(NULL, false, false, NULL); - if (wo->event == NULL) { + wo->event = CreateEvent(nullptr, false, false, nullptr); + if (wo->event == nullptr) { g_set_error(error_r, winmm_output_quark(), 0, "CreateEvent() failed"); return false; } - switch (audio_format->format) { - case SAMPLE_FORMAT_S8: - case SAMPLE_FORMAT_S16: + switch (audio_format.format) { + case SampleFormat::S8: + case SampleFormat::S16: break; - case SAMPLE_FORMAT_S24_P32: - case SAMPLE_FORMAT_S32: - case SAMPLE_FORMAT_UNDEFINED: + case SampleFormat::S24_P32: + case SampleFormat::S32: + case SampleFormat::FLOAT: + case SampleFormat::DSD: + case SampleFormat::UNDEFINED: /* we havn't tested formats other than S16 */ - audio_format->format = SAMPLE_FORMAT_S16; + audio_format.format = SampleFormat::S16; break; } - if (audio_format->channels > 2) + if (audio_format.channels > 2) /* same here: more than stereo was not tested */ - audio_format->channels = 2; + audio_format.channels = 2; WAVEFORMATEX format; format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = audio_format->channels; - format.nSamplesPerSec = audio_format->sample_rate; - format.nBlockAlign = audio_format_frame_size(audio_format); + format.nChannels = audio_format.channels; + format.nSamplesPerSec = audio_format.sample_rate; + format.nBlockAlign = audio_format.GetFrameSize(); format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; - format.wBitsPerSample = audio_format_sample_size(audio_format) * 8; + format.wBitsPerSample = audio_format.GetSampleSize() * 8; format.cbSize = 0; MMRESULT result = waveOutOpen(&wo->handle, wo->device_id, &format, @@ -192,7 +192,6 @@ winmm_output_open(struct audio_output *ao, struct audio_format *audio_format, } for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) { - pcm_buffer_init(&wo->buffers[i].buffer); memset(&wo->buffers[i].hdr, 0, sizeof(wo->buffers[i].hdr)); } @@ -204,10 +203,10 @@ winmm_output_open(struct audio_output *ao, struct audio_format *audio_format, static void winmm_output_close(struct audio_output *ao) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) - pcm_buffer_deinit(&wo->buffers[i].buffer); + wo->buffers[i].buffer.Clear(); waveOutClose(wo->handle); @@ -218,17 +217,17 @@ winmm_output_close(struct audio_output *ao) * Copy data into a buffer, and prepare the wave header. */ static bool -winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer, +winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer, const void *data, size_t size, GError **error_r) { - void *dest = pcm_buffer_get(&buffer->buffer, size); - assert(dest != NULL); + void *dest = buffer->buffer.Get(size); + assert(dest != nullptr); memcpy(dest, data, size); memset(&buffer->hdr, 0, sizeof(buffer->hdr)); - buffer->hdr.lpData = dest; + buffer->hdr.lpData = (LPSTR)dest; buffer->hdr.dwBufferLength = size; MMRESULT result = waveOutPrepareHeader(wo->handle, &buffer->hdr, @@ -246,7 +245,7 @@ winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer, * Wait until the buffer is finished. */ static bool -winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer, +winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer, GError **error_r) { if ((buffer->hdr.dwFlags & WHDR_DONE) == WHDR_DONE) @@ -273,10 +272,10 @@ winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer, static size_t winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; /* get the next buffer from the ring and prepare it */ - struct winmm_buffer *buffer = &wo->buffers[wo->next_buffer]; + WinmmBuffer *buffer = &wo->buffers[wo->next_buffer]; if (!winmm_drain_buffer(wo, buffer, error_r) || !winmm_set_buffer(wo, buffer, chunk, size, error_r)) return 0; @@ -300,7 +299,7 @@ winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GErro } static bool -winmm_drain_all_buffers(struct winmm_output *wo, GError **error_r) +winmm_drain_all_buffers(WinmmOutput *wo, GError **error_r) { for (unsigned i = wo->next_buffer; i < G_N_ELEMENTS(wo->buffers); ++i) if (!winmm_drain_buffer(wo, &wo->buffers[i], error_r)) @@ -314,12 +313,12 @@ winmm_drain_all_buffers(struct winmm_output *wo, GError **error_r) } static void -winmm_stop(struct winmm_output *wo) +winmm_stop(WinmmOutput *wo) { waveOutReset(wo->handle); for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) { - struct winmm_buffer *buffer = &wo->buffers[i]; + WinmmBuffer *buffer = &wo->buffers[i]; waveOutUnprepareHeader(wo->handle, &buffer->hdr, sizeof(buffer->hdr)); } @@ -328,29 +327,34 @@ winmm_stop(struct winmm_output *wo) static void winmm_output_drain(struct audio_output *ao) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; - if (!winmm_drain_all_buffers(wo, NULL)) + if (!winmm_drain_all_buffers(wo, nullptr)) winmm_stop(wo); } static void winmm_output_cancel(struct audio_output *ao) { - struct winmm_output *wo = (struct winmm_output *)ao; + WinmmOutput *wo = (WinmmOutput *)ao; winmm_stop(wo); } const struct audio_output_plugin winmm_output_plugin = { - .name = "winmm", - .test_default_device = winmm_output_test_default_device, - .init = winmm_output_init, - .finish = winmm_output_finish, - .open = winmm_output_open, - .close = winmm_output_close, - .play = winmm_output_play, - .drain = winmm_output_drain, - .cancel = winmm_output_cancel, - .mixer_plugin = &winmm_mixer_plugin, + "winmm", + winmm_output_test_default_device, + winmm_output_init, + winmm_output_finish, + nullptr, + nullptr, + winmm_output_open, + winmm_output_close, + nullptr, + nullptr, + winmm_output_play, + winmm_output_drain, + winmm_output_cancel, + nullptr, + &winmm_mixer_plugin, }; diff --git a/src/output/winmm_output_plugin.h b/src/output/WinmmOutputPlugin.hxx index 0605530e1..e8688782e 100644 --- a/src/output/winmm_output_plugin.h +++ b/src/output/WinmmOutputPlugin.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,20 +17,25 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_WINMM_OUTPUT_PLUGIN_H -#define MPD_WINMM_OUTPUT_PLUGIN_H +#ifndef MPD_WINMM_OUTPUT_PLUGIN_HXX +#define MPD_WINMM_OUTPUT_PLUGIN_HXX #include "check.h" #ifdef ENABLE_WINMM_OUTPUT +#include "gcc.h" + #include <windows.h> +#include <mmsystem.h> -struct winmm_output; +struct WinmmOutput; extern const struct audio_output_plugin winmm_output_plugin; -HWAVEOUT winmm_output_get_handle(struct winmm_output*); +gcc_pure +HWAVEOUT +winmm_output_get_handle(WinmmOutput *); #endif diff --git a/src/output/ffado_output_plugin.c b/src/output/ffado_output_plugin.c deleted file mode 100644 index ba239a4ad..000000000 --- a/src/output/ffado_output_plugin.c +++ /dev/null @@ -1,359 +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. - */ - -/* - * Warning: this plugin was not tested successfully. I just couldn't - * keep libffado2 from crashing. Use at your own risk. - * - * For details, see my Debian bug reports: - * - * http://bugs.debian.org/601657 - * http://bugs.debian.org/601659 - * http://bugs.debian.org/601663 - * - */ - -#include "config.h" -#include "ffado_output_plugin.h" -#include "output_api.h" -#include "timer.h" - -#include <glib.h> -#include <assert.h> - -#include <libffado/ffado.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "ffado" - -enum { - MAX_STREAMS = 8, -}; - -struct mpd_ffado_stream { - /** libffado's stream number */ - int number; - - float *buffer; -}; - -struct mpd_ffado_device { - struct audio_output base; - - char *device_name; - int verbose; - unsigned period_size, nb_buffers; - - ffado_device_t *dev; - - /** - * The current sample position inside the stream buffers. New - * samples get appended at this position on all streams at the - * same time. When the buffers are full - * (buffer_position==period_size), - * ffado_streaming_transfer_playback_buffers() gets called to - * hand them over to libffado. - */ - unsigned buffer_position; - - /** - * The number of streams which are really used by MPD. - */ - int num_streams; - struct mpd_ffado_stream streams[MAX_STREAMS]; -}; - -static inline GQuark -ffado_output_quark(void) -{ - return g_quark_from_static_string("ffado_output"); -} - -static struct audio_output * -ffado_init(const struct config_param *param, - GError **error_r) -{ - g_debug("using libffado version %s, API=%d", - ffado_get_version(), ffado_get_api_version()); - - struct mpd_ffado_device *fd = g_new(struct mpd_ffado_device, 1); - if (!ao_base_init(&fd->base, &ffado_output_plugin, param, error_r)) { - g_free(fd); - return NULL; - } - - fd->device_name = config_dup_block_string(param, "device", NULL); - fd->verbose = config_get_block_unsigned(param, "verbose", 0); - - fd->period_size = config_get_block_unsigned(param, "period_size", - 1024); - if (fd->period_size == 0 || fd->period_size > 1024 * 1024) { - ao_base_finish(&fd->base); - g_set_error(error_r, ffado_output_quark(), 0, - "invalid period_size setting"); - return false; - } - - fd->nb_buffers = config_get_block_unsigned(param, "nb_buffers", 3); - if (fd->nb_buffers == 0 || fd->nb_buffers > 1024) { - ao_base_finish(&fd->base); - g_set_error(error_r, ffado_output_quark(), 0, - "invalid nb_buffers setting"); - return false; - } - - return &fd->base; -} - -static void -ffado_finish(struct audio_output *ao) -{ - struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; - - g_free(fd->device_name); - ao_base_finish(&fd->base); - g_free(fd); -} - -static bool -ffado_configure_stream(ffado_device_t *dev, struct mpd_ffado_stream *stream, - GError **error_r) -{ - char *buffer = (char *)stream->buffer; - if (ffado_streaming_set_playback_stream_buffer(dev, stream->number, - buffer) != 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "failed to configure stream buffer"); - return false; - } - - if (ffado_streaming_playback_stream_onoff(dev, stream->number, - 1) != 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "failed to disable stream"); - return false; - } - - return true; -} - -static bool -ffado_configure(struct mpd_ffado_device *fd, struct audio_format *audio_format, - GError **error_r) -{ - assert(fd != NULL); - assert(fd->dev != NULL); - assert(audio_format->channels <= MAX_STREAMS); - - if (ffado_streaming_set_audio_datatype(fd->dev, - ffado_audio_datatype_float) != 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_set_audio_datatype() failed"); - return false; - } - - int num_streams = ffado_streaming_get_nb_playback_streams(fd->dev); - if (num_streams < 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_get_nb_playback_streams() failed"); - return false; - } - - g_debug("there are %d playback streams", num_streams); - - fd->num_streams = 0; - for (int i = 0; i < num_streams; ++i) { - char name[256]; - ffado_streaming_get_playback_stream_name(fd->dev, i, name, - sizeof(name) - 1); - - ffado_streaming_stream_type type = - ffado_streaming_get_playback_stream_type(fd->dev, i); - if (type != ffado_stream_type_audio) { - g_debug("stream %d name='%s': not an audio stream", - i, name); - continue; - } - - if (fd->num_streams >= audio_format->channels) { - g_debug("stream %d name='%s': ignoring", - i, name); - continue; - } - - g_debug("stream %d name='%s'", i, name); - - struct mpd_ffado_stream *stream = - &fd->streams[fd->num_streams++]; - - stream->number = i; - - /* allocated buffer is zeroed = silence */ - stream->buffer = g_new0(float, fd->period_size); - - if (!ffado_configure_stream(fd->dev, stream, error_r)) - return false; - } - - if (!audio_valid_channel_count(fd->num_streams)) { - g_set_error(error_r, ffado_output_quark(), 0, - "invalid channel count from libffado: %u", - audio_format->channels); - return false; - } - - g_debug("configured %d audio streams", fd->num_streams); - - if (ffado_streaming_prepare(fd->dev) != 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_prepare() failed"); - return false; - } - - if (ffado_streaming_start(fd->dev) != 0) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_start() failed"); - return false; - } - - audio_format->channels = fd->num_streams; - return true; -} - -static bool -ffado_open(struct audio_output *ao, struct audio_format *audio_format, - GError **error_r) -{ - struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; - - /* will be converted to floating point, choose best input - format */ - audio_format->format = SAMPLE_FORMAT_S24_P32; - - ffado_device_info_t device_info; - memset(&device_info, 0, sizeof(device_info)); - if (fd->device_name != NULL) { - device_info.nb_device_spec_strings = 1; - device_info.device_spec_strings = &fd->device_name; - } - - ffado_options_t options; - memset(&options, 0, sizeof(options)); - options.sample_rate = audio_format->sample_rate; - options.period_size = fd->period_size; - options.nb_buffers = fd->nb_buffers; - options.verbose = fd->verbose; - - fd->dev = ffado_streaming_init(device_info, options); - if (fd->dev == NULL) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_init() failed"); - return false; - } - - if (!ffado_configure(fd, audio_format, error_r)) { - ffado_streaming_finish(fd->dev); - - for (int i = 0; i < fd->num_streams; ++i) { - struct mpd_ffado_stream *stream = &fd->streams[i]; - g_free(stream->buffer); - } - - return false; - } - - fd->buffer_position = 0; - - return true; -} - -static void -ffado_close(struct audio_output *ao) -{ - struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; - - ffado_streaming_stop(fd->dev); - ffado_streaming_finish(fd->dev); - - for (int i = 0; i < fd->num_streams; ++i) { - struct mpd_ffado_stream *stream = &fd->streams[i]; - g_free(stream->buffer); - } -} - -static size_t -ffado_play(struct audio_output *ao, const void *chunk, size_t size, - GError **error_r) -{ - struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; - - /* wait for prefious buffer to finish (if it was full) */ - - if (fd->buffer_position >= fd->period_size) { - switch (ffado_streaming_wait(fd->dev)) { - case ffado_wait_ok: - case ffado_wait_xrun: - break; - - default: - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_wait() failed"); - return 0; - } - - fd->buffer_position = 0; - } - - /* copy samples to stream buffers, non-interleaved */ - - const int32_t *p = chunk; - unsigned num_frames = size / sizeof(*p) / fd->num_streams; - if (num_frames > fd->period_size - fd->buffer_position) - num_frames = fd->period_size - fd->buffer_position; - - for (unsigned i = num_frames; i > 0; --i) { - for (int stream = 0; stream < fd->num_streams; ++stream) - fd->streams[stream].buffer[fd->buffer_position] = - *p++ / (float)(1 << 23); - ++fd->buffer_position; - } - - /* if buffer full, transfer to device */ - - if (fd->buffer_position >= fd->period_size && - /* libffado documentation says this function returns -1 on - error, but that is a lie - it returns a boolean value, - and "false" means error */ - !ffado_streaming_transfer_playback_buffers(fd->dev)) { - g_set_error(error_r, ffado_output_quark(), 0, - "ffado_streaming_transfer_playback_buffers() failed"); - return 0; - } - - return num_frames * sizeof(*p) * fd->num_streams; -} - -const struct audio_output_plugin ffado_output_plugin = { - .name = "ffado", - .init = ffado_init, - .finish = ffado_finish, - .open = ffado_open, - .close = ffado_close, - .play = ffado_play, -}; diff --git a/src/output/ffado_output_plugin.h b/src/output/ffado_output_plugin.h deleted file mode 100644 index 4dde01859..000000000 --- a/src/output/ffado_output_plugin.h +++ /dev/null @@ -1,25 +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_FFADO_OUTPUT_PLUGIN_H -#define MPD_FFADO_OUTPUT_PLUGIN_H - -extern const struct audio_output_plugin ffado_output_plugin; - -#endif diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c deleted file mode 100644 index 72de90457..000000000 --- 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 739163f42..000000000 --- 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 1d730df7f..000000000 --- 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/mvp_output_plugin.c b/src/output/mvp_output_plugin.c deleted file mode 100644 index 37e0f7c93..000000000 --- a/src/output/mvp_output_plugin.c +++ /dev/null @@ -1,344 +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. - */ - -/* - * Media MVP audio output based on code from MVPMC project: - * http://mvpmc.sourceforge.net/ - */ - -#include "config.h" -#include "mvp_output_plugin.h" -#include "output_api.h" -#include "fd_util.h" - -#include <glib.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mvp" - -typedef struct { - unsigned long dsp_status; - unsigned long stream_decode_type; - unsigned long sample_rate; - unsigned long bit_rate; - unsigned long raw[64 / sizeof(unsigned long)]; -} aud_status_t; - -#define MVP_SET_AUD_STOP _IOW('a',1,int) -#define MVP_SET_AUD_PLAY _IOW('a',2,int) -#define MVP_SET_AUD_PAUSE _IOW('a',3,int) -#define MVP_SET_AUD_UNPAUSE _IOW('a',4,int) -#define MVP_SET_AUD_SRC _IOW('a',5,int) -#define MVP_SET_AUD_MUTE _IOW('a',6,int) -#define MVP_SET_AUD_BYPASS _IOW('a',8,int) -#define MVP_SET_AUD_CHANNEL _IOW('a',9,int) -#define MVP_GET_AUD_STATUS _IOR('a',10,aud_status_t) -#define MVP_SET_AUD_VOLUME _IOW('a',13,int) -#define MVP_GET_AUD_VOLUME _IOR('a',14,int) -#define MVP_SET_AUD_STREAMTYPE _IOW('a',15,int) -#define MVP_SET_AUD_FORMAT _IOW('a',16,int) -#define MVP_GET_AUD_SYNC _IOR('a',21,pts_sync_data_t*) -#define MVP_SET_AUD_STC _IOW('a',22,long long int *) -#define MVP_SET_AUD_SYNC _IOW('a',23,int) -#define MVP_SET_AUD_END_STREAM _IOW('a',25,int) -#define MVP_SET_AUD_RESET _IOW('a',26,int) -#define MVP_SET_AUD_DAC_CLK _IOW('a',27,int) -#define MVP_GET_AUD_REGS _IOW('a',28,aud_ctl_regs_t*) - -struct mvp_data { - struct audio_output base; - - struct audio_format audio_format; - int fd; -}; - -static const unsigned mvp_sample_rates[][3] = { - {9, 8000, 32000}, - {10, 11025, 44100}, - {11, 12000, 48000}, - {1, 16000, 32000}, - {2, 22050, 44100}, - {3, 24000, 48000}, - {5, 32000, 32000}, - {0, 44100, 44100}, - {7, 48000, 48000}, - {13, 64000, 32000}, - {14, 88200, 44100}, - {15, 96000, 48000} -}; - -/** - * The quark used for GError.domain. - */ -static inline GQuark -mvp_output_quark(void) -{ - return g_quark_from_static_string("mvp_output"); -} - -/** - * Translate a sample rate to a MVP sample rate. - * - * @param sample_rate the sample rate in Hz - */ -static unsigned -mvp_find_sample_rate(unsigned sample_rate) -{ - for (unsigned i = 0; i < G_N_ELEMENTS(mvp_sample_rates); ++i) - if (mvp_sample_rates[i][1] == sample_rate) - return mvp_sample_rates[i][0]; - - return (unsigned)-1; -} - -static bool -mvp_output_test_default_device(void) -{ - int fd; - - fd = open_cloexec("/dev/adec_pcm", O_WRONLY, 0); - - if (fd >= 0) { - close(fd); - return true; - } - - g_warning("Error opening PCM device \"/dev/adec_pcm\": %s\n", - g_strerror(errno)); - - return false; -} - -static struct audio_output * -mvp_output_init(G_GNUC_UNUSED const struct config_param *param, GError **error) -{ - struct mvp_data *md = g_new(struct mvp_data, 1); - - if (!ao_base_init(&md->base, &mvp_output_plugin, param, error)) { - g_free(md); - return NULL; - } - - md->fd = -1; - - return &md->base; -} - -static void -mvp_output_finish(struct audio_output *ao) -{ - struct mvp_data *md = (struct mvp_data *)ao; - ao_base_finish(&md->base); - g_free(md); -} - -static bool -mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format, - GError **error) -{ - unsigned mix[5]; - - switch (audio_format->channels) { - case 1: - mix[0] = 1; - break; - - case 2: - mix[0] = 0; - break; - - default: - g_debug("unsupported channel count %u - falling back to stereo", - audio_format->channels); - audio_format->channels = 2; - mix[0] = 0; - break; - } - - /* 0,1=24bit(24) , 2,3=16bit */ - switch (audio_format->format) { - case SAMPLE_FORMAT_S16: - mix[1] = 2; - break; - - case SAMPLE_FORMAT_S24_P32: - mix[1] = 0; - break; - - default: - g_debug("unsupported sample format %s - falling back to 16 bit", - sample_format_to_string(audio_format->format)); - audio_format->format = SAMPLE_FORMAT_S16; - mix[1] = 2; - break; - } - - mix[3] = 0; /* stream type? */ - mix[4] = G_BYTE_ORDER == G_LITTLE_ENDIAN; - - /* - * if there is an exact match for the frequency, use it. - */ - mix[2] = mvp_find_sample_rate(audio_format->sample_rate); - if (mix[2] == (unsigned)-1) { - g_set_error(error, mvp_output_quark(), 0, - "Can not find suitable output frequency for %u", - audio_format->sample_rate); - return false; - } - - if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Can not set audio format"); - return false; - } - - if (ioctl(md->fd, MVP_SET_AUD_SYNC, 2) != 0) { - g_set_error(error, mvp_output_quark(), errno, - "Can not set audio sync"); - return false; - } - - if (ioctl(md->fd, MVP_SET_AUD_PLAY, 0) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Can not set audio play mode"); - return false; - } - - return true; -} - -static bool -mvp_output_open(struct audio_output *ao, struct audio_format *audio_format, - GError **error) -{ - struct mvp_data *md = (struct mvp_data *)ao; - long long int stc = 0; - int mix[5] = { 0, 2, 7, 1, 0 }; - bool success; - - md->fd = open_cloexec("/dev/adec_pcm", O_RDWR | O_NONBLOCK, 0); - if (md->fd < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Error opening /dev/adec_pcm: %s", - g_strerror(errno)); - return false; - } - if (ioctl(md->fd, MVP_SET_AUD_SRC, 1) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Error setting audio source: %s", - g_strerror(errno)); - return false; - } - if (ioctl(md->fd, MVP_SET_AUD_STREAMTYPE, 0) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Error setting audio streamtype: %s", - g_strerror(errno)); - return false; - } - if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Error setting audio format: %s", - g_strerror(errno)); - return false; - } - ioctl(md->fd, MVP_SET_AUD_STC, &stc); - if (ioctl(md->fd, MVP_SET_AUD_BYPASS, 1) < 0) { - g_set_error(error, mvp_output_quark(), errno, - "Error setting audio streamtype: %s", - g_strerror(errno)); - return false; - } - - success = mvp_set_pcm_params(md, audio_format, error); - if (!success) - return false; - - md->audio_format = *audio_format; - return true; -} - -static void mvp_output_close(struct audio_output *ao) -{ - struct mvp_data *md = (struct mvp_data *)ao; - if (md->fd >= 0) - close(md->fd); - md->fd = -1; -} - -static void mvp_output_cancel(struct audio_output *ao) -{ - struct mvp_data *md = (struct mvp_data *)ao; - if (md->fd >= 0) { - ioctl(md->fd, MVP_SET_AUD_RESET, 0x11); - close(md->fd); - md->fd = -1; - } -} - -static size_t -mvp_output_play(struct audio_output *ao, const void *chunk, size_t size, - GError **error) -{ - struct mvp_data *md = (struct mvp_data *)ao; - ssize_t ret; - - /* reopen the device since it was closed by dropBufferedAudio */ - if (md->fd < 0) { - bool success; - - success = mvp_output_open(ao, &md->audio_format, error); - if (!success) - return 0; - } - - while (true) { - ret = write(md->fd, chunk, size); - if (ret > 0) - return (size_t)ret; - - if (ret < 0) { - if (errno == EINTR) - continue; - - g_set_error(error, mvp_output_quark(), errno, - "Failed to write: %s", g_strerror(errno)); - return 0; - } - } -} - -const struct audio_output_plugin mvp_output_plugin = { - .name = "mvp", - .test_default_device = mvp_output_test_default_device, - .init = mvp_output_init, - .finish = mvp_output_finish, - .open = mvp_output_open, - .close = mvp_output_close, - .play = mvp_output_play, - .cancel = mvp_output_cancel, -}; diff --git a/src/output/mvp_output_plugin.h b/src/output/mvp_output_plugin.h deleted file mode 100644 index e403de2b7..000000000 --- a/src/output/mvp_output_plugin.h +++ /dev/null @@ -1,25 +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_MVP_OUTPUT_PLUGIN_H -#define MPD_MVP_OUTPUT_PLUGIN_H - -extern const struct audio_output_plugin mvp_output_plugin; - -#endif diff --git a/src/output/null_output_plugin.c b/src/output/null_output_plugin.c deleted file mode 100644 index 9d7588fff..000000000 --- a/src/output/null_output_plugin.c +++ /dev/null @@ -1,129 +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 "null_output_plugin.h" -#include "output_api.h" -#include "timer.h" - -#include <glib.h> - -#include <assert.h> - -struct null_data { - struct audio_output base; - - bool sync; - - struct timer *timer; -}; - -static struct audio_output * -null_init(const struct config_param *param, GError **error_r) -{ - struct null_data *nd = g_new(struct null_data, 1); - - if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) { - g_free(nd); - return NULL; - } - - nd->sync = config_get_block_bool(param, "sync", true); - - return &nd->base; -} - -static void -null_finish(struct audio_output *ao) -{ - struct null_data *nd = (struct null_data *)ao; - - ao_base_finish(&nd->base); - g_free(nd); -} - -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; - - if (nd->sync) - nd->timer = timer_new(audio_format); - - return true; -} - -static void -null_close(struct audio_output *ao) -{ - struct null_data *nd = (struct null_data *)ao; - - if (nd->sync) - timer_free(nd->timer); -} - -static unsigned -null_delay(struct audio_output *ao) -{ - struct null_data *nd = (struct null_data *)ao; - - return nd->sync && nd->timer->started - ? timer_delay(nd->timer) - : 0; -} - -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; - struct timer *timer = nd->timer; - - if (!nd->sync) - return size; - - if (!timer->started) - timer_start(timer); - timer_add(timer, size); - - return size; -} - -static void -null_cancel(struct audio_output *ao) -{ - struct null_data *nd = (struct null_data *)ao; - - if (!nd->sync) - return; - - timer_reset(nd->timer); -} - -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, -}; |