diff options
Diffstat (limited to '')
-rw-r--r-- | src/InputInit.cxx (renamed from src/input_init.c) | 6 | ||||
-rw-r--r-- | src/InputInit.hxx (renamed from src/input_init.h) | 11 | ||||
-rw-r--r-- | src/InputRegistry.cxx (renamed from src/input_registry.c) | 8 | ||||
-rw-r--r-- | src/InputRegistry.hxx (renamed from src/input_registry.h) | 8 | ||||
-rw-r--r-- | src/InputStream.cxx (renamed from src/input_stream.c) | 14 | ||||
-rw-r--r-- | src/input/CurlInputPlugin.cxx (renamed from src/input/curl_input_plugin.c) | 556 | ||||
-rw-r--r-- | src/input/CurlInputPlugin.hxx (renamed from src/input/curl_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/SoupInputPlugin.cxx (renamed from src/input/soup_input_plugin.c) | 58 | ||||
-rw-r--r-- | src/input/SoupInputPlugin.hxx (renamed from src/input/soup_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/file_input_plugin.c | 17 | ||||
-rw-r--r-- | src/input_stream.h | 15 |
11 files changed, 314 insertions, 391 deletions
diff --git a/src/input_init.c b/src/InputInit.cxx index 771d648d1..6714cc727 100644 --- a/src/input_init.c +++ b/src/InputInit.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,9 +18,9 @@ */ #include "config.h" -#include "input_init.h" +#include "InputInit.hxx" +#include "InputRegistry.hxx" #include "input_plugin.h" -#include "input_registry.h" #include "conf.h" #include <assert.h> diff --git a/src/input_init.h b/src/InputInit.hxx index ad92cda08..9d503e5a8 100644 --- a/src/input_init.h +++ b/src/InputInit.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,13 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_INPUT_INIT_H -#define MPD_INPUT_INIT_H +#ifndef MPD_INPUT_INIT_HXX +#define MPD_INPUT_INIT_HXX -#include "check.h" - -#include <glib.h> -#include <stdbool.h> +#include "gerror.h" /** * Initializes this library and all input_stream implementations. diff --git a/src/input_registry.c b/src/InputRegistry.cxx index 5987d5da2..3dd825915 100644 --- a/src/input_registry.c +++ b/src/InputRegistry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,7 +18,7 @@ */ #include "config.h" -#include "input_registry.h" +#include "InputRegistry.hxx" #include "input/file_input_plugin.h" #ifdef ENABLE_ARCHIVE @@ -26,11 +26,11 @@ #endif #ifdef ENABLE_CURL -#include "input/curl_input_plugin.h" +#include "input/CurlInputPlugin.hxx" #endif #ifdef ENABLE_SOUP -#include "input/soup_input_plugin.h" +#include "input/SoupInputPlugin.hxx" #endif #ifdef HAVE_FFMPEG diff --git a/src/input_registry.h b/src/InputRegistry.hxx index 4f5fff8da..a080d108b 100644 --- a/src/input_registry.h +++ b/src/InputRegistry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,13 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_INPUT_REGISTRY_H -#define MPD_INPUT_REGISTRY_H +#ifndef MPD_INPUT_REGISTRY_HXX +#define MPD_INPUT_REGISTRY_HXX #include "check.h" -#include <stdbool.h> - /** * NULL terminated list of all input plugins which were enabled at * compile time. diff --git a/src/input_stream.c b/src/InputStream.cxx index e445dca6c..64f347555 100644 --- a/src/input_stream.c +++ b/src/InputStream.cxx @@ -19,9 +19,13 @@ #include "config.h" #include "input_stream.h" -#include "input_registry.h" +#include "InputRegistry.hxx" #include "input_plugin.h" + +extern "C" { #include "input/rewind_input_plugin.h" +#include "uri.h" +} #include <glib.h> #include <assert.h> @@ -115,6 +119,12 @@ input_stream_lock_wait_ready(struct input_stream *is) } bool +input_stream_cheap_seeking(const struct input_stream *is) +{ + return is->seekable && (is->uri == NULL || !uri_has_scheme(is->uri)); +} + +bool input_stream_seek(struct input_stream *is, goffset offset, int whence, GError **error_r) { @@ -165,7 +175,7 @@ input_stream_lock_tag(struct input_stream *is) assert(is->plugin != NULL); if (is->plugin->tag == NULL) - return false; + return nullptr; if (is->mutex == NULL) /* no locking */ diff --git a/src/input/curl_input_plugin.c b/src/input/CurlInputPlugin.cxx index 3f191141e..29d8266ce 100644 --- a/src/input/curl_input_plugin.c +++ b/src/input/CurlInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,13 +18,19 @@ */ #include "config.h" -#include "input/curl_input_plugin.h" -#include "input_internal.h" +#include "CurlInputPlugin.hxx" #include "input_plugin.h" #include "conf.h" #include "tag.h" -#include "icy_metadata.h" -#include "io_thread.h" +#include "IcyMetaDataParser.hxx" +#include "event/MultiSocketMonitor.hxx" + +extern "C" { +#include "input_internal.h" +} + +#include "event/Loop.hxx" +#include "IOThread.hxx" #include "glib_compat.h" #include <assert.h> @@ -38,9 +44,16 @@ #include <string.h> #include <errno.h> +#include <list> +#include <forward_list> + #include <curl/curl.h> #include <glib.h> +#if LIBCURL_VERSION_NUM < 0x071200 +#error libcurl is too old +#endif + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_curl" @@ -59,7 +72,7 @@ static const size_t CURL_RESUME_AT = 384 * 1024; /** * Buffers created by input_curl_writefunction(). */ -struct buffer { +class CurlInputBuffer { /** size of the payload */ size_t size; @@ -67,7 +80,55 @@ struct buffer { size_t consumed; /** the payload */ - unsigned char data[sizeof(long)]; + uint8_t *data; + +public: + CurlInputBuffer(const void *_data, size_t _size) + :size(_size), consumed(0), data(new uint8_t[size]) { + memcpy(data, _data, size); + } + + ~CurlInputBuffer() { + delete[] data; + } + + CurlInputBuffer(const CurlInputBuffer &) = delete; + CurlInputBuffer &operator=(const CurlInputBuffer &) = delete; + + const void *Begin() const { + return data + consumed; + } + + size_t TotalSize() const { + return size; + } + + size_t Available() const { + return size - consumed; + } + + /** + * Mark a part of the buffer as consumed. + * + * @return false if the buffer is now empty + */ + bool Consume(size_t length) { + assert(consumed < size); + + consumed += length; + if (consumed < size) + return true; + + assert(consumed == size); + return false; + } + + bool Read(void *dest, size_t length) { + assert(consumed + length <= size); + + memcpy(dest, data + consumed, length); + return Consume(length); + } }; struct input_curl { @@ -75,40 +136,28 @@ struct input_curl { /* some buffers which were passed to libcurl, which we have too free */ - char *url, *range; + char *range; struct curl_slist *request_headers; /** the curl handles */ CURL *easy; - /** the GMainLoop source used to poll all CURL file - descriptors */ - GSource *source; - - /** the source id of #source */ - guint source_id; - - /** a linked list of all registered GPollFD objects */ - GSList *fds; - /** list of buffers, where input_curl_writefunction() appends to, and input_curl_read() reads from them */ - GQueue *buffers; + std::list<CurlInputBuffer> buffers; -#if LIBCURL_VERSION_NUM >= 0x071200 /** * Is the connection currently paused? That happens when the * buffer was getting too large. It will be unpaused when the * buffer is below the threshold again. */ bool paused; -#endif /** error message provided by libcurl */ char error[CURL_ERROR_SIZE]; /** parser for icy-metadata */ - struct icy_metadata icy_metadata; + IcyMetaDataParser icy; /** the stream name from the icy-name response header */ char *meta_name; @@ -118,6 +167,50 @@ struct input_curl { struct tag *tag; GError *postponed_error; + + input_curl(const char *url, GMutex *mutex, GCond *cond) + :range(nullptr), request_headers(nullptr), + paused(false), + meta_name(nullptr), + tag(nullptr), + postponed_error(nullptr) { + input_stream_init(&base, &input_plugin_curl, url, mutex, cond); + } + + ~input_curl(); + + input_curl(const input_curl &) = delete; + input_curl &operator=(const input_curl &) = delete; +}; + +/** + * This class monitors all CURL file descriptors. + */ +class CurlSockets final : private MultiSocketMonitor { + /** + * Did CURL give us a timeout? If yes, then we need to call + * curl_multi_perform(), even if there was no event on any + * file descriptor. + */ + bool have_timeout; + + /** + * The absolute time stamp when the timeout expires. + */ + gint64 absolute_timeout; + +public: + CurlSockets(EventLoop &_loop) + :MultiSocketMonitor(_loop) {} + + using MultiSocketMonitor::InvalidateSockets; + +private: + void UpdateSockets(); + + virtual void PrepareSockets(gcc_unused gint *timeout_r) override; + virtual bool CheckSockets() const override; + virtual void DispatchSockets() override; }; /** libcurl should accept "ICY 200 OK" */ @@ -134,35 +227,9 @@ static struct { * A linked list of all active HTTP requests. An active * request is one that doesn't have the "eof" flag set. */ - GSList *requests; - - /** - * The GMainLoop source used to poll all CURL file - * descriptors. - */ - GSource *source; - - /** - * The source id of #source. - */ - guint source_id; - - GSList *fds; - -#if LIBCURL_VERSION_NUM >= 0x070f04 - /** - * Did CURL give us a timeout? If yes, then we need to call - * curl_multi_perform(), even if there was no event on any - * file descriptor. - */ - bool timeout; + std::forward_list<input_curl *> requests; - /** - * The absolute time stamp when the timeout expires. This is - * used in the GSource method check(). - */ - gint64 absolute_timeout; -#endif + CurlSockets *sockets; } curl; static inline GQuark @@ -181,23 +248,19 @@ input_curl_find_request(CURL *easy) { assert(io_thread_inside()); - for (GSList *i = curl.requests; i != NULL; i = g_slist_next(i)) { - struct input_curl *c = i->data; + for (auto c : curl.requests) if (c->easy == easy) return c; - } return NULL; } -#if LIBCURL_VERSION_NUM >= 0x071200 - static gpointer input_curl_resume(gpointer data) { assert(io_thread_inside()); - struct input_curl *c = data; + struct input_curl *c = (struct input_curl *)data; if (c->paused) { c->paused = false; @@ -207,13 +270,11 @@ input_curl_resume(gpointer data) return NULL; } -#endif - /** * Calculates the GLib event bit mask for one file descriptor, * obtained from three #fd_set objects filled by curl_multi_fdset(). */ -static gushort +static unsigned input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds) { gushort events = 0; @@ -242,8 +303,8 @@ input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds) * * Runs in the I/O thread. No lock needed. */ -static void -curl_update_fds(void) +void +CurlSockets::UpdateSockets() { assert(io_thread_inside()); @@ -262,42 +323,15 @@ curl_update_fds(void) return; } - GSList *fds = curl.fds; - curl.fds = NULL; - - while (fds != NULL) { - GPollFD *poll_fd = fds->data; - gushort events = input_curl_fd_events(poll_fd->fd, &rfds, - &wfds, &efds); - - assert(poll_fd->events != 0); - - fds = g_slist_remove(fds, poll_fd); - - if (events != poll_fd->events) - g_source_remove_poll(curl.source, poll_fd); - - if (events != 0) { - if (events != poll_fd->events) { - poll_fd->events = events; - g_source_add_poll(curl.source, poll_fd); - } - - curl.fds = g_slist_prepend(curl.fds, poll_fd); - } else { - g_free(poll_fd); - } - } + UpdateSocketList([&rfds, &wfds, &efds](int fd){ + return input_curl_fd_events(fd, &rfds, + &wfds, &efds); + }); for (int fd = 0; fd <= max_fd; ++fd) { - gushort events = input_curl_fd_events(fd, &rfds, &wfds, &efds); - if (events != 0) { - GPollFD *poll_fd = g_new(GPollFD, 1); - poll_fd->fd = fd; - poll_fd->events = events; - g_source_add_poll(curl.source, poll_fd); - curl.fds = g_slist_prepend(curl.fds, poll_fd); - } + unsigned events = input_curl_fd_events(fd, &rfds, &wfds, &efds); + if (events != 0) + AddSocket(fd, events); } } @@ -312,7 +346,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r) assert(c->easy != NULL); assert(input_curl_find_request(c->easy) == NULL); - curl.requests = g_slist_prepend(curl.requests, c); + curl.requests.push_front(c); CURLMcode mcode = curl_multi_add_handle(curl.multi, c->easy); if (mcode != CURLM_OK) { @@ -322,7 +356,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r) return false; } - curl_update_fds(); + curl.sockets->InvalidateSockets(); return true; } @@ -335,7 +369,8 @@ struct easy_add_params { static gpointer input_curl_easy_add_callback(gpointer data) { - const struct easy_add_params *params = data; + const struct easy_add_params *params = + (const struct easy_add_params *)data; bool success = input_curl_easy_add(params->c, params->error_r); return GUINT_TO_POINTER(success); @@ -352,8 +387,8 @@ input_curl_easy_add_indirect(struct input_curl *c, GError **error_r) assert(c->easy != NULL); struct easy_add_params params = { - .c = c, - .error_r = error_r, + c, + error_r, }; gpointer result = @@ -376,7 +411,7 @@ input_curl_easy_free(struct input_curl *c) if (c->easy == NULL) return; - curl.requests = g_slist_remove(curl.requests, c); + curl.requests.remove(c); curl_multi_remove_handle(curl.multi, c->easy); curl_easy_cleanup(c->easy); @@ -392,10 +427,10 @@ input_curl_easy_free(struct input_curl *c) static gpointer input_curl_easy_free_callback(gpointer data) { - struct input_curl *c = data; + struct input_curl *c = (struct input_curl *)data; input_curl_easy_free(c); - curl_update_fds(); + curl.sockets->InvalidateSockets(); return NULL; } @@ -424,8 +459,8 @@ input_curl_abort_all_requests(GError *error) assert(io_thread_inside()); assert(error != NULL); - while (curl.requests != NULL) { - struct input_curl *c = curl.requests->data; + while (!curl.requests.empty()) { + struct input_curl *c = curl.requests.front(); assert(c->postponed_error == NULL); input_curl_easy_free(c); @@ -532,28 +567,18 @@ input_curl_perform(void) return true; } -/* - * GSource methods - * - */ - -/** - * The GSource prepare() method implementation. - */ -static gboolean -input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r) +void +CurlSockets::PrepareSockets(gint *timeout_r) { - curl_update_fds(); + UpdateSockets(); -#if LIBCURL_VERSION_NUM >= 0x070f04 - curl.timeout = false; + have_timeout = false; long timeout2; CURLMcode mcode = curl_multi_timeout(curl.multi, &timeout2); if (mcode == CURLM_OK) { if (timeout2 >= 0) - curl.absolute_timeout = g_source_get_time(source) - + timeout2 * 1000; + absolute_timeout = GetTime() + timeout2 * 1000; if (timeout2 >= 0 && timeout2 < 10) /* CURL 7.21.1 likes to report "timeout=0", @@ -564,69 +589,28 @@ input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r) *timeout_r = timeout2; - curl.timeout = timeout2 >= 0; + have_timeout = timeout2 >= 0; } else g_warning("curl_multi_timeout() failed: %s\n", curl_multi_strerror(mcode)); -#else - (void)timeout_r; -#endif - - return false; } -/** - * The GSource check() method implementation. - */ -static gboolean -input_curl_source_check(G_GNUC_UNUSED GSource *source) +bool +CurlSockets::CheckSockets() const { -#if LIBCURL_VERSION_NUM >= 0x070f04 - if (curl.timeout) { - /* when a timeout has expired, we need to call - curl_multi_perform(), even if there was no file - descriptor event */ - - if (g_source_get_time(source) >= curl.absolute_timeout) - return true; - } -#endif - - for (GSList *i = curl.fds; i != NULL; i = i->next) { - GPollFD *poll_fd = i->data; - if (poll_fd->revents != 0) - return true; - } - - return false; + /* when a timeout has expired, we need to call + curl_multi_perform(), even if there was no file descriptor + event */ + return have_timeout && GetTime() >= absolute_timeout; } -/** - * The GSource dispatch() method implementation. The callback isn't - * used, because we're handling all events directly. - */ -static gboolean -input_curl_source_dispatch(G_GNUC_UNUSED GSource *source, - G_GNUC_UNUSED GSourceFunc callback, - G_GNUC_UNUSED gpointer user_data) +void +CurlSockets::DispatchSockets() { if (input_curl_perform()) input_curl_info_read(); - - return true; } -/** - * The vtable for our GSource implementation. Unfortunately, we - * cannot declare it "const", because g_source_new() takes a non-const - * pointer, for whatever reason. - */ -static GSourceFuncs curl_source_funcs = { - .prepare = input_curl_source_prepare, - .check = input_curl_source_check, - .dispatch = input_curl_source_dispatch, -}; - /* * input_plugin methods * @@ -668,8 +652,7 @@ input_curl_init(const struct config_param *param, return false; } - curl.source = g_source_new(&curl_source_funcs, sizeof(*curl.source)); - curl.source_id = g_source_attach(curl.source, io_thread_context()); + curl.sockets = new CurlSockets(io_thread_get()); return true; } @@ -677,7 +660,7 @@ input_curl_init(const struct config_param *param, static gpointer curl_destroy_sources(G_GNUC_UNUSED gpointer data) { - g_source_destroy(curl.source); + delete curl.sockets; return NULL; } @@ -685,7 +668,7 @@ curl_destroy_sources(G_GNUC_UNUSED gpointer data) static void input_curl_finish(void) { - assert(curl.requests == NULL); + assert(curl.requests.empty()); io_thread_call(curl_destroy_sources, NULL); @@ -696,8 +679,6 @@ input_curl_finish(void) curl_global_cleanup(); } -#if LIBCURL_VERSION_NUM >= 0x071200 - /** * Determine the total sizes of all buffers, including portions that * have already been consumed. @@ -710,55 +691,24 @@ curl_total_buffer_size(const struct input_curl *c) { size_t total = 0; - for (GList *i = g_queue_peek_head_link(c->buffers); - i != NULL; i = g_list_next(i)) { - struct buffer *buffer = i->data; - total += buffer->size; - } + for (const auto &i : c->buffers) + total += i.TotalSize(); return total; } -#endif - -static void -buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) +input_curl::~input_curl() { - struct buffer *buffer = data; + if (tag != NULL) + tag_free(tag); + g_free(meta_name); - assert(buffer->consumed <= buffer->size); + input_curl_easy_free_indirect(this); - g_free(buffer); -} + if (postponed_error != NULL) + g_error_free(postponed_error); -static void -input_curl_flush_buffers(struct input_curl *c) -{ - g_queue_foreach(c->buffers, buffer_free_callback, NULL); - g_queue_clear(c->buffers); -} - -/** - * Frees this stream, including the input_stream struct. - */ -static void -input_curl_free(struct input_curl *c) -{ - if (c->tag != NULL) - tag_free(c->tag); - g_free(c->meta_name); - - input_curl_easy_free_indirect(c); - input_curl_flush_buffers(c); - - g_queue_free(c->buffers); - - if (c->postponed_error != NULL) - g_error_free(c->postponed_error); - - g_free(c->url); - input_stream_deinit(&c->base); - g_free(c); + input_stream_deinit(&base); } static bool @@ -788,7 +738,7 @@ input_curl_tag(struct input_stream *is) static bool fill_buffer(struct input_curl *c, GError **error_r) { - while (c->easy != NULL && g_queue_is_empty(c->buffers)) + while (c->easy != NULL && c->buffers.empty()) g_cond_wait(c->base.cond, c->base.mutex); if (c->postponed_error != NULL) { @@ -797,86 +747,63 @@ fill_buffer(struct input_curl *c, GError **error_r) return false; } - return !g_queue_is_empty(c->buffers); -} - -/** - * Mark a part of the buffer object as consumed. - */ -static struct buffer * -consume_buffer(struct buffer *buffer, size_t length) -{ - assert(buffer != NULL); - assert(buffer->consumed < buffer->size); - - buffer->consumed += length; - if (buffer->consumed < buffer->size) - return buffer; - - assert(buffer->consumed == buffer->size); - - g_free(buffer); - - return NULL; + return !c->buffers.empty(); } static size_t -read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers, +read_from_buffer(IcyMetaDataParser &icy, std::list<CurlInputBuffer> &buffers, void *dest0, size_t length) { - struct buffer *buffer = g_queue_pop_head(buffers); - uint8_t *dest = dest0; + auto &buffer = buffers.front(); + uint8_t *dest = (uint8_t *)dest0; size_t nbytes = 0; - assert(buffer->size > 0); - assert(buffer->consumed < buffer->size); - - if (length > buffer->size - buffer->consumed) - length = buffer->size - buffer->consumed; + if (length > buffer.Available()) + length = buffer.Available(); while (true) { size_t chunk; - chunk = icy_data(icy_metadata, length); + chunk = icy.Data(length); if (chunk > 0) { - memcpy(dest, buffer->data + buffer->consumed, - chunk); - buffer = consume_buffer(buffer, chunk); + const bool empty = !buffer.Read(dest, chunk); nbytes += chunk; dest += chunk; length -= chunk; - if (length == 0) + if (empty) { + buffers.pop_front(); break; + } - assert(buffer != NULL); + if (length == 0) + break; } - chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed, - length); + chunk = icy.Meta(buffer.Begin(), length); if (chunk > 0) { - buffer = consume_buffer(buffer, chunk); + const bool empty = !buffer.Consume(chunk); length -= chunk; - if (length == 0) + if (empty) { + buffers.pop_front(); break; + } - assert(buffer != NULL); + if (length == 0) + break; } } - if (buffer != NULL) - g_queue_push_head(buffers, buffer); - return nbytes; } static void copy_icy_tag(struct input_curl *c) { - struct tag *tag = icy_tag(&c->icy_metadata); + struct tag *tag = c->icy.ReadTag(); if (tag == NULL) return; @@ -896,7 +823,7 @@ input_curl_available(struct input_stream *is) struct input_curl *c = (struct input_curl *)is; return c->postponed_error != NULL || c->easy == NULL || - !g_queue_is_empty(c->buffers); + !c->buffers.empty(); } static size_t @@ -906,7 +833,7 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size, struct input_curl *c = (struct input_curl *)is; bool success; size_t nbytes = 0; - char *dest = ptr; + char *dest = (char *)ptr; do { /* fill the buffer */ @@ -917,8 +844,8 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size, /* send buffer contents */ - while (size > 0 && !g_queue_is_empty(c->buffers)) { - size_t copy = read_from_buffer(&c->icy_metadata, c->buffers, + while (size > 0 && !c->buffers.empty()) { + size_t copy = read_from_buffer(c->icy, c->buffers, dest + nbytes, size); nbytes += copy; @@ -926,18 +853,16 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size, } } while (nbytes == 0); - if (icy_defined(&c->icy_metadata)) + if (c->icy.IsDefined()) copy_icy_tag(c); is->offset += (goffset)nbytes; -#if LIBCURL_VERSION_NUM >= 0x071200 if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) { g_mutex_unlock(c->base.mutex); io_thread_call(input_curl_resume, c); g_mutex_lock(c->base.mutex); } -#endif return nbytes; } @@ -947,7 +872,7 @@ input_curl_close(struct input_stream *is) { struct input_curl *c = (struct input_curl *)is; - input_curl_free(c); + delete c; } static bool @@ -955,7 +880,7 @@ input_curl_eof(G_GNUC_UNUSED struct input_stream *is) { struct input_curl *c = (struct input_curl *)is; - return c->easy == NULL && g_queue_is_empty(c->buffers); + return c->easy == NULL && c->buffers.empty(); } /** called by curl when new data is available */ @@ -963,13 +888,14 @@ static size_t input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) { struct input_curl *c = (struct input_curl *)stream; - const char *header = ptr, *end, *value; char name[64]; size *= nmemb; - end = header + size; - value = memchr(header, ':', size); + const char *header = (const char *)ptr; + const char *end = header + size; + + const char *value = (const char *)memchr(header, ':', size); if (value == NULL || (size_t)(value - header) >= sizeof(name)) return size; @@ -990,7 +916,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) if (g_ascii_strcasecmp(name, "accept-ranges") == 0) { /* a stream with icy-metadata is not seekable */ - if (!icy_defined(&c->icy_metadata)) + if (!c->icy.IsDefined()) c->base.seekable = true; } else if (g_ascii_strcasecmp(name, "content-length") == 0) { char buffer[64]; @@ -1021,7 +947,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) size_t icy_metaint; if ((size_t)(end - header) >= sizeof(buffer) || - icy_defined(&c->icy_metadata)) + c->icy.IsDefined()) return size; memcpy(buffer, value, end - value); @@ -1031,7 +957,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) g_debug("icy-metaint=%zu", icy_metaint); if (icy_metaint > 0) { - icy_start(&c->icy_metadata, icy_metaint); + c->icy.Start(icy_metaint); /* a stream with icy-metadata is not seekable */ @@ -1047,7 +973,6 @@ static size_t input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) { struct input_curl *c = (struct input_curl *)stream; - struct buffer *buffer; size *= nmemb; if (size == 0) @@ -1055,20 +980,13 @@ input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) g_mutex_lock(c->base.mutex); -#if LIBCURL_VERSION_NUM >= 0x071200 if (curl_total_buffer_size(c) + size >= CURL_MAX_BUFFERED) { c->paused = true; g_mutex_unlock(c->base.mutex); return CURL_WRITEFUNC_PAUSE; } -#endif - - buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size); - buffer->size = size; - buffer->consumed = 0; - memcpy(buffer->data, ptr, size); - g_queue_push_tail(c->buffers, buffer); + c->buffers.emplace_back(ptr, size); c->base.ready = true; g_cond_broadcast(c->base.cond); @@ -1120,7 +1038,7 @@ input_curl_easy_init(struct input_curl *c, GError **error_r) g_free(proxy_auth_str); } - code = curl_easy_setopt(c->easy, CURLOPT_URL, c->url); + code = curl_easy_setopt(c->easy, CURLOPT_URL, c->base.uri); if (code != CURLE_OK) { g_set_error(error_r, curl_quark(), code, "curl_easy_setopt() failed: %s", @@ -1179,19 +1097,15 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence, /* check if we can fast-forward the buffer */ - while (offset > is->offset && !g_queue_is_empty(c->buffers)) { - struct buffer *buffer; - size_t length; - - buffer = (struct buffer *)g_queue_pop_head(c->buffers); - - length = buffer->size - buffer->consumed; + while (offset > is->offset && !c->buffers.empty()) { + auto &buffer = c->buffers.front(); + size_t length = buffer.Available(); if (offset - is->offset < (goffset)length) length = offset - is->offset; - buffer = consume_buffer(buffer, length); - if (buffer != NULL) - g_queue_push_head(c->buffers, buffer); + const bool empty = !buffer.Consume(length); + if (empty) + c->buffers.pop_front(); is->offset += length; } @@ -1204,7 +1118,7 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence, g_mutex_unlock(c->base.mutex); input_curl_easy_free_indirect(c); - input_curl_flush_buffers(c); + c->buffers.clear(); is->offset = offset; if (is->offset == is->size) { @@ -1251,34 +1165,18 @@ input_curl_open(const char *url, GMutex *mutex, GCond *cond, assert(mutex != NULL); assert(cond != NULL); - struct input_curl *c; - if (strncmp(url, "http://", 7) != 0) return NULL; - c = g_new0(struct input_curl, 1); - input_stream_init(&c->base, &input_plugin_curl, url, - mutex, cond); - - c->url = g_strdup(url); - c->buffers = g_queue_new(); - - icy_clear(&c->icy_metadata); - c->tag = NULL; - - c->postponed_error = NULL; - -#if LIBCURL_VERSION_NUM >= 0x071200 - c->paused = false; -#endif + struct input_curl *c = new input_curl(url, mutex, cond); if (!input_curl_easy_init(c, error_r)) { - input_curl_free(c); + delete c; return NULL; } if (!input_curl_easy_add_indirect(c, error_r)) { - input_curl_free(c); + delete c; return NULL; } @@ -1286,16 +1184,16 @@ input_curl_open(const char *url, GMutex *mutex, GCond *cond, } const struct input_plugin input_plugin_curl = { - .name = "curl", - .init = input_curl_init, - .finish = input_curl_finish, - - .open = input_curl_open, - .close = input_curl_close, - .check = input_curl_check, - .tag = input_curl_tag, - .available = input_curl_available, - .read = input_curl_read, - .eof = input_curl_eof, - .seek = input_curl_seek, + "curl", + input_curl_init, + input_curl_finish, + input_curl_open, + input_curl_close, + input_curl_check, + nullptr, + input_curl_tag, + input_curl_available, + input_curl_read, + input_curl_eof, + input_curl_seek, }; diff --git a/src/input/curl_input_plugin.h b/src/input/CurlInputPlugin.hxx index c6e71bf40..20d1309d8 100644 --- a/src/input/curl_input_plugin.h +++ b/src/input/CurlInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_INPUT_CURL_H -#define MPD_INPUT_CURL_H +#ifndef MPD_INPUT_CURL_HXX +#define MPD_INPUT_CURL_HXX struct input_stream; diff --git a/src/input/soup_input_plugin.c b/src/input/SoupInputPlugin.cxx index fc903b48c..5a60fa725 100644 --- a/src/input/soup_input_plugin.c +++ b/src/input/SoupInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,14 +18,21 @@ */ #include "config.h" -#include "input/soup_input_plugin.h" -#include "input_internal.h" +#include "SoupInputPlugin.hxx" #include "input_plugin.h" -#include "io_thread.h" + +extern "C" { +#include "input_internal.h" +} + +#include "IOThread.hxx" +#include "event/Loop.hxx" #include "conf.h" +extern "C" { #include <libsoup/soup-uri.h> #include <libsoup/soup-session-async.h> +} #include <assert.h> #include <string.h> @@ -99,7 +106,7 @@ input_soup_init(const struct config_param *param, GError **error_r) soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI, soup_proxy, SOUP_SESSION_ASYNC_CONTEXT, - io_thread_context(), + io_thread_get().GetContext(), NULL); return true; @@ -156,7 +163,7 @@ static void input_soup_session_callback(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + struct input_soup *s = (struct input_soup *)user_data; assert(msg == s->msg); assert(!s->completed); @@ -177,7 +184,7 @@ input_soup_session_callback(G_GNUC_UNUSED SoupSession *session, static void input_soup_got_headers(SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + struct input_soup *s = (struct input_soup *)user_data; g_mutex_lock(s->base.mutex); @@ -199,7 +206,7 @@ input_soup_got_headers(SoupMessage *msg, gpointer user_data) static void input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) { - struct input_soup *s = user_data; + struct input_soup *s = (struct input_soup *)user_data; assert(msg == s->msg); @@ -220,7 +227,7 @@ input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) static void input_soup_got_body(G_GNUC_UNUSED SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + struct input_soup *s = (struct input_soup *)user_data; assert(msg == s->msg); @@ -256,7 +263,7 @@ input_soup_wait_data(struct input_soup *s) static gpointer input_soup_queue(gpointer data) { - struct input_soup *s = data; + struct input_soup *s = (struct input_soup *)data; soup_session_queue_message(soup_session, s->msg, input_soup_session_callback, s); @@ -320,7 +327,7 @@ input_soup_open(const char *uri, static gpointer input_soup_cancel(gpointer data) { - struct input_soup *s = data; + struct input_soup *s = (struct input_soup *)data; if (!s->completed) soup_session_cancel_message(soup_session, s->msg, @@ -352,7 +359,7 @@ input_soup_close(struct input_stream *is) g_mutex_unlock(s->base.mutex); SoupBuffer *buffer; - while ((buffer = g_queue_pop_head(s->buffers)) != NULL) + while ((buffer = (SoupBuffer *)g_queue_pop_head(s->buffers)) != NULL) soup_buffer_free(buffer); g_queue_free(s->buffers); @@ -403,10 +410,11 @@ input_soup_read(struct input_stream *is, void *ptr, size_t size, return 0; } - char *p0 = ptr, *p = p0, *p_end = p0 + size; + char *p0 = (char *)ptr, *p = p0, *p_end = p0 + size; while (p < p_end) { - SoupBuffer *buffer = g_queue_pop_head(s->buffers); + SoupBuffer *buffer = (SoupBuffer *) + g_queue_pop_head(s->buffers); if (buffer == NULL) { assert(s->current_consumed == 0); break; @@ -460,14 +468,16 @@ input_soup_eof(G_GNUC_UNUSED struct input_stream *is) } const struct input_plugin input_plugin_soup = { - .name = "soup", - .init = input_soup_init, - .finish = input_soup_finish, - - .open = input_soup_open, - .close = input_soup_close, - .check = input_soup_check, - .available = input_soup_available, - .read = input_soup_read, - .eof = input_soup_eof, + "soup", + input_soup_init, + input_soup_finish, + input_soup_open, + input_soup_close, + input_soup_check, + nullptr, + nullptr, + input_soup_available, + input_soup_read, + input_soup_eof, + nullptr, }; diff --git a/src/input/soup_input_plugin.h b/src/input/SoupInputPlugin.hxx index 689b2d971..4c089b39b 100644 --- a/src/input/soup_input_plugin.h +++ b/src/input/SoupInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_INPUT_SOUP_H -#define MPD_INPUT_SOUP_H +#ifndef MPD_INPUT_SOUP_HXX +#define MPD_INPUT_SOUP_HXX extern const struct input_plugin input_plugin_soup; diff --git a/src/input/file_input_plugin.c b/src/input/file_input_plugin.c index 5ee3f200b..e130230a7 100644 --- a/src/input/file_input_plugin.c +++ b/src/input/file_input_plugin.c @@ -23,6 +23,7 @@ #include "input_plugin.h" #include "fd_util.h" #include "open.h" +#include "io_error.h" #include <sys/stat.h> #include <unistd.h> @@ -39,12 +40,6 @@ struct file_input_stream { int fd; }; -static inline GQuark -file_quark(void) -{ - return g_quark_from_static_string("file"); -} - static struct input_stream * input_file_open(const char *filename, GMutex *mutex, GCond *cond, @@ -60,7 +55,7 @@ input_file_open(const char *filename, fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0); if (fd < 0) { if (errno != ENOENT && errno != ENOTDIR) - g_set_error(error_r, file_quark(), errno, + g_set_error(error_r, errno_quark(), errno, "Failed to open \"%s\": %s", filename, g_strerror(errno)); return NULL; @@ -68,7 +63,7 @@ input_file_open(const char *filename, ret = fstat(fd, &st); if (ret < 0) { - g_set_error(error_r, file_quark(), errno, + g_set_error(error_r, errno_quark(), errno, "Failed to stat \"%s\": %s", filename, g_strerror(errno)); close(fd); @@ -76,7 +71,7 @@ input_file_open(const char *filename, } if (!S_ISREG(st.st_mode)) { - g_set_error(error_r, file_quark(), 0, + g_set_error(error_r, errno_quark(), 0, "Not a regular file: %s", filename); close(fd); return NULL; @@ -107,7 +102,7 @@ input_file_seek(struct input_stream *is, goffset offset, int whence, offset = (goffset)lseek(fis->fd, (off_t)offset, whence); if (offset < 0) { - g_set_error(error_r, file_quark(), errno, + g_set_error(error_r, errno_quark(), errno, "Failed to seek: %s", g_strerror(errno)); return false; } @@ -125,7 +120,7 @@ input_file_read(struct input_stream *is, void *ptr, size_t size, nbytes = read(fis->fd, ptr, size); if (nbytes < 0) { - g_set_error(error_r, file_quark(), errno, + g_set_error(error_r, errno_quark(), errno, "Failed to read: %s", g_strerror(errno)); return 0; } diff --git a/src/input_stream.h b/src/input_stream.h index 10ad97161..d5b1dae2e 100644 --- a/src/input_stream.h +++ b/src/input_stream.h @@ -88,6 +88,10 @@ struct input_stream { char *mime; }; +#ifdef __cplusplus +extern "C" { +#endif + /** * Opens a new input stream. You may not access it until the "ready" * flag is set. @@ -164,6 +168,13 @@ void input_stream_lock_wait_ready(struct input_stream *is); /** + * Determines whether seeking is cheap. This is true for local files. + */ +gcc_pure gcc_nonnull(1) +bool +input_stream_cheap_seeking(const struct input_stream *is); + +/** * Seeks to the specified position in the stream. This will most * likely fail if the "seekable" flag is false. * @@ -264,4 +275,8 @@ size_t input_stream_lock_read(struct input_stream *is, void *ptr, size_t size, GError **error_r); +#ifdef __cplusplus +} +#endif + #endif |