input/curl: move code to IcyInputStream
This commit is contained in:
parent
09b84c51ee
commit
a5d0300787
@ -1065,6 +1065,7 @@ endif
|
|||||||
|
|
||||||
if ENABLE_CURL
|
if ENABLE_CURL
|
||||||
libinput_a_SOURCES += \
|
libinput_a_SOURCES += \
|
||||||
|
src/input/IcyInputStream.cxx src/input/IcyInputStream.hxx \
|
||||||
src/input/plugins/CurlInputPlugin.cxx src/input/plugins/CurlInputPlugin.hxx \
|
src/input/plugins/CurlInputPlugin.cxx src/input/plugins/CurlInputPlugin.hxx \
|
||||||
src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx
|
src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx
|
||||||
endif
|
endif
|
||||||
|
@ -220,3 +220,32 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
|
|||||||
|
|
||||||
return 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;
|
||||||
|
}
|
||||||
|
@ -75,6 +75,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
size_t Meta(const void *data, size_t length);
|
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 *ReadTag() {
|
||||||
Tag *result = tag;
|
Tag *result = tag;
|
||||||
tag = nullptr;
|
tag = nullptr;
|
||||||
|
99
src/input/IcyInputStream.cxx
Normal file
99
src/input/IcyInputStream.cxx
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/input/IcyInputStream.hxx
Normal file
67
src/input/IcyInputStream.hxx
Normal file
@ -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
|
@ -19,13 +19,13 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "CurlInputPlugin.hxx"
|
#include "CurlInputPlugin.hxx"
|
||||||
|
#include "../IcyInputStream.hxx"
|
||||||
#include "../InputStream.hxx"
|
#include "../InputStream.hxx"
|
||||||
#include "../InputPlugin.hxx"
|
#include "../InputPlugin.hxx"
|
||||||
#include "config/ConfigGlobal.hxx"
|
#include "config/ConfigGlobal.hxx"
|
||||||
#include "config/ConfigData.hxx"
|
#include "config/ConfigData.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "tag/TagBuilder.hxx"
|
#include "tag/TagBuilder.hxx"
|
||||||
#include "IcyMetaDataParser.hxx"
|
|
||||||
#include "event/SocketMonitor.hxx"
|
#include "event/SocketMonitor.hxx"
|
||||||
#include "event/TimeoutMonitor.hxx"
|
#include "event/TimeoutMonitor.hxx"
|
||||||
#include "event/Call.hxx"
|
#include "event/Call.hxx"
|
||||||
@ -93,10 +93,7 @@ struct CurlInputStream final : public InputStream {
|
|||||||
char error_buffer[CURL_ERROR_SIZE];
|
char error_buffer[CURL_ERROR_SIZE];
|
||||||
|
|
||||||
/** parser for icy-metadata */
|
/** parser for icy-metadata */
|
||||||
IcyMetaDataParser icy;
|
IcyInputStream *icy;
|
||||||
|
|
||||||
/** the stream name from the icy-name response header */
|
|
||||||
std::string meta_name;
|
|
||||||
|
|
||||||
/** the tag object ready to be requested via
|
/** the tag object ready to be requested via
|
||||||
InputStream::ReadTag() */
|
InputStream::ReadTag() */
|
||||||
@ -110,6 +107,7 @@ struct CurlInputStream final : public InputStream {
|
|||||||
request_headers(nullptr),
|
request_headers(nullptr),
|
||||||
buffer((uint8_t *)_buffer, CURL_MAX_BUFFERED),
|
buffer((uint8_t *)_buffer, CURL_MAX_BUFFERED),
|
||||||
paused(false),
|
paused(false),
|
||||||
|
icy(new IcyInputStream(this)),
|
||||||
tag(nullptr) {}
|
tag(nullptr) {}
|
||||||
|
|
||||||
~CurlInputStream();
|
~CurlInputStream();
|
||||||
@ -155,8 +153,6 @@ struct CurlInputStream final : public InputStream {
|
|||||||
return buffer.GetSize();
|
return buffer.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyIcyTag();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A HTTP request is finished.
|
* A HTTP request is finished.
|
||||||
*
|
*
|
||||||
@ -683,83 +679,19 @@ CurlInputStream::FillBuffer(Error &error)
|
|||||||
return !buffer.IsEmpty();
|
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
|
size_t
|
||||||
CurlInputStream::Read(void *ptr, size_t read_size, Error &error)
|
CurlInputStream::Read(void *ptr, size_t read_size, Error &error)
|
||||||
{
|
{
|
||||||
size_t nbytes;
|
if (!FillBuffer(error))
|
||||||
|
return 0;
|
||||||
|
|
||||||
do {
|
auto r = buffer.Read();
|
||||||
/* fill the buffer */
|
if (r.IsEmpty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!FillBuffer(error))
|
const size_t nbytes = std::min(read_size, r.size);
|
||||||
return 0;
|
memcpy(ptr, r.data, nbytes);
|
||||||
|
buffer.Consume(nbytes);
|
||||||
nbytes = read_from_buffer(icy, buffer, ptr, read_size);
|
|
||||||
} while (nbytes == 0);
|
|
||||||
|
|
||||||
if (icy.IsDefined())
|
|
||||||
CopyIcyTag();
|
|
||||||
|
|
||||||
offset += (InputPlugin::offset_type)nbytes;
|
offset += (InputPlugin::offset_type)nbytes;
|
||||||
|
|
||||||
@ -781,7 +713,7 @@ CurlInputStream::HeaderReceived(const char *name, std::string &&value)
|
|||||||
{
|
{
|
||||||
if (StringEqualsCaseASCII(name, "accept-ranges")) {
|
if (StringEqualsCaseASCII(name, "accept-ranges")) {
|
||||||
/* a stream with icy-metadata is not seekable */
|
/* a stream with icy-metadata is not seekable */
|
||||||
if (!icy.IsDefined())
|
if (!icy->IsEnabled())
|
||||||
seekable = true;
|
seekable = true;
|
||||||
} else if (StringEqualsCaseASCII(name, "content-length")) {
|
} else if (StringEqualsCaseASCII(name, "content-length")) {
|
||||||
size = offset + ParseUint64(value.c_str());
|
size = offset + ParseUint64(value.c_str());
|
||||||
@ -790,23 +722,21 @@ CurlInputStream::HeaderReceived(const char *name, std::string &&value)
|
|||||||
} else if (StringEqualsCaseASCII(name, "icy-name") ||
|
} else if (StringEqualsCaseASCII(name, "icy-name") ||
|
||||||
StringEqualsCaseASCII(name, "ice-name") ||
|
StringEqualsCaseASCII(name, "ice-name") ||
|
||||||
StringEqualsCaseASCII(name, "x-audiocast-name")) {
|
StringEqualsCaseASCII(name, "x-audiocast-name")) {
|
||||||
meta_name = std::move(value);
|
|
||||||
|
|
||||||
delete tag;
|
delete tag;
|
||||||
|
|
||||||
TagBuilder tag_builder;
|
TagBuilder tag_builder;
|
||||||
tag_builder.AddItem(TAG_NAME, meta_name.c_str());
|
tag_builder.AddItem(TAG_NAME, value.c_str());
|
||||||
|
|
||||||
tag = tag_builder.CommitNew();
|
tag = tag_builder.CommitNew();
|
||||||
} else if (StringEqualsCaseASCII(name, "icy-metaint")) {
|
} else if (StringEqualsCaseASCII(name, "icy-metaint")) {
|
||||||
if (icy.IsDefined())
|
if (icy->IsEnabled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t icy_metaint = ParseUint64(value.c_str());
|
size_t icy_metaint = ParseUint64(value.c_str());
|
||||||
FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint);
|
FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint);
|
||||||
|
|
||||||
if (icy_metaint > 0) {
|
if (icy_metaint > 0) {
|
||||||
icy.Start(icy_metaint);
|
icy->Enable(icy_metaint);
|
||||||
|
|
||||||
/* a stream with icy-metadata is not
|
/* a stream with icy-metadata is not
|
||||||
seekable */
|
seekable */
|
||||||
@ -1072,7 +1002,7 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c;
|
return c->icy;
|
||||||
}
|
}
|
||||||
|
|
||||||
static InputStream *
|
static InputStream *
|
||||||
|
Loading…
Reference in New Issue
Block a user