output_plugin: report errors with GError
Use GLib's GError library for reporting output device failures. Note that some init() methods don't clean up properly after a failure, but that's ok for now, because the MPD core will abort anyway.
This commit is contained in:
parent
353ae5e558
commit
ec926539a3
|
@ -74,6 +74,15 @@ struct alsa_data {
|
||||||
struct mixer *mixer;
|
struct mixer *mixer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
alsa_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("alsa_output");
|
||||||
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
alsa_device(const struct alsa_data *ad)
|
alsa_device(const struct alsa_data *ad)
|
||||||
{
|
{
|
||||||
|
@ -130,7 +139,8 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
alsa_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
alsa_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
/* no need for pthread_once thread-safety when reading config */
|
/* no need for pthread_once thread-safety when reading config */
|
||||||
static int free_global_registered;
|
static int free_global_registered;
|
||||||
|
@ -198,7 +208,8 @@ get_bitformat(const struct audio_format *af)
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
|
alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
|
||||||
snd_pcm_format_t bitformat)
|
snd_pcm_format_t bitformat,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
snd_pcm_hw_params_t *hwparams;
|
snd_pcm_hw_params_t *hwparams;
|
||||||
snd_pcm_sw_params_t *swparams;
|
snd_pcm_sw_params_t *swparams;
|
||||||
|
@ -256,17 +267,20 @@ configure_hw:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
g_warning("ALSA device \"%s\" does not support %u bit audio: %s\n",
|
g_set_error(error, alsa_output_quark(), err,
|
||||||
alsa_device(ad), audio_format->bits, snd_strerror(-err));
|
"ALSA device \"%s\" does not support %u bit audio: %s",
|
||||||
|
alsa_device(ad), audio_format->bits,
|
||||||
|
snd_strerror(-err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
|
err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
|
||||||
&channels);
|
&channels);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
g_warning("ALSA device \"%s\" does not support %i channels: %s\n",
|
g_set_error(error, alsa_output_quark(), err,
|
||||||
alsa_device(ad), (int)audio_format->channels,
|
"ALSA device \"%s\" does not support %i channels: %s",
|
||||||
snd_strerror(-err));
|
alsa_device(ad), (int)audio_format->channels,
|
||||||
|
snd_strerror(-err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
audio_format->channels = (int8_t)channels;
|
audio_format->channels = (int8_t)channels;
|
||||||
|
@ -274,8 +288,9 @@ configure_hw:
|
||||||
err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
|
err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
|
||||||
&sample_rate, NULL);
|
&sample_rate, NULL);
|
||||||
if (err < 0 || sample_rate == 0) {
|
if (err < 0 || sample_rate == 0) {
|
||||||
g_warning("ALSA device \"%s\" does not support %u Hz audio\n",
|
g_set_error(error, alsa_output_quark(), err,
|
||||||
alsa_device(ad), audio_format->sample_rate);
|
"ALSA device \"%s\" does not support %u Hz audio",
|
||||||
|
alsa_device(ad), audio_format->sample_rate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
audio_format->sample_rate = sample_rate;
|
audio_format->sample_rate = sample_rate;
|
||||||
|
@ -348,14 +363,14 @@ configure_hw:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
g_warning("Error opening ALSA device \"%s\" (%s): %s\n",
|
g_set_error(error, alsa_output_quark(), err,
|
||||||
alsa_device(ad), cmd, snd_strerror(-err));
|
"Error opening ALSA device \"%s\" (%s): %s",
|
||||||
|
alsa_device(ad), cmd, snd_strerror(-err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
alsa_open(void *data, struct audio_format *audio_format)
|
alsa_open(void *data, struct audio_format *audio_format, GError **error)
|
||||||
{
|
{
|
||||||
struct alsa_data *ad = data;
|
struct alsa_data *ad = data;
|
||||||
snd_pcm_format_t bitformat;
|
snd_pcm_format_t bitformat;
|
||||||
|
@ -378,12 +393,13 @@ alsa_open(void *data, struct audio_format *audio_format)
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
ad->pcm = NULL;
|
ad->pcm = NULL;
|
||||||
|
|
||||||
g_warning("Error opening ALSA device \"%s\": %s\n",
|
g_set_error(error, alsa_output_quark(), err,
|
||||||
alsa_device(ad), snd_strerror(-err));
|
"Failed to open ALSA device \"%s\": %s",
|
||||||
|
alsa_device(ad), snd_strerror(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
success = alsa_setup(ad, audio_format, bitformat);
|
success = alsa_setup(ad, audio_format, bitformat, error);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
snd_pcm_close(ad->pcm);
|
snd_pcm_close(ad->pcm);
|
||||||
ad->pcm = NULL;
|
ad->pcm = NULL;
|
||||||
|
@ -463,7 +479,7 @@ alsa_close(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
alsa_play(void *data, const void *chunk, size_t size)
|
alsa_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
struct alsa_data *ad = data;
|
struct alsa_data *ad = data;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -477,9 +493,8 @@ alsa_play(void *data, const void *chunk, size_t size)
|
||||||
|
|
||||||
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
|
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
|
||||||
alsa_recover(ad, ret) < 0) {
|
alsa_recover(ad, ret) < 0) {
|
||||||
g_warning("closing ALSA device \"%s\" due to write "
|
g_set_error(error, alsa_output_quark(), errno,
|
||||||
"error: %s\n",
|
"%s", snd_strerror(-errno));
|
||||||
alsa_device(ad), snd_strerror(-errno));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,14 @@ struct ao_data {
|
||||||
ao_device *device;
|
ao_device *device;
|
||||||
} AoData;
|
} AoData;
|
||||||
|
|
||||||
|
static inline GQuark
|
||||||
|
ao_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("ao_output");
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ao_output_error(const char *msg)
|
ao_output_error(GError **error_r)
|
||||||
{
|
{
|
||||||
const char *error;
|
const char *error;
|
||||||
|
|
||||||
|
@ -63,12 +69,14 @@ ao_output_error(const char *msg)
|
||||||
error = strerror(errno);
|
error = strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_warning("%s: %s\n", msg, error);
|
g_set_error(error_r, ao_output_quark(), errno,
|
||||||
|
"%s", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct ao_data *ad = g_new(struct ao_data, 1);
|
struct ao_data *ad = g_new(struct ao_data, 1);
|
||||||
ao_info *ai;
|
ao_info *ai;
|
||||||
|
@ -84,15 +92,22 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
ao_output_ref++;
|
ao_output_ref++;
|
||||||
|
|
||||||
value = config_get_block_string(param, "driver", "default");
|
value = config_get_block_string(param, "driver", "default");
|
||||||
if (0 == strcmp(value, "default")) {
|
if (0 == strcmp(value, "default"))
|
||||||
ad->driver = ao_default_driver_id();
|
ad->driver = ao_default_driver_id();
|
||||||
} else if ((ad->driver = ao_driver_id(value)) < 0)
|
else
|
||||||
g_error("\"%s\" is not a valid ao driver at line %i\n",
|
ad->driver = ao_driver_id(value);
|
||||||
value, param->line);
|
|
||||||
|
if (ad->driver < 0) {
|
||||||
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
|
"\"%s\" is not a valid ao driver",
|
||||||
|
value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if ((ai = ao_driver_info(ad->driver)) == NULL) {
|
if ((ai = ao_driver_info(ad->driver)) == NULL) {
|
||||||
g_error("problems getting driver info for device defined at line %i\n"
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
"you may not have permission to the audio device\n", param->line);
|
"problems getting driver info");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_debug("using ao driver \"%s\" for \"%s\"\n", ai->short_name,
|
g_debug("using ao driver \"%s\" for \"%s\"\n", ai->short_name,
|
||||||
|
@ -105,9 +120,12 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
for (unsigned i = 0; options[i] != NULL; ++i) {
|
for (unsigned i = 0; options[i] != NULL; ++i) {
|
||||||
gchar **key_value = g_strsplit(options[i], "=", 2);
|
gchar **key_value = g_strsplit(options[i], "=", 2);
|
||||||
|
|
||||||
if (key_value[0] == NULL || key_value[1] == NULL)
|
if (key_value[0] == NULL || key_value[1] == NULL) {
|
||||||
g_error("problems parsing options \"%s\"\n",
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
options[i]);
|
"problems parsing options \"%s\"",
|
||||||
|
options[i]);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ao_append_option(&ad->options, key_value[0],
|
ao_append_option(&ad->options, key_value[0],
|
||||||
key_value[1]);
|
key_value[1]);
|
||||||
|
@ -144,7 +162,8 @@ ao_output_close(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
ao_output_open(void *data, struct audio_format *audio_format)
|
ao_output_open(void *data, struct audio_format *audio_format,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
ao_sample_format format;
|
ao_sample_format format;
|
||||||
struct ao_data *ad = (struct ao_data *)data;
|
struct ao_data *ad = (struct ao_data *)data;
|
||||||
|
@ -163,7 +182,7 @@ ao_output_open(void *data, struct audio_format *audio_format)
|
||||||
ad->device = ao_open_live(ad->driver, &format, ad->options);
|
ad->device = ao_open_live(ad->driver, &format, ad->options);
|
||||||
|
|
||||||
if (ad->device == NULL) {
|
if (ad->device == NULL) {
|
||||||
ao_output_error("Failed to open libao");
|
ao_output_error(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +207,8 @@ static int ao_play_deconst(ao_device *device, const void *output_samples,
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
ao_output_play(void *data, const void *chunk, size_t size)
|
ao_output_play(void *data, const void *chunk, size_t size,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct ao_data *ad = (struct ao_data *)data;
|
struct ao_data *ad = (struct ao_data *)data;
|
||||||
|
|
||||||
|
@ -196,7 +216,7 @@ ao_output_play(void *data, const void *chunk, size_t size)
|
||||||
size = ad->write_size;
|
size = ad->write_size;
|
||||||
|
|
||||||
if (ao_play_deconst(ad->device, chunk, size) == 0) {
|
if (ao_play_deconst(ad->device, chunk, size) == 0) {
|
||||||
ao_output_error("Closing libao device due to play error");
|
ao_output_error(error);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,15 @@ struct fifo_data {
|
||||||
Timer *timer;
|
Timer *timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
fifo_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("fifo_output");
|
||||||
|
}
|
||||||
|
|
||||||
static struct fifo_data *fifo_data_new(void)
|
static struct fifo_data *fifo_data_new(void)
|
||||||
{
|
{
|
||||||
struct fifo_data *ret;
|
struct fifo_data *ret;
|
||||||
|
@ -95,11 +104,12 @@ fifo_close(struct fifo_data *fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fifo_make(struct fifo_data *fd)
|
fifo_make(struct fifo_data *fd, GError **error)
|
||||||
{
|
{
|
||||||
if (mkfifo(fd->path, 0666) < 0) {
|
if (mkfifo(fd->path, 0666) < 0) {
|
||||||
g_warning("Couldn't create FIFO \"%s\": %s",
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
fd->path, strerror(errno));
|
"Couldn't create FIFO \"%s\": %s",
|
||||||
|
fd->path, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,24 +119,26 @@ fifo_make(struct fifo_data *fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fifo_check(struct fifo_data *fd)
|
fifo_check(struct fifo_data *fd, GError **error)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (stat(fd->path, &st) < 0) {
|
if (stat(fd->path, &st) < 0) {
|
||||||
if (errno == ENOENT) {
|
if (errno == ENOENT) {
|
||||||
/* Path doesn't exist */
|
/* Path doesn't exist */
|
||||||
return fifo_make(fd);
|
return fifo_make(fd, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_warning("Failed to stat FIFO \"%s\": %s",
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
fd->path, strerror(errno));
|
"Failed to stat FIFO \"%s\": %s",
|
||||||
|
fd->path, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISFIFO(st.st_mode)) {
|
if (!S_ISFIFO(st.st_mode)) {
|
||||||
g_warning("\"%s\" already exists, but is not a FIFO",
|
g_set_error(error, fifo_output_quark(), 0,
|
||||||
fd->path);
|
"\"%s\" already exists, but is not a FIFO",
|
||||||
|
fd->path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,23 +146,25 @@ fifo_check(struct fifo_data *fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fifo_open(struct fifo_data *fd)
|
fifo_open(struct fifo_data *fd, GError **error)
|
||||||
{
|
{
|
||||||
if (!fifo_check(fd))
|
if (!fifo_check(fd, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
fd->input = open(fd->path, O_RDONLY|O_NONBLOCK);
|
fd->input = open(fd->path, O_RDONLY|O_NONBLOCK);
|
||||||
if (fd->input < 0) {
|
if (fd->input < 0) {
|
||||||
g_warning("Could not open FIFO \"%s\" for reading: %s",
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
fd->path, strerror(errno));
|
"Could not open FIFO \"%s\" for reading: %s",
|
||||||
|
fd->path, strerror(errno));
|
||||||
fifo_close(fd);
|
fifo_close(fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd->output = open(fd->path, O_WRONLY|O_NONBLOCK);
|
fd->output = open(fd->path, O_WRONLY|O_NONBLOCK);
|
||||||
if (fd->output < 0) {
|
if (fd->output < 0) {
|
||||||
g_warning("Could not open FIFO \"%s\" for writing: %s",
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
fd->path, strerror(errno));
|
"Could not open FIFO \"%s\" for writing: %s",
|
||||||
|
fd->path, strerror(errno));
|
||||||
fifo_close(fd);
|
fifo_close(fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -160,27 +174,31 @@ fifo_open(struct fifo_data *fd)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct fifo_data *fd;
|
struct fifo_data *fd;
|
||||||
char *value, *path;
|
char *value, *path;
|
||||||
|
|
||||||
value = config_dup_block_string(param, "path", NULL);
|
value = config_dup_block_string(param, "path", NULL);
|
||||||
if (value == NULL)
|
if (value == NULL) {
|
||||||
g_error("No \"path\" parameter specified for fifo output "
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
"defined at line %i", param->line);
|
"No \"path\" parameter specified");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
path = parsePath(value);
|
path = parsePath(value);
|
||||||
g_free(value);
|
g_free(value);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
g_error("Could not parse \"path\" parameter for fifo output "
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
"at line %i", param->line);
|
"Could not parse \"path\" parameter");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = fifo_data_new();
|
fd = fifo_data_new();
|
||||||
fd->path = path;
|
fd->path = path;
|
||||||
|
|
||||||
if (!fifo_open(fd)) {
|
if (!fifo_open(fd, error)) {
|
||||||
fifo_data_free(fd);
|
fifo_data_free(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +216,8 @@ fifo_output_finish(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fifo_output_open(void *data, struct audio_format *audio_format)
|
fifo_output_open(void *data, struct audio_format *audio_format,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct fifo_data *fd = (struct fifo_data *)data;
|
struct fifo_data *fd = (struct fifo_data *)data;
|
||||||
|
|
||||||
|
@ -234,7 +253,8 @@ fifo_output_cancel(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
fifo_output_play(void *data, const void *chunk, size_t size)
|
fifo_output_play(void *data, const void *chunk, size_t size,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct fifo_data *fd = (struct fifo_data *)data;
|
struct fifo_data *fd = (struct fifo_data *)data;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
|
@ -261,8 +281,9 @@ fifo_output_play(void *data, const void *chunk, size_t size)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_warning("Closing FIFO output \"%s\" due to write error: %s",
|
g_set_error(error, fifo_output_quark(), errno,
|
||||||
fd->path, strerror(errno));
|
"Failed to write to FIFO %s: %s",
|
||||||
|
fd->path, g_strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,15 @@ struct jack_data {
|
||||||
bool shutdown;
|
bool shutdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
jack_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("jack_output");
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mpd_jack_client_free(struct jack_data *jd)
|
mpd_jack_client_free(struct jack_data *jd)
|
||||||
{
|
{
|
||||||
|
@ -158,7 +167,7 @@ mpd_jack_info(const char *msg)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param, GError **error)
|
||||||
{
|
{
|
||||||
struct jack_data *jd;
|
struct jack_data *jd;
|
||||||
const char *value;
|
const char *value;
|
||||||
|
@ -172,9 +181,12 @@ mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
char **ports = g_strsplit(value, ",", 0);
|
char **ports = g_strsplit(value, ",", 0);
|
||||||
|
|
||||||
if (ports[0] == NULL || ports[1] == NULL || ports[2] != NULL)
|
if (ports[0] == NULL || ports[1] == NULL || ports[2] != NULL) {
|
||||||
g_error("two port names expected in line %d",
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
param->line);
|
"two port names expected in line %d",
|
||||||
|
param->line);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
jd->output_ports[0] = ports[0];
|
jd->output_ports[0] = ports[0];
|
||||||
jd->output_ports[1] = ports[1];
|
jd->output_ports[1] = ports[1];
|
||||||
|
@ -204,7 +216,7 @@ mpd_jack_test_default_device(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
mpd_jack_connect(struct jack_data *jd)
|
mpd_jack_connect(struct jack_data *jd, GError **error)
|
||||||
{
|
{
|
||||||
const char *output_ports[2], **jports;
|
const char *output_ports[2], **jports;
|
||||||
|
|
||||||
|
@ -215,7 +227,8 @@ mpd_jack_connect(struct jack_data *jd)
|
||||||
jd->shutdown = false;
|
jd->shutdown = false;
|
||||||
|
|
||||||
if ((jd->client = jack_client_new(jd->name)) == NULL) {
|
if ((jd->client = jack_client_new(jd->name)) == NULL) {
|
||||||
g_warning("jack server not running?");
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
|
"Failed to connect to JACK server");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,14 +240,16 @@ mpd_jack_connect(struct jack_data *jd)
|
||||||
JACK_DEFAULT_AUDIO_TYPE,
|
JACK_DEFAULT_AUDIO_TYPE,
|
||||||
JackPortIsOutput, 0);
|
JackPortIsOutput, 0);
|
||||||
if (jd->ports[i] == NULL) {
|
if (jd->ports[i] == NULL) {
|
||||||
g_warning("Cannot register %s output port.",
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
port_names[i]);
|
"Cannot register output port \"%s\"",
|
||||||
|
port_names[i]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( jack_activate(jd->client) ) {
|
if ( jack_activate(jd->client) ) {
|
||||||
g_warning("cannot activate client");
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
|
"cannot activate client");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +259,8 @@ mpd_jack_connect(struct jack_data *jd)
|
||||||
jports = jack_get_ports(jd->client, NULL, NULL,
|
jports = jack_get_ports(jd->client, NULL, NULL,
|
||||||
JackPortIsPhysical | JackPortIsInput);
|
JackPortIsPhysical | JackPortIsInput);
|
||||||
if (jports == NULL) {
|
if (jports == NULL) {
|
||||||
g_warning("no ports found");
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
|
"no ports found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,8 +283,9 @@ mpd_jack_connect(struct jack_data *jd)
|
||||||
ret = jack_connect(jd->client, jack_port_name(jd->ports[i]),
|
ret = jack_connect(jd->client, jack_port_name(jd->ports[i]),
|
||||||
output_ports[i]);
|
output_ports[i]);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
g_warning("%s is not a valid Jack Client / Port",
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
output_ports[i]);
|
"Not a valid JACK port: %s",
|
||||||
|
output_ports[i]);
|
||||||
|
|
||||||
if (jports != NULL)
|
if (jports != NULL)
|
||||||
free(jports);
|
free(jports);
|
||||||
|
@ -284,13 +301,13 @@ mpd_jack_connect(struct jack_data *jd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
mpd_jack_open(void *data, struct audio_format *audio_format)
|
mpd_jack_open(void *data, struct audio_format *audio_format, GError **error)
|
||||||
{
|
{
|
||||||
struct jack_data *jd = data;
|
struct jack_data *jd = data;
|
||||||
|
|
||||||
assert(jd != NULL);
|
assert(jd != NULL);
|
||||||
|
|
||||||
if (!mpd_jack_connect(jd)) {
|
if (!mpd_jack_connect(jd, error)) {
|
||||||
mpd_jack_client_free(jd);
|
mpd_jack_client_free(jd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -381,21 +398,23 @@ mpd_jack_write_samples(struct jack_data *jd, const void *src,
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
mpd_jack_play(void *data, const void *chunk, size_t size)
|
mpd_jack_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
struct jack_data *jd = data;
|
struct jack_data *jd = data;
|
||||||
const size_t frame_size = audio_format_frame_size(&jd->audio_format);
|
const size_t frame_size = audio_format_frame_size(&jd->audio_format);
|
||||||
size_t space = 0, space1;
|
size_t space = 0, space1;
|
||||||
|
|
||||||
if (jd->shutdown) {
|
|
||||||
g_warning("Refusing to play, because there is no client thread.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(size % frame_size == 0);
|
assert(size % frame_size == 0);
|
||||||
size /= frame_size;
|
size /= frame_size;
|
||||||
|
|
||||||
while (!jd->shutdown) {
|
while (true) {
|
||||||
|
if (jd->shutdown) {
|
||||||
|
g_set_error(error, jack_output_quark(), 0,
|
||||||
|
"Refusing to play, because "
|
||||||
|
"there is no client thread");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
space = jack_ringbuffer_write_space(jd->ringbuffer[0]);
|
space = jack_ringbuffer_write_space(jd->ringbuffer[0]);
|
||||||
space1 = jack_ringbuffer_write_space(jd->ringbuffer[1]);
|
space1 = jack_ringbuffer_write_space(jd->ringbuffer[1]);
|
||||||
if (space > space1)
|
if (space > space1)
|
||||||
|
|
|
@ -83,6 +83,15 @@ static const unsigned mvp_sample_rates[][3] = {
|
||||||
{15, 96000, 48000}
|
{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.
|
* Translate a sample rate to a MVP sample rate.
|
||||||
*
|
*
|
||||||
|
@ -118,7 +127,8 @@ mvp_output_test_default_device(void)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
mvp_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
mvp_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
G_GNUC_UNUSED const struct config_param *param)
|
G_GNUC_UNUSED const struct config_param *param,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct mvp_data *md = g_new(struct mvp_data, 1);
|
struct mvp_data *md = g_new(struct mvp_data, 1);
|
||||||
md->fd = -1;
|
md->fd = -1;
|
||||||
|
@ -134,7 +144,8 @@ mvp_output_finish(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format)
|
mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
unsigned mix[5];
|
unsigned mix[5];
|
||||||
|
|
||||||
|
@ -181,23 +192,27 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format)
|
||||||
*/
|
*/
|
||||||
mix[2] = mvp_find_sample_rate(audio_format->sample_rate);
|
mix[2] = mvp_find_sample_rate(audio_format->sample_rate);
|
||||||
if (mix[2] == (unsigned)-1) {
|
if (mix[2] == (unsigned)-1) {
|
||||||
g_warning("Can not find suitable output frequency for %u\n",
|
g_set_error(error, mvp_output_quark(), 0,
|
||||||
audio_format->sample_rate);
|
"Can not find suitable output frequency for %u",
|
||||||
|
audio_format->sample_rate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
|
||||||
g_warning("Can not set audio format\n");
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
|
"Can not set audio format");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_SYNC, 2) != 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_SYNC, 2) != 0) {
|
||||||
g_warning("Can not set audio sync\n");
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
|
"Can not set audio sync");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_PLAY, 0) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_PLAY, 0) < 0) {
|
||||||
g_warning("Can not set audio play mode\n");
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
|
"Can not set audio play mode");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +220,7 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
mvp_output_open(void *data, struct audio_format *audio_format)
|
mvp_output_open(void *data, struct audio_format *audio_format, GError **error)
|
||||||
{
|
{
|
||||||
struct mvp_data *md = data;
|
struct mvp_data *md = data;
|
||||||
long long int stc = 0;
|
long long int stc = 0;
|
||||||
|
@ -213,33 +228,38 @@ mvp_output_open(void *data, struct audio_format *audio_format)
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
if ((md->fd = open("/dev/adec_pcm", O_RDWR | O_NONBLOCK)) < 0) {
|
if ((md->fd = open("/dev/adec_pcm", O_RDWR | O_NONBLOCK)) < 0) {
|
||||||
g_warning("Error opening /dev/adec_pcm: %s\n",
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error opening /dev/adec_pcm: %s",
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_SRC, 1) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_SRC, 1) < 0) {
|
||||||
g_warning("Error setting audio source: %s\n",
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error setting audio source: %s",
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_STREAMTYPE, 0) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_STREAMTYPE, 0) < 0) {
|
||||||
g_warning("Error setting audio streamtype: %s\n",
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error setting audio streamtype: %s",
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
|
||||||
g_warning("Error setting audio format: %s\n",
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error setting audio format: %s",
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ioctl(md->fd, MVP_SET_AUD_STC, &stc);
|
ioctl(md->fd, MVP_SET_AUD_STC, &stc);
|
||||||
if (ioctl(md->fd, MVP_SET_AUD_BYPASS, 1) < 0) {
|
if (ioctl(md->fd, MVP_SET_AUD_BYPASS, 1) < 0) {
|
||||||
g_warning("Error setting audio streamtype: %s\n",
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error setting audio streamtype: %s",
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
success = mvp_set_pcm_params(md, audio_format);
|
success = mvp_set_pcm_params(md, audio_format, error);
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -266,7 +286,7 @@ static void mvp_output_cancel(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
mvp_output_play(void *data, const void *chunk, size_t size)
|
mvp_output_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
struct mvp_data *md = data;
|
struct mvp_data *md = data;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
@ -275,7 +295,7 @@ mvp_output_play(void *data, const void *chunk, size_t size)
|
||||||
if (md->fd < 0) {
|
if (md->fd < 0) {
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
success = mvp_output_open(md, &md->audio_format);
|
success = mvp_output_open(md, &md->audio_format, error);
|
||||||
if (!success)
|
if (!success)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -288,8 +308,9 @@ mvp_output_play(void *data, const void *chunk, size_t size)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
g_warning("closing mvp PCM device due to write error: "
|
|
||||||
"%s\n", strerror(errno));
|
g_set_error(error, mvp_output_quark(), errno,
|
||||||
|
"Failed to write: %s", strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ struct null_data {
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
null_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
null_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
G_GNUC_UNUSED const struct config_param *param)
|
G_GNUC_UNUSED const struct config_param *param,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct null_data *nd = g_new(struct null_data, 1);
|
struct null_data *nd = g_new(struct null_data, 1);
|
||||||
|
|
||||||
|
@ -52,7 +53,8 @@ null_finish(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
null_open(void *data, struct audio_format *audio_format)
|
null_open(void *data, struct audio_format *audio_format,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct null_data *nd = data;
|
struct null_data *nd = data;
|
||||||
|
|
||||||
|
@ -74,7 +76,8 @@ null_close(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size)
|
null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct null_data *nd = data;
|
struct null_data *nd = data;
|
||||||
Timer *timer = nd->timer;
|
Timer *timer = nd->timer;
|
||||||
|
|
|
@ -72,6 +72,15 @@ enum oss_param {
|
||||||
OSS_BITS = 2,
|
OSS_BITS = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
oss_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("oss_output");
|
||||||
|
}
|
||||||
|
|
||||||
static enum oss_param
|
static enum oss_param
|
||||||
oss_param_from_ioctl(unsigned param)
|
oss_param_from_ioctl(unsigned param)
|
||||||
{
|
{
|
||||||
|
@ -353,7 +362,7 @@ oss_output_test_default_device(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
oss_open_default(const struct config_param *param)
|
oss_open_default(GError **error)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int err[G_N_ELEMENTS(default_devices)];
|
int err[G_N_ELEMENTS(default_devices)];
|
||||||
|
@ -364,17 +373,11 @@ oss_open_default(const struct config_param *param)
|
||||||
if (ret[i] == OSS_STAT_NO_ERROR) {
|
if (ret[i] == OSS_STAT_NO_ERROR) {
|
||||||
struct oss_data *od = oss_data_new();
|
struct oss_data *od = oss_data_new();
|
||||||
od->device = default_devices[i];
|
od->device = default_devices[i];
|
||||||
od->mixer = mixer_new(&oss_mixer, param);
|
od->mixer = mixer_new(&oss_mixer, NULL);
|
||||||
return od;
|
return od;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param)
|
|
||||||
g_warning("error trying to open specified OSS device"
|
|
||||||
" at line %i\n", param->line);
|
|
||||||
else
|
|
||||||
g_warning("error trying to open default OSS device\n");
|
|
||||||
|
|
||||||
for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
|
for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
|
||||||
const char *dev = default_devices[i];
|
const char *dev = default_devices[i];
|
||||||
switch(ret[i]) {
|
switch(ret[i]) {
|
||||||
|
@ -395,13 +398,16 @@ oss_open_default(const struct config_param *param)
|
||||||
dev, strerror(err[i]));
|
dev, strerror(err[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
return NULL; /* some compilers can be dumb... */
|
g_set_error(error, oss_output_quark(), 0,
|
||||||
|
"error trying to open default OSS device");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
const char *device = config_get_block_string(param, "device", NULL);
|
const char *device = config_get_block_string(param, "device", NULL);
|
||||||
if (device != NULL) {
|
if (device != NULL) {
|
||||||
|
@ -411,7 +417,7 @@ oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
return od;
|
return od;
|
||||||
}
|
}
|
||||||
|
|
||||||
return oss_open_default(param);
|
return oss_open_default(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -473,24 +479,26 @@ oss_close(struct oss_data *od)
|
||||||
* Sets up the OSS device which was opened before.
|
* Sets up the OSS device which was opened before.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
oss_setup(struct oss_data *od)
|
oss_setup(struct oss_data *od, GError **error)
|
||||||
{
|
{
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
||||||
tmp = od->audio_format.channels;
|
tmp = od->audio_format.channels;
|
||||||
if (oss_set_param(od, SNDCTL_DSP_CHANNELS, &tmp)) {
|
if (oss_set_param(od, SNDCTL_DSP_CHANNELS, &tmp)) {
|
||||||
g_warning("OSS device \"%s\" does not support %u channels: %s\n",
|
g_set_error(error, oss_output_quark(), errno,
|
||||||
od->device, od->audio_format.channels,
|
"OSS device \"%s\" does not support %u channels: %s",
|
||||||
strerror(errno));
|
od->device, od->audio_format.channels,
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
od->audio_format.channels = tmp;
|
od->audio_format.channels = tmp;
|
||||||
|
|
||||||
tmp = od->audio_format.sample_rate;
|
tmp = od->audio_format.sample_rate;
|
||||||
if (oss_set_param(od, SNDCTL_DSP_SPEED, &tmp)) {
|
if (oss_set_param(od, SNDCTL_DSP_SPEED, &tmp)) {
|
||||||
g_warning("OSS device \"%s\" does not support %u Hz audio: %s\n",
|
g_set_error(error, oss_output_quark(), errno,
|
||||||
od->device, od->audio_format.sample_rate,
|
"OSS device \"%s\" does not support %u Hz audio: %s",
|
||||||
strerror(errno));
|
od->device, od->audio_format.sample_rate,
|
||||||
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
od->audio_format.sample_rate = tmp;
|
od->audio_format.sample_rate = tmp;
|
||||||
|
@ -511,8 +519,9 @@ oss_setup(struct oss_data *od)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oss_set_param(od, SNDCTL_DSP_SAMPLESIZE, &tmp)) {
|
if (oss_set_param(od, SNDCTL_DSP_SAMPLESIZE, &tmp)) {
|
||||||
g_warning("OSS device \"%s\" does not support %u bit audio: %s\n",
|
g_set_error(error, oss_output_quark(), errno,
|
||||||
od->device, tmp, strerror(errno));
|
"OSS device \"%s\" does not support %u bit audio: %s",
|
||||||
|
od->device, tmp, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,17 +529,18 @@ oss_setup(struct oss_data *od)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
oss_open(struct oss_data *od)
|
oss_open(struct oss_data *od, GError **error)
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
if ((od->fd = open(od->device, O_WRONLY)) < 0) {
|
if ((od->fd = open(od->device, O_WRONLY)) < 0) {
|
||||||
g_warning("Error opening OSS device \"%s\": %s\n", od->device,
|
g_set_error(error, oss_output_quark(), errno,
|
||||||
strerror(errno));
|
"Error opening OSS device \"%s\": %s",
|
||||||
|
od->device, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
success = oss_setup(od);
|
success = oss_setup(od, error);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
oss_close(od);
|
oss_close(od);
|
||||||
return false;
|
return false;
|
||||||
|
@ -540,14 +550,14 @@ oss_open(struct oss_data *od)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
oss_output_open(void *data, struct audio_format *audio_format)
|
oss_output_open(void *data, struct audio_format *audio_format, GError **error)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
struct oss_data *od = data;
|
struct oss_data *od = data;
|
||||||
|
|
||||||
od->audio_format = *audio_format;
|
od->audio_format = *audio_format;
|
||||||
|
|
||||||
ret = oss_open(od);
|
ret = oss_open(od, error);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -584,14 +594,14 @@ oss_output_cancel(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
oss_output_play(void *data, const void *chunk, size_t size)
|
oss_output_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
struct oss_data *od = data;
|
struct oss_data *od = data;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
/* reopen the device since it was closed by dropBufferedAudio */
|
/* reopen the device since it was closed by dropBufferedAudio */
|
||||||
if (od->fd < 0 && !oss_open(od))
|
if (od->fd < 0 && !oss_open(od, error))
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ret = write(od->fd, chunk, size);
|
ret = write(od->fd, chunk, size);
|
||||||
|
@ -599,8 +609,9 @@ oss_output_play(void *data, const void *chunk, size_t size)
|
||||||
return (size_t)ret;
|
return (size_t)ret;
|
||||||
|
|
||||||
if (ret < 0 && errno != EINTR) {
|
if (ret < 0 && errno != EINTR) {
|
||||||
g_warning("closing oss device \"%s\" due to write error: "
|
g_set_error(error, oss_output_quark(), errno,
|
||||||
"%s\n", od->device, strerror(errno));
|
"Write error on %s: %s",
|
||||||
|
od->device, strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,15 @@ struct osx_output {
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
osx_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("osx_output");
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
osx_output_test_default_device(void)
|
osx_output_test_default_device(void)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +53,8 @@ osx_output_test_default_device(void)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
G_GNUC_UNUSED const struct config_param *param)
|
G_GNUC_UNUSED const struct config_param *param,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct osx_output *oo = g_new(struct osx_output, 1);
|
struct osx_output *oo = g_new(struct osx_output, 1);
|
||||||
|
|
||||||
|
@ -143,7 +153,7 @@ osx_render(void *vdata,
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
osx_output_open(void *data, struct audio_format *audio_format)
|
osx_output_open(void *data, struct audio_format *audio_format, GError **error)
|
||||||
{
|
{
|
||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
ComponentDescription desc;
|
ComponentDescription desc;
|
||||||
|
@ -164,22 +174,25 @@ osx_output_open(void *data, struct audio_format *audio_format)
|
||||||
|
|
||||||
comp = FindNextComponent(NULL, &desc);
|
comp = FindNextComponent(NULL, &desc);
|
||||||
if (comp == 0) {
|
if (comp == 0) {
|
||||||
g_warning("Error finding OS X component\n");
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
|
"Error finding OS X component");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = OpenAComponent(comp, &od->au);
|
status = OpenAComponent(comp, &od->au);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
g_warning("Unable to open OS X component: %s",
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
GetMacOSStatusCommentString(status));
|
"Unable to open OS X component: %s",
|
||||||
|
GetMacOSStatusCommentString(status));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = AudioUnitInitialize(od->au);
|
status = AudioUnitInitialize(od->au);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
CloseComponent(od->au);
|
CloseComponent(od->au);
|
||||||
g_warning("Unable to initialize OS X audio unit: %s",
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
GetMacOSStatusCommentString(status));
|
"Unable to initialize OS X audio unit: %s",
|
||||||
|
GetMacOSStatusCommentString(status));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +206,8 @@ osx_output_open(void *data, struct audio_format *audio_format)
|
||||||
if (result != noErr) {
|
if (result != noErr) {
|
||||||
AudioUnitUninitialize(od->au);
|
AudioUnitUninitialize(od->au);
|
||||||
CloseComponent(od->au);
|
CloseComponent(od->au);
|
||||||
g_warning("unable to set callback for OS X audio unit\n");
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
|
"unable to set callback for OS X audio unit");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +232,8 @@ osx_output_open(void *data, struct audio_format *audio_format)
|
||||||
if (result != noErr) {
|
if (result != noErr) {
|
||||||
AudioUnitUninitialize(od->au);
|
AudioUnitUninitialize(od->au);
|
||||||
CloseComponent(od->au);
|
CloseComponent(od->au);
|
||||||
g_warning("Unable to set format on OS X device\n");
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
|
"Unable to set format on OS X device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +247,9 @@ osx_output_open(void *data, struct audio_format *audio_format)
|
||||||
|
|
||||||
status = AudioOutputUnitStart(od->au);
|
status = AudioOutputUnitStart(od->au);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
g_warning("unable to start audio output: %s",
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
GetMacOSStatusCommentString(status));
|
"unable to start audio output: %s",
|
||||||
|
GetMacOSStatusCommentString(status));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +257,8 @@ osx_output_open(void *data, struct audio_format *audio_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
osx_output_play(void *data, const void *chunk, size_t size)
|
osx_output_play(void *data, const void *chunk, size_t size,
|
||||||
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
size_t start, nbytes;
|
size_t start, nbytes;
|
||||||
|
|
|
@ -32,6 +32,15 @@ struct pulse_data {
|
||||||
char *sink;
|
char *sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
pulse_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("pulse_output");
|
||||||
|
}
|
||||||
|
|
||||||
static struct pulse_data *pulse_new_data(void)
|
static struct pulse_data *pulse_new_data(void)
|
||||||
{
|
{
|
||||||
struct pulse_data *ret;
|
struct pulse_data *ret;
|
||||||
|
@ -53,7 +62,7 @@ static void pulse_free_data(struct pulse_data *pd)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
pulse_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
pulse_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param, G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct pulse_data *pd;
|
struct pulse_data *pd;
|
||||||
|
|
||||||
|
@ -98,7 +107,7 @@ static bool pulse_test_default_device(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
pulse_open(void *data, struct audio_format *audio_format)
|
pulse_open(void *data, struct audio_format *audio_format, GError **error_r)
|
||||||
{
|
{
|
||||||
struct pulse_data *pd = data;
|
struct pulse_data *pd = data;
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
|
@ -117,9 +126,9 @@ pulse_open(void *data, struct audio_format *audio_format)
|
||||||
&ss, NULL, NULL,
|
&ss, NULL, NULL,
|
||||||
&error);
|
&error);
|
||||||
if (!pd->s) {
|
if (!pd->s) {
|
||||||
g_warning("Cannot connect to server in PulseAudio output "
|
g_set_error(error_r, pulse_output_quark(), error,
|
||||||
"\"%s\": %s\n",
|
"Cannot connect to PulseAudio server: %s",
|
||||||
pd->name, pa_strerror(error));
|
pa_strerror(error));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,15 +160,14 @@ static void pulse_close(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
pulse_play(void *data, const void *chunk, size_t size)
|
pulse_play(void *data, const void *chunk, size_t size, GError **error_r)
|
||||||
{
|
{
|
||||||
struct pulse_data *pd = data;
|
struct pulse_data *pd = data;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (pa_simple_write(pd->s, chunk, size, &error) < 0) {
|
if (pa_simple_write(pd->s, chunk, size, &error) < 0) {
|
||||||
g_warning("PulseAudio output \"%s\" disconnecting due to "
|
g_set_error(error_r, pulse_output_quark(), error,
|
||||||
"write error: %s\n",
|
"%s", pa_strerror(error));
|
||||||
pd->name, pa_strerror(error));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,15 @@ struct shout_data {
|
||||||
|
|
||||||
static int shout_init_count;
|
static int shout_init_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quark used for GError.domain.
|
||||||
|
*/
|
||||||
|
static inline GQuark
|
||||||
|
shout_output_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string("shout_output");
|
||||||
|
}
|
||||||
|
|
||||||
static const struct encoder_plugin *
|
static const struct encoder_plugin *
|
||||||
shout_encoder_plugin_get(const char *name)
|
shout_encoder_plugin_get(const char *name)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +106,8 @@ static void free_shout_data(struct shout_data *sd)
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
my_shout_init_driver(const struct audio_format *audio_format,
|
my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct shout_data *sd;
|
struct shout_data *sd;
|
||||||
char *test;
|
char *test;
|
||||||
|
@ -107,7 +117,6 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
char *passwd;
|
char *passwd;
|
||||||
const char *encoding;
|
const char *encoding;
|
||||||
const struct encoder_plugin *encoder_plugin;
|
const struct encoder_plugin *encoder_plugin;
|
||||||
GError *error = NULL;
|
|
||||||
unsigned shout_format;
|
unsigned shout_format;
|
||||||
unsigned protocol;
|
unsigned protocol;
|
||||||
const char *user;
|
const char *user;
|
||||||
|
@ -131,8 +140,9 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
|
|
||||||
port = config_get_block_unsigned(param, "port", 0);
|
port = config_get_block_unsigned(param, "port", 0);
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
g_error("shout port \"%s\" is not a positive integer, line %i\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
block_param->value, block_param->line);
|
"shout port must be configured");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_block_param("password");
|
check_block_param("password");
|
||||||
|
@ -150,27 +160,33 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
sd->quality = strtod(value, &test);
|
sd->quality = strtod(value, &test);
|
||||||
|
|
||||||
if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
|
if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
|
||||||
g_error("shout quality \"%s\" is not a number in the "
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"range -1 to 10, line %i",
|
"shout quality \"%s\" is not a number in the "
|
||||||
value, param->line);
|
"range -1 to 10, line %i",
|
||||||
|
value, param->line);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
|
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
|
||||||
g_error("quality and bitrate are "
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"both defined for shout output (line %i)",
|
"quality and bitrate are "
|
||||||
param->line);
|
"both defined");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = config_get_block_string(param, "bitrate", NULL);
|
value = config_get_block_string(param, "bitrate", NULL);
|
||||||
if (value == NULL)
|
if (value == NULL) {
|
||||||
g_error("neither bitrate nor quality defined for shout "
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"output at line %i", param->line);
|
"neither bitrate nor quality defined");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
sd->bitrate = strtol(value, &test, 10);
|
sd->bitrate = strtol(value, &test, 10);
|
||||||
|
|
||||||
if (*test != '\0' || sd->bitrate <= 0) {
|
if (*test != '\0' || sd->bitrate <= 0) {
|
||||||
g_error("bitrate at line %i should be a positive integer "
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"\n", param->line);
|
"bitrate must be a positive integer");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,13 +194,16 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
|
|
||||||
encoding = config_get_block_string(param, "encoding", "ogg");
|
encoding = config_get_block_string(param, "encoding", "ogg");
|
||||||
encoder_plugin = shout_encoder_plugin_get(encoding);
|
encoder_plugin = shout_encoder_plugin_get(encoding);
|
||||||
if (encoder_plugin == NULL)
|
if (encoder_plugin == NULL) {
|
||||||
g_error("couldn't find shout encoder plugin \"%s\"\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
encoding);
|
"couldn't find shout encoder plugin \"%s\"",
|
||||||
|
encoding);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
sd->encoder = encoder_init(encoder_plugin, param, &error);
|
sd->encoder = encoder_init(encoder_plugin, param, error);
|
||||||
if (sd->encoder == NULL)
|
if (sd->encoder == NULL)
|
||||||
g_error("%s", error->message);
|
return NULL;
|
||||||
|
|
||||||
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
||||||
shout_format = SHOUT_FORMAT_MP3;
|
shout_format = SHOUT_FORMAT_MP3;
|
||||||
|
@ -194,20 +213,24 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
value = config_get_block_string(param, "protocol", NULL);
|
value = config_get_block_string(param, "protocol", NULL);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
if (0 == strcmp(value, "shoutcast") &&
|
if (0 == strcmp(value, "shoutcast") &&
|
||||||
0 != strcmp(encoding, "mp3"))
|
0 != strcmp(encoding, "mp3")) {
|
||||||
g_error("you cannot stream \"%s\" to shoutcast, use mp3\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
encoding);
|
"you cannot stream \"%s\" to shoutcast, use mp3",
|
||||||
else if (0 == strcmp(value, "shoutcast"))
|
encoding);
|
||||||
|
return NULL;
|
||||||
|
} else if (0 == strcmp(value, "shoutcast"))
|
||||||
protocol = SHOUT_PROTOCOL_ICY;
|
protocol = SHOUT_PROTOCOL_ICY;
|
||||||
else if (0 == strcmp(value, "icecast1"))
|
else if (0 == strcmp(value, "icecast1"))
|
||||||
protocol = SHOUT_PROTOCOL_XAUDIOCAST;
|
protocol = SHOUT_PROTOCOL_XAUDIOCAST;
|
||||||
else if (0 == strcmp(value, "icecast2"))
|
else if (0 == strcmp(value, "icecast2"))
|
||||||
protocol = SHOUT_PROTOCOL_HTTP;
|
protocol = SHOUT_PROTOCOL_HTTP;
|
||||||
else
|
else {
|
||||||
g_error("shout protocol \"%s\" is not \"shoutcast\" or "
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"\"icecast1\"or "
|
"shout protocol \"%s\" is not \"shoutcast\" or "
|
||||||
"\"icecast2\", line %i\n",
|
"\"icecast1\"or \"icecast2\"",
|
||||||
value, param->line);
|
value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
protocol = SHOUT_PROTOCOL_HTTP;
|
protocol = SHOUT_PROTOCOL_HTTP;
|
||||||
}
|
}
|
||||||
|
@ -223,8 +246,9 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
!= SHOUTERR_SUCCESS ||
|
!= SHOUTERR_SUCCESS ||
|
||||||
shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS ||
|
shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS ||
|
||||||
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
|
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
|
||||||
g_error("error configuring shout defined at line %i: %s\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
param->line, shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* optional paramters */
|
/* optional paramters */
|
||||||
|
@ -233,14 +257,16 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
|
|
||||||
value = config_get_block_string(param, "genre", NULL);
|
value = config_get_block_string(param, "genre", NULL);
|
||||||
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
|
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
|
||||||
g_error("error configuring shout defined at line %i: %s\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
param->line, shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = config_get_block_string(param, "description", NULL);
|
value = config_get_block_string(param, "description", NULL);
|
||||||
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
|
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
|
||||||
g_error("error configuring shout defined at line %i: %s\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
param->line, shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -269,7 +295,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
handle_shout_error(struct shout_data *sd, int err)
|
handle_shout_error(struct shout_data *sd, int err, GError **error)
|
||||||
{
|
{
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case SHOUTERR_SUCCESS:
|
case SHOUTERR_SUCCESS:
|
||||||
|
@ -277,17 +303,19 @@ handle_shout_error(struct shout_data *sd, int err)
|
||||||
|
|
||||||
case SHOUTERR_UNCONNECTED:
|
case SHOUTERR_UNCONNECTED:
|
||||||
case SHOUTERR_SOCKET:
|
case SHOUTERR_SOCKET:
|
||||||
g_warning("Lost shout connection to %s:%i: %s\n",
|
g_set_error(error, shout_output_quark(), err,
|
||||||
shout_get_host(sd->shout_conn),
|
"Lost shout connection to %s:%i: %s",
|
||||||
shout_get_port(sd->shout_conn),
|
shout_get_host(sd->shout_conn),
|
||||||
shout_get_error(sd->shout_conn));
|
shout_get_port(sd->shout_conn),
|
||||||
|
shout_get_error(sd->shout_conn));
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
g_warning("shout: connection to %s:%i error: %s\n",
|
g_set_error(error, shout_output_quark(), err,
|
||||||
shout_get_host(sd->shout_conn),
|
"connection to %s:%i error: %s",
|
||||||
shout_get_port(sd->shout_conn),
|
shout_get_host(sd->shout_conn),
|
||||||
shout_get_error(sd->shout_conn));
|
shout_get_port(sd->shout_conn),
|
||||||
|
shout_get_error(sd->shout_conn));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +323,7 @@ handle_shout_error(struct shout_data *sd, int err)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
write_page(struct shout_data *sd)
|
write_page(struct shout_data *sd, GError **error)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -308,7 +336,7 @@ write_page(struct shout_data *sd)
|
||||||
|
|
||||||
shout_sync(sd->shout_conn);
|
shout_sync(sd->shout_conn);
|
||||||
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
|
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
|
||||||
if (!handle_shout_error(sd, err))
|
if (!handle_shout_error(sd, err, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -320,7 +348,7 @@ static void close_shout_conn(struct shout_data * sd)
|
||||||
|
|
||||||
if (sd->encoder != NULL) {
|
if (sd->encoder != NULL) {
|
||||||
if (encoder_flush(sd->encoder, NULL))
|
if (encoder_flush(sd->encoder, NULL))
|
||||||
write_page(sd);
|
write_page(sd, NULL);
|
||||||
|
|
||||||
encoder_close(sd->encoder);
|
encoder_close(sd->encoder);
|
||||||
}
|
}
|
||||||
|
@ -362,7 +390,7 @@ static void my_shout_close_device(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
shout_connect(struct shout_data *sd)
|
shout_connect(struct shout_data *sd, GError **error)
|
||||||
{
|
{
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
|
@ -373,58 +401,47 @@ shout_connect(struct shout_data *sd)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
g_warning("problem opening connection to shout server %s:%i: %s\n",
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
shout_get_host(sd->shout_conn),
|
"problem opening connection to shout server %s:%i: %s",
|
||||||
shout_get_port(sd->shout_conn),
|
shout_get_host(sd->shout_conn),
|
||||||
shout_get_error(sd->shout_conn));
|
shout_get_port(sd->shout_conn),
|
||||||
|
shout_get_error(sd->shout_conn));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
my_shout_open_device(void *data, struct audio_format *audio_format)
|
my_shout_open_device(void *data, struct audio_format *audio_format,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
struct shout_data *sd = (struct shout_data *)data;
|
struct shout_data *sd = (struct shout_data *)data;
|
||||||
bool ret;
|
bool ret;
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
ret = shout_connect(sd);
|
ret = shout_connect(sd, error);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
sd->buf.len = 0;
|
sd->buf.len = 0;
|
||||||
|
|
||||||
ret = encoder_open(sd->encoder, audio_format, &error);
|
ret = encoder_open(sd->encoder, audio_format, error) &&
|
||||||
|
write_page(sd, error);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
shout_close(sd->shout_conn);
|
shout_close(sd->shout_conn);
|
||||||
g_warning("%s", error->message);
|
|
||||||
g_error_free(error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_page(sd);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
my_shout_play(void *data, const void *chunk, size_t size)
|
my_shout_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
struct shout_data *sd = (struct shout_data *)data;
|
struct shout_data *sd = (struct shout_data *)data;
|
||||||
bool ret;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
ret = encoder_write(sd->encoder, chunk, size, &error);
|
return encoder_write(sd->encoder, chunk, size, error) &&
|
||||||
if (!ret) {
|
write_page(sd, error)
|
||||||
g_warning("%s", error->message);
|
? size
|
||||||
g_error_free(error);
|
: 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!write_page(sd))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -432,7 +449,7 @@ my_shout_pause(void *data)
|
||||||
{
|
{
|
||||||
static const char silence[1020];
|
static const char silence[1020];
|
||||||
|
|
||||||
return my_shout_play(data, silence, sizeof(silence));
|
return my_shout_play(data, silence, sizeof(silence), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -479,7 +496,9 @@ static void my_shout_set_tag(void *data,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_page(sd);
|
ret = write_page(sd, NULL);
|
||||||
|
if (!ret)
|
||||||
|
return;
|
||||||
|
|
||||||
ret = encoder_tag(sd->encoder, tag, &error);
|
ret = encoder_tag(sd->encoder, tag, &error);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
|
@ -499,7 +518,7 @@ static void my_shout_set_tag(void *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_page(sd);
|
write_page(sd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct audio_output_plugin shoutPlugin = {
|
const struct audio_output_plugin shoutPlugin = {
|
||||||
|
|
|
@ -48,6 +48,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param)
|
||||||
char *format = NULL;
|
char *format = NULL;
|
||||||
struct block_param *bp = NULL;
|
struct block_param *bp = NULL;
|
||||||
const struct audio_output_plugin *plugin = NULL;
|
const struct audio_output_plugin *plugin = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
if (param) {
|
if (param) {
|
||||||
const char *type = NULL;
|
const char *type = NULL;
|
||||||
|
@ -97,7 +98,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param)
|
||||||
pcm_convert_init(&ao->convert_state);
|
pcm_convert_init(&ao->convert_state);
|
||||||
|
|
||||||
if (format) {
|
if (format) {
|
||||||
GError *error = NULL;
|
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
ret = audio_format_parse(&ao->config_audio_format, format,
|
ret = audio_format_parse(&ao->config_audio_format, format,
|
||||||
|
@ -114,9 +114,14 @@ audio_output_init(struct audio_output *ao, const struct config_param *param)
|
||||||
|
|
||||||
ao->data = ao_plugin_init(plugin,
|
ao->data = ao_plugin_init(plugin,
|
||||||
format ? &ao->config_audio_format : NULL,
|
format ? &ao->config_audio_format : NULL,
|
||||||
param);
|
param, &error);
|
||||||
if (ao->data == NULL)
|
if (ao->data == NULL) {
|
||||||
|
g_warning("Failed to initialize \"%s\" [%s]: %s",
|
||||||
|
ao->name, ao->plugin->name,
|
||||||
|
error->message);
|
||||||
|
g_error_free(error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#ifndef MPD_OUTPUT_PLUGIN_H
|
#ifndef MPD_OUTPUT_PLUGIN_H
|
||||||
#define MPD_OUTPUT_PLUGIN_H
|
#define MPD_OUTPUT_PLUGIN_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
@ -45,16 +47,18 @@ struct audio_output_plugin {
|
||||||
* Configure and initialize the device, but do not open it
|
* Configure and initialize the device, but do not open it
|
||||||
* yet.
|
* yet.
|
||||||
*
|
*
|
||||||
* @param ao an opaque pointer to the audio_output structure
|
|
||||||
* @param audio_format the configured audio format, or NULL if
|
* @param audio_format the configured audio format, or NULL if
|
||||||
* none is configured
|
* none is configured
|
||||||
* @param param the configuration section, or NULL if there is
|
* @param param the configuration section, or NULL if there is
|
||||||
* no configuration
|
* no configuration
|
||||||
|
* @param error location to store the error occuring, or NULL
|
||||||
|
* to ignore errors
|
||||||
* @return NULL on error, or an opaque pointer to the plugin's
|
* @return NULL on error, or an opaque pointer to the plugin's
|
||||||
* data
|
* data
|
||||||
*/
|
*/
|
||||||
void *(*init)(const struct audio_format *audio_format,
|
void *(*init)(const struct audio_format *audio_format,
|
||||||
const struct config_param *param);
|
const struct config_param *param,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free resources allocated by this device.
|
* Free resources allocated by this device.
|
||||||
|
@ -72,10 +76,14 @@ struct audio_output_plugin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Really open the device.
|
* Really open the device.
|
||||||
|
*
|
||||||
* @param audio_format the audio format in which data is going
|
* @param audio_format the audio format in which data is going
|
||||||
* to be delivered; may be modified by the plugin
|
* to be delivered; may be modified by the plugin
|
||||||
|
* @param error location to store the error occuring, or NULL
|
||||||
|
* to ignore errors
|
||||||
*/
|
*/
|
||||||
bool (*open)(void *data, struct audio_format *audio_format);
|
bool (*open)(void *data, struct audio_format *audio_format,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the device.
|
* Close the device.
|
||||||
|
@ -91,9 +99,12 @@ struct audio_output_plugin {
|
||||||
/**
|
/**
|
||||||
* Play a chunk of audio data.
|
* Play a chunk of audio data.
|
||||||
*
|
*
|
||||||
|
* @param error location to store the error occuring, or NULL
|
||||||
|
* to ignore errors
|
||||||
* @return the number of bytes played, or 0 on error
|
* @return the number of bytes played, or 0 on error
|
||||||
*/
|
*/
|
||||||
size_t (*play)(void *data, const void *chunk, size_t size);
|
size_t (*play)(void *data, const void *chunk, size_t size,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to cancel data which may still be in the device's
|
* Try to cancel data which may still be in the device's
|
||||||
|
@ -126,9 +137,10 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin)
|
||||||
static inline void *
|
static inline void *
|
||||||
ao_plugin_init(const struct audio_output_plugin *plugin,
|
ao_plugin_init(const struct audio_output_plugin *plugin,
|
||||||
const struct audio_format *audio_format,
|
const struct audio_format *audio_format,
|
||||||
const struct config_param *param)
|
const struct config_param *param,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
return plugin->init(audio_format, param);
|
return plugin->init(audio_format, param, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -147,9 +159,10 @@ ao_plugin_get_mixer(const struct audio_output_plugin *plugin, void *data)
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
ao_plugin_open(const struct audio_output_plugin *plugin,
|
ao_plugin_open(const struct audio_output_plugin *plugin,
|
||||||
void *data, struct audio_format *audio_format)
|
void *data, struct audio_format *audio_format,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
return plugin->open(data, audio_format);
|
return plugin->open(data, audio_format, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -168,9 +181,10 @@ ao_plugin_send_tag(const struct audio_output_plugin *plugin,
|
||||||
|
|
||||||
static inline size_t
|
static inline size_t
|
||||||
ao_plugin_play(const struct audio_output_plugin *plugin,
|
ao_plugin_play(const struct audio_output_plugin *plugin,
|
||||||
void *data, const void *chunk, size_t size)
|
void *data, const void *chunk, size_t size,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
return plugin->play(data, chunk, size);
|
return plugin->play(data, chunk, size, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|
|
@ -56,6 +56,7 @@ static void ao_play(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
const char *data = ao->args.play.data;
|
const char *data = ao->args.play.data;
|
||||||
size_t size = ao->args.play.size;
|
size_t size = ao->args.play.size;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
assert(size > 0);
|
assert(size > 0);
|
||||||
assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);
|
assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);
|
||||||
|
@ -76,9 +77,14 @@ static void ao_play(struct audio_output *ao)
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
size_t nbytes;
|
size_t nbytes;
|
||||||
|
|
||||||
nbytes = ao_plugin_play(ao->plugin, ao->data, data, size);
|
nbytes = ao_plugin_play(ao->plugin, ao->data, data, size,
|
||||||
|
&error);
|
||||||
if (nbytes == 0) {
|
if (nbytes == 0) {
|
||||||
/* play()==0 means failure */
|
/* play()==0 means failure */
|
||||||
|
g_warning("\"%s\" [%s] failed to play: %s",
|
||||||
|
ao->name, ao->plugin->name, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
|
||||||
ao_plugin_cancel(ao->plugin, ao->data);
|
ao_plugin_cancel(ao->plugin, ao->data);
|
||||||
ao_close(ao);
|
ao_close(ao);
|
||||||
break;
|
break;
|
||||||
|
@ -114,6 +120,7 @@ static gpointer audio_output_task(gpointer arg)
|
||||||
{
|
{
|
||||||
struct audio_output *ao = arg;
|
struct audio_output *ao = arg;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
switch (ao->command) {
|
switch (ao->command) {
|
||||||
|
@ -123,15 +130,23 @@ static gpointer audio_output_task(gpointer arg)
|
||||||
case AO_COMMAND_OPEN:
|
case AO_COMMAND_OPEN:
|
||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
ret = ao_plugin_open(ao->plugin, ao->data,
|
ret = ao_plugin_open(ao->plugin, ao->data,
|
||||||
&ao->out_audio_format);
|
&ao->out_audio_format,
|
||||||
|
&error);
|
||||||
|
|
||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pcm_convert_init(&ao->convert_state);
|
pcm_convert_init(&ao->convert_state);
|
||||||
ao->open = true;
|
ao->open = true;
|
||||||
} else
|
} else {
|
||||||
|
g_warning("Failed to open \"%s\" [%s]: %s",
|
||||||
|
ao->name, ao->plugin->name,
|
||||||
|
error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
|
||||||
ao->reopen_after = time(NULL) + REOPEN_AFTER;
|
ao->reopen_after = time(NULL) + REOPEN_AFTER;
|
||||||
|
}
|
||||||
|
|
||||||
ao_command_finished(ao);
|
ao_command_finished(ao);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue