summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/output/HttpdClient.cxx4
-rw-r--r--src/output/HttpdInternal.hxx86
-rw-r--r--src/output/HttpdOutputPlugin.cxx291
3 files changed, 210 insertions, 171 deletions
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx
index 6b1b9c50a..0d0e3c949 100644
--- a/src/output/HttpdClient.cxx
+++ b/src/output/HttpdClient.cxx
@@ -55,7 +55,7 @@ HttpdClient::~HttpdClient()
void
HttpdClient::Close()
{
- httpd_output_remove_client(httpd, this);
+ httpd->RemoveClient(*this);
}
void
@@ -74,7 +74,7 @@ HttpdClient::BeginResponse()
write_source_id = 0;
current_page = nullptr;
- httpd_output_send_header(httpd, this);
+ httpd->SendHeader(*this);
}
/**
diff --git a/src/output/HttpdInternal.hxx b/src/output/HttpdInternal.hxx
index e948afa1b..1c2fdfb16 100644
--- a/src/output/HttpdInternal.hxx
+++ b/src/output/HttpdInternal.hxx
@@ -122,21 +122,79 @@ struct HttpdOutput {
* at the same time.
*/
guint clients_max, clients_cnt;
-};
-/**
- * Removes a client from the httpd_output.clients linked list.
- */
-void
-httpd_output_remove_client(struct HttpdOutput *httpd,
- HttpdClient *client);
+ bool Bind(GError **error_r);
+ void Unbind();
-/**
- * Sends the encoder header to the client. This is called right after
- * the response headers have been sent.
- */
-void
-httpd_output_send_header(struct HttpdOutput *httpd,
- HttpdClient *client);
+ /**
+ * Caller must lock the mutex.
+ */
+ bool OpenEncoder(struct audio_format *audio_format,
+ GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool Open(struct audio_format *audio_format, GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ void Close();
+
+ /**
+ * Check whether there is at least one client.
+ *
+ * Caller must lock the mutex.
+ */
+ gcc_pure
+ bool HasClients() const {
+ return !clients.empty();
+ }
+
+ /**
+ * Check whether there is at least one client.
+ */
+ gcc_pure
+ bool LockHasClients() const {
+ const ScopeLock protect(mutex);
+ return HasClients();
+ }
+
+ void AddClient(int fd);
+
+ /**
+ * Removes a client from the httpd_output.clients linked list.
+ */
+ void RemoveClient(HttpdClient &client);
+
+ /**
+ * Sends the encoder header to the client. This is called
+ * right after the response headers have been sent.
+ */
+ void SendHeader(HttpdClient &client) const;
+
+ /**
+ * Reads data from the encoder (as much as available) and
+ * returns it as a new #page object.
+ */
+ page *ReadPage();
+
+ /**
+ * Broadcasts a page struct to all clients.
+ *
+ * Mutext must not be locked.
+ */
+ void BroadcastPage(struct page *page);
+
+ /**
+ * Broadcasts data from the encoder to all clients.
+ */
+ void BroadcastFromEncoder();
+
+ bool EncodeAndPlay(const void *chunk, size_t size, GError **error_r);
+
+ void SendTag(const struct tag *tag);
+};
#endif
diff --git a/src/output/HttpdOutputPlugin.cxx b/src/output/HttpdOutputPlugin.cxx
index 985a10b7a..8d46a0228 100644
--- a/src/output/HttpdOutputPlugin.cxx
+++ b/src/output/HttpdOutputPlugin.cxx
@@ -54,49 +54,26 @@ httpd_output_quark(void)
return g_quark_from_static_string("httpd_output");
}
-/**
- * Check whether there is at least one client.
- *
- * Caller must lock the mutex.
- */
-G_GNUC_PURE
-static bool
-httpd_output_has_clients(const HttpdOutput *httpd)
-{
- return !httpd->clients.empty();
-}
-
-/**
- * Check whether there is at least one client.
- */
-G_GNUC_PURE
-static bool
-httpd_output_lock_has_clients(const HttpdOutput *httpd)
-{
- const ScopeLock protect(httpd->mutex);
- return httpd_output_has_clients(httpd);
-}
-
static void
httpd_listen_in_event(int fd, const struct sockaddr *address,
size_t address_length, int uid, void *ctx);
-static bool
-httpd_output_bind(HttpdOutput *httpd, GError **error_r)
+inline bool
+HttpdOutput::Bind(GError **error_r)
{
- httpd->open = false;
+ open = false;
- const ScopeLock protect(httpd->mutex);
- return server_socket_open(httpd->server_socket, error_r);
+ const ScopeLock protect(mutex);
+ return server_socket_open(server_socket, error_r);
}
-static void
-httpd_output_unbind(HttpdOutput *httpd)
+inline void
+HttpdOutput::Unbind()
{
- assert(!httpd->open);
+ assert(!open);
- const ScopeLock protect(httpd->mutex);
- server_socket_close(httpd->server_socket);
+ const ScopeLock protect(mutex);
+ server_socket_close(server_socket);
}
static struct audio_output *
@@ -191,16 +168,16 @@ httpd_output_finish(struct audio_output *ao)
* Creates a new #HttpdClient object and adds it into the
* HttpdOutput.clients linked list.
*/
-static void
-httpd_client_add(HttpdOutput *httpd, int fd)
+inline void
+HttpdOutput::AddClient(int fd)
{
- httpd->clients.emplace_front(httpd, fd,
- httpd->encoder->plugin->tag == NULL);
- httpd->clients_cnt++;
+ clients.emplace_front(this, fd,
+ encoder->plugin->tag == NULL);
+ ++clients_cnt;
/* pass metadata to client */
- if (httpd->metadata)
- httpd->clients.front().PushMetaData(httpd->metadata);
+ if (metadata != nullptr)
+ clients.front().PushMetaData(metadata);
}
static void
@@ -245,7 +222,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
if (httpd->open &&
(httpd->clients_max == 0 ||
httpd->clients_cnt < httpd->clients_max))
- httpd_client_add(httpd, fd);
+ httpd->AddClient(fd);
else
close_socket(fd);
} else if (fd < 0 && errno != EINTR) {
@@ -253,72 +230,87 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
}
}
-/**
- * Reads data from the encoder (as much as available) and returns it
- * as a new #page object.
- */
-static struct page *
-httpd_output_read_page(HttpdOutput *httpd)
+struct page *
+HttpdOutput::ReadPage()
{
- if (httpd->unflushed_input >= 65536) {
+ if (unflushed_input >= 65536) {
/* we have fed a lot of input into the encoder, but it
didn't give anything back yet - flush now to avoid
buffer underruns */
- encoder_flush(httpd->encoder, NULL);
- httpd->unflushed_input = 0;
+ encoder_flush(encoder, NULL);
+ unflushed_input = 0;
}
size_t size = 0;
do {
- size_t nbytes = encoder_read(httpd->encoder,
- httpd->buffer + size,
- sizeof(httpd->buffer) - size);
+ size_t nbytes = encoder_read(encoder,
+ buffer + size,
+ sizeof(buffer) - size);
if (nbytes == 0)
break;
- httpd->unflushed_input = 0;
+ unflushed_input = 0;
size += nbytes;
- } while (size < sizeof(httpd->buffer));
+ } while (size < sizeof(buffer));
if (size == 0)
return NULL;
- return page_new_copy(httpd->buffer, size);
+ return page_new_copy(buffer, size);
}
static bool
-httpd_output_encoder_open(HttpdOutput *httpd,
- struct audio_format *audio_format,
- GError **error)
+httpd_output_enable(struct audio_output *ao, GError **error_r)
{
- if (!encoder_open(httpd->encoder, audio_format, error))
+ HttpdOutput *httpd = (HttpdOutput *)ao;
+
+ return httpd->Bind(error_r);
+}
+
+static void
+httpd_output_disable(struct audio_output *ao)
+{
+ HttpdOutput *httpd = (HttpdOutput *)ao;
+
+ httpd->Unbind();
+}
+
+inline bool
+HttpdOutput::OpenEncoder(struct audio_format *audio_format, GError **error)
+{
+ if (!encoder_open(encoder, audio_format, error))
return false;
/* we have to remember the encoder header, i.e. the first
bytes of encoder output after opening it, because it has to
be sent to every new client */
- httpd->header = httpd_output_read_page(httpd);
+ header = ReadPage();
- httpd->unflushed_input = 0;
+ unflushed_input = 0;
return true;
}
-static bool
-httpd_output_enable(struct audio_output *ao, GError **error_r)
+inline bool
+HttpdOutput::Open(struct audio_format *audio_format, GError **error_r)
{
- HttpdOutput *httpd = (HttpdOutput *)ao;
+ assert(!open);
+ assert(clients.empty());
- return httpd_output_bind(httpd, error_r);
-}
+ /* open the encoder */
-static void
-httpd_output_disable(struct audio_output *ao)
-{
- HttpdOutput *httpd = (HttpdOutput *)ao;
+ if (!OpenEncoder(audio_format, error_r))
+ return false;
+
+ /* initialize other attributes */
+
+ clients_cnt = 0;
+ timer = timer_new(audio_format);
- httpd_output_unbind(httpd);
+ open = true;
+
+ return true;
}
static bool
@@ -330,20 +322,24 @@ httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
assert(httpd->clients.empty());
const ScopeLock protect(httpd->mutex);
+ return httpd->Open(audio_format, error);
+}
- /* open the encoder */
+inline void
+HttpdOutput::Close()
+{
+ assert(open);
- if (!httpd_output_encoder_open(httpd, audio_format, error))
- return false;
+ open = false;
- /* initialize other attributes */
+ timer_free(timer);
- httpd->clients_cnt = 0;
- httpd->timer = timer_new(audio_format);
+ clients.clear();
- httpd->open = true;
+ if (header != NULL)
+ page_unref(header);
- return true;
+ encoder_close(encoder);
}
static void
@@ -352,42 +348,30 @@ httpd_output_close(struct audio_output *ao)
HttpdOutput *httpd = (HttpdOutput *)ao;
const ScopeLock protect(httpd->mutex);
-
- httpd->open = false;
-
- timer_free(httpd->timer);
-
- httpd->clients.clear();
-
- if (httpd->header != NULL)
- page_unref(httpd->header);
-
- encoder_close(httpd->encoder);
+ httpd->Close();
}
void
-httpd_output_remove_client(HttpdOutput *httpd, HttpdClient *client)
+HttpdOutput::RemoveClient(HttpdClient &client)
{
- assert(httpd != NULL);
- assert(httpd->clients_cnt > 0);
- assert(client != NULL);
+ assert(clients_cnt > 0);
- for (auto prev = httpd->clients.before_begin(), i = std::next(prev);;
+ for (auto prev = clients.before_begin(), i = std::next(prev);;
prev = i, i = std::next(prev)) {
- assert(i != httpd->clients.end());
- if (&*i == client) {
- httpd->clients.erase_after(prev);
- httpd->clients_cnt--;
+ assert(i != clients.end());
+ if (&*i == &client) {
+ clients.erase_after(prev);
+ clients_cnt--;
break;
}
}
}
void
-httpd_output_send_header(HttpdOutput *httpd, HttpdClient *client)
+HttpdOutput::SendHeader(HttpdClient &client) const
{
- if (httpd->header != NULL)
- client->PushPage(httpd->header);
+ if (header != NULL)
+ client.PushPage(header);
}
static unsigned
@@ -395,7 +379,7 @@ httpd_output_delay(struct audio_output *ao)
{
HttpdOutput *httpd = (HttpdOutput *)ao;
- if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
+ if (!httpd->LockHasClients() && httpd->base.pause) {
/* if there's no client and this output is paused,
then httpd_output_pause() will not do anything, it
will not fill the buffer and it will not update the
@@ -413,52 +397,44 @@ httpd_output_delay(struct audio_output *ao)
: 0;
}
-/**
- * Broadcasts a page struct to all clients.
- */
-static void
-httpd_output_broadcast_page(HttpdOutput *httpd, struct page *page)
+void
+HttpdOutput::BroadcastPage(struct page *page)
{
assert(page != NULL);
- const ScopeLock protect(httpd->mutex);
- for (auto &client : httpd->clients)
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
client.PushPage(page);
}
-/**
- * Broadcasts data from the encoder to all clients.
- */
-static void
-httpd_output_encoder_to_clients(HttpdOutput *httpd)
+void
+HttpdOutput::BroadcastFromEncoder()
{
- httpd->mutex.lock();
- for (auto &client : httpd->clients) {
+ mutex.lock();
+ for (auto &client : clients) {
if (client.GetQueueSize() > 256 * 1024) {
g_debug("client is too slow, flushing its queue");
client.CancelQueue();
}
}
- httpd->mutex.unlock();
+ mutex.unlock();
struct page *page;
- while ((page = httpd_output_read_page(httpd)) != NULL) {
- httpd_output_broadcast_page(httpd, page);
+ while ((page = ReadPage()) != nullptr) {
+ BroadcastPage(page);
page_unref(page);
}
}
-static bool
-httpd_output_encode_and_play(HttpdOutput *httpd,
- const void *chunk, size_t size, GError **error)
+inline bool
+HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, GError **error_r)
{
- if (!encoder_write(httpd->encoder, chunk, size, error))
+ if (!encoder_write(encoder, chunk, size, error_r))
return false;
- httpd->unflushed_input += size;
-
- httpd_output_encoder_to_clients(httpd);
+ unflushed_input += size;
+ BroadcastFromEncoder();
return true;
}
@@ -468,8 +444,8 @@ httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
{
HttpdOutput *httpd = (HttpdOutput *)ao;
- if (httpd_output_lock_has_clients(httpd)) {
- if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
+ if (httpd->LockHasClients()) {
+ if (!httpd->EncodeAndPlay(chunk, size, error_r))
return 0;
}
@@ -485,7 +461,7 @@ httpd_output_pause(struct audio_output *ao)
{
HttpdOutput *httpd = (HttpdOutput *)ao;
- if (httpd_output_lock_has_clients(httpd)) {
+ if (httpd->LockHasClients()) {
static const char silence[1020] = { 0 };
return httpd_output_play(ao, silence, sizeof(silence),
NULL) > 0;
@@ -494,56 +470,61 @@ httpd_output_pause(struct audio_output *ao)
}
}
-static void
-httpd_output_tag(struct audio_output *ao, const struct tag *tag)
+inline void
+HttpdOutput::SendTag(const struct tag *tag)
{
- HttpdOutput *httpd = (HttpdOutput *)ao;
-
assert(tag != NULL);
- if (httpd->encoder->plugin->tag != NULL) {
+ if (encoder->plugin->tag != NULL) {
/* embed encoder tags */
/* flush the current stream, and end it */
- encoder_pre_tag(httpd->encoder, NULL);
- httpd_output_encoder_to_clients(httpd);
+ encoder_pre_tag(encoder, NULL);
+ BroadcastFromEncoder();
/* send the tag to the encoder - which starts a new
stream now */
- encoder_tag(httpd->encoder, tag, NULL);
+ encoder_tag(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 */
- struct page *page = httpd_output_read_page(httpd);
+ struct page *page = ReadPage();
if (page != NULL) {
- if (httpd->header != NULL)
- page_unref(httpd->header);
- httpd->header = page;
- httpd_output_broadcast_page(httpd, page);
+ if (header != NULL)
+ page_unref(header);
+ header = page;
+ BroadcastPage(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) {
- const ScopeLock protect(httpd->mutex);
- for (auto &client : httpd->clients)
- client.PushMetaData(httpd->metadata);
+ if (metadata != NULL)
+ page_unref(metadata);
+
+ metadata = icy_server_metadata_page(tag, TAG_ALBUM,
+ TAG_ARTIST, TAG_TITLE,
+ TAG_NUM_OF_ITEM_TYPES);
+ if (metadata != NULL) {
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushMetaData(metadata);
}
}
}
static void
+httpd_output_tag(struct audio_output *ao, const struct tag *tag)
+{
+ HttpdOutput *httpd = (HttpdOutput *)ao;
+
+ httpd->SendTag(tag);
+}
+
+static void
httpd_output_cancel(struct audio_output *ao)
{
HttpdOutput *httpd = (HttpdOutput *)ao;