static GstFlowReturn gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf) { GstShout2send *sink; glong ret; gint delay; GstFlowReturn fret; GstMapInfo map; sink = GST_SHOUT2SEND (basesink); /* presumably we connect here because we need to know the format before * we can set up the connection, which we don't know yet in _start() */ if (!sink->connected) { if (!gst_shout2send_connect (sink)) return GST_FLOW_ERROR; } delay = shout_delay (sink->conn); if (delay > 0) { GST_LOG_OBJECT (sink, "waiting %d msec", delay); if (gst_poll_wait (sink->timer, GST_MSECOND * delay) == -1) { GST_LOG_OBJECT (sink, "unlocked"); fret = gst_base_sink_wait_preroll (basesink); if (fret != GST_FLOW_OK) return fret; } } else { GST_LOG_OBJECT (sink, "we're %d msec late", -delay); } gst_buffer_map (buf, &map, GST_MAP_READ); GST_LOG_OBJECT (sink, "sending %u bytes of data", (guint) map.size); ret = shout_send (sink->conn, map.data, map.size); gst_buffer_unmap (buf, &map); if (ret != SHOUTERR_SUCCESS) goto send_error; return GST_FLOW_OK; /* ERRORS */ send_error: { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("shout_send() failed: %s", shout_get_error (sink->conn))); g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0, shout_get_errno (sink->conn)); return GST_FLOW_ERROR; } }
static gboolean gst_shout2send_connect (GstShout2send * sink) { const char *format = (sink->audio_format == SHOUT_FORMAT_VORBIS) ? "vorbis" : ((sink->audio_format == SHOUT_FORMAT_MP3) ? "mp3" : "unknown"); #ifdef SHOUT_FORMAT_WEBM if (sink->audio_format == SHOUT_FORMAT_WEBM) format = "webm"; #endif GST_DEBUG_OBJECT (sink, "Connection format is: %s", format); if (shout_set_format (sink->conn, sink->audio_format) != SHOUTERR_SUCCESS) goto could_not_set_format; if (shout_open (sink->conn) != SHOUTERR_SUCCESS) goto could_not_connect; GST_DEBUG_OBJECT (sink, "connected to server"); sink->connected = TRUE; /* let's set metadata */ if (sink->songmetadata) { shout_metadata_t *pmetadata; GST_DEBUG_OBJECT (sink, "shout metadata now: %s", sink->songmetadata); pmetadata = shout_metadata_new (); shout_metadata_add (pmetadata, "song", sink->songmetadata); shout_set_metadata (sink->conn, pmetadata); shout_metadata_free (pmetadata); } return TRUE; /* ERRORS */ could_not_set_format: { GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL), ("Error setting connection format: %s", shout_get_error (sink->conn))); return FALSE; } could_not_connect: { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (_("Could not connect to server")), ("shout_open() failed: err=%s", shout_get_error (sink->conn))); g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0, shout_get_errno (sink->conn)); return FALSE; } }
static GstFlowReturn gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf) { GstShout2send *sink; glong ret; sink = GST_SHOUT2SEND (basesink); /* presumably we connect here because we need to know the format before * we can set up the connection, which we don't know yet in _start() */ if (!sink->connected) { if (!gst_shout2send_connect (sink)) return GST_FLOW_ERROR; } /* FIXME: do we want to do syncing here at all? (tpm) */ /* GST_LOG_OBJECT (sink, "using libshout to sync"); */ shout_sync (sink->conn); GST_LOG_OBJECT (sink, "sending %u bytes of data", GST_BUFFER_SIZE (buf)); ret = shout_send (sink->conn, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); if (ret != SHOUTERR_SUCCESS) goto send_error; return GST_FLOW_OK; /* ERRORS */ send_error: { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("shout_send() failed: %s", shout_get_error (sink->conn))); g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0, shout_get_errno (sink->conn)); return GST_FLOW_ERROR; } }
/* 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; }
static void raise_shout_error(shout_t *conn) { rb_raise(cShoutError, "%d: %s", shout_get_errno(conn), shout_get_error(conn)); }