static int ebml_create_client_data (format_plugin_t *format, client_t *client) { ebml_client_data_t *ebml_client_data = calloc(1, sizeof(ebml_client_data_t)); ebml_source_state_t *ebml_source_state = format->_state; int ret = 0; if ((ebml_client_data) && (ebml_source_state->header)) { ebml_client_data->header = ebml_source_state->header; refbuf_addref (ebml_client_data->header); // broken, assumes too much currently } client->format_data = ebml_client_data; client->free_client_data = ebml_free_client_data; if (client->refbuf == NULL) client->refbuf = refbuf_new (4096); client->refbuf->len = 0; if (format_general_headers (format, client) < 0) return -1; return ret; }
void client_set_queue (client_t *client, refbuf_t *refbuf) { refbuf_t *to_release = client->refbuf; client->refbuf = refbuf; if (refbuf) refbuf_addref (client->refbuf); client->pos = 0; if (to_release) refbuf_release (to_release); }
refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self) { refbuf_queue_t *queue; int i; vstate_t *state = (vstate_t *)self->_state; queue = NULL; for (i = 0; i < MAX_HEADER_PAGES; i++) { if (state->headbuf[i]) { refbuf_addref(state->headbuf[i]); refbuf_queue_add(&queue, state->headbuf[i]); } else { break; } } return queue; }
static int ebml_create_client_data(source_t *source, client_t *client) { ebml_client_data_t *ebml_client_data; ebml_source_state_t *ebml_source_state = source->format->_state; if (!ebml_source_state->header) return -1; ebml_client_data = calloc(1, sizeof(ebml_client_data_t)); if (!ebml_client_data) return -1; ebml_client_data->header = ebml_source_state->header; refbuf_addref(ebml_client_data->header); client->format_data = ebml_client_data; client->free_client_data = ebml_free_client_data; return 0; }
/* called when preparing a refbuf with audio data to be passed * back for queueing */ static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf) { ogg_state_t *ogg_info = source->format->_state; refbuf->associated = ogg_info->header_pages; refbuf_addref (refbuf->associated); if (ogg_info->log_metadata) { update_comments (source); ogg_info->log_metadata = 0; } /* listeners can start anywhere unless the codecs themselves are * marking starting points */ if (ogg_info->codec_sync == NULL) refbuf->flags |= SOURCE_BLOCK_SYNC; source->client->queue_pos += refbuf->len; return refbuf; }
/* read an mp3 stream which does not have shoutcast style metadata */ static refbuf_t *mp3_get_no_meta (source_t *source) { refbuf_t *refbuf; mp3_state *source_mp3 = source->format->_state; if (complete_read (source) == 0) return NULL; refbuf = source_mp3->read_data; source_mp3->read_data = NULL; if (source_mp3->update_metadata) { mp3_set_title (source); source_mp3->update_metadata = 0; } refbuf->associated = source_mp3->metadata; refbuf_addref (source_mp3->metadata); refbuf->sync_point = 1; return refbuf; }
void client_set_queue (client_t *client, refbuf_t *refbuf) { refbuf_t *next, *to_release = client->refbuf; while (to_release) { if (to_release->flags & REFBUF_SHARED) { ERROR1 ("content has a shared flag status for %s", client->connection.ip); break; } next = to_release->next; to_release->next = NULL; refbuf_release (to_release); to_release = next; } client->refbuf = refbuf; if (refbuf) refbuf_addref (client->refbuf); client->pos = 0; }
/* called when preparing a refbuf with audio data to be passed * back for queueing */ static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf) { ogg_state_t *ogg_info = source->format->_state; refbuf_t *header = ogg_info->header_pages; while (header) { refbuf_addref (header); header = header->next; } refbuf->associated = ogg_info->header_pages; if (ogg_info->log_metadata) { update_comments (source); ogg_info->log_metadata = 0; } /* listeners can start anywhere unless the codecs themselves are * marking starting points */ if (ogg_info->codec_sync == NULL) refbuf->sync_point = 1; return refbuf; }
/* read mp3 data with inlined metadata from the source. Filter out the * metadata so that the mp3 data itself is store on the queue and the * metadata is is associated with it */ static refbuf_t *mp3_get_filter_meta (source_t *source) { refbuf_t *refbuf; format_plugin_t *plugin = source->format; mp3_state *source_mp3 = plugin->_state; unsigned char *src; unsigned int bytes, mp3_block; if (complete_read (source) == 0) return NULL; refbuf = source_mp3->read_data; source_mp3->read_data = NULL; src = (unsigned char *)refbuf->data; if (source_mp3->update_metadata) { mp3_set_title (source); source_mp3->update_metadata = 0; } /* fill the buffer with the read data */ bytes = source_mp3->read_count; refbuf->len = 0; while (bytes > 0) { unsigned int metadata_remaining; mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset; /* is there only enough to account for mp3 data */ if (bytes <= mp3_block) { refbuf->len += bytes; source_mp3->offset += bytes; break; } /* we have enough data to get to the metadata * block, but only transfer upto it */ if (mp3_block) { src += mp3_block; bytes -= mp3_block; refbuf->len += mp3_block; source_mp3->offset += mp3_block; continue; } /* process the inline metadata, len == 0 indicates not seen any yet */ if (source_mp3->build_metadata_len == 0) { memset (source_mp3->build_metadata, 0, sizeof (source_mp3->build_metadata)); source_mp3->build_metadata_offset = 0; source_mp3->build_metadata_len = 1 + (*src * 16); } /* do we have all of the metatdata block */ metadata_remaining = source_mp3->build_metadata_len - source_mp3->build_metadata_offset; if (bytes < metadata_remaining) { memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset, src, bytes); source_mp3->build_metadata_offset += bytes; break; } /* copy all bytes except the last one, that way we * know a null byte terminates the message */ memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset, src, metadata_remaining-1); /* overwrite metadata in the buffer */ bytes -= metadata_remaining; memmove (src, src+metadata_remaining, bytes); /* assign metadata if it's greater than 1 byte, and the text has changed */ if (source_mp3->build_metadata_len > 1 && strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0) { refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len); memcpy (meta->data, source_mp3->build_metadata, source_mp3->build_metadata_len); ICECAST_LOG_DEBUG("shoutcast metadata %.*s", 4080, meta->data+1); if (strncmp (meta->data+1, "StreamTitle=", 12) == 0) { filter_shoutcast_metadata (source, source_mp3->build_metadata, source_mp3->build_metadata_len); refbuf_release (source_mp3->metadata); source_mp3->metadata = meta; source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='"); } else { ICECAST_LOG_ERROR("Incorrect metadata format, ending stream"); source->running = 0; refbuf_release (refbuf); refbuf_release (meta); return NULL; } } source_mp3->offset = 0; source_mp3->build_metadata_len = 0; } /* the data we have just read may of just been metadata */ if (refbuf->len == 0) { refbuf_release (refbuf); return NULL; } refbuf->associated = source_mp3->metadata; refbuf_addref (source_mp3->metadata); refbuf->sync_point = 1; return refbuf; }
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer) { char *buf; int i, result; ogg_packet op; char *tag; refbuf_t *refbuf; vstate_t *state = (vstate_t *)self->_state; #ifdef USE_YP source_t *source; time_t current_time; #endif if (data) { /* write the data to the buffer */ buf = ogg_sync_buffer(&state->oy, len); memcpy(buf, data, len); ogg_sync_wrote(&state->oy, len); } refbuf = NULL; if (ogg_sync_pageout(&state->oy, &state->og) == 1) { refbuf = refbuf_new(state->og.header_len + state->og.body_len); memcpy(refbuf->data, state->og.header, state->og.header_len); memcpy(&refbuf->data[state->og.header_len], state->og.body, state->og.body_len); if (state->serialno != ogg_page_serialno(&state->og)) { /* this is a new logical bitstream */ state->header = 0; state->packets = 0; /* release old headers, stream state, vorbis data */ for (i = 0; i < MAX_HEADER_PAGES; i++) { if (state->headbuf[i]) { refbuf_release(state->headbuf[i]); state->headbuf[i] = NULL; } } /* Clear old stuff. Rarely but occasionally needed. */ ogg_stream_clear(&state->os); vorbis_comment_clear(&state->vc); vorbis_info_clear(&state->vi); state->serialno = ogg_page_serialno(&state->og); ogg_stream_init(&state->os, state->serialno); vorbis_info_init(&state->vi); vorbis_comment_init(&state->vc); } if (state->header >= 0) { /* FIXME: In some streams (non-vorbis ogg streams), this could get * extras pages beyond the header. We need to collect the pages * here anyway, but they may have to be discarded later. */ if (ogg_page_granulepos(&state->og) <= 0) { state->header++; } else { /* we're done caching headers */ state->header = -1; /* put known comments in the stats */ tag = vorbis_comment_query(&state->vc, "TITLE", 0); if (tag) stats_event(self->mount, "title", tag); else stats_event(self->mount, "title", "unknown"); tag = vorbis_comment_query(&state->vc, "ARTIST", 0); if (tag) stats_event(self->mount, "artist", tag); else stats_event(self->mount, "artist", "unknown"); /* don't need these now */ ogg_stream_clear(&state->os); vorbis_comment_clear(&state->vc); vorbis_info_clear(&state->vi); #ifdef USE_YP /* If we get an update on the mountpoint, force a yp touch */ avl_tree_rlock(global.source_tree); source = source_find_mount(self->mount); avl_tree_unlock(global.source_tree); if (source) { /* If we get an update on the mountpoint, force a yp touch */ current_time = time(NULL); for (i=0; i<source->num_yp_directories; i++) { source->ypdata[i]->yp_last_touch = current_time - source->ypdata[i]->yp_touch_interval + 2; } } #endif } } /* cache header pages */ if (state->header > 0 && state->packets < 3) { if(state->header > MAX_HEADER_PAGES) { refbuf_release(refbuf); ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES); return -1; } refbuf_addref(refbuf); state->headbuf[state->header - 1] = refbuf; if (state->packets >= 0 && state->packets < 3) { ogg_stream_pagein(&state->os, &state->og); while (state->packets < 3) { result = ogg_stream_packetout(&state->os, &op); if (result == 0) break; /* need more data */ if (result < 0) { state->packets = -1; break; } state->packets++; if (vorbis_synthesis_headerin(&state->vi, &state->vc, &op) < 0) { state->packets = -1; break; } } } } } *buffer = refbuf; return 0; }
void source_main (source_t *source) { refbuf_t *refbuf; client_t *client; avl_node *client_node; source_init (source); while (global.running == ICE_RUNNING && source->running) { int remove_from_q; refbuf = get_next_buffer (source); remove_from_q = 0; source->short_delay = 0; if (refbuf) { /* append buffer to the in-flight data queue, */ if (source->stream_data == NULL) { source->stream_data = refbuf; source->burst_point = refbuf; } if (source->stream_data_tail) source->stream_data_tail->next = refbuf; source->stream_data_tail = refbuf; source->queue_size += refbuf->len; /* new buffer is referenced for burst */ refbuf_addref (refbuf); /* new data on queue, so check the burst point */ source->burst_offset += refbuf->len; while (source->burst_offset > source->burst_size) { refbuf_t *to_release = source->burst_point; if (to_release->next) { source->burst_point = to_release->next; source->burst_offset -= to_release->len; refbuf_release (to_release); continue; } break; } /* save stream to file */ if (source->dumpfile && source->format->write_buf_to_file) source->format->write_buf_to_file (source, refbuf); } /* lets see if we have too much data in the queue, but don't remove it until later */ if (source->queue_size > source->queue_size_limit) remove_from_q = 1; /* acquire write lock on pending_tree */ avl_tree_wlock(source->pending_tree); /* acquire write lock on client_tree */ avl_tree_wlock(source->client_tree); client_node = avl_get_first(source->client_tree); while (client_node) { client = (client_t *)client_node->key; send_to_listener (source, client, remove_from_q); if (client->con->error) { client_node = avl_get_next(client_node); if (client->respcode == 200) stats_event_dec (NULL, "listeners"); avl_delete(source->client_tree, (void *)client, _free_client); source->listeners--; DEBUG0("Client removed"); continue; } client_node = avl_get_next(client_node); } /** add pending clients **/ client_node = avl_get_first(source->pending_tree); while (client_node) { if(source->max_listeners != -1 && source->listeners >= (unsigned long)source->max_listeners) { /* The common case is caught in the main connection handler, * this deals with rarer cases (mostly concerning fallbacks) * and doesn't give the listening client any information about * why they were disconnected */ client = (client_t *)client_node->key; client_node = avl_get_next(client_node); avl_delete(source->pending_tree, (void *)client, _free_client); INFO0("Client deleted, exceeding maximum listeners for this " "mountpoint."); continue; } /* Otherwise, the client is accepted, add it */ avl_insert(source->client_tree, client_node->key); source->listeners++; DEBUG0("Client added"); stats_event_inc(source->mount, "connections"); client_node = avl_get_next(client_node); } /** clear pending tree **/ while (avl_get_first(source->pending_tree)) { avl_delete(source->pending_tree, avl_get_first(source->pending_tree)->key, source_remove_client); } /* release write lock on pending_tree */ avl_tree_unlock(source->pending_tree); /* update the stats if need be */ if (source->listeners != source->prev_listeners) { source->prev_listeners = source->listeners; INFO2("listener count on %s now %lu", source->mount, source->listeners); if (source->listeners > source->peak_listeners) { source->peak_listeners = source->listeners; stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners); } stats_event_args (source->mount, "listeners", "%lu", source->listeners); if (source->listeners == 0 && source->on_demand) source->running = 0; } /* lets reduce the queue, any lagging clients should of been * terminated by now */ if (source->stream_data) { /* normal unreferenced queue data will have a refcount 1, but * burst queue data will be at least 2, active clients will also * increase refcount */ while (source->stream_data->_count == 1) { refbuf_t *to_go = source->stream_data; if (to_go->next == NULL || source->burst_point == to_go) { /* this should not happen */ ERROR0 ("queue state is unexpected"); source->running = 0; break; } source->stream_data = to_go->next; source->queue_size -= to_go->len; to_go->next = NULL; refbuf_release (to_go); } } /* release write lock on client_tree */ avl_tree_unlock(source->client_tree); } source_shutdown (source); }