예제 #1
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 sndio_read(void *self, ref_buffer *rb)
{
    int result;
    im_sndio_state *s = self;

    rb->buf = malloc(BUFSIZE * s->par.bps * s->par.rchan);
    if(!rb->buf)
        return -1;
    result = sio_read(s->hdl, rb->buf, BUFSIZE * s->par.bps * s->par.rchan);

    rb->len = result;
    rb->aux_data = s->par.rate * s->par.rchan * s->par.bps;

    if(s->newtrack)
    {
        rb->critical = 1;
        s->newtrack = 0;
    }

    if(result == 0)
    {
        if(sio_eof(s->hdl))
        {
            LOG_ERROR0("Error reading from audio device");
            free(rb->buf);
            return -1;
        }
    }

    return rb->len;
}
예제 #2
0
파일: im_alsa.c 프로젝트: miksago/icecast
/* 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 alsa_read(void *self, ref_buffer *rb)
{
    int result;
    im_alsa_state *s = self;

    rb->buf = malloc(SAMPLES*2*s->channels);
    if(!rb->buf)
        return -1;
    result = snd_pcm_readi(s->fd, rb->buf, SAMPLES);

    rb->len = result*4;
    rb->aux_data = s->rate*s->channels*2;

    if(s->newtrack)
    {
        rb->critical = 1;
        s->newtrack = 0;
    }

    if (result == -EPIPE)
    {
        snd_pcm_prepare(s->fd);
        return 0;
    }
    else if (result == -EBADFD)
    {
        LOG_ERROR0("Bad descriptor passed to snd_pcm_readi");
        free(rb->buf);
        return -1;
    }

    return rb->len;
}
예제 #3
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;
}
예제 #4
0
파일: input.c 프로젝트: xiph/Icecast-IceS
int input_calculate_ogg_sleep(ogg_page *page)
{
    static ogg_stream_state os;
    ogg_packet op;
    static vorbis_info vi;
    static vorbis_comment vc;
    static input_type codec = ICES_INPUT_UNKNOWN;
    static int need_start_pos, need_headers, state_in_use = 0;
    static int serialno = 0;
    static uint64_t offset;
    static uint64_t first_granulepos;

    if (ogg_page_granulepos(page) == -1)
    {
        LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream.");
        return -1;
    }
    if (ogg_page_bos (page))
    {
        control.oldsamples = 0;

        if (state_in_use)
            ogg_stream_clear (&os);
        ogg_stream_init (&os, ogg_page_serialno (page));
        serialno = ogg_page_serialno (page);
        state_in_use = 1;
        vorbis_info_init (&vi);
        vorbis_comment_init (&vc);
        need_start_pos = 1;
        need_headers = 3;
        codec = ICES_INPUT_UNKNOWN;
        offset = (uint64_t)0;
    }
    if (need_start_pos)
    {
        int found_first_granulepos = 0;

        ogg_stream_pagein (&os, page);
        while (ogg_stream_packetout (&os, &op) == 1)
        {
            if (need_headers)
            {
                /* check for Vorbis. For Vorbis the Magic is {0x01|0x03|0x05}"vorbis" */
                if (op.bytes > 7 && memcmp(op.packet+1, "vorbis", 6) == 0)
                {
                    if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0)
                    {
                        LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis.");
                        control.samplerate = 0;
                        vorbis_info_clear (&vi);
                        ogg_stream_clear (&os);
                        return -1;
                    }
                    control.samplerate = vi.rate;
                    codec = ICES_INPUT_VORBIS;
                }
                /* check for Opus. For Opus the magic is "OpusHead" */
                else if (op.bytes == 19 && memcmp(op.packet, "OpusHead", 8) == 0)
                {
                    if (op.packet[8] != 1)
                    {
                        LOG_ERROR0("Timing control: can't determine sample rate for input, unsupported Opus version.");
                        control.samplerate = 0;
                        vorbis_info_clear (&vi);
                        ogg_stream_clear (&os);
                        return -1;
                    }
                    /* Sample rate is fixed for Opus: 48kHz */
                    control.samplerate = 48000;
                    codec = ICES_INPUT_OGG;
                    /* No more headers after this one needed */
                    need_headers = 1;
                }
                else if (op.bytes >= 80 && memcmp(op.packet, "Speex   ", 8) == 0)
                {
                    if (__read_int32_le(op.packet+28) != 1 || __read_int32_le(op.packet+32) != op.bytes)
                    {
                        LOG_ERROR0("Timing control: can't determine sample rate for input, bad or unsupported Speex header.");
                        control.samplerate = 0;
                        vorbis_info_clear (&vi);
                        ogg_stream_clear (&os);
                        return -1;
                    }

                    control.samplerate = __read_int32_le(op.packet+36);
                    codec = ICES_INPUT_OGG;
                    /* No more headers after this one needed */
                    need_headers = 1;
                }
                else if (op.bytes >= 51 && memcmp(op.packet, "\177FLAC\1\0", 7) == 0 && memcmp(op.packet+9, "fLaC\0", 5) == 0)
                {
                    control.samplerate = __read_int20_be(op.packet+27);
                    codec = ICES_INPUT_OGG;
                    /* No more headers after this one needed */
                    need_headers = 1;
                }
                else if (codec == ICES_INPUT_UNKNOWN)
                {
                    LOG_ERROR0("Timing control: can't determine sample rate for input, unsupported input format.");
                    control.samplerate = 0;
                    vorbis_info_clear (&vi);
                    ogg_stream_clear (&os);
                    return -1;
                }
                need_headers--;

                if (need_headers == 0)
                {
                    vorbis_comment_clear (&vc);
                    first_granulepos = (uint64_t)0;
                    return 0;
                }
                continue;
            }
            /* headers have been read */
            if (first_granulepos == 0 && op.granulepos > 0)
            {
                first_granulepos = op.granulepos;
                found_first_granulepos = 1;
            }
            if (codec == ICES_INPUT_VORBIS)
            {
                offset += vorbis_packet_blocksize (&vi, &op) / 4;
            }
        }
        if (!found_first_granulepos)
            return 0;

        need_start_pos = 0;
        control.oldsamples = first_granulepos - offset;
        vorbis_info_clear (&vi);
        ogg_stream_clear (&os);
        state_in_use = 0;
    }
    if (serialno != ogg_page_serialno (page))
    {
        LOG_ERROR0 ("Found page which does not belong to current logical stream");
        return -1;
    }
    control.samples = ogg_page_granulepos (page) - control.oldsamples;
    control.oldsamples = ogg_page_granulepos (page);

    control.senttime += ((uint64_t)control.samples * 1000000 /
            (uint64_t)control.samplerate);

    return 0;
}
예제 #5
0
/* Process a buffer (including reencoding or encoding, if desired).
 * Returns: >0 - success
 *           0 - shout error occurred
 *          -1 - no data produced
 *          -2 - fatal error occurred
 */
int process_and_send_buffer(stream_description *sdsc, ref_buffer *buffer)
{
    if(sdsc->reenc)
    {
        unsigned char *buf;
        int buflen,ret;

        ret = reencode_page(sdsc->reenc, buffer, &buf, &buflen);
        if(ret > 0) 
        {
            ret = stream_send_data(sdsc, buf, buflen);
            free(buf);
            return ret;
        }
        else if(ret==0) /* No data produced by reencode */
            return -1;
        else
        {
            LOG_ERROR0("Fatal reencoding error encountered");
            return -2;
        }
    }
    else if (sdsc->enc)
    {
        ogg_page og;
        int be = (sdsc->input->subtype == INPUT_PCM_BE_16)?1:0;
        int ret=1;

        /* We use critical as a flag to say 'start a new stream' */
        if(buffer->critical)
        {
            if(sdsc->resamp) {
                resample_finish(sdsc->resamp);
                encode_data_float(sdsc->enc, sdsc->resamp->buffers,
                        sdsc->resamp->buffill);
                resample_clear(sdsc->resamp);
                sdsc->resamp = resample_initialise (sdsc->stream->channels,
                        sdsc->stream->resampleinrate, sdsc->stream->resampleoutrate);
            }
            encode_finish(sdsc->enc);
            while(encode_flush(sdsc->enc, &og) != 0)
            {
                if ((ret = stream_send_data(sdsc, og.header, og.header_len)) == 0)
                    return 0;
                if ((ret = stream_send_data(sdsc, og.body, og.body_len)) == 0)
                    return 0;
            }
            encode_clear(sdsc->enc);

            if(sdsc->input->metadata_update)
            {
                vorbis_comment_clear(&sdsc->vc);
                vorbis_comment_init(&sdsc->vc);

                sdsc->input->metadata_update(sdsc->input->internal, &sdsc->vc);
            }

            sdsc->enc = encode_initialise(sdsc->stream->channels,
                    sdsc->stream->samplerate, sdsc->stream->managed, 
                    sdsc->stream->min_br, sdsc->stream->nom_br, 
                    sdsc->stream->max_br, sdsc->stream->quality,
                    &sdsc->vc);
            if(!sdsc->enc) {
                LOG_ERROR0("Failed to initialise encoder");
                return -2;
            }
            sdsc->enc->max_samples_ppage = sdsc->stream->max_samples_ppage;
        }

        if(sdsc->downmix) {
            downmix_buffer(sdsc->downmix, (signed char *)buffer->buf, buffer->len, be);
            if(sdsc->resamp) {
                resample_buffer_float(sdsc->resamp, &sdsc->downmix->buffer, 
                        buffer->len/4);
                encode_data_float(sdsc->enc, sdsc->resamp->buffers, 
                        sdsc->resamp->buffill);
            }
            else
                encode_data_float(sdsc->enc, &sdsc->downmix->buffer,
                       buffer->len/4);
        }
        else if(sdsc->resamp) {
            resample_buffer(sdsc->resamp, (signed char *)buffer->buf, 
                    buffer->len, be);
            encode_data_float(sdsc->enc, sdsc->resamp->buffers,
                    sdsc->resamp->buffill);
        }
        else {
            encode_data(sdsc->enc, (signed char *)(buffer->buf), 
                    buffer->len, be);
        }

        while(encode_dataout(sdsc->enc, &og) > 0)
        {
            if ((ret = stream_send_data(sdsc, og.header, og.header_len)) == 0)
                return 0;
            if ((ret = stream_send_data(sdsc, og.body, og.body_len)) == 0)
                return 0;
        }
                        
        return ret;
    }
    else    
        return stream_send_data(sdsc, buffer->buf, buffer->len);
}
예제 #6
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;
}
예제 #7
0
input_module_t *playlist_open_module(module_param_t *params)
{
    input_module_t *mod = calloc(1, sizeof(input_module_t));
    playlist_state_t *pl;
    module_param_t *current;
    int (*init)(module_param_t *, playlist_state_t *)=NULL;

    mod->type = ICES_INPUT_VORBIS;
    mod->getdata = playlist_read;
    mod->handle_event = event_handler;
    mod->metadata_update = NULL; /* Not used for playlists */

    mod->internal = calloc(1, sizeof(playlist_state_t));
    pl = (playlist_state_t *)mod->internal;

    current = params;
    while(current)
    {
        if (!strcmp(current->name, "type"))
        {
            int current_module = 0;

            while(modules[current_module].init)
            {
                if(!strcmp(current->value, modules[current_module].name))
                {
                    init = modules[current_module].init; 
                    break;
                }
                current_module++;
            }
            
            if(!init)
            {
                LOG_ERROR1("Unknown playlist type \"%s\"", current->value);
                goto fail;
            }
        }
        current = current->next;
    }

    if(init)
    {
        if(init(params, pl))
        {
            LOG_ERROR0("Playlist initialisation failed");
            goto fail;
        }
        else 
        {
            ogg_sync_init(&pl->oy);
            return mod; /* Success. Finished initialising */
        }
    }
    else
        LOG_ERROR0("No playlist type given, cannot initialise playlist module");

fail:
    if (mod) 
    {
        if (mod->internal)
            free(mod->internal);
        free(mod);
    }

    return NULL;
}
예제 #8
0
파일: input.c 프로젝트: miksago/icecast
int input_calculate_ogg_sleep(ogg_page *page)
{
    static ogg_stream_state os;
    ogg_packet op;
    static vorbis_info vi;
    static vorbis_comment vc;
    static int need_start_pos, need_headers, state_in_use = 0;
    static int serialno = 0;
    static uint64_t offset;
    static uint64_t first_granulepos;

    if (ogg_page_granulepos(page) == -1)
    {
        LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream.");
        return -1;
    }
    if (ogg_page_bos (page))
    {
        control.oldsamples = 0;

        if (state_in_use)
            ogg_stream_clear (&os);
        ogg_stream_init (&os, ogg_page_serialno (page));
        serialno = ogg_page_serialno (page);
        state_in_use = 1;
        vorbis_info_init (&vi);
        vorbis_comment_init (&vc);
        need_start_pos = 1;
        need_headers = 3;
        offset = (uint64_t)0;
    }
    if (need_start_pos)
    {
        int found_first_granulepos = 0;

        ogg_stream_pagein (&os, page);
        while (ogg_stream_packetout (&os, &op) == 1)
        {
            if (need_headers)
            {
                if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0)
                {
                    LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis.");
                    control.samplerate = 0;
                    return -1;
                }
                need_headers--;
                control.samplerate = vi.rate;

                if (need_headers == 0)
                {
                    vorbis_comment_clear (&vc);
                    first_granulepos = (uint64_t)0;
                    return 0;
                }
                continue;
            }
            /* headers have been read */
            if (first_granulepos == 0 && op.granulepos > 0)
            {
                first_granulepos = op.granulepos;
                found_first_granulepos = 1;
            }
            offset += vorbis_packet_blocksize (&vi, &op) / 4;
        }
        if (!found_first_granulepos)
            return 0;

        need_start_pos = 0;
        control.oldsamples = first_granulepos - offset;
        vorbis_info_clear (&vi);
        ogg_stream_clear (&os);
        state_in_use = 0;
    }
    if (serialno != ogg_page_serialno (page))
    {
        LOG_ERROR0 ("Found page which does not belong to current logical stream");
        return -1;
    }
    control.samples = ogg_page_granulepos (page) - control.oldsamples;
    control.oldsamples = ogg_page_granulepos (page);

    control.senttime += ((uint64_t)control.samples * 1000000 /
            (uint64_t)control.samplerate);

    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
파일: stream.c 프로젝트: miksago/icecast
/* The main loop for each instance. Gets data passed to it from the stream
 * manager (which gets it from the input module), and streams it to the
 * specified server
 */
void *ices_instance_stream(void *arg)
{
	int ret, shouterr;
	ref_buffer *buffer;
	char *connip;
	stream_description *sdsc = arg;
	instance_t *stream = sdsc->stream;
	input_module_t *inmod = sdsc->input;
	int reencoding = (inmod->type == ICES_INPUT_VORBIS) && stream->encode;
	int encoding = (inmod->type == ICES_INPUT_PCM) && stream->encode;
    char *stream_name = NULL, *stream_genre = NULL, *stream_description = NULL;
    char *user = NULL;
	
	vorbis_comment_init(&sdsc->vc);

	sdsc->shout = shout_new();

	/* we only support the ice protocol and vorbis streams currently */
	shout_set_format(sdsc->shout, SHOUT_FORMAT_VORBIS);
	//shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_ICE);
	shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_HTTP);

    signal(SIGPIPE, signal_hup_handler);

	connip = malloc(16);
	if(!resolver_getip(stream->hostname, connip, 16))
	{
		LOG_ERROR1("Could not resolve hostname \"%s\"", stream->hostname);
		free(connip);
		stream->died = 1;
		return NULL;
	}

	if (!(shout_set_host(sdsc->shout, connip)) == SHOUTERR_SUCCESS) {
		LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
		free(connip);
		stream->died = 1;
		return NULL;
	}

	shout_set_port(sdsc->shout, stream->port);
	if (!(shout_set_password(sdsc->shout, stream->password)) == SHOUTERR_SUCCESS) {
		LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
		free(connip);
		stream->died = 1;
		return NULL;
	}
    if (stream->user)
        user = stream->user;
    else
        user = "******";

    if(shout_set_user(sdsc->shout, user) != SHOUTERR_SUCCESS) {
		LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
		free(connip);
		stream->died = 1;
		return NULL;
    }

	if (!(shout_set_agent(sdsc->shout, VERSIONSTRING)) == SHOUTERR_SUCCESS) {
		LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
		free(connip);
		stream->died = 1;
		return NULL;
	}

	if (!(shout_set_mount(sdsc->shout, stream->mount)) == SHOUTERR_SUCCESS) {
		LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
		free(connip);
		stream->died = 1;
		return NULL;
	}

	/* set the metadata for the stream */
    if(stream->stream_name)
        stream_name = stream->stream_name;
    else if (ices_config->stream_name)
        stream_name = ices_config->stream_name;

    if(stream->stream_description)
        stream_description = stream->stream_description;
    else if (ices_config->stream_description)
        stream_description = ices_config->stream_description;

    if(stream->stream_genre)
        stream_genre = stream->stream_genre;
    else if (ices_config->stream_genre)
        stream_genre = ices_config->stream_genre;

    if(stream_name)
		if (!(shout_set_name(sdsc->shout, stream_name)) == SHOUTERR_SUCCESS) {
			LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
			free(connip);
			stream->died = 1;
			return NULL;
		}
	if (stream_genre)
		if (!(shout_set_genre(sdsc->shout, stream_genre)) == SHOUTERR_SUCCESS) {
			LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
			free(connip);
			stream->died = 1;
			return NULL;
		}
	if (stream_description)
		if (!(shout_set_description(sdsc->shout, stream_description)) == SHOUTERR_SUCCESS) {
			LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
			free(connip);
			stream->died = 1;
			return NULL;
		}

    if(stream->downmix && encoding && stream->channels == 1) {
        stream->channels = 1;
        sdsc->downmix = downmix_initialise();
    }

    if(stream->resampleinrate && stream->resampleoutrate && encoding) {
        stream->samplerate = stream->resampleoutrate;
        sdsc->resamp = resample_initialise(stream->channels, 
                stream->resampleinrate, stream->resampleoutrate);
    }

	if(encoding)
	{
		if(inmod->metadata_update)
			inmod->metadata_update(inmod->internal, &sdsc->vc);
		sdsc->enc = encode_initialise(stream->channels, stream->samplerate,
				stream->managed, stream->min_br, stream->nom_br, stream->max_br,
                stream->quality, stream->serial++, &sdsc->vc);
        if(!sdsc->enc) {
            LOG_ERROR0("Failed to configure encoder");
		    stream->died = 1;
            return NULL; /* FIXME: probably leaking some memory here */
        }
	}
	else if(reencoding)
		sdsc->reenc = reencode_init(stream);

    if(stream->savefilename != NULL) 
    {
        stream->savefile = fopen(stream->savefilename, "wb");
        if(!stream->savefile)
            LOG_ERROR2("Failed to open stream save file %s: %s", 
                    stream->savefilename, strerror(errno));
        else
            LOG_INFO1("Saving stream to file %s", stream->savefilename);
    }

	if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
	{
		LOG_INFO3("Connected to server: %s:%d%s", 
				shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), shout_get_mount(sdsc->shout));

		while(1)
		{
			if(stream->buffer_failures > MAX_ERRORS)
			{
				LOG_WARN0("Too many errors, shutting down");
				break;
			}

			buffer = stream_wait_for_data(stream);

			/* buffer being NULL means that either a fatal error occured,
			 * or we've been told to shut down
			 */
			if(!buffer)
				break;

			/* If data is NULL or length is 0, we should just skip this one.
			 * Probably, we've been signalled to shut down, and that'll be
			 * caught next iteration. Add to the error count just in case,
			 * so that we eventually break out anyway 
			 */
			if(!buffer->buf || !buffer->len)
			{
				LOG_WARN0("Bad buffer dequeued!");
				stream->buffer_failures++;
				continue; 
			}

            if(stream->wait_for_critical)
            {
                LOG_INFO0("Trying restart on new substream");
                stream->wait_for_critical = 0;
            }

            ret = process_and_send_buffer(sdsc, buffer);

            /* No data produced, do nothing */
            if(ret == -1)
                ;
            /* Fatal error */
            else if(ret == -2)
            {
                LOG_ERROR0("Serious error, waiting to restart on "
                           "next substream. Stream temporarily suspended.");
                /* Set to wait until a critical buffer comes through (start of
                 * a new substream, typically), and flush existing queue.
                 */
                thread_mutex_lock(&ices_config->flush_lock);
                stream->wait_for_critical = 1;
                input_flush_queue(stream->queue, 0);
                thread_mutex_unlock(&ices_config->flush_lock);
            }
            /* Non-fatal shout error */
            else if(ret == 0)
			{
				LOG_ERROR2("Send error: %s (%s)", 
                        shout_get_error(sdsc->shout), strerror(errno));
				if(shout_get_errno(sdsc->shout) == SHOUTERR_SOCKET)
				{
					int i=0;

					/* While we're trying to reconnect, don't receive data
					 * to this instance, or we'll overflow once reconnect
					 * succeeds
					 */
					thread_mutex_lock(&ices_config->flush_lock);
					stream->skip = 1;

					/* Also, flush the current queue */
					input_flush_queue(stream->queue, 1);
					thread_mutex_unlock(&ices_config->flush_lock);
					
					while((i < stream->reconnect_attempts ||
							stream->reconnect_attempts==-1) && 
                            !ices_config->shutdown)
					{
						i++;
						LOG_WARN0("Trying reconnect after server socket error");
						shout_close(sdsc->shout);
						if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
						{
							LOG_INFO3("Connected to server: %s:%d%s", 
                                    shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), 
                                    shout_get_mount(sdsc->shout));
                            /* This stream can't restart until the next
                             * logical stream comes along, since the
                             * server won't have any cached headers for
                             * this source/connection. So, don't continue
                             * yet.
                             */
                            thread_mutex_lock(&ices_config->flush_lock);
                            stream->wait_for_critical = 1;
                            input_flush_queue(stream->queue, 0);
                            thread_mutex_unlock(&ices_config->flush_lock);
							break;
						}
						else
						{
							LOG_ERROR3("Failed to reconnect to %s:%d (%s)",
								shout_get_host(sdsc->shout),shout_get_port(sdsc->shout),
								shout_get_error(sdsc->shout));
							if(i==stream->reconnect_attempts)
							{
								LOG_ERROR0("Reconnect failed too many times, "
										  "giving up.");
                                /* We want to die now */
								stream->buffer_failures = MAX_ERRORS+1; 
							}
							else /* Don't try again too soon */
								sleep(stream->reconnect_delay); 
						}
					}
					stream->skip = 0;
				}
				stream->buffer_failures++;
			}
			stream_release_buffer(buffer);
		}
	}
	else
	{
		LOG_ERROR3("Failed initial connect to %s:%d (%s)", 
				shout_get_host(sdsc->shout),shout_get_port(sdsc->shout), shout_get_error(sdsc->shout));
	}
	
	shout_close(sdsc->shout);

    if(stream->savefile != NULL) 
        fclose(stream->savefile);

    shout_free(sdsc->shout);
	encode_clear(sdsc->enc);
	reencode_clear(sdsc->reenc);
    downmix_clear(sdsc->downmix);
    resample_clear(sdsc->resamp);
	vorbis_comment_clear(&sdsc->vc);

	stream->died = 1;
	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;
}