static void httpd_output_tag(struct audio_output *ao, const struct tag *tag) { struct httpd_output *httpd = (struct httpd_output *)ao; assert(tag != NULL); if (httpd->encoder->plugin->tag != NULL) { /* embed encoder tags */ struct page *page; /* flush the current stream, and end it */ encoder_pre_tag(httpd->encoder, NULL); httpd_output_encoder_to_clients(httpd); /* send the tag to the encoder - which starts a new stream now */ encoder_tag(httpd->encoder, tag, NULL); /* the first page generated by the encoder will now be used as the new "header" page, which is sent to all new clients */ page = httpd_output_read_page(httpd); if (page != NULL) { if (httpd->header != NULL) page_unref(httpd->header); httpd->header = page; httpd_output_broadcast_page(httpd, page); } } else { /* use Icy-Metadata */ if (httpd->metadata != NULL) page_unref (httpd->metadata); httpd->metadata = icy_server_metadata_page(tag, TAG_ALBUM, TAG_ARTIST, TAG_TITLE, TAG_NUM_OF_ITEM_TYPES); if (httpd->metadata != NULL) { g_mutex_lock(httpd->mutex); g_list_foreach(httpd->clients, httpd_send_metadata, httpd->metadata); g_mutex_unlock(httpd->mutex); } } }
static void httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct page *page = data; page_unref(page); }
static bool httpd_output_encode_and_play(struct httpd_output *httpd, const void *chunk, size_t size, GError **error) { bool success; struct page *page; success = encoder_write(httpd->encoder, chunk, size, error); if (!success) return false; g_mutex_lock(httpd->mutex); g_list_foreach(httpd->clients, httpd_client_check_queue, NULL); g_mutex_unlock(httpd->mutex); while ((page = httpd_output_read_page(httpd)) != NULL) { g_mutex_lock(httpd->mutex); g_list_foreach(httpd->clients, httpd_client_send_page, page); g_mutex_unlock(httpd->mutex); page_unref(page); } return true; }
void httpd_client_send_metadata(struct httpd_client *client, struct page *page) { if (client->metadata) { page_unref(client->metadata); client->metadata = NULL; } g_return_if_fail (page); page_ref(page); client->metadata = page; client->metadata_sent = false; }
/** * Broadcasts data from the encoder to all clients. */ static void httpd_output_encoder_to_clients(struct httpd_output *httpd) { struct page *page; g_mutex_lock(httpd->mutex); g_list_foreach(httpd->clients, httpd_client_check_queue, NULL); g_mutex_unlock(httpd->mutex); while ((page = httpd_output_read_page(httpd)) != NULL) { httpd_output_broadcast_page(httpd, page); page_unref(page); } }
static void httpd_output_finish(struct audio_output *ao) { struct httpd_output *httpd = (struct httpd_output *)ao; if (httpd->metadata) page_unref(httpd->metadata); encoder_finish(httpd->encoder); server_socket_free(httpd->server_socket); g_mutex_free(httpd->mutex); ao_base_finish(&httpd->base); g_free(httpd); }
void httpd_client_free(struct httpd_client *client) { assert(client != NULL); if (client->state == RESPONSE) { if (client->write_source_id != 0) g_source_remove(client->write_source_id); if (client->current_page != NULL) page_unref(client->current_page); g_queue_foreach(client->pages, httpd_client_unref_page, NULL); g_queue_free(client->pages); } else fifo_buffer_free(client->input); if (client->metadata) page_unref (client->metadata); g_source_remove(client->read_source_id); g_io_channel_unref(client->channel); g_free(client); }
static void httpd_output_close(void *data) { struct httpd_output *httpd = data; g_mutex_lock(httpd->mutex); timer_free(httpd->timer); g_list_foreach(httpd->clients, httpd_client_delete, NULL); g_list_free(httpd->clients); if (httpd->header != NULL) page_unref(httpd->header); encoder_close(httpd->encoder); g_source_remove(httpd->source_id); close(httpd->fd); g_mutex_unlock(httpd->mutex); }
static void httpd_output_close(struct audio_output *ao) { struct httpd_output *httpd = (struct httpd_output *)ao; g_mutex_lock(httpd->mutex); httpd->open = false; timer_free(httpd->timer); g_list_foreach(httpd->clients, httpd_client_delete, NULL); g_list_free(httpd->clients); if (httpd->header != NULL) page_unref(httpd->header); encoder_close(httpd->encoder); g_mutex_unlock(httpd->mutex); }
static gboolean httpd_client_out_event(GIOChannel *source, G_GNUC_UNUSED GIOCondition condition, gpointer data) { struct httpd_client *client = data; struct httpd_output *httpd = client->httpd; GError *error = NULL; GIOStatus status; gsize bytes_written; gint bytes_to_write; g_mutex_lock(httpd->mutex); assert(condition == G_IO_OUT); assert(client->state == RESPONSE); if (client->write_source_id == 0) { /* another thread has removed the event source while this thread was waiting for httpd->mutex */ g_mutex_unlock(httpd->mutex); return false; } if (client->current_page == NULL) { client->current_page = g_queue_pop_head(client->pages); client->current_position = 0; } bytes_to_write = bytes_left_till_metadata(client); if (bytes_to_write == 0) { gint metadata_to_write; metadata_to_write = client->metadata_current_position; if (!client->metadata_sent) { status = write_page_to_channel(source, client->metadata, metadata_to_write, &bytes_written, &error); client->metadata_current_position += bytes_written; if (client->metadata->size - client->metadata_current_position == 0) { client->metadata_fill = 0; client->metadata_current_position = 0; client->metadata_sent = true; } } else { struct page *empty_meta; guchar empty_data = 0; empty_meta = page_new_copy(&empty_data, 1); status = write_page_to_channel(source, empty_meta, metadata_to_write, &bytes_written, &error); client->metadata_current_position += bytes_written; if (empty_meta->size - client->metadata_current_position == 0) { client->metadata_fill = 0; client->metadata_current_position = 0; } } bytes_written = 0; } else { status = write_n_bytes_to_channel(source, client->current_page, client->current_position, bytes_to_write, &bytes_written, &error); } switch (status) { case G_IO_STATUS_NORMAL: client->current_position += bytes_written; assert(client->current_position <= client->current_page->size); if (client->metadata_requested) client->metadata_fill += bytes_written; if (client->current_position >= client->current_page->size) { page_unref(client->current_page); client->current_page = NULL; if (g_queue_is_empty(client->pages)) { /* all pages are sent: remove the event source */ client->write_source_id = 0; g_mutex_unlock(httpd->mutex); return false; } } g_mutex_unlock(httpd->mutex); return true; case G_IO_STATUS_AGAIN: g_mutex_unlock(httpd->mutex); return true; case G_IO_STATUS_EOF: /* client has disconnected */ httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; case G_IO_STATUS_ERROR: /* I/O error */ g_warning("failed to write to client: %s", error->message); g_error_free(error); httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; } /* unreachable */ httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; }