diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/IcyMetaDataParser.cxx | 29 | ||||
-rw-r--r-- | src/IcyMetaDataParser.hxx | 7 | ||||
-rw-r--r-- | src/input/IcyInputStream.cxx | 99 | ||||
-rw-r--r-- | src/input/IcyInputStream.hxx | 67 | ||||
-rw-r--r-- | src/input/plugins/CurlInputPlugin.cxx | 102 |
5 files changed, 218 insertions, 86 deletions
diff --git a/src/IcyMetaDataParser.cxx b/src/IcyMetaDataParser.cxx index 79023dd64..4c13c2c2c 100644 --- a/src/IcyMetaDataParser.cxx +++ b/src/IcyMetaDataParser.cxx @@ -220,3 +220,32 @@ IcyMetaDataParser::Meta(const void *data, size_t length) return length; } + +size_t +IcyMetaDataParser::ParseInPlace(void *data, size_t length) +{ + uint8_t *const dest0 = (uint8_t *)data; + uint8_t *dest = dest0; + const uint8_t *src = dest0; + + while (length > 0) { + size_t chunk = Data(length); + if (chunk > 0) { + memmove(dest, src, chunk); + dest += chunk; + src += chunk; + length -= chunk; + + if (length == 0) + break; + } + + chunk = Meta(src, length); + if (chunk > 0) { + src += chunk; + length -= chunk; + } + } + + return dest - dest0; +} diff --git a/src/IcyMetaDataParser.hxx b/src/IcyMetaDataParser.hxx index 4956a9904..3075485b2 100644 --- a/src/IcyMetaDataParser.hxx +++ b/src/IcyMetaDataParser.hxx @@ -75,6 +75,13 @@ public: */ size_t Meta(const void *data, size_t length); + /** + * Parse data and eliminate metadata. + * + * @return the number of data bytes remaining in the buffer + */ + size_t ParseInPlace(void *data, size_t length); + Tag *ReadTag() { Tag *result = tag; tag = nullptr; diff --git a/src/input/IcyInputStream.cxx b/src/input/IcyInputStream.cxx new file mode 100644 index 000000000..fb82cdec6 --- /dev/null +++ b/src/input/IcyInputStream.cxx @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "IcyInputStream.hxx" +#include "tag/Tag.hxx" + +IcyInputStream::IcyInputStream(InputStream *_input) + :ProxyInputStream(_input), + input_tag(nullptr), icy_tag(nullptr), + override_offset(0) +{ +} + +IcyInputStream::~IcyInputStream() +{ + delete input_tag; + delete icy_tag; +} + +void +IcyInputStream::Update() +{ + ProxyInputStream::Update(); + + if (IsEnabled()) + offset = override_offset; +} + +Tag * +IcyInputStream::ReadTag() +{ + Tag *new_input_tag = ProxyInputStream::ReadTag(); + if (!IsEnabled()) + return new_input_tag; + + if (new_input_tag != nullptr) { + delete input_tag; + input_tag = new_input_tag; + } + + Tag *new_icy_tag = parser.ReadTag(); + if (new_icy_tag != nullptr) { + delete icy_tag; + icy_tag = new_icy_tag; + } + + if (new_input_tag == nullptr && new_icy_tag == nullptr) + /* no change */ + return nullptr; + + if (input_tag == nullptr && icy_tag == nullptr) + /* no tag */ + return nullptr; + + if (input_tag == nullptr) + return new Tag(*icy_tag); + + if (icy_tag == nullptr) + return new Tag(*input_tag); + + return Tag::Merge(*input_tag, *icy_tag); +} + +size_t +IcyInputStream::Read(void *ptr, size_t read_size, Error &error) +{ + if (!IsEnabled()) + return ProxyInputStream::Read(ptr, read_size, error); + + while (true) { + size_t nbytes = ProxyInputStream::Read(ptr, read_size, error); + if (nbytes == 0) + return 0; + + size_t result = parser.ParseInPlace(ptr, nbytes); + if (result > 0) { + override_offset += result; + offset = override_offset; + return result; + } + } +} diff --git a/src/input/IcyInputStream.hxx b/src/input/IcyInputStream.hxx new file mode 100644 index 000000000..6a85f9ffd --- /dev/null +++ b/src/input/IcyInputStream.hxx @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ICY_INPUT_STREAM_HXX +#define MPD_ICY_INPUT_STREAM_HXX + +#include "ProxyInputStream.hxx" +#include "IcyMetaDataParser.hxx" + +struct Tag; + +/** + * An #InputStream filter that parses Icy metadata. + */ +class IcyInputStream final : public ProxyInputStream { + IcyMetaDataParser parser; + + /** + * The #Tag object ready to be requested via ReadTag(). + */ + Tag *input_tag; + + /** + * The #Tag object ready to be requested via ReadTag(). + */ + Tag *icy_tag; + + offset_type override_offset; + +public: + IcyInputStream(InputStream *_input); + virtual ~IcyInputStream(); + + IcyInputStream(const IcyInputStream &) = delete; + IcyInputStream &operator=(const IcyInputStream &) = delete; + + void Enable(size_t _data_size) { + parser.Start(_data_size); + } + + bool IsEnabled() const { + return parser.IsDefined(); + } + + /* virtual methods from InputStream */ + void Update() override; + Tag *ReadTag() override; + size_t Read(void *ptr, size_t size, Error &error) override; +}; + +#endif diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index 1b00258ea..738937a26 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -19,13 +19,13 @@ #include "config.h" #include "CurlInputPlugin.hxx" +#include "../IcyInputStream.hxx" #include "../InputStream.hxx" #include "../InputPlugin.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigData.hxx" #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" -#include "IcyMetaDataParser.hxx" #include "event/SocketMonitor.hxx" #include "event/TimeoutMonitor.hxx" #include "event/Call.hxx" @@ -93,10 +93,7 @@ struct CurlInputStream final : public InputStream { char error_buffer[CURL_ERROR_SIZE]; /** parser for icy-metadata */ - IcyMetaDataParser icy; - - /** the stream name from the icy-name response header */ - std::string meta_name; + IcyInputStream *icy; /** the tag object ready to be requested via InputStream::ReadTag() */ @@ -110,6 +107,7 @@ struct CurlInputStream final : public InputStream { request_headers(nullptr), buffer((uint8_t *)_buffer, CURL_MAX_BUFFERED), paused(false), + icy(new IcyInputStream(this)), tag(nullptr) {} ~CurlInputStream(); @@ -155,8 +153,6 @@ struct CurlInputStream final : public InputStream { return buffer.GetSize(); } - void CopyIcyTag(); - /** * A HTTP request is finished. * @@ -683,83 +679,19 @@ CurlInputStream::FillBuffer(Error &error) return !buffer.IsEmpty(); } -static size_t -read_from_buffer(IcyMetaDataParser &icy, CircularBuffer<uint8_t> &buffer, - void *dest0, size_t length) -{ - uint8_t *dest = (uint8_t *)dest0; - size_t nbytes = 0; - - while (true) { - auto r = buffer.Read(); - if (r.IsEmpty()) - break; - - if (r.size > length) - r.size = length; - - size_t chunk = icy.Data(r.size); - if (chunk > 0) { - memcpy(dest, r.data, chunk); - buffer.Consume(chunk); - - nbytes += chunk; - dest += chunk; - length -= chunk; - - if (length == 0) - break; - } - - r = buffer.Read(); - if (r.IsEmpty()) - break; - - chunk = icy.Meta(r.data, r.size); - if (chunk > 0) { - buffer.Consume(chunk); - if (length == 0) - break; - } - } - - return nbytes; -} - -inline void -CurlInputStream::CopyIcyTag() -{ - Tag *new_tag = icy.ReadTag(); - if (new_tag == nullptr) - return; - - delete tag; - - if (!meta_name.empty() && !new_tag->HasType(TAG_NAME)) { - TagBuilder tag_builder(std::move(*new_tag)); - tag_builder.AddItem(TAG_NAME, meta_name.c_str()); - *new_tag = tag_builder.Commit(); - } - - tag = new_tag; -} - size_t CurlInputStream::Read(void *ptr, size_t read_size, Error &error) { - size_t nbytes; - - do { - /* fill the buffer */ - - if (!FillBuffer(error)) - return 0; + if (!FillBuffer(error)) + return 0; - nbytes = read_from_buffer(icy, buffer, ptr, read_size); - } while (nbytes == 0); + auto r = buffer.Read(); + if (r.IsEmpty()) + return 0; - if (icy.IsDefined()) - CopyIcyTag(); + const size_t nbytes = std::min(read_size, r.size); + memcpy(ptr, r.data, nbytes); + buffer.Consume(nbytes); offset += (InputPlugin::offset_type)nbytes; @@ -781,7 +713,7 @@ CurlInputStream::HeaderReceived(const char *name, std::string &&value) { if (StringEqualsCaseASCII(name, "accept-ranges")) { /* a stream with icy-metadata is not seekable */ - if (!icy.IsDefined()) + if (!icy->IsEnabled()) seekable = true; } else if (StringEqualsCaseASCII(name, "content-length")) { size = offset + ParseUint64(value.c_str()); @@ -790,23 +722,21 @@ CurlInputStream::HeaderReceived(const char *name, std::string &&value) } else if (StringEqualsCaseASCII(name, "icy-name") || StringEqualsCaseASCII(name, "ice-name") || StringEqualsCaseASCII(name, "x-audiocast-name")) { - meta_name = std::move(value); - delete tag; TagBuilder tag_builder; - tag_builder.AddItem(TAG_NAME, meta_name.c_str()); + tag_builder.AddItem(TAG_NAME, value.c_str()); tag = tag_builder.CommitNew(); } else if (StringEqualsCaseASCII(name, "icy-metaint")) { - if (icy.IsDefined()) + if (icy->IsEnabled()) return; size_t icy_metaint = ParseUint64(value.c_str()); FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint); if (icy_metaint > 0) { - icy.Start(icy_metaint); + icy->Enable(icy_metaint); /* a stream with icy-metadata is not seekable */ @@ -1072,7 +1002,7 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond, return nullptr; } - return c; + return c->icy; } static InputStream * |