aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/IcyMetaDataParser.cxx29
-rw-r--r--src/IcyMetaDataParser.hxx7
-rw-r--r--src/input/IcyInputStream.cxx99
-rw-r--r--src/input/IcyInputStream.hxx67
-rw-r--r--src/input/plugins/CurlInputPlugin.cxx102
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 *