aboutsummaryrefslogtreecommitdiffstats
path: root/src/audioOutputs
diff options
context:
space:
mode:
Diffstat (limited to 'src/audioOutputs')
-rw-r--r--src/audioOutputs/audioOutput_alsa.c254
-rw-r--r--src/audioOutputs/audioOutput_ao.c21
-rw-r--r--src/audioOutputs/audioOutput_jack.c17
-rw-r--r--src/audioOutputs/audioOutput_shout.c32
4 files changed, 147 insertions, 177 deletions
diff --git a/src/audioOutputs/audioOutput_alsa.c b/src/audioOutputs/audioOutput_alsa.c
index f0aa32713..b7b0a1151 100644
--- a/src/audioOutputs/audioOutput_alsa.c
+++ b/src/audioOutputs/audioOutput_alsa.c
@@ -23,6 +23,8 @@
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
+static const char default_device[] = "default";
+
#define MPD_ALSA_BUFFER_TIME_US 500000
/* the default period time of xmms is 50 ms, so let's use that as well.
* a user can tweak this parameter via the "period_time" config parameter.
@@ -36,26 +38,42 @@
#include <alsa/asoundlib.h>
+/* #define MPD_SND_PCM_NONBLOCK SND_PCM_NONBLOCK */
+#define MPD_SND_PCM_NONBLOCK 0
+
+/*
+ * This macro will evaluate both statements, but only returns the result
+ * of the second statement to the reader. Thus it'll stringify the
+ * command name and assign it to the scoped cmd variable.
+ * Note that ALSA is strictly for Linux , and anybody compiling
+ * on Linux will have gcc or a gcc-compatible compiler anyways.
+ */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define E(command , arg1 , ...) \
+ (err_cmd = #command, command( arg1 , __VA_ARGS__ ))
+#else /* ! C99, this works for gcc 2.95 at least: */
+# define E(command , arg1 , args...) \
+ (err_cmd = #command, command( arg1 , ##args ))
+#endif /* ! C99 */
+
typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
snd_pcm_uframes_t size);
typedef struct _AlsaData {
- char *device;
+ const char *device;
snd_pcm_t *pcmHandle;
alsa_writei_t *writei;
unsigned int buffer_time;
unsigned int period_time;
int sampleSize;
int useMmap;
- int canPause;
- int canResume;
} AlsaData;
static AlsaData *newAlsaData(void)
{
AlsaData *ret = xmalloc(sizeof(AlsaData));
- ret->device = NULL;
+ ret->device = default_device;
ret->pcmHandle = NULL;
ret->writei = snd_pcm_writei;
ret->useMmap = 0;
@@ -67,20 +85,27 @@ static AlsaData *newAlsaData(void)
static void freeAlsaData(AlsaData * ad)
{
- if (ad->device)
- free(ad->device);
-
+ if (ad->device && ad->device != default_device)
+ free(deconst_ptr(ad->device));
free(ad);
}
static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
{
+ /* no need for pthread_once thread-safety when reading config */
+ static int free_global_registered;
AlsaData *ad = newAlsaData();
+ if (!free_global_registered) {
+ atexit((void(*)(void))snd_config_update_free_global);
+ free_global_registered = 1;
+ }
+
if (param) {
- BlockParam *bp = getBlockParam(param, "device");
- ad->device = bp ? xstrdup(bp->value) : xstrdup("default");
+ BlockParam *bp;
+ if ((bp = getBlockParam(param, "device")))
+ ad->device = xstrdup(bp->value);
ad->useMmap = getBoolBlockParam(param, "use_mmap", 1);
if (ad->useMmap == CONF_BOOL_UNSET)
ad->useMmap = 0;
@@ -88,8 +113,7 @@ static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
ad->buffer_time = atoi(bp->value);
if ((bp = getBlockParam(param, "period_time")))
ad->period_time = atoi(bp->value);
- } else
- ad->device = xstrdup("default");
+ }
audioOutput->data = ad;
return 0;
@@ -106,12 +130,10 @@ static int alsa_testDefault(void)
{
snd_pcm_t *handle;
- int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK);
- snd_config_update_free_global();
-
+ int ret = snd_pcm_open(&handle, default_device,
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (ret) {
- WARNING("Error opening default alsa device: %s\n",
+ WARNING("Error opening default ALSA device: %s\n",
snd_strerror(-ret));
return -1;
} else
@@ -120,6 +142,17 @@ static int alsa_testDefault(void)
return 0;
}
+static snd_pcm_format_t get_bitformat(const AudioFormat * af)
+{
+ switch (af->bits) {
+ case 8: return SND_PCM_FORMAT_S8;
+ case 16: return SND_PCM_FORMAT_S16;
+ case 24: return SND_PCM_FORMAT_S24;
+ case 32: return SND_PCM_FORMAT_S32;
+ }
+ return SND_PCM_FORMAT_UNKNOWN;
+}
+
static int alsa_openDevice(AudioOutput * audioOutput)
{
AlsaData *ad = audioOutput->data;
@@ -129,84 +162,57 @@ static int alsa_openDevice(AudioOutput * audioOutput)
snd_pcm_sw_params_t *swparams;
unsigned int sampleRate = audioFormat->sampleRate;
unsigned int channels = audioFormat->channels;
- snd_pcm_uframes_t alsa_buffer_size;
- snd_pcm_uframes_t alsa_period_size;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
int err;
- const char *cmd = NULL;
+ const char *err_cmd = NULL;
int retry = MPD_ALSA_RETRY_NR;
unsigned int period_time, period_time_ro;
unsigned int buffer_time;
- switch (audioFormat->bits) {
- case 8:
- bitformat = SND_PCM_FORMAT_S8;
- break;
- case 16:
- bitformat = SND_PCM_FORMAT_S16;
- break;
- case 24:
- bitformat = SND_PCM_FORMAT_S24;
- break;
- case 32:
- bitformat = SND_PCM_FORMAT_S32;
- break;
- default:
+ if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
ERROR("ALSA device \"%s\" doesn't support %i bit audio\n",
ad->device, audioFormat->bits);
- return -1;
- }
- err = snd_pcm_open(&ad->pcmHandle, ad->device,
- SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- snd_config_update_free_global();
+ err = E(snd_pcm_open, &ad->pcmHandle, ad->device,
+ SND_PCM_STREAM_PLAYBACK, MPD_SND_PCM_NONBLOCK);
if (err < 0) {
ad->pcmHandle = NULL;
goto error;
}
- cmd = "snd_pcm_nonblock";
- err = snd_pcm_nonblock(ad->pcmHandle, 0);
- if (err < 0)
+#if MPD_SND_PCM_NONBLOCK == SND_PCM_NONBLOCK
+ if ((err = E(snd_pcm_nonblock, ad->pcmHandle, 0)) < 0)
goto error;
+#endif /* MPD_SND_PCM_NONBLOCK == SND_PCM_NONBLOCK */
period_time_ro = period_time = ad->period_time;
configure_hw:
/* configure HW params */
snd_pcm_hw_params_alloca(&hwparams);
-
- cmd = "snd_pcm_hw_params_any";
- err = snd_pcm_hw_params_any(ad->pcmHandle, hwparams);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_any, ad->pcmHandle, hwparams)) < 0)
goto error;
if (ad->useMmap) {
- err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
- SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (err < 0) {
- ERROR("Cannot set mmap'ed mode on alsa device \"%s\": "
- " %s\n", ad->device, snd_strerror(-err));
- ERROR("Falling back to direct write mode\n");
- ad->useMmap = 0;
- } else
+ if (!(err = snd_pcm_hw_params_set_access(ad->pcmHandle,
+ hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED))) {
ad->writei = snd_pcm_mmap_writei;
- }
-
- if (!ad->useMmap) {
- cmd = "snd_pcm_hw_params_set_access";
- err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0)
- goto error;
- ad->writei = snd_pcm_writei;
- }
+ } else {
+ ERROR("ALSA cannot enable mmap on device \"%s\": %s. "
+ "Falling back to direct write mode\n",
+ ad->device, snd_strerror(-err));
+ ad->useMmap = 0;
+ }
+ } else if ((err = E(snd_pcm_hw_params_set_access, ad->pcmHandle,
+ hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ goto error;
err = snd_pcm_hw_params_set_format(ad->pcmHandle, hwparams, bitformat);
if (err < 0) {
- ERROR("ALSA device \"%s\" does not support %i bit audio: "
- "%s\n", ad->device, audioFormat->bits, snd_strerror(-err));
+ ERROR("ALSA device \"%s\" does not support %i bit audio:%s\n",
+ ad->device, audioFormat->bits, snd_strerror(-err));
goto fail;
}
-
err = snd_pcm_hw_params_set_channels_near(ad->pcmHandle, hwparams,
&channels);
if (err < 0) {
@@ -227,92 +233,62 @@ configure_hw:
audioFormat->sampleRate = sampleRate;
buffer_time = ad->buffer_time;
- cmd = "snd_pcm_hw_params_set_buffer_time_near";
- err = snd_pcm_hw_params_set_buffer_time_near(ad->pcmHandle, hwparams,
- &buffer_time, NULL);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_set_buffer_time_near, ad->pcmHandle,
+ hwparams, &buffer_time, NULL)) < 0)
goto error;
period_time = period_time_ro;
- cmd = "snd_pcm_hw_params_set_period_time_near";
- err = snd_pcm_hw_params_set_period_time_near(ad->pcmHandle, hwparams,
- &period_time, NULL);
- if (err < 0)
+
+ if ((err = E(snd_pcm_hw_params_set_period_time_near,
+ ad->pcmHandle, hwparams, &period_time, NULL)) < 0)
goto error;
- cmd = "snd_pcm_hw_params";
- err = snd_pcm_hw_params(ad->pcmHandle, hwparams);
+ err = E(snd_pcm_hw_params, ad->pcmHandle, hwparams);
if (err == -EPIPE && --retry > 0) {
period_time_ro = period_time_ro >> 1;
goto configure_hw;
} else if (err < 0)
goto error;
- if (retry != MPD_ALSA_RETRY_NR)
- DEBUG("ALSA period_time set to %d\n", period_time);
- cmd = "snd_pcm_hw_params_get_buffer_size";
- err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
- if (err < 0)
+ DEBUG("ALSA(%s) period_time: %u, buffer_time: %u\n",
+ ad->device, period_time, buffer_time);
+ if ((err = E(snd_pcm_hw_params_get_buffer_size, hwparams,
+ &buffer_size)) < 0)
goto error;
- cmd = "snd_pcm_hw_params_get_period_size";
- err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
- NULL);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_get_period_size, hwparams,
+ &period_size, NULL)) < 0)
goto error;
-
- ad->canPause = snd_pcm_hw_params_can_pause(hwparams);
- ad->canResume = snd_pcm_hw_params_can_resume(hwparams);
+ DEBUG("ALSA(%s) period_size: %lu buffer_size: %lu\n",
+ ad->device, period_size, buffer_size);
/* configure SW params */
snd_pcm_sw_params_alloca(&swparams);
- cmd = "snd_pcm_sw_params_current";
- err = snd_pcm_sw_params_current(ad->pcmHandle, swparams);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_sw_params_set_start_threshold";
- err = snd_pcm_sw_params_set_start_threshold(ad->pcmHandle, swparams,
- alsa_buffer_size -
- alsa_period_size);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_current, ad->pcmHandle, swparams)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params_set_avail_min";
- err = snd_pcm_sw_params_set_avail_min(ad->pcmHandle, swparams,
- alsa_period_size);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_set_start_threshold, ad->pcmHandle,
+ swparams, buffer_size - period_size)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params_set_xfer_align";
- err = snd_pcm_sw_params_set_xfer_align(ad->pcmHandle, swparams, 1);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_set_avail_min, ad->pcmHandle,
+ swparams, period_size)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params";
- err = snd_pcm_sw_params(ad->pcmHandle, swparams);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params, ad->pcmHandle, swparams)) < 0)
goto error;
ad->sampleSize = (audioFormat->bits / 8) * audioFormat->channels;
audioOutput->open = 1;
- DEBUG("alsa device \"%s\" will be playing %i bit, %i channel audio at "
+ DEBUG("ALSA device \"%s\" will be playing %i bit, %i channel audio at "
"%i Hz\n", ad->device, (int)audioFormat->bits,
channels, sampleRate);
return 0;
error:
- if (cmd) {
- ERROR("Error opening alsa device \"%s\" (%s): %s\n",
- ad->device, cmd, snd_strerror(-err));
- } else {
- ERROR("Error opening alsa device \"%s\": %s\n", ad->device,
- snd_strerror(-err));
- }
+ ERROR("Error opening ALSA device \"%s\" (%s): %s\n",
+ ad->device, (err_cmd ? err_cmd : ""), snd_strerror(-err));
fail:
if (ad->pcmHandle)
snd_pcm_close(ad->pcmHandle);
@@ -323,24 +299,25 @@ fail:
static int alsa_errorRecovery(AlsaData * ad, int err)
{
- if (err == -EPIPE) {
- DEBUG("Underrun on alsa device \"%s\"\n", ad->device);
- } else if (err == -ESTRPIPE) {
- DEBUG("alsa device \"%s\" was suspended\n", ad->device);
- }
+ snd_pcm_state_t state = snd_pcm_state(ad->pcmHandle);
+ const char *err_cmd = NULL;
+
+ if (err == -EPIPE)
+ DEBUG("Underrun on ALSA device \"%s\"\n", ad->device);
+ else if (err == -ESTRPIPE)
+ DEBUG("ALSA device \"%s\" was suspended\n", ad->device);
- switch (snd_pcm_state(ad->pcmHandle)) {
+ switch (state) {
case SND_PCM_STATE_PAUSED:
- err = snd_pcm_pause(ad->pcmHandle, /* disable */ 0);
+ err = E(snd_pcm_pause, ad->pcmHandle, /* disable */ 0);
break;
case SND_PCM_STATE_SUSPENDED:
- err = ad->canResume ?
- snd_pcm_resume(ad->pcmHandle) :
- snd_pcm_prepare(ad->pcmHandle);
- break;
+ if ((err = E(snd_pcm_resume, ad->pcmHandle)) == -EAGAIN)
+ return 0;
+ /* fall-through to snd_pcm_prepare: */
case SND_PCM_STATE_SETUP:
case SND_PCM_STATE_XRUN:
- err = snd_pcm_prepare(ad->pcmHandle);
+ err = E(snd_pcm_prepare, ad->pcmHandle);
break;
case SND_PCM_STATE_DISCONNECTED:
/* so alsa_closeDevice won't try to drain: */
@@ -349,13 +326,20 @@ static int alsa_errorRecovery(AlsaData * ad, int err)
break;
/* this is no error, so just keep running */
case SND_PCM_STATE_RUNNING:
- err = 0;
+ if (mpd_unlikely(err)) {
+ DEBUG("ALSA(%s) ignoring possible error: %s\n",
+ ad->device, snd_strerror(-err));
+ err = 0;
+ }
break;
default:
- /* unknown state, do nothing */
+ DEBUG("ALSA device \"%s\" in unknown state: %s\n",
+ ad->device, snd_pcm_state_name(state));
break;
}
-
+ if (err && err_cmd)
+ ERROR("ALSA error on device \"%s\" (%s): %s\n",
+ ad->device, err_cmd, snd_strerror(-err));
return err;
}
@@ -397,7 +381,7 @@ static int alsa_playAudio(AudioOutput * audioOutput,
if (ret < 0) {
if (alsa_errorRecovery(ad, ret) < 0) {
- ERROR("closing alsa device \"%s\" due to write "
+ ERROR("closing ALSA device \"%s\" due to write "
"error: %s\n", ad->device,
snd_strerror(-errno));
alsa_closeDevice(audioOutput);
diff --git a/src/audioOutputs/audioOutput_ao.c b/src/audioOutputs/audioOutput_ao.c
index e7e201add..ed8eaa796 100644
--- a/src/audioOutputs/audioOutput_ao.c
+++ b/src/audioOutputs/audioOutput_ao.c
@@ -199,23 +199,6 @@ static int audioOutputAo_openDevice(AudioOutput * audioOutput)
return 0;
}
-/**
- * For whatever reason, libao wants a non-const pointer. Let's hope
- * it does not write to the buffer, and use the union deconst hack to
- * work around this API misdesign.
- */
-static int ao_play_deconst(ao_device *device, const void *output_samples,
- uint_32 num_bytes)
-{
- union {
- const void *in;
- void *out;
- } u;
-
- u.in = output_samples;
- return ao_play(device, u.out, num_bytes);
-}
-
static int audioOutputAo_play(AudioOutput * audioOutput,
const char *playChunk, size_t size)
{
@@ -229,7 +212,9 @@ static int audioOutputAo_play(AudioOutput * audioOutput,
chunk_size = (size_t)ad->writeSize > size
? size : (size_t)ad->writeSize;
- if (ao_play_deconst(ad->device, playChunk, chunk_size) == 0) {
+ if (!ao_play(ad->device,
+ (char *)deconst_ptr(playChunk),
+ (uint_32)chunk_size)) {
audioOutputAo_error();
ERROR("closing audio device due to write error\n");
audioOutputAo_closeDevice(audioOutput);
diff --git a/src/audioOutputs/audioOutput_jack.c b/src/audioOutputs/audioOutput_jack.c
index 8818bb739..77ee077a4 100644
--- a/src/audioOutputs/audioOutput_jack.c
+++ b/src/audioOutputs/audioOutput_jack.c
@@ -31,8 +31,8 @@ static const size_t sample_size = sizeof(jack_default_audio_sample_t);
typedef struct _JackData {
/* configuration */
- char *name;
- char *output_ports[2];
+ const char *name;
+ const char *output_ports[2];
int ringbuf_sz;
/* locks */
@@ -52,6 +52,7 @@ typedef struct _JackData {
static JackData *newJackData(void)
{
JackData *ret;
+
ret = xcalloc(sizeof(JackData), 1);
ret->name = "mpd";
@@ -97,12 +98,12 @@ static void freeJackData(AudioOutput *audioOutput)
freeJackClient(jd);
if (strcmp(jd->name, "mpd") != 0)
- free(jd->name);
+ free(deconst_ptr(jd->name));
for ( i = ARRAY_SIZE(jd->output_ports); --i >= 0; ) {
if (!jd->output_ports[i])
continue;
- free(jd->output_ports[i]);
+ free(deconst_ptr(jd->output_ports[i]));
}
free(jd);
@@ -267,7 +268,7 @@ static int jack_testDefault(void)
static int connect_jack(AudioOutput *audioOutput)
{
JackData *jd = audioOutput->data;
- char **jports;
+ const char **jports;
char *port_name;
if ( (jd->client = jack_client_new(jd->name)) == NULL ) {
@@ -304,9 +305,9 @@ static int connect_jack(AudioOutput *audioOutput)
/* hay que buscar que hay */
if ( !jd->output_ports[1]
- && (jports = (char **)jack_get_ports(jd->client, NULL, NULL,
- JackPortIsPhysical|
- JackPortIsInput)) ) {
+ && (jports = jack_get_ports(jd->client, NULL, NULL,
+ JackPortIsPhysical|
+ JackPortIsInput)) ) {
jd->output_ports[0] = jports[0];
jd->output_ports[1] = jports[1] ? jports[1] : jports[0];
DEBUG("output_ports: %s %s\n",
diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c
index 49d69eebd..2ee942809 100644
--- a/src/audioOutputs/audioOutput_shout.c
+++ b/src/audioOutputs/audioOutput_shout.c
@@ -56,7 +56,7 @@ typedef struct _ShoutData {
int opened;
- MpdTag *tag;
+ struct mpd_tag *tag;
int tagToSend;
int timeout;
@@ -93,7 +93,7 @@ static void freeShoutData(ShoutData * sd)
if (sd->shoutConn)
shout_free(sd->shoutConn);
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
if (sd->timer)
timer_free(sd->timer);
@@ -390,11 +390,10 @@ static void myShout_closeDevice(AudioOutput * audioOutput)
static void addTag(ShoutData *sd, const char *name, char *value)
{
- if (value) {
- union const_hack u;
- u.in = name;
- vorbis_comment_add_tag(&(sd->vc), u.out, value);
- }
+ if (value)
+ vorbis_comment_add_tag(&(sd->vc),
+ (char *)deconst_ptr(name),
+ value);
}
static void copyTagToVorbisComment(ShoutData * sd)
@@ -403,15 +402,15 @@ static void copyTagToVorbisComment(ShoutData * sd)
int i;
for (i = 0; i < sd->tag->numOfItems; i++) {
- switch (sd->tag->items[i].type) {
+ switch (sd->tag->items[i]->type) {
case TAG_ITEM_ARTIST:
- addTag(sd, "ARTIST", sd->tag->items[i].value);
+ addTag(sd, "ARTIST", sd->tag->items[i]->value);
break;
case TAG_ITEM_ALBUM:
- addTag(sd, "ALBUM", sd->tag->items[i].value);
+ addTag(sd, "ALBUM", sd->tag->items[i]->value);
break;
case TAG_ITEM_TITLE:
- addTag(sd, "TITLE", sd->tag->items[i].value);
+ addTag(sd, "TITLE", sd->tag->items[i]->value);
break;
default:
break;
@@ -594,7 +593,7 @@ static void myShout_sendMetadata(ShoutData * sd)
}
}
- /*if(sd->tag) freeMpdTag(sd->tag);
+ /*if(sd->tag) tag_free(sd->tag);
sd->tag = NULL; */
sd->tagToSend = 0;
}
@@ -637,7 +636,8 @@ static int myShout_play(AudioOutput * audioOutput,
for (i = 0; i < samples; i++) {
for (j = 0; j < sd->audioFormat->channels; j++) {
- vorbbuf[j][i] = (*((mpd_sint16 *) playChunk)) / 32768.0;
+ vorbbuf[j][i] = (*((const mpd_sint16 *) playChunk))
+ / 32768.0;
playChunk += bytes;
}
}
@@ -663,19 +663,19 @@ static int myShout_play(AudioOutput * audioOutput,
return 0;
}
-static void myShout_setTag(AudioOutput * audioOutput, MpdTag * tag)
+static void myShout_setTag(AudioOutput * audioOutput, const struct mpd_tag *tag)
{
ShoutData *sd = (ShoutData *) audioOutput->data;
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
sd->tag = NULL;
sd->tagToSend = 0;
if (!tag)
return;
- sd->tag = mpdTagDup(tag);
+ sd->tag = tag_dup(tag);
sd->tagToSend = 1;
}