예제 #1
0
static int event_handler(input_module_t *mod, enum event_type ev, void *param)
{
    im_sndio_state *s = mod->internal;

    switch(ev)
    {
        case EVENT_SHUTDOWN:
            close_module(mod);
            break;
        case EVENT_NEXTTRACK:
            s->newtrack = 1;
            break;
        case EVENT_METADATAUPDATE:
            thread_mutex_lock(&s->metadatalock);
            if(s->metadata)
            {
                char **md = s->metadata;
                while(*md)
                    free(*md++);
                free(s->metadata);
            }
            s->metadata = (char **)param;
            s->newtrack = 1;
            thread_mutex_unlock(&s->metadatalock);
            break;
        default:
            LOG_WARN1("Unhandled event %d", ev);
            return -1;
    }

    return 0;
}
예제 #2
0
input_module_t *stdin_open_module(module_param_t *params)
{
    input_module_t *mod = calloc(1, sizeof(input_module_t));
    stdinpcm_state *s;
    module_param_t *current;
    int use_metadata = 1; /* Default to on */

    mod->type = ICES_INPUT_PCM;
    mod->getdata = stdin_read;
    mod->handle_event = event_handler;
    mod->metadata_update = metadata_update;

    mod->internal = malloc(sizeof(stdinpcm_state));
    s = mod->internal;

    s->rate = 44100; /* Defaults */
    s->channels = 2; 
    s->metadata = NULL;

    thread_mutex_create(&s->metadatalock);

    current = params;

    while(current)
    {
        if(!strcmp(current->name, "rate"))
            s->rate = atoi(current->value);
        else if(!strcmp(current->name, "channels"))
            s->channels = atoi(current->value);
        else if(!strcmp(current->name, "metadata"))
            use_metadata = atoi(current->value);
        else if(!strcmp(current->name, "metadatafilename"))
            ices_config->metadata_filename = current->value;
        else
            LOG_WARN1("Unknown parameter %s for stdinpcm module", current->name);

        current = current->next;
    }
    if(use_metadata)
    {
        if (ices_config->metadata_filename)
        {
            LOG_INFO0("Starting metadata update thread");
            thread_create("im_stdinpcm-metadata", metadata_thread_signal, mod, 1);
        }
    }

    return mod;
}
예제 #3
0
static int event_handler(input_module_t *mod, enum event_type ev, void *param)
{
    switch(ev)
    {
        case EVENT_SHUTDOWN:
            close_module(mod);
            break;
        case EVENT_NEXTTRACK:
            LOG_INFO0("Moving to next file in playlist.");
            ((playlist_state_t *)mod->internal)->nexttrack = 1;
            break;
        default:
            LOG_WARN1("Unhandled event %d", ev);
            return -1;
    }

    return 0;
}
예제 #4
0
char *playlist_script_get_filename(void *data) {
    script_playlist *pl = data;
    char *prog = pl->program;
    FILE *pipe;
    char *buf = calloc(1,1024);

    if(!buf)
        return NULL;

    pipe = popen(prog, "r");

    if(!pipe) {
        LOG_ERROR1("Couldn't open pipe to program \"%s\"", prog);
        return NULL;
    }

    if(fgets(buf, 1024, pipe) == NULL) {
        LOG_ERROR1("Couldn't read filename from pipe to program \"%s\"", prog);
        free(buf);
        pclose(pipe);
        return NULL;
    }

    pclose(pipe);

    if(buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) {
        LOG_ERROR1("Got newlines instead of filename from program \"%s\"", prog);
        free(buf);
        return NULL;
    }

    if(buf[strlen(buf)-1] == '\n')
        buf[strlen(buf)-1] = 0;
    else
        LOG_WARN1("Retrieved overly long filename \"%s\" from script, this may fail", buf);

    /* De-f**k windows filenames. */
    if(strlen(buf) > 0 && buf[strlen(buf)-1] == '\r')
        buf[strlen(buf)-1] = 0;

    LOG_DEBUG2("Program/script (\"%s\") returned filename \"%s\"", prog, buf);

    return buf;
}
예제 #5
0
int playlist_script_initialise(module_param_t *params, playlist_state_t *pl)
{
    script_playlist *data;

    pl->get_filename = playlist_script_get_filename;
    pl->clear = playlist_script_clear;
    pl->free_filename = playlist_script_free_filename;
    pl->file_ended = playlist_script_file_ended;

    pl->data = calloc(1, sizeof(script_playlist));
    if(!pl->data)
        return -1;

    data = (script_playlist *)pl->data;

    while(params != NULL) {
        if(!strcmp(params->name, "program")) {
            if(data->program) free(data->program);
            data->program = params->value;
        }
        else if(!strcmp(params->name, "on-ended")) {
            if(data->on_ended) free(data->on_ended);
            data->on_ended = params->value;
        }
        else if(!strcmp(params->name, "allow-repeats"))
            pl->allow_repeat = atoi(params->value);
        else if(!strcmp(params->name, "type")) {
            /* We ignore this one */
        }
        else
            LOG_WARN1("Unknown parameter to playlist script module: %s",
                    params->name);
        params = params->next;
    }

    if(!data->program) {
        LOG_ERROR0("No program name specified for playlist module");
        free(data);
        return -1;
    }

    return 0;
}
예제 #6
0
파일: input.c 프로젝트: xiph/Icecast-IceS
/* This is identical to shout_sync(), really. */
void input_sleep(void)
{
    int64_t sleep;

    /* no need to sleep if we haven't sent data */
    if (control.senttime == 0) return;

    sleep = ((double)control.senttime / 1000) - 
        (timing_get_time() - control.starttime);

    /* trap for long sleeps, typically indicating a clock change.  it's not */
    /* perfect though, as low bitrate/low samplerate vorbis can trigger this */
    if(sleep > 8000) {
        LOG_WARN1("Extended sleep requested (%ld ms), sleeping for 5 seconds",
                sleep);
        timing_sleep(5000);
    }
    else if(sleep > 0) 
        timing_sleep((uint64_t)sleep);
}
예제 #7
0
/* Core streaming function for this module
 * This is what actually produces the data which gets streamed.
 *
 * returns:  >0  Number of bytes read
 *            0  Non-fatal error.
 *           <0  Fatal error.
 */
static int playlist_read(void *self, ref_buffer *rb)
{
    playlist_state_t *pl = (playlist_state_t *)self;
    int bytes;
    unsigned char *buf;
    char *newfn;
    int result;
    ogg_page og;

    if (pl->errors > 5) 
    {
        LOG_WARN0("Too many consecutive errors - exiting");
        return -1;
    }

    if (!pl->current_file || pl->nexttrack) 
    {
        pl->nexttrack = 0;

        if (pl->current_file && strcmp (pl->filename, "-"))
        {
            fclose(pl->current_file);
            pl->current_file = NULL;
        }
	if (pl->file_ended)
	{
	    pl->file_ended(pl->data, pl->filename);
	}

        newfn = pl->get_filename(pl->data);
        if (!newfn)
        {
            LOG_INFO0("No more filenames available, end of playlist");
            return -1; /* No more files available */
        }

        if (strcmp (newfn, "-"))
        {
            if (!pl->allow_repeat && pl->filename && !strcmp(pl->filename, newfn))
            {
                LOG_ERROR0("Cannot play same file twice in a row, skipping");
                pl->errors++;
                pl->free_filename (pl->data, newfn);
                return 0;
            }
            pl->free_filename(pl->data, pl->filename);
            pl->filename = newfn;

            pl->current_file = fopen(pl->filename, "rb");
            if (!pl->current_file) 
            {
                LOG_WARN2("Error opening file \"%s\": %s",pl->filename, strerror(errno));
                pl->errors++;
                return 0;
            }
            LOG_INFO1("Currently playing \"%s\"", pl->filename);
        }
        else
        {
            LOG_INFO0("Currently playing from stdin");
            pl->current_file = stdin;
            pl->free_filename(pl->data, pl->filename);
            pl->filename = newfn;
        }

        /* Reinit sync, so that dead data from previous file is discarded */
        ogg_sync_clear(&pl->oy);
        ogg_sync_init(&pl->oy);
    }
    input_sleep ();

    while(1)
    {
        result = ogg_sync_pageout(&pl->oy, &og);
        if(result < 0)
            LOG_WARN1("Corrupt or missing data in file (%s)", pl->filename);
        else if(result > 0)
        {
            if (ogg_page_bos (&og))
            {
               if (ogg_page_serialno (&og) == pl->current_serial)
                   LOG_WARN1 ("detected duplicate serial number reading \"%s\"", pl->filename);

               pl->current_serial = ogg_page_serialno (&og);
            }
            if (input_calculate_ogg_sleep (&og) < 0)
            {
                pl->nexttrack = 1;
                return 0;
            }
            rb->len = og.header_len + og.body_len;
            rb->buf = malloc(rb->len);
            rb->aux_data = og.header_len;

            memcpy(rb->buf, og.header, og.header_len);
            memcpy(rb->buf+og.header_len, og.body, og.body_len);
            if(ogg_page_granulepos(&og)==0)
                rb->critical = 1;
            break;
        }

        /* If we got to here, we didn't have enough data. */
        buf = ogg_sync_buffer(&pl->oy, BUFSIZE);
        bytes = fread(buf,1, BUFSIZE, pl->current_file);
        if (bytes <= 0) 
        {
            if (feof(pl->current_file)) 
            {
                pl->nexttrack = 1;
                return playlist_read(pl,rb);
            } 
            else 
            {
                LOG_ERROR2("Read error from \"%s\": %s", pl->filename, strerror(errno));
                fclose(pl->current_file);
                pl->current_file=NULL;
                pl->errors++;
                return 0; 
            }
        }
        else
            ogg_sync_wrote(&pl->oy, bytes);
    }

    pl->errors=0;

    return rb->len;
}
예제 #8
0
int main(int argc, char **argv)
{
    char logpath[FILENAME_MAX];
    int log;

    if (argc != 2) 
    {
        fprintf(stderr, PACKAGE_STRING "\n"
                "  (c) Copyright 2001-2004 The IceS Development Team <*****@*****.**>\n"
                "        Michael Smith <*****@*****.**>\n"
                "        Karl Heyes    <*****@*****.**>\n"
                "        and others\n"
                "\n"
                "Usage: \"ices config.xml\"\n");
        return 1;
    }

    config_initialize();

    if (config_read(argv[1]) <= 0) 
    {
        fprintf(stderr, "Failed to read config file \"%s\"\n", argv[1]);
        goto fail;
    }
	
    if (ices_config->background)
    {
#ifndef _WIN32		
        int ret = 0;
        /* Start up new session, to lose old session and process group */
        switch (fork())
        {
        case 0: break; /* child continues */
        case -1: perror ("fork"); ret = -1;
        default:
            exit (ret);
        }

        /* Disassociate process group and controlling terminal */ 
        setsid();

        /* Become a NON-session leader so that a */
        /* control terminal can't be reacquired */
        switch (fork())
        {
        case 0: break; /* child continues */
        case -1: perror ("fork"); ret = -1;
        default:
            exit (ret);
        }
#else
        FreeConsole();
#endif    		
    }

    log_initialize();
    thread_initialize();
    shout_init();
    encode_init();
#ifndef _WIN32	
    signals_setup();
#endif

    snprintf(logpath, FILENAME_MAX, "%s/%s", ices_config->logpath, 
            ices_config->logfile);
    if(ices_config->log_stderr)
        log = log_open_file(stderr);
    else
    {
        log = log_open(logpath);
        if (log < 0)
            fprintf (stderr, "unable to open log %s\n", logpath);
        log_set_trigger (log, ices_config->logsize);
    }
    /* Set the log level, if requested - defaults to 2 (WARN) otherwise */
    if (ices_config->loglevel)
        log_set_level(log, ices_config->loglevel);

    ices_config->log_id = log;

    LOG_INFO0(PACKAGE_STRING " started...");
    if (ices_config->pidfile != NULL)
    {
        FILE *f = fopen (ices_config->pidfile, "w");
        if (f)
        {
            fprintf (f, "%i", getpid());
            fclose (f);
        }
        else
        {
            LOG_WARN1("pidfile \"%s\" cannot be written to", ices_config->pidfile);
            xmlFree (ices_config->pidfile);
            ices_config->pidfile = NULL;
        }
    }

    /* Start the core streaming loop */
    input_loop();

    if (ices_config->pidfile)
        remove (ices_config->pidfile);

    LOG_INFO0("Shutdown complete");

    log_close(log);

 fail:
    encode_close();
    shout_shutdown();
    config_shutdown();
    thread_shutdown();
    log_shutdown();

    return 0;
}
예제 #9
0
input_module_t *sndio_open_module(module_param_t *params)
{
    input_module_t *mod = calloc(1, sizeof(input_module_t));
    im_sndio_state *s;
    module_param_t *current;
    char *device = NULL; /* default device */
    int sample_rate = 44100;
    int channels = 2;
    int use_metadata = 1; /* Default to on */

    mod->type = ICES_INPUT_PCM;
#ifdef WORDS_BIGENDIAN
    mod->subtype = INPUT_PCM_BE_16;
#else
    mod->subtype = INPUT_PCM_LE_16;
#endif
    mod->getdata = sndio_read;
    mod->handle_event = event_handler;
    mod->metadata_update = metadata_update;

    mod->internal = calloc(1, sizeof(im_sndio_state));
    s = mod->internal;

    thread_mutex_create(&s->metadatalock);

    current = params;

    while (current) {
        if (!strcmp(current->name, "rate"))
            sample_rate = atoi(current->value);
        else if (!strcmp(current->name, "channels"))
            channels = atoi(current->value);
        else if (!strcmp(current->name, "device"))
            device = current->value;
        else if (!strcmp(current->name, "metadata"))
            use_metadata = atoi(current->value);
        else if(!strcmp(current->name, "metadatafilename"))
            ices_config->metadata_filename = current->value;
        else
            LOG_WARN1("Unknown parameter %s for sndio module", current->name);
        current = current->next;
    }

    /* First up, lets open the audio device */
    if((s->hdl = sio_open(device, SIO_REC, 0)) == NULL) {
        LOG_ERROR0("Failed to open sndio device");
        goto fail;
    }

    /* Try and set up what we want */
    sio_initpar(&s->par);
    s->par.rate = sample_rate;
    s->par.rchan = channels; 
    s->par.bits = 16;
    s->par.sig = 1;
    s->par.le = SIO_LE_NATIVE;
    s->par.round = BUFSIZE;
    s->par.appbufsz = BUFSIZE * 4;

    if (!sio_setpar(s->hdl, &s->par) || !sio_getpar(s->hdl, &s->par)) {
        LOG_ERROR0("Failed to configure sndio device");
        goto fail;
    }

    /* Check all went according to plan */
    if (s->par.rate != sample_rate) {
        LOG_ERROR0("Couldn't set sampling rate");
        goto fail;
    }
    if (s->par.rchan != channels) {
        LOG_ERROR0("Couldn't set number of channels");
        goto fail;
    }
    if (s->par.bits != 16) {
        LOG_ERROR0("Couldn't set 16 bit precision");
        goto fail;
    }
    if (s->par.sig != 1) {
        LOG_ERROR0("Couldn't set signed linear encoding");
        goto fail;
    }
    if (s->par.le != SIO_LE_NATIVE) {
        LOG_ERROR0("Couldn't set proper endianness");
        goto fail;
    }

    if (!sio_start(s->hdl)) {
        LOG_ERROR0("Couldn't start sndio");
        goto fail;
    }

    /* We're done, and we didn't fail! */
    LOG_INFO2("Opened audio device for %d channel(s), %d Hz", 
            channels, sample_rate);

    if(use_metadata)
    {
        LOG_INFO0("Starting metadata update thread");
        if(ices_config->metadata_filename)
            thread_create("im_sndio-metadata", metadata_thread_signal, mod, 1);
        else
            thread_create("im_sndio-metadata", metadata_thread_stdin, mod, 1);
    }

    return mod;

fail:
    close_module(mod); /* safe, this checks for valid contents */
    return NULL;
}
예제 #10
0
파일: im_alsa.c 프로젝트: miksago/icecast
input_module_t *alsa_open_module(module_param_t *params)
{
    input_module_t *mod = calloc(1, sizeof(input_module_t));
    im_alsa_state *s;
    module_param_t *current;
    char *device = "plughw:0,0"; /* default device */
    int format = AFMT_S16_LE;
    int channels, rate;
    int use_metadata = 1; /* Default to on */
    unsigned int buffered_time;

    snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
    snd_pcm_hw_params_t *hwparams;

    int err;

    mod->type = ICES_INPUT_PCM;
    mod->subtype = INPUT_PCM_LE_16;
    mod->getdata = alsa_read;
    mod->handle_event = event_handler;
    mod->metadata_update = metadata_update;

    mod->internal = calloc(1, sizeof(im_alsa_state));
    s = mod->internal;

    s->fd = NULL; /* Set it to something invalid, for now */
    s->rate = 44100; /* Defaults */
    s->channels = 2; 

    thread_mutex_create(&s->metadatalock);

    current = params;

    while(current)
    {
        if(!strcmp(current->name, "rate"))
            s->rate = atoi(current->value);
        else if(!strcmp(current->name, "channels"))
            s->channels = atoi(current->value);
        else if(!strcmp(current->name, "device"))
            device = current->value;
        else if(!strcmp(current->name, "metadata"))
            use_metadata = atoi(current->value);
        else if(!strcmp(current->name, "metadatafilename"))
            ices_config->metadata_filename = current->value;
        else
            LOG_WARN1("Unknown parameter %s for alsa module", current->name);

        current = current->next;
    }

    snd_pcm_hw_params_alloca(&hwparams);

    if ((err = snd_pcm_open(&s->fd, device, stream, 0)) < 0)
    {
        LOG_ERROR2("Failed to open audio device %s: %s", device, snd_strerror(err));
        goto fail;
    }

    if ((err = snd_pcm_hw_params_any(s->fd, hwparams)) < 0)
    {
        LOG_ERROR1("Failed to initialize hwparams: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params_set_access(s->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
    {
        LOG_ERROR1("Error setting access: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params_set_format(s->fd, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
    {
        LOG_ERROR1("Couldn't set sample format to SND_PCM_FORMAT_S16_LE: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params_set_rate_near(s->fd, hwparams, &s->rate, 0)) < 0)
    {
        LOG_ERROR1("Error setting rate: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params_set_channels(s->fd, hwparams, s->channels)) < 0)
    {
        LOG_ERROR1("Error setting channels: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params_set_periods(s->fd, hwparams, 2, 0)) < 0)
    {
        LOG_ERROR1("Error setting periods: %s", snd_strerror(err));
        goto fail;
    }
    buffered_time = 500000;
    if ((err = snd_pcm_hw_params_set_buffer_time_near(s->fd, hwparams, &buffered_time, 0)) < 0)
    {
        LOG_ERROR1("Error setting buffersize: %s", snd_strerror(err));
        goto fail;
    }
    if ((err = snd_pcm_hw_params(s->fd, hwparams)) < 0)
    {
        LOG_ERROR1("Error setting HW params: %s", snd_strerror(err));
        goto fail;
    }

    /* We're done, and we didn't fail! */
    LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz", 
            device, s->channels, s->rate);

    if(use_metadata)
    {
        if(ices_config->metadata_filename)
            thread_create("im_alsa-metadata", metadata_thread_signal, mod, 1);
        else
            thread_create("im_alsa-metadata", metadata_thread_stdin, mod, 1);
        LOG_INFO0("Started metadata update thread");
    }

    return mod;

fail:
    close_module(mod); /* safe, this checks for valid contents */
    return NULL;
}
예제 #11
0
input_module_t *roar_open_module(module_param_t *params)
{
    input_module_t *mod = calloc(1, sizeof(input_module_t));
    im_roar_state *s;
    module_param_t *current;
    const char * server = NULL;
    int    dir    = ROAR_DIR_MONITOR;
    enum { MD_NONE = 0, MD_FILE = 1, MD_STREAM = 2 } use_metadata = MD_STREAM;
    int err;

    mod->getdata = roar_read;
    mod->handle_event = event_handler;
    mod->metadata_update = metadata_update;

    mod->internal = calloc(1, sizeof(im_roar_state));
    s = mod->internal;

    if(roar_profile2info(&s->info, "default") == -1)
    {
        LOG_ERROR1("Failed to get default audio profile: %s",
                roar_error2str(roar_error));
        return NULL;
    }
    s->info.bits = 16;

    s->vss       = NULL;

    s->plugins   = roar_plugincontainer_new_simple(IM_ROAR_APPNAME, IM_ROAR_ABIVERSION);
    if (!s->plugins)
    {
        LOG_ERROR1("Failed to create plugin container: %s",
                roar_error2str(roar_error));
        return NULL;
    }

    thread_mutex_create(&s->metadatalock);

    current = params;

    while(current)
    {
        if(!strcmp(current->name, "rate"))
            s->info.rate = roar_str2rate(current->value);
        else if(!strcmp(current->name, "channels"))
            s->info.channels = roar_str2channels(current->value);
        else if(!strcmp(current->name, "codec"))
            s->info.codec = roar_str2codec(current->value);
        else if(!strcmp(current->name, "aiprofile")) {
            if (roar_profile2info(&s->info, current->value) == -1) {
                LOG_WARN2("Can not get audio info profile %s: %s", current->value, roar_error2str(roar_error));
            }
            s->info.bits = 16;
        } else if(!strcmp(current->name, "dir")) {
            if ( !strcasecmp(current->value, "monitor") ) {
                dir = ROAR_DIR_MONITOR;
            } else if ( !strcasecmp(current->value, "record") ) {
                dir = ROAR_DIR_RECORD;
            } else {
                LOG_WARN2("Unknown value %s for parameter %s for roar module", current->value, current->name);
            }
        } else if(!strcmp(current->name, "device") || !strcmp(current->name, "server"))
            server = current->value;
        else if(!strcmp(current->name, "metadata")) {
            if ( !strcasecmp(current->value, "none") ) {
                use_metadata = MD_NONE;
            } else if ( !strcasecmp(current->value, "file") ) {
                use_metadata = MD_FILE;
            } else if ( !strcasecmp(current->value, "stream") ) {
                use_metadata = MD_STREAM;
            } else {
                use_metadata = atoi(current->value);
            }
        } else if(!strcmp(current->name, "metadatafilename")) {
            ices_config->metadata_filename = current->value;
            use_metadata = MD_FILE;
        } else if(!strcmp(current->name, "plugin")) {
            roar_plugin_load(mod, current->value);
        } else
            LOG_WARN1("Unknown parameter %s for roar module", current->name);

        current = current->next;
    }

    mod->type = ICES_INPUT_PCM;

    switch (s->info.codec) {
        case ROAR_CODEC_PCM_LE:
          mod->subtype = INPUT_PCM_LE_16;
         break;
        case ROAR_CODEC_PCM_BE:
          mod->subtype = INPUT_PCM_BE_16;
         break;
        case ROAR_CODEC_OGG_GENERAL:
          LOG_WARN0("Codec may not work, specify ogg_vorbis for Vorbis streaming");
        case ROAR_CODEC_OGG_VORBIS:
          mod->type = ICES_INPUT_VORBIS;
          // we do not set mod->subtype here, strange design ices2 has...
         break;
        case -1:
         LOG_ERROR0("Unknown Codec");
         return NULL;
        default:
         LOG_ERROR1("Unsupported Codec: %s", roar_codec2str(s->info.codec));
         return NULL;
    }

    roar_plugincontainer_appsched_trigger(s->plugins, ROAR_DL_APPSCHED_INIT);

    /* Open the VS connection */
    if ( (s->vss = roar_vs_new(server, IM_ROAR_PROGNAME, &err)) == NULL ) {
        LOG_ERROR2("Failed to open sound server %s: %s",
                server, roar_vs_strerr(err));
        goto fail;
    }

    /* Now, set the required parameters on that device */
    if ( roar_vs_stream(s->vss, &s->info, dir, &err) == -1 ) { 
        LOG_ERROR2("Failed to create a new stream on sound server %s: %s",
                server, roar_vs_strerr(err));
        goto fail;
    }

    if ( _set_flags(roar_vs_connection_obj(s->vss, NULL), roar_vs_stream_obj(s->vss, NULL),
                                ROAR_FLAG_META, ROAR_RESET_FLAG) != 0 ) {
        LOG_WARN0("Can not reset metadata flag from stream");
    }

    /* We're done, and we didn't fail! */
    LOG_INFO3("Opened sound server at %s at %d channel(s), %d Hz", 
            server, s->info.channels, s->info.rate);

    switch (use_metadata) {
     case MD_NONE:
      break;
     case MD_FILE:
        LOG_INFO0("Starting metadata update thread");
        if(ices_config->metadata_filename)
            thread_create("im_roar-metadata", metadata_thread_signal, mod, 1);
        else
            thread_create("im_roar-metadata", metadata_thread_stdin, mod, 1);
      break;
     case MD_STREAM:
        if ( _set_flags(roar_vs_connection_obj(s->vss, NULL), roar_vs_stream_obj(s->vss, NULL),
                                    ROAR_FLAG_META, ROAR_SET_FLAG) != 0 ) {
            LOG_WARN0("Can not set metadata flag from stream");
        }
      break;
    }

    return mod;

fail:
    close_module(mod); /* safe, this checks for valid contents */
    return NULL;
}