diff options
Diffstat (limited to 'src/input')
-rw-r--r-- | src/input/ArchiveInputPlugin.cxx (renamed from src/input/archive_input_plugin.c) | 40 | ||||
-rw-r--r-- | src/input/ArchiveInputPlugin.hxx (renamed from src/input/archive_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/CdioParanoiaInputPlugin.cxx (renamed from src/input/cdio_paranoia_input_plugin.c) | 114 | ||||
-rw-r--r-- | src/input/CdioParanoiaInputPlugin.hxx (renamed from src/input/cdio_paranoia_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/CurlInputPlugin.cxx (renamed from src/input/curl_input_plugin.c) | 596 | ||||
-rw-r--r-- | src/input/CurlInputPlugin.hxx (renamed from src/input/curl_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/DespotifyInputPlugin.cxx (renamed from src/input/despotify_input_plugin.c) | 107 | ||||
-rw-r--r-- | src/input/DespotifyInputPlugin.hxx (renamed from src/input/despotify_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/FfmpegInputPlugin.cxx (renamed from src/input/ffmpeg_input_plugin.c) | 136 | ||||
-rw-r--r-- | src/input/FfmpegInputPlugin.hxx (renamed from src/input/ffmpeg_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/FileInputPlugin.cxx (renamed from src/input/file_input_plugin.c) | 92 | ||||
-rw-r--r-- | src/input/FileInputPlugin.hxx (renamed from src/input/file_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/MmsInputPlugin.cxx (renamed from src/input/mms_input_plugin.c) | 81 | ||||
-rw-r--r-- | src/input/MmsInputPlugin.hxx (renamed from src/input/mms_input_plugin.h) | 0 | ||||
-rw-r--r-- | src/input/RewindInputPlugin.cxx (renamed from src/input/rewind_input_plugin.c) | 148 | ||||
-rw-r--r-- | src/input/RewindInputPlugin.hxx (renamed from src/input/rewind_input_plugin.h) | 6 | ||||
-rw-r--r-- | src/input/SoupInputPlugin.cxx (renamed from src/input/soup_input_plugin.c) | 287 | ||||
-rw-r--r-- | src/input/SoupInputPlugin.hxx (renamed from src/input/soup_input_plugin.h) | 6 |
18 files changed, 789 insertions, 860 deletions
diff --git a/src/input/archive_input_plugin.c b/src/input/ArchiveInputPlugin.cxx index 4a038b9e2..0d856527f 100644 --- a/src/input/archive_input_plugin.c +++ b/src/input/ArchiveInputPlugin.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,10 +18,12 @@ */ #include "config.h" -#include "input/archive_input_plugin.h" -#include "archive_api.h" -#include "archive_list.h" -#include "input_plugin.h" +#include "ArchiveInputPlugin.hxx" +#include "ArchiveLookup.hxx" +#include "ArchiveList.hxx" +#include "ArchivePlugin.hxx" +#include "ArchiveFile.hxx" +#include "InputPlugin.hxx" #include <glib.h> @@ -35,11 +37,10 @@ */ static struct input_stream * input_archive_open(const char *pathname, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, GError **error_r) { const struct archive_plugin *arplug; - struct archive_file *file; char *archive, *filename, *suffix, *pname; struct input_stream *is; @@ -62,20 +63,31 @@ input_archive_open(const char *pathname, return NULL; } - file = archive_file_open(arplug, archive, error_r); - if (file == NULL) + auto file = archive_file_open(arplug, archive, error_r); + if (file == NULL) { + g_free(pname); return NULL; + } //setup fileops - is = archive_file_open_stream(file, filename, mutex, cond, - error_r); - archive_file_close(file); + is = file->OpenStream(filename, mutex, cond, error_r); g_free(pname); + file->Close(); return is; } const struct input_plugin input_plugin_archive = { - .name = "archive", - .open = input_archive_open, + "archive", + nullptr, + nullptr, + input_archive_open, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, }; diff --git a/src/input/archive_input_plugin.h b/src/input/ArchiveInputPlugin.hxx index 51095f37f..96fcd0dd1 100644 --- a/src/input/archive_input_plugin.h +++ b/src/input/ArchiveInputPlugin.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_ARCHIVE_H -#define MPD_INPUT_ARCHIVE_H +#ifndef MPD_INPUT_ARCHIVE_HXX +#define MPD_INPUT_ARCHIVE_HXX extern const struct input_plugin input_plugin_archive; diff --git a/src/input/cdio_paranoia_input_plugin.c b/src/input/CdioParanoiaInputPlugin.cxx index 1de7623a1..f0fa835b3 100644 --- a/src/input/cdio_paranoia_input_plugin.c +++ b/src/input/CdioParanoiaInputPlugin.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 @@ -22,10 +22,10 @@ */ #include "config.h" -#include "input/cdio_paranoia_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" -#include "refcount.h" +#include "CdioParanoiaInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include <stdio.h> #include <stdint.h> @@ -38,7 +38,7 @@ #include <cdio/paranoia.h> #include <cdio/cd_types.h> -struct input_cdio_paranoia { +struct CdioParanoiaInputStream { struct input_stream base; cdrom_drive_t *drv; @@ -52,6 +52,23 @@ struct input_cdio_paranoia { char buffer[CDIO_CD_FRAMESIZE_RAW]; int buffer_lsn; + + CdioParanoiaInputStream(const char *uri, Mutex &mutex, Cond &cond, + int _trackno) + :base(input_plugin_cdio_paranoia, uri, mutex, cond), + drv(nullptr), cdio(nullptr), para(nullptr), + trackno(_trackno) + { + } + + ~CdioParanoiaInputStream() { + if (para != nullptr) + cdio_paranoia_free(para); + if (drv != nullptr) + cdio_cddap_close_no_free_cdio(drv); + if (cdio != nullptr) + cdio_destroy(cdio); + } }; static inline GQuark @@ -63,17 +80,9 @@ cdio_quark(void) static void input_cdio_close(struct input_stream *is) { - struct input_cdio_paranoia *i = (struct input_cdio_paranoia *)is; + CdioParanoiaInputStream *i = (CdioParanoiaInputStream *)is; - if (i->para) - cdio_paranoia_free(i->para); - if (i->drv) - cdio_cddap_close_no_free_cdio( i->drv); - if (i->cdio) - cdio_destroy( i->cdio ); - - input_stream_deinit(&i->base); - g_free(i); + delete i; } struct cdio_uri { @@ -97,7 +106,7 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r) } const char *slash = strrchr(src, '/'); - if (slash == NULL) { + if (slash == nullptr) { /* play the whole CD in the specified drive */ g_strlcpy(dest->device, src, sizeof(dest->device)); dest->track = -1; @@ -131,9 +140,10 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r) static char * cdio_detect_device(void) { - char **devices = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); - if (devices == NULL) - return NULL; + char **devices = cdio_get_devices_with_cap(nullptr, CDIO_FS_AUDIO, + false); + if (devices == nullptr) + return nullptr; char *device = g_strdup(devices[0]); cdio_free_device_list(devices); @@ -143,55 +153,47 @@ cdio_detect_device(void) static struct input_stream * input_cdio_open(const char *uri, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, GError **error_r) { - struct input_cdio_paranoia *i; - struct cdio_uri parsed_uri; if (!parse_cdio_uri(&parsed_uri, uri, error_r)) - return NULL; - - i = g_new(struct input_cdio_paranoia, 1); - input_stream_init(&i->base, &input_plugin_cdio_paranoia, uri, - mutex, cond); + return nullptr; - /* initialize everything (should be already) */ - i->drv = NULL; - i->cdio = NULL; - i->para = NULL; - i->trackno = parsed_uri.track; + CdioParanoiaInputStream *i = + new CdioParanoiaInputStream(uri, mutex, cond, + parsed_uri.track); /* get list of CD's supporting CD-DA */ char *device = parsed_uri.device[0] != 0 ? g_strdup(parsed_uri.device) : cdio_detect_device(); - if (device == NULL) { + if (device == nullptr) { g_set_error(error_r, cdio_quark(), 0, "Unable find or access a CD-ROM drive with an audio CD in it."); - input_cdio_close(&i->base); - return NULL; + delete i; + return nullptr; } /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */ i->cdio = cdio_open(device, DRIVER_UNKNOWN); g_free(device); - i->drv = cdio_cddap_identify_cdio(i->cdio, 1, NULL); + i->drv = cdio_cddap_identify_cdio(i->cdio, 1, nullptr); if ( !i->drv ) { g_set_error(error_r, cdio_quark(), 0, "Unable to identify audio CD disc."); - input_cdio_close(&i->base); - return NULL; + delete i; + return nullptr; } cdda_verbose_set(i->drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT); if ( 0 != cdio_cddap_open(i->drv) ) { g_set_error(error_r, cdio_quark(), 0, "Unable to open disc."); - input_cdio_close(&i->base); - return NULL; + delete i; + return nullptr; } bool reverse_endian; @@ -212,8 +214,8 @@ input_cdio_open(const char *uri, g_set_error(error_r, cdio_quark(), 0, "Drive returns unknown data type %d", data_bigendianp(i->drv)); - input_cdio_close(&i->base); - return NULL; + delete i; + return nullptr; } i->lsn_relofs = 0; @@ -250,7 +252,7 @@ static bool input_cdio_seek(struct input_stream *is, goffset offset, int whence, GError **error_r) { - struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is; + CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is; /* calculate absolute offset */ switch (whence) { @@ -288,7 +290,7 @@ static size_t input_cdio_read(struct input_stream *is, void *ptr, size_t length, GError **error_r) { - struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is; + CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is; size_t nbytes = 0; int diff; size_t len, maxwrite; @@ -305,7 +307,7 @@ input_cdio_read(struct input_stream *is, void *ptr, size_t length, //current sector was changed ? if (cis->lsn_relofs != cis->buffer_lsn) { - rbuf = cdio_paranoia_read(cis->para, NULL); + rbuf = cdio_paranoia_read(cis->para, nullptr); s_err = cdda_errors(cis->drv); if (s_err) { @@ -356,16 +358,22 @@ input_cdio_read(struct input_stream *is, void *ptr, size_t length, static bool input_cdio_eof(struct input_stream *is) { - struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is; + CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is; return (cis->lsn_from + cis->lsn_relofs > cis->lsn_to); } const struct input_plugin input_plugin_cdio_paranoia = { - .name = "cdio_paranoia", - .open = input_cdio_open, - .close = input_cdio_close, - .seek = input_cdio_seek, - .read = input_cdio_read, - .eof = input_cdio_eof + "cdio_paranoia", + nullptr, + nullptr, + input_cdio_open, + input_cdio_close, + nullptr, + nullptr, + nullptr, + nullptr, + input_cdio_read, + input_cdio_eof, + input_cdio_seek, }; diff --git a/src/input/cdio_paranoia_input_plugin.h b/src/input/CdioParanoiaInputPlugin.hxx index 71c5cbe8d..80d98b4bf 100644 --- a/src/input/cdio_paranoia_input_plugin.h +++ b/src/input/CdioParanoiaInputPlugin.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_CDIO_PARANOIA_INPUT_PLUGIN_H -#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_H +#ifndef MPD_CDIO_PARANOIA_INPUT_PLUGIN_HXX +#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_HXX /** * An input plugin based on libcdio_paranoia library. diff --git a/src/input/curl_input_plugin.c b/src/input/CurlInputPlugin.cxx index 3f191141e..fe944b752 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,14 +18,16 @@ */ #include "config.h" -#include "input/curl_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "CurlInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include "conf.h" #include "tag.h" -#include "icy_metadata.h" -#include "io_thread.h" -#include "glib_compat.h" +#include "IcyMetaDataParser.hxx" +#include "event/MultiSocketMonitor.hxx" +#include "event/Loop.hxx" +#include "IOThread.hxx" #include <assert.h> @@ -38,9 +40,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 +68,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 +76,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 +132,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 +163,50 @@ struct input_curl { struct tag *tag; GError *postponed_error; + + input_curl(const char *url, Mutex &mutex, Cond &cond) + :base(input_plugin_curl, url, mutex, cond), + range(nullptr), request_headers(nullptr), + paused(false), + meta_name(nullptr), + tag(nullptr), + postponed_error(nullptr) { + } + + ~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 +223,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; + std::forward_list<input_curl *> requests; - /** - * 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; - - /** - * 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 +244,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 +266,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 +299,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 +319,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 +342,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 +352,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r) return false; } - curl_update_fds(); + curl.sockets->InvalidateSockets(); return true; } @@ -335,7 +365,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 +383,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 +407,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 +423,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,17 +455,18 @@ 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); - g_mutex_lock(c->base.mutex); + const ScopeLock protect(c->base.mutex); + c->postponed_error = g_error_copy(error); c->base.ready = true; - g_cond_broadcast(c->base.cond); - g_mutex_unlock(c->base.mutex); + + c->base.cond.broadcast(); } g_error_free(error); @@ -454,7 +486,7 @@ input_curl_request_done(struct input_curl *c, CURLcode result, long status) assert(c->easy == NULL); assert(c->postponed_error == NULL); - g_mutex_lock(c->base.mutex); + const ScopeLock protect(c->base.mutex); if (result != CURLE_OK) { c->postponed_error = g_error_new(curl_quark(), result, @@ -467,8 +499,8 @@ input_curl_request_done(struct input_curl *c, CURLcode result, long status) } c->base.ready = true; - g_cond_broadcast(c->base.cond); - g_mutex_unlock(c->base.mutex); + + c->base.cond.broadcast(); } static void @@ -532,28 +564,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 +586,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 +649,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 +657,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 +665,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 +676,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 +688,22 @@ 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); -} - -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); + if (postponed_error != NULL) + g_error_free(postponed_error); } static bool @@ -788,8 +733,8 @@ 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)) - g_cond_wait(c->base.cond, c->base.mutex); + while (c->easy != NULL && c->buffers.empty()) + c->base.cond.wait(c->base.mutex); if (c->postponed_error != NULL) { g_propagate_error(error_r, c->postponed_error); @@ -797,86 +742,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 +818,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 +828,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 +839,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 +848,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); + c->base.mutex.unlock(); io_thread_call(input_curl_resume, c); - g_mutex_lock(c->base.mutex); + c->base.mutex.lock(); } -#endif return nbytes; } @@ -947,7 +867,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 +875,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 +883,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 +911,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]; @@ -1003,8 +924,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) c->base.size = c->base.offset + g_ascii_strtoull(buffer, NULL, 10); } else if (g_ascii_strcasecmp(name, "content-type") == 0) { - g_free(c->base.mime); - c->base.mime = g_strndup(value, end - value); + c->base.mime.assign(value, end); } else if (g_ascii_strcasecmp(name, "icy-name") == 0 || g_ascii_strcasecmp(name, "ice-name") == 0 || g_ascii_strcasecmp(name, "x-audiocast-name") == 0) { @@ -1021,7 +941,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 +951,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,33 +967,22 @@ 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) return 0; - g_mutex_lock(c->base.mutex); + const ScopeLock protect(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); - g_mutex_unlock(c->base.mutex); - + c->base.cond.broadcast(); return size; } @@ -1120,7 +1029,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.c_str()); if (code != CURLE_OK) { g_set_error(error_r, curl_quark(), code, "curl_easy_setopt() failed: %s", @@ -1179,19 +1088,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; } @@ -1201,10 +1106,10 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence, /* close the old connection and open a new one */ - g_mutex_unlock(c->base.mutex); + c->base.mutex.unlock(); input_curl_easy_free_indirect(c); - input_curl_flush_buffers(c); + c->buffers.clear(); is->offset = offset; if (is->offset == is->size) { @@ -1230,10 +1135,10 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence, if (!input_curl_easy_add_indirect(c, error_r)) return false; - g_mutex_lock(c->base.mutex); + c->base.mutex.lock(); while (!c->base.ready) - g_cond_wait(c->base.cond, c->base.mutex); + c->base.cond.wait(c->base.mutex); if (c->postponed_error != NULL) { g_propagate_error(error_r, c->postponed_error); @@ -1245,40 +1150,21 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence, } static struct input_stream * -input_curl_open(const char *url, GMutex *mutex, GCond *cond, +input_curl_open(const char *url, Mutex &mutex, Cond &cond, GError **error_r) { - 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 +1172,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/despotify_input_plugin.c b/src/input/DespotifyInputPlugin.cxx index 200a0afd6..1e5a8c606 100644 --- a/src/input/despotify_input_plugin.c +++ b/src/input/DespotifyInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Music Player Daemon Project + * Copyright (C) 2011-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,22 +18,26 @@ */ #include "config.h" -#include "input/despotify_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "DespotifyInputPlugin.hxx" +#include "DespotifyUtils.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include "tag.h" -#include "despotify_utils.h" + +extern "C" { +#include <despotify.h> +} #include <glib.h> #include <unistd.h> #include <string.h> #include <errno.h> -#include <despotify.h> #include <stdio.h> -struct input_despotify { +struct DespotifyInputStream { struct input_stream base; struct despotify_session *session; @@ -42,11 +46,33 @@ struct input_despotify { struct ds_pcm_data pcm; size_t len_available; bool eof; -}; + DespotifyInputStream(const char *uri, + Mutex &mutex, Cond &cond, + despotify_session *_session, + ds_track *_track) + :base(input_plugin_despotify, uri, mutex, cond), + session(_session), track(_track), + tag(mpd_despotify_tag_from_track(track)), + len_available(0), eof(false) { + + memset(&pcm, 0, sizeof(pcm)); + + /* Despotify outputs pcm data */ + base.mime = g_strdup("audio/x-mpd-cdda-pcm"); + base.ready = true; + } + + ~DespotifyInputStream() { + if (tag != NULL) + tag_free(tag); + + despotify_free_track(track); + } +}; static void -refill_buffer(struct input_despotify *ctx) +refill_buffer(DespotifyInputStream *ctx) { /* Wait until there is data */ while (1) { @@ -73,7 +99,7 @@ refill_buffer(struct input_despotify *ctx) static void callback(G_GNUC_UNUSED struct despotify_session* ds, int sig, G_GNUC_UNUSED void* data, void* callback_data) { - struct input_despotify *ctx = (struct input_despotify *)callback_data; + DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data; switch (sig) { case DESPOTIFY_NEW_TRACK: @@ -98,10 +124,9 @@ static void callback(G_GNUC_UNUSED struct despotify_session* ds, static struct input_stream * input_despotify_open(const char *url, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, G_GNUC_UNUSED GError **error_r) { - struct input_despotify *ctx; struct despotify_session *session; struct ds_link *ds_link; struct ds_track *track; @@ -123,35 +148,23 @@ input_despotify_open(const char *url, return NULL; } - ctx = g_new(struct input_despotify, 1); - memset(ctx, 0, sizeof(*ctx)); - track = despotify_link_get_track(session, ds_link); despotify_free_link(ds_link); - if (!track) { - g_free(ctx); + if (!track) return NULL; - } - input_stream_init(&ctx->base, &input_plugin_despotify, url, - mutex, cond); - ctx->session = session; - ctx->track = track; - ctx->tag = mpd_despotify_tag_from_track(track); - ctx->eof = false; - /* Despotify outputs pcm data */ - ctx->base.mime = g_strdup("audio/x-mpd-cdda-pcm"); - ctx->base.ready = true; + DespotifyInputStream *ctx = + new DespotifyInputStream(url, mutex, cond, + session, track); if (!mpd_despotify_register_callback(callback, ctx)) { - despotify_free_link(ds_link); - + delete ctx; return NULL; } if (despotify_play(ctx->session, ctx->track, false) == false) { - despotify_free_track(ctx->track); - g_free(ctx); + mpd_despotify_unregister_callback(callback); + delete ctx; return NULL; } @@ -162,7 +175,7 @@ static size_t input_despotify_read(struct input_stream *is, void *ptr, size_t size, G_GNUC_UNUSED GError **error_r) { - struct input_despotify *ctx = (struct input_despotify *)is; + DespotifyInputStream *ctx = (DespotifyInputStream *)is; size_t to_cpy = size; if (ctx->len_available == 0) @@ -181,21 +194,16 @@ input_despotify_read(struct input_stream *is, void *ptr, size_t size, static void input_despotify_close(struct input_stream *is) { - struct input_despotify *ctx = (struct input_despotify *)is; - - if (ctx->tag != NULL) - tag_free(ctx->tag); + DespotifyInputStream *ctx = (DespotifyInputStream *)is; mpd_despotify_unregister_callback(callback); - despotify_free_track(ctx->track); - input_stream_deinit(&ctx->base); - g_free(ctx); + delete ctx; } static bool input_despotify_eof(struct input_stream *is) { - struct input_despotify *ctx = (struct input_despotify *)is; + DespotifyInputStream *ctx = (DespotifyInputStream *)is; return ctx->eof; } @@ -211,7 +219,7 @@ input_despotify_seek(G_GNUC_UNUSED struct input_stream *is, static struct tag * input_despotify_tag(struct input_stream *is) { - struct input_despotify *ctx = (struct input_despotify *)is; + DespotifyInputStream *ctx = (DespotifyInputStream *)is; struct tag *tag = ctx->tag; ctx->tag = NULL; @@ -220,11 +228,16 @@ input_despotify_tag(struct input_stream *is) } const struct input_plugin input_plugin_despotify = { - .name = "spt", - .open = input_despotify_open, - .close = input_despotify_close, - .read = input_despotify_read, - .eof = input_despotify_eof, - .seek = input_despotify_seek, + "spt", + nullptr, + nullptr, + input_despotify_open, + input_despotify_close, + nullptr, + nullptr, .tag = input_despotify_tag, + nullptr, + input_despotify_read, + input_despotify_eof, + input_despotify_seek, }; diff --git a/src/input/despotify_input_plugin.h b/src/input/DespotifyInputPlugin.hxx index 4c070d882..00d699408 100644 --- a/src/input/despotify_input_plugin.h +++ b/src/input/DespotifyInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Music Player Daemon Project + * Copyright (C) 2011-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 INPUT_DESPOTIFY_H -#define INPUT_DESPOTIFY_H +#ifndef INPUT_DESPOTIFY_HXX +#define INPUT_DESPOTIFY_HXX extern const struct input_plugin input_plugin_despotify; diff --git a/src/input/ffmpeg_input_plugin.c b/src/input/FfmpegInputPlugin.cxx index 6d339a067..1660f177d 100644 --- a/src/input/ffmpeg_input_plugin.c +++ b/src/input/FfmpegInputPlugin.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 @@ -17,28 +17,49 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/* necessary because libavutil/common.h uses UINT64_C */ +#define __STDC_CONSTANT_MACROS + #include "config.h" -#include "input/ffmpeg_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "FfmpegInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" +extern "C" { #include <libavutil/avutil.h> #include <libavformat/avio.h> #include <libavformat/avformat.h> +} #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_ffmpeg" -struct input_ffmpeg { +struct FfmpegInputStream { struct input_stream base; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) AVIOContext *h; -#else - URLContext *h; -#endif bool eof; + + FfmpegInputStream(const char *uri, Mutex &mutex, Cond &cond, + AVIOContext *_h) + :base(input_plugin_ffmpeg, uri, mutex, cond), + h(_h), eof(false) { + base.ready = true; + base.seekable = (h->seekable & AVIO_SEEKABLE_NORMAL) != 0; + base.size = avio_size(h); + + /* hack to make MPD select the "ffmpeg" decoder plugin + - since avio.h doesn't tell us the MIME type of the + resource, we can't select a decoder plugin, but the + "ffmpeg" plugin is quite good at auto-detection */ + base.mime = g_strdup("audio/x-mpd-ffmpeg"); + } + + ~FfmpegInputStream() { + avio_close(h); + } }; static inline GQuark @@ -50,12 +71,8 @@ ffmpeg_quark(void) static inline bool input_ffmpeg_supported(void) { -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) - void *opaque = NULL; - return avio_enum_protocols(&opaque, 0) != NULL; -#else - return av_protocol_next(NULL) != NULL; -#endif + void *opaque = nullptr; + return avio_enum_protocols(&opaque, 0) != nullptr; } static bool @@ -76,54 +93,26 @@ input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param, static struct input_stream * input_ffmpeg_open(const char *uri, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, GError **error_r) { - struct input_ffmpeg *i; - if (!g_str_has_prefix(uri, "gopher://") && !g_str_has_prefix(uri, "rtp://") && !g_str_has_prefix(uri, "rtsp://") && !g_str_has_prefix(uri, "rtmp://") && !g_str_has_prefix(uri, "rtmpt://") && !g_str_has_prefix(uri, "rtmps://")) - return NULL; - - i = g_new(struct input_ffmpeg, 1); - input_stream_init(&i->base, &input_plugin_ffmpeg, uri, - mutex, cond); - -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0) - int ret = avio_open(&i->h, uri, AVIO_FLAG_READ); -#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) - int ret = avio_open(&i->h, uri, AVIO_RDONLY); -#else - int ret = url_open(&i->h, uri, URL_RDONLY); -#endif + return nullptr; + + AVIOContext *h; + int ret = avio_open(&h, uri, AVIO_FLAG_READ); if (ret != 0) { - g_free(i); g_set_error(error_r, ffmpeg_quark(), ret, "libavformat failed to open the URI"); - return NULL; + return nullptr; } - i->eof = false; - - i->base.ready = true; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) - i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0; - i->base.size = avio_size(i->h); -#else - i->base.seekable = !i->h->is_streamed; - i->base.size = url_filesize(i->h); -#endif - - /* hack to make MPD select the "ffmpeg" decoder plugin - since - avio.h doesn't tell us the MIME type of the resource, we - can't select a decoder plugin, but the "ffmpeg" plugin is - quite good at auto-detection */ - i->base.mime = g_strdup("audio/x-mpd-ffmpeg"); - + auto *i = new FfmpegInputStream(uri, mutex, cond, h); return &i->base; } @@ -131,13 +120,9 @@ static size_t input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size, GError **error_r) { - struct input_ffmpeg *i = (struct input_ffmpeg *)is; + FfmpegInputStream *i = (FfmpegInputStream *)is; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) - int ret = avio_read(i->h, ptr, size); -#else - int ret = url_read(i->h, ptr, size); -#endif + int ret = avio_read(i->h, (unsigned char *)ptr, size); if (ret <= 0) { if (ret < 0) g_set_error(error_r, ffmpeg_quark(), 0, @@ -154,21 +139,15 @@ input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size, static void input_ffmpeg_close(struct input_stream *is) { - struct input_ffmpeg *i = (struct input_ffmpeg *)is; - -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) - avio_close(i->h); -#else - url_close(i->h); -#endif - input_stream_deinit(&i->base); - g_free(i); + FfmpegInputStream *i = (FfmpegInputStream *)is; + + delete i; } static bool input_ffmpeg_eof(struct input_stream *is) { - struct input_ffmpeg *i = (struct input_ffmpeg *)is; + FfmpegInputStream *i = (FfmpegInputStream *)is; return i->eof; } @@ -177,12 +156,8 @@ static bool input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence, G_GNUC_UNUSED GError **error_r) { - struct input_ffmpeg *i = (struct input_ffmpeg *)is; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0) + FfmpegInputStream *i = (FfmpegInputStream *)is; int64_t ret = avio_seek(i->h, offset, whence); -#else - int64_t ret = url_seek(i->h, offset, whence); -#endif if (ret >= 0) { i->eof = false; @@ -194,11 +169,16 @@ input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence, } const struct input_plugin input_plugin_ffmpeg = { - .name = "ffmpeg", - .init = input_ffmpeg_init, - .open = input_ffmpeg_open, - .close = input_ffmpeg_close, - .read = input_ffmpeg_read, - .eof = input_ffmpeg_eof, - .seek = input_ffmpeg_seek, + "ffmpeg", + input_ffmpeg_init, + nullptr, + input_ffmpeg_open, + input_ffmpeg_close, + nullptr, + nullptr, + nullptr, + nullptr, + input_ffmpeg_read, + input_ffmpeg_eof, + input_ffmpeg_seek, }; diff --git a/src/input/ffmpeg_input_plugin.h b/src/input/FfmpegInputPlugin.hxx index 393836ca5..d5e3a8d9b 100644 --- a/src/input/ffmpeg_input_plugin.h +++ b/src/input/FfmpegInputPlugin.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_FFMPEG_INPUT_PLUGIN_H -#define MPD_FFMPEG_INPUT_PLUGIN_H +#ifndef MPD_FFMPEG_INPUT_PLUGIN_HXX +#define MPD_FFMPEG_INPUT_PLUGIN_HXX /** * An input plugin based on libavformat's "avio" library. diff --git a/src/input/file_input_plugin.c b/src/input/FileInputPlugin.cxx index 5ee3f200b..2eecf32b6 100644 --- a/src/input/file_input_plugin.c +++ b/src/input/FileInputPlugin.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,11 +18,13 @@ */ #include "config.h" /* must be first for large file support */ -#include "input/file_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "FileInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include "fd_util.h" #include "open.h" +#include "io_error.h" #include <sys/stat.h> #include <unistd.h> @@ -33,69 +35,67 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_file" -struct file_input_stream { +struct FileInputStream { struct input_stream base; int fd; -}; -static inline GQuark -file_quark(void) -{ - return g_quark_from_static_string("file"); -} + FileInputStream(const char *path, int _fd, off_t size, + Mutex &mutex, Cond &cond) + :base(input_plugin_file, path, mutex, cond), + fd(_fd) { + base.size = size; + base.seekable = true; + base.ready = true; + } + + ~FileInputStream() { + close(fd); + } +}; static struct input_stream * input_file_open(const char *filename, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, GError **error_r) { int fd, ret; struct stat st; - struct file_input_stream *fis; if (!g_path_is_absolute(filename)) - return NULL; + return nullptr; 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; + return nullptr; } 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); - return NULL; + return nullptr; } 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; + return nullptr; } #ifdef POSIX_FADV_SEQUENTIAL posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL); #endif - fis = g_new(struct file_input_stream, 1); - input_stream_init(&fis->base, &input_plugin_file, filename, - mutex, cond); - - fis->base.size = st.st_size; - fis->base.seekable = true; - fis->base.ready = true; - - fis->fd = fd; - + FileInputStream *fis = new FileInputStream(filename, fd, st.st_size, + mutex, cond); return &fis->base; } @@ -103,11 +103,11 @@ static bool input_file_seek(struct input_stream *is, goffset offset, int whence, GError **error_r) { - struct file_input_stream *fis = (struct file_input_stream *)is; + FileInputStream *fis = (FileInputStream *)is; 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; } @@ -120,12 +120,12 @@ static size_t input_file_read(struct input_stream *is, void *ptr, size_t size, GError **error_r) { - struct file_input_stream *fis = (struct file_input_stream *)is; + FileInputStream *fis = (FileInputStream *)is; ssize_t nbytes; 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; } @@ -137,11 +137,9 @@ input_file_read(struct input_stream *is, void *ptr, size_t size, static void input_file_close(struct input_stream *is) { - struct file_input_stream *fis = (struct file_input_stream *)is; + FileInputStream *fis = (FileInputStream *)is; - close(fis->fd); - input_stream_deinit(&fis->base); - g_free(fis); + delete fis; } static bool @@ -151,10 +149,16 @@ input_file_eof(struct input_stream *is) } const struct input_plugin input_plugin_file = { - .name = "file", - .open = input_file_open, - .close = input_file_close, - .read = input_file_read, - .eof = input_file_eof, - .seek = input_file_seek, + "file", + nullptr, + nullptr, + input_file_open, + input_file_close, + nullptr, + nullptr, + nullptr, + nullptr, + input_file_read, + input_file_eof, + input_file_seek, }; diff --git a/src/input/file_input_plugin.h b/src/input/FileInputPlugin.hxx index f24769d57..aacfd0b5d 100644 --- a/src/input/file_input_plugin.h +++ b/src/input/FileInputPlugin.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_FILE_H -#define MPD_INPUT_FILE_H +#ifndef MPD_INPUT_FILE_HXX +#define MPD_INPUT_FILE_HXX extern const struct input_plugin input_plugin_file; diff --git a/src/input/mms_input_plugin.c b/src/input/MmsInputPlugin.cxx index cff15125b..b347eb92b 100644 --- a/src/input/mms_input_plugin.c +++ b/src/input/MmsInputPlugin.cxx @@ -18,9 +18,10 @@ */ #include "config.h" -#include "input/mms_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "MmsInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include <glib.h> #include <libmms/mmsx.h> @@ -31,12 +32,28 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_mms" -struct input_mms { +struct MmsInputStream { struct input_stream base; mmsx_t *mms; bool eof; + + MmsInputStream(const char *uri, + Mutex &mutex, Cond &cond, + mmsx_t *_mms) + :base(input_plugin_mms, uri, mutex, cond), + mms(_mms), eof(false) { + /* XX is this correct? at least this selects the ffmpeg + decoder, which seems to work fine*/ + base.mime = g_strdup("audio/x-ms-wma"); + + base.ready = true; + } + + ~MmsInputStream() { + mmsx_close(mms); + } }; static inline GQuark @@ -47,36 +64,22 @@ mms_quark(void) static struct input_stream * input_mms_open(const char *url, - GMutex *mutex, GCond *cond, + Mutex &mutex, Cond &cond, GError **error_r) { - struct input_mms *m; - if (!g_str_has_prefix(url, "mms://") && !g_str_has_prefix(url, "mmsh://") && !g_str_has_prefix(url, "mmst://") && !g_str_has_prefix(url, "mmsu://")) - return NULL; - - m = g_new(struct input_mms, 1); - input_stream_init(&m->base, &input_plugin_mms, url, - mutex, cond); + return nullptr; - m->mms = mmsx_connect(NULL, NULL, url, 128 * 1024); - if (m->mms == NULL) { - g_free(m); + const auto mms = mmsx_connect(nullptr, nullptr, url, 128 * 1024); + if (mms == nullptr) { g_set_error(error_r, mms_quark(), 0, "mmsx_connect() failed"); - return NULL; + return nullptr; } - m->eof = false; - - /* XX is this correct? at least this selects the ffmpeg - decoder, which seems to work fine*/ - m->base.mime = g_strdup("audio/x-ms-wma"); - - m->base.ready = true; - + auto m = new MmsInputStream(url, mutex, cond, mms); return &m->base; } @@ -84,10 +87,10 @@ static size_t input_mms_read(struct input_stream *is, void *ptr, size_t size, GError **error_r) { - struct input_mms *m = (struct input_mms *)is; + MmsInputStream *m = (MmsInputStream *)is; int ret; - ret = mmsx_read(NULL, m->mms, ptr, size); + ret = mmsx_read(nullptr, m->mms, (char *)ptr, size); if (ret <= 0) { if (ret < 0) { g_set_error(error_r, mms_quark(), errno, @@ -107,17 +110,15 @@ input_mms_read(struct input_stream *is, void *ptr, size_t size, static void input_mms_close(struct input_stream *is) { - struct input_mms *m = (struct input_mms *)is; + MmsInputStream *m = (MmsInputStream *)is; - mmsx_close(m->mms); - input_stream_deinit(&m->base); - g_free(m); + delete m; } static bool input_mms_eof(struct input_stream *is) { - struct input_mms *m = (struct input_mms *)is; + MmsInputStream *m = (MmsInputStream *)is; return m->eof; } @@ -131,10 +132,16 @@ input_mms_seek(G_GNUC_UNUSED struct input_stream *is, } const struct input_plugin input_plugin_mms = { - .name = "mms", - .open = input_mms_open, - .close = input_mms_close, - .read = input_mms_read, - .eof = input_mms_eof, - .seek = input_mms_seek, + "mms", + nullptr, + nullptr, + input_mms_open, + input_mms_close, + nullptr, + nullptr, + nullptr, + nullptr, + input_mms_read, + input_mms_eof, + input_mms_seek, }; diff --git a/src/input/mms_input_plugin.h b/src/input/MmsInputPlugin.hxx index d6aa593f2..d6aa593f2 100644 --- a/src/input/mms_input_plugin.h +++ b/src/input/MmsInputPlugin.hxx diff --git a/src/input/rewind_input_plugin.c b/src/input/RewindInputPlugin.cxx index cf06fc57b..d93d7d1ce 100644 --- a/src/input/rewind_input_plugin.c +++ b/src/input/RewindInputPlugin.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,10 @@ */ #include "config.h" -#include "input/rewind_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" +#include "RewindInputPlugin.hxx" +#include "InputInternal.hxx" +#include "InputStream.hxx" +#include "InputPlugin.hxx" #include "tag.h" #include <glib.h> @@ -31,14 +32,16 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_rewind" -struct input_rewind { +extern const struct input_plugin rewind_input_plugin; + +struct RewindInputStream { struct input_stream base; struct input_stream *input; /** * The read position within the buffer. Undefined as long as - * reading_from_buffer() returns false. + * ReadingFromBuffer() returns false. */ size_t head; @@ -56,61 +59,61 @@ struct input_rewind { * stream (offset 0). */ char buffer[64 * 1024]; -}; -/** - * Are we currently reading from the buffer, and does the buffer - * contain more data for the next read operation? - */ -static bool -reading_from_buffer(const struct input_rewind *r) -{ - return r->tail > 0 && r->base.offset < r->input->offset; -} + RewindInputStream(input_stream *_input) + :base(rewind_input_plugin, _input->uri.c_str(), + _input->mutex, _input->cond), + input(_input), tail(0) { + } -/** - * Copy public attributes from the underlying input stream to the - * "rewind" input stream. This function is called when a method of - * the underlying stream has returned, which may have modified these - * attributes. - */ -static void -copy_attributes(struct input_rewind *r) -{ - struct input_stream *dest = &r->base; - const struct input_stream *src = r->input; + ~RewindInputStream() { + input_stream_close(input); + } - assert(dest != src); - assert(src->mime == NULL || dest->mime != src->mime); + /** + * Are we currently reading from the buffer, and does the + * buffer contain more data for the next read operation? + */ + bool ReadingFromBuffer() const { + return tail > 0 && base.offset < input->offset; + } + + /** + * Copy public attributes from the underlying input stream to the + * "rewind" input stream. This function is called when a method of + * the underlying stream has returned, which may have modified these + * attributes. + */ + void CopyAttributes() { + struct input_stream *dest = &base; + const struct input_stream *src = input; + + assert(dest != src); - bool dest_ready = dest->ready; + bool dest_ready = dest->ready; - dest->ready = src->ready; - dest->seekable = src->seekable; - dest->size = src->size; - dest->offset = src->offset; + dest->ready = src->ready; + dest->seekable = src->seekable; + dest->size = src->size; + dest->offset = src->offset; - if (!dest_ready && src->ready) { - g_free(dest->mime); - dest->mime = g_strdup(src->mime); + if (!dest_ready && src->ready) + dest->mime = src->mime; } -} +}; static void input_rewind_close(struct input_stream *is) { - struct input_rewind *r = (struct input_rewind *)is; - - input_stream_close(r->input); + RewindInputStream *r = (RewindInputStream *)is; - input_stream_deinit(&r->base); - g_free(r); + delete r; } static bool input_rewind_check(struct input_stream *is, GError **error_r) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; return input_stream_check(r->input, error_r); } @@ -118,16 +121,16 @@ input_rewind_check(struct input_stream *is, GError **error_r) static void input_rewind_update(struct input_stream *is) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; - if (!reading_from_buffer(r)) - copy_attributes(r); + if (!r->ReadingFromBuffer()) + r->CopyAttributes(); } static struct tag * input_rewind_tag(struct input_stream *is) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; return input_stream_tag(r->input); } @@ -135,7 +138,7 @@ input_rewind_tag(struct input_stream *is) static bool input_rewind_available(struct input_stream *is) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; return input_stream_available(r->input); } @@ -144,9 +147,9 @@ static size_t input_rewind_read(struct input_stream *is, void *ptr, size_t size, GError **error_r) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; - if (reading_from_buffer(r)) { + if (r->ReadingFromBuffer()) { /* buffered read */ assert(r->head == (size_t)is->offset); @@ -177,7 +180,7 @@ input_rewind_read(struct input_stream *is, void *ptr, size_t size, assert(r->tail == (size_t)r->input->offset); } - copy_attributes(r); + r->CopyAttributes(); return nbytes; } @@ -186,23 +189,23 @@ input_rewind_read(struct input_stream *is, void *ptr, size_t size, static bool input_rewind_eof(struct input_stream *is) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; - return !reading_from_buffer(r) && input_stream_eof(r->input); + return !r->ReadingFromBuffer() && input_stream_eof(r->input); } static bool input_rewind_seek(struct input_stream *is, goffset offset, int whence, GError **error_r) { - struct input_rewind *r = (struct input_rewind *)is; + RewindInputStream *r = (RewindInputStream *)is; assert(is->ready); if (whence == SEEK_SET && r->tail > 0 && offset <= (goffset)r->tail) { /* buffered seek */ - assert(!reading_from_buffer(r) || + assert(!r->ReadingFromBuffer() || r->head == (size_t)is->offset); assert(r->tail == (size_t)r->input->offset); @@ -213,7 +216,7 @@ input_rewind_seek(struct input_stream *is, goffset offset, int whence, } else { bool success = input_stream_seek(r->input, offset, whence, error_r); - copy_attributes(r); + r->CopyAttributes(); /* disable the buffer, because r->input has left the buffered range now */ @@ -223,22 +226,24 @@ input_rewind_seek(struct input_stream *is, goffset offset, int whence, } } -static const struct input_plugin rewind_input_plugin = { - .close = input_rewind_close, - .check = input_rewind_check, - .update = input_rewind_update, - .tag = input_rewind_tag, - .available = input_rewind_available, - .read = input_rewind_read, - .eof = input_rewind_eof, - .seek = input_rewind_seek, +const struct input_plugin rewind_input_plugin = { + nullptr, + nullptr, + nullptr, + nullptr, + input_rewind_close, + input_rewind_check, + input_rewind_update, + input_rewind_tag, + input_rewind_available, + input_rewind_read, + input_rewind_eof, + input_rewind_seek, }; struct input_stream * input_rewind_open(struct input_stream *is) { - struct input_rewind *c; - assert(is != NULL); assert(is->offset == 0); @@ -246,11 +251,6 @@ input_rewind_open(struct input_stream *is) /* seekable resources don't need this plugin */ return is; - c = g_new(struct input_rewind, 1); - input_stream_init(&c->base, &rewind_input_plugin, is->uri, - is->mutex, is->cond); - c->tail = 0; - c->input = is; - + RewindInputStream *c = new RewindInputStream(is); return &c->base; } diff --git a/src/input/rewind_input_plugin.h b/src/input/RewindInputPlugin.hxx index 83abe257a..cf21e92f1 100644 --- a/src/input/rewind_input_plugin.h +++ b/src/input/RewindInputPlugin.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 @@ -24,8 +24,8 @@ * each decoder plugin peek a portion from the stream). */ -#ifndef MPD_INPUT_REWIND_H -#define MPD_INPUT_REWIND_H +#ifndef MPD_INPUT_REWIND_HXX +#define MPD_INPUT_REWIND_HXX #include "check.h" diff --git a/src/input/soup_input_plugin.c b/src/input/SoupInputPlugin.cxx index fc903b48c..e9767c20e 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,18 @@ */ #include "config.h" -#include "input/soup_input_plugin.h" -#include "input_internal.h" -#include "input_plugin.h" -#include "io_thread.h" +#include "SoupInputPlugin.hxx" +#include "InputPlugin.hxx" +#include "InputStream.hxx" +#include "InputInternal.hxx" +#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> @@ -48,7 +52,7 @@ static const size_t SOUP_RESUME_AT = 384 * 1024; static SoupURI *soup_proxy; static SoupSession *soup_session; -struct input_soup { +struct SoupInputStream { struct input_stream base; SoupMessage *msg; @@ -68,6 +72,15 @@ struct input_soup { bool completed; GError *postponed_error; + + SoupInputStream(const char *uri, Mutex &mutex, Cond &cond); + ~SoupInputStream(); + + bool CopyError(const SoupMessage *msg); + + bool WaitData(); + + size_t Read(void *ptr, size_t size, GError **error_r); }; static inline GQuark @@ -99,7 +112,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; @@ -123,31 +136,31 @@ input_soup_finish(void) * * @return true if there was no error */ -static bool -input_soup_copy_error(struct input_soup *s, const SoupMessage *msg) +bool +SoupInputStream::CopyError(const SoupMessage *src) { - if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) + if (SOUP_STATUS_IS_SUCCESSFUL(src->status_code)) return true; - if (msg->status_code == SOUP_STATUS_CANCELLED) + if (src->status_code == SOUP_STATUS_CANCELLED) /* failure, but don't generate a GError, because this status was caused by _close() */ return false; - if (s->postponed_error != NULL) + if (postponed_error != nullptr) /* there's already a GError, don't overwrite it */ return false; - if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) - s->postponed_error = - g_error_new(soup_quark(), msg->status_code, + if (SOUP_STATUS_IS_TRANSPORT_ERROR(src->status_code)) + postponed_error = + g_error_new(soup_quark(), src->status_code, "HTTP client error: %s", - msg->reason_phrase); + src->reason_phrase); else - s->postponed_error = - g_error_new(soup_quark(), msg->status_code, + postponed_error = + g_error_new(soup_quark(), src->status_code, "got HTTP status: %d %s", - msg->status_code, msg->reason_phrase); + src->status_code, src->reason_phrase); return false; } @@ -156,33 +169,32 @@ static void input_soup_session_callback(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + SoupInputStream *s = (SoupInputStream *)user_data; assert(msg == s->msg); assert(!s->completed); - g_mutex_lock(s->base.mutex); + const ScopeLock protect(s->base.mutex); if (!s->base.ready) - input_soup_copy_error(s, msg); + s->CopyError(msg); s->base.ready = true; s->alive = false; s->completed = true; - g_cond_broadcast(s->base.cond); - g_mutex_unlock(s->base.mutex); + s->base.cond.broadcast(); } static void input_soup_got_headers(SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + SoupInputStream *s = (SoupInputStream *)user_data; - g_mutex_lock(s->base.mutex); + s->base.mutex.lock(); - if (!input_soup_copy_error(s, msg)) { - g_mutex_unlock(s->base.mutex); + if (!s->CopyError(msg)) { + s->base.mutex.unlock(); soup_session_cancel_message(soup_session, msg, SOUP_STATUS_CANCELLED); @@ -190,8 +202,8 @@ input_soup_got_headers(SoupMessage *msg, gpointer user_data) } s->base.ready = true; - g_cond_broadcast(s->base.cond); - g_mutex_unlock(s->base.mutex); + s->base.cond.broadcast(); + s->base.mutex.unlock(); soup_message_body_set_accumulate(msg->response_body, false); } @@ -199,11 +211,11 @@ 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; + SoupInputStream *s = (SoupInputStream *)user_data; assert(msg == s->msg); - g_mutex_lock(s->base.mutex); + const ScopeLock protect(s->base.mutex); g_queue_push_tail(s->buffers, soup_buffer_copy(chunk)); s->total_buffered += chunk->length; @@ -213,50 +225,50 @@ input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) soup_session_pause_message(soup_session, msg); } - g_cond_broadcast(s->base.cond); - g_mutex_unlock(s->base.mutex); + s->base.cond.broadcast(); + s->base.mutex.unlock(); } static void input_soup_got_body(G_GNUC_UNUSED SoupMessage *msg, gpointer user_data) { - struct input_soup *s = user_data; + SoupInputStream *s = (SoupInputStream *)user_data; assert(msg == s->msg); - g_mutex_lock(s->base.mutex); + const ScopeLock protect(s->base.mutex); s->base.ready = true; s->eof = true; s->alive = false; - g_cond_broadcast(s->base.cond); - g_mutex_unlock(s->base.mutex); + s->base.cond.broadcast(); + s->base.mutex.unlock(); } -static bool -input_soup_wait_data(struct input_soup *s) +inline bool +SoupInputStream::WaitData() { while (true) { - if (s->eof) + if (eof) return true; - if (!s->alive) + if (!alive) return false; - if (!g_queue_is_empty(s->buffers)) + if (!g_queue_is_empty(buffers)) return true; - assert(s->current_consumed == 0); + assert(current_consumed == 0); - g_cond_wait(s->base.cond, s->base.mutex); + base.cond.wait(base.mutex); } } static gpointer input_soup_queue(gpointer data) { - struct input_soup *s = data; + SoupInputStream *s = (SoupInputStream *)data; soup_session_queue_message(soup_session, s->msg, input_soup_session_callback, s); @@ -264,22 +276,14 @@ input_soup_queue(gpointer data) return NULL; } -static struct input_stream * -input_soup_open(const char *uri, - GMutex *mutex, GCond *cond, - G_GNUC_UNUSED GError **error_r) +SoupInputStream::SoupInputStream(const char *uri, + Mutex &mutex, Cond &cond) + :base(input_plugin_soup, uri, mutex, cond), + buffers(g_queue_new()), + current_consumed(0), total_buffered(0), + alive(false), pause(false), eof(false), completed(false), + postponed_error(nullptr) { - if (strncmp(uri, "http://", 7) != 0) - return NULL; - - struct input_soup *s = g_new(struct input_soup, 1); - input_stream_init(&s->base, &input_plugin_soup, uri, - mutex, cond); - - s->buffers = g_queue_new(); - s->current_consumed = 0; - s->total_buffered = 0; - #if GCC_CHECK_VERSION(4,6) #pragma GCC diagnostic push /* the libsoup macro SOUP_METHOD_GET discards the "const" @@ -288,39 +292,43 @@ input_soup_open(const char *uri, #pragma GCC diagnostic ignored "-Wcast-qual" #endif - s->msg = soup_message_new(SOUP_METHOD_GET, uri); + msg = soup_message_new(SOUP_METHOD_GET, uri); #if GCC_CHECK_VERSION(4,6) #pragma GCC diagnostic pop #endif - soup_message_set_flags(s->msg, SOUP_MESSAGE_NO_REDIRECT); + soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT); - soup_message_headers_append(s->msg->request_headers, "User-Agent", + soup_message_headers_append(msg->request_headers, "User-Agent", "Music Player Daemon " VERSION); - g_signal_connect(s->msg, "got-headers", - G_CALLBACK(input_soup_got_headers), s); - g_signal_connect(s->msg, "got-chunk", - G_CALLBACK(input_soup_got_chunk), s); - g_signal_connect(s->msg, "got-body", - G_CALLBACK(input_soup_got_body), s); + g_signal_connect(msg, "got-headers", + G_CALLBACK(input_soup_got_headers), this); + g_signal_connect(msg, "got-chunk", + G_CALLBACK(input_soup_got_chunk), this); + g_signal_connect(msg, "got-body", + G_CALLBACK(input_soup_got_body), this); - s->alive = true; - s->pause = false; - s->eof = false; - s->completed = false; - s->postponed_error = NULL; + io_thread_call(input_soup_queue, this); +} - io_thread_call(input_soup_queue, s); +static struct input_stream * +input_soup_open(const char *uri, + Mutex &mutex, Cond &cond, + G_GNUC_UNUSED GError **error_r) +{ + if (strncmp(uri, "http://", 7) != 0) + return NULL; + SoupInputStream *s = new SoupInputStream(uri, mutex, cond); return &s->base; } static gpointer input_soup_cancel(gpointer data) { - struct input_soup *s = data; + SoupInputStream *s = (SoupInputStream *)data; if (!s->completed) soup_session_cancel_message(soup_session, s->msg, @@ -329,44 +337,46 @@ input_soup_cancel(gpointer data) return NULL; } -static void -input_soup_close(struct input_stream *is) +SoupInputStream::~SoupInputStream() { - struct input_soup *s = (struct input_soup *)is; - - g_mutex_lock(s->base.mutex); + base.mutex.lock(); - if (!s->completed) { + if (!completed) { /* the messages's session callback hasn't been invoked yet; cancel it and wait for completion */ - g_mutex_unlock(s->base.mutex); + base.mutex.unlock(); - io_thread_call(input_soup_cancel, s); + io_thread_call(input_soup_cancel, this); - g_mutex_lock(s->base.mutex); - while (!s->completed) - g_cond_wait(s->base.cond, s->base.mutex); + base.mutex.lock(); + while (!completed) + base.cond.wait(base.mutex); } - g_mutex_unlock(s->base.mutex); + base.mutex.unlock(); SoupBuffer *buffer; - while ((buffer = g_queue_pop_head(s->buffers)) != NULL) + while ((buffer = (SoupBuffer *)g_queue_pop_head(buffers)) != NULL) soup_buffer_free(buffer); - g_queue_free(s->buffers); + g_queue_free(buffers); - if (s->postponed_error != NULL) - g_error_free(s->postponed_error); + if (postponed_error != NULL) + g_error_free(postponed_error); +} + +static void +input_soup_close(struct input_stream *is) +{ + SoupInputStream *s = (SoupInputStream *)is; - input_stream_deinit(&s->base); - g_free(s); + delete s; } static bool input_soup_check(struct input_stream *is, GError **error_r) { - struct input_soup *s = (struct input_soup *)is; + SoupInputStream *s = (SoupInputStream *)is; bool success = s->postponed_error == NULL; if (!success) { @@ -380,45 +390,43 @@ input_soup_check(struct input_stream *is, GError **error_r) static bool input_soup_available(struct input_stream *is) { - struct input_soup *s = (struct input_soup *)is; + SoupInputStream *s = (SoupInputStream *)is; return s->eof || !s->alive || !g_queue_is_empty(s->buffers); } -static size_t -input_soup_read(struct input_stream *is, void *ptr, size_t size, - G_GNUC_UNUSED GError **error_r) +inline size_t +SoupInputStream::Read(void *ptr, size_t size, GError **error_r) { - struct input_soup *s = (struct input_soup *)is; + if (!WaitData()) { + assert(!alive); - if (!input_soup_wait_data(s)) { - assert(!s->alive); - - if (s->postponed_error != NULL) { - g_propagate_error(error_r, s->postponed_error); - s->postponed_error = NULL; + if (postponed_error != nullptr) { + g_propagate_error(error_r, postponed_error); + postponed_error = nullptr; } else g_set_error_literal(error_r, soup_quark(), 0, "HTTP failure"); 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(buffers); if (buffer == NULL) { - assert(s->current_consumed == 0); + assert(current_consumed == 0); break; } - assert(s->current_consumed < buffer->length); - assert(s->total_buffered >= buffer->length); + assert(current_consumed < buffer->length); + assert(total_buffered >= buffer->length); const char *q = buffer->data; - q += s->current_consumed; + q += current_consumed; - size_t remaining = buffer->length - s->current_consumed; + size_t remaining = buffer->length - current_consumed; size_t nbytes = p_end - p; if (nbytes > remaining) nbytes = remaining; @@ -426,48 +434,59 @@ input_soup_read(struct input_stream *is, void *ptr, size_t size, memcpy(p, q, nbytes); p += nbytes; - s->current_consumed += remaining; - if (s->current_consumed >= buffer->length) { + current_consumed += remaining; + if (current_consumed >= buffer->length) { /* done with this buffer */ - s->total_buffered -= buffer->length; + total_buffered -= buffer->length; soup_buffer_free(buffer); - s->current_consumed = 0; + current_consumed = 0; } else { /* partial read */ assert(p == p_end); - g_queue_push_head(s->buffers, buffer); + g_queue_push_head(buffers, buffer); } } - if (s->pause && s->total_buffered < SOUP_RESUME_AT) { - s->pause = false; - soup_session_unpause_message(soup_session, s->msg); + if (pause && total_buffered < SOUP_RESUME_AT) { + pause = false; + soup_session_unpause_message(soup_session, msg); } size_t nbytes = p - p0; - s->base.offset += nbytes; + base.offset += nbytes; return nbytes; } +static size_t +input_soup_read(struct input_stream *is, void *ptr, size_t size, + GError **error_r) +{ + SoupInputStream *s = (SoupInputStream *)is; + + return s->Read(ptr, size, error_r); +} + static bool input_soup_eof(G_GNUC_UNUSED struct input_stream *is) { - struct input_soup *s = (struct input_soup *)is; + SoupInputStream *s = (SoupInputStream *)is; return !s->alive && g_queue_is_empty(s->buffers); } 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; |