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; }
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; }
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; }
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; }
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; }
/* 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); }
/* 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; }
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; }
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; }
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; }
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; }