input/curl: move code to CurlInputStream methods
This commit is contained in:
parent
e9f16fca96
commit
23eacbd132
|
@ -177,6 +177,63 @@ struct CurlInputStream {
|
||||||
|
|
||||||
CurlInputStream(const CurlInputStream &) = delete;
|
CurlInputStream(const CurlInputStream &) = delete;
|
||||||
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
||||||
|
|
||||||
|
bool Check(Error &error);
|
||||||
|
|
||||||
|
bool IsEOF() const {
|
||||||
|
return easy == nullptr && buffers.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag *ReadTag();
|
||||||
|
|
||||||
|
bool IsAvailable() const {
|
||||||
|
return postponed_error.IsDefined() || easy == nullptr ||
|
||||||
|
!buffers.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Read(void *ptr, size_t size, Error &error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the current "libcurl easy" handle, and everything
|
||||||
|
* associated with it.
|
||||||
|
*
|
||||||
|
* Runs in the I/O thread.
|
||||||
|
*/
|
||||||
|
void FreeEasy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the current "libcurl easy" handle, and everything associated
|
||||||
|
* with it.
|
||||||
|
*
|
||||||
|
* The mutex must not be locked.
|
||||||
|
*/
|
||||||
|
void FreeEasyIndirect();
|
||||||
|
|
||||||
|
void HeaderReceived(const char *name,
|
||||||
|
const char *value, const char *end);
|
||||||
|
|
||||||
|
size_t DataReceived(const void *ptr, size_t size);
|
||||||
|
|
||||||
|
void Resume();
|
||||||
|
bool FillBuffer(Error &error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the total sizes of all buffers, including
|
||||||
|
* portions that have already been consumed.
|
||||||
|
*
|
||||||
|
* The caller must lock the mutex.
|
||||||
|
*/
|
||||||
|
gcc_pure
|
||||||
|
size_t GetTotalBufferSize() const;
|
||||||
|
|
||||||
|
void CopyIcyTag();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HTTP request is finished.
|
||||||
|
*
|
||||||
|
* Runs in the I/O thread. The caller must not hold locks.
|
||||||
|
*/
|
||||||
|
void RequestDone(CURLcode result, long status);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CurlMulti;
|
class CurlMulti;
|
||||||
|
@ -335,14 +392,14 @@ input_curl_find_request(CURL *easy)
|
||||||
return (CurlInputStream *)p;
|
return (CurlInputStream *)p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
input_curl_resume(CurlInputStream *c)
|
CurlInputStream::Resume()
|
||||||
{
|
{
|
||||||
assert(io_thread_inside());
|
assert(io_thread_inside());
|
||||||
|
|
||||||
if (c->paused) {
|
if (paused) {
|
||||||
c->paused = false;
|
paused = false;
|
||||||
curl_easy_pause(c->easy, CURLPAUSE_CONT);
|
curl_easy_pause(easy, CURLPAUSE_CONT);
|
||||||
|
|
||||||
if (curl_version_num < 0x072000)
|
if (curl_version_num < 0x072000)
|
||||||
/* libcurl older than 7.32.0 does not update
|
/* libcurl older than 7.32.0 does not update
|
||||||
|
@ -445,74 +502,55 @@ CurlMulti::Remove(CurlInputStream *c)
|
||||||
curl_multi_remove_handle(multi, c->easy);
|
curl_multi_remove_handle(multi, c->easy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void
|
||||||
* Frees the current "libcurl easy" handle, and everything associated
|
CurlInputStream::FreeEasy()
|
||||||
* with it.
|
|
||||||
*
|
|
||||||
* Runs in the I/O thread.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
input_curl_easy_free(CurlInputStream *c)
|
|
||||||
{
|
{
|
||||||
assert(io_thread_inside());
|
assert(io_thread_inside());
|
||||||
assert(c != nullptr);
|
|
||||||
|
|
||||||
if (c->easy == nullptr)
|
if (easy == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
curl_multi->Remove(c);
|
curl_multi->Remove(this);
|
||||||
|
|
||||||
curl_easy_cleanup(c->easy);
|
curl_easy_cleanup(easy);
|
||||||
c->easy = nullptr;
|
easy = nullptr;
|
||||||
|
|
||||||
curl_slist_free_all(c->request_headers);
|
curl_slist_free_all(request_headers);
|
||||||
c->request_headers = nullptr;
|
request_headers = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void
|
||||||
* Frees the current "libcurl easy" handle, and everything associated
|
CurlInputStream::FreeEasyIndirect()
|
||||||
* with it.
|
|
||||||
*
|
|
||||||
* The mutex must not be locked.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
input_curl_easy_free_indirect(CurlInputStream *c)
|
|
||||||
{
|
{
|
||||||
BlockingCall(io_thread_get(), [c](){
|
BlockingCall(io_thread_get(), [this](){
|
||||||
input_curl_easy_free(c);
|
FreeEasy();
|
||||||
curl_multi->InvalidateSockets();
|
curl_multi->InvalidateSockets();
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(c->easy == nullptr);
|
assert(easy == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
inline void
|
||||||
* A HTTP request is finished.
|
CurlInputStream::RequestDone(CURLcode result, long status)
|
||||||
*
|
|
||||||
* Runs in the I/O thread. The caller must not hold locks.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
input_curl_request_done(CurlInputStream *c, CURLcode result, long status)
|
|
||||||
{
|
{
|
||||||
assert(io_thread_inside());
|
assert(io_thread_inside());
|
||||||
assert(c != nullptr);
|
assert(!postponed_error.IsDefined());
|
||||||
assert(c->easy == nullptr);
|
|
||||||
assert(!c->postponed_error.IsDefined());
|
|
||||||
|
|
||||||
const ScopeLock protect(c->base.mutex);
|
FreeEasy();
|
||||||
|
|
||||||
|
const ScopeLock protect(base.mutex);
|
||||||
|
|
||||||
if (result != CURLE_OK) {
|
if (result != CURLE_OK) {
|
||||||
c->postponed_error.Format(curl_domain, result,
|
postponed_error.Format(curl_domain, result,
|
||||||
"curl failed: %s", c->error_buffer);
|
"curl failed: %s", error_buffer);
|
||||||
} else if (status < 200 || status >= 300) {
|
} else if (status < 200 || status >= 300) {
|
||||||
c->postponed_error.Format(http_domain, status,
|
postponed_error.Format(http_domain, status,
|
||||||
"got HTTP status %ld",
|
"got HTTP status %ld",
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
|
|
||||||
c->base.ready = true;
|
base.ready = true;
|
||||||
|
base.cond.broadcast();
|
||||||
c->base.cond.broadcast();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -524,8 +562,7 @@ input_curl_handle_done(CURL *easy_handle, CURLcode result)
|
||||||
long status = 0;
|
long status = 0;
|
||||||
curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &status);
|
curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &status);
|
||||||
|
|
||||||
input_curl_easy_free(c);
|
c->RequestDone(result, status);
|
||||||
input_curl_request_done(c, result, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -656,19 +693,11 @@ input_curl_finish(void)
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
size_t
|
||||||
* Determine the total sizes of all buffers, including portions that
|
CurlInputStream::GetTotalBufferSize() const
|
||||||
* have already been consumed.
|
|
||||||
*
|
|
||||||
* The caller must lock the mutex.
|
|
||||||
*/
|
|
||||||
gcc_pure
|
|
||||||
static size_t
|
|
||||||
curl_total_buffer_size(const CurlInputStream *c)
|
|
||||||
{
|
{
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
for (const auto &i : buffers)
|
||||||
for (const auto &i : c->buffers)
|
|
||||||
total += i.TotalSize();
|
total += i.TotalSize();
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
|
@ -678,46 +707,56 @@ CurlInputStream::~CurlInputStream()
|
||||||
{
|
{
|
||||||
delete tag;
|
delete tag;
|
||||||
|
|
||||||
input_curl_easy_free_indirect(this);
|
FreeEasyIndirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
inline bool
|
||||||
input_curl_check(InputStream *is, Error &error)
|
CurlInputStream::Check(Error &error)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)is;
|
bool success = !postponed_error.IsDefined();
|
||||||
|
|
||||||
bool success = !c->postponed_error.IsDefined();
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
error = std::move(c->postponed_error);
|
error = std::move(postponed_error);
|
||||||
c->postponed_error.Clear();
|
postponed_error.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
input_curl_check(InputStream *is, Error &error)
|
||||||
|
{
|
||||||
|
CurlInputStream &c = *(CurlInputStream *)is;
|
||||||
|
return c.Check(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Tag *
|
||||||
|
CurlInputStream::ReadTag()
|
||||||
|
{
|
||||||
|
Tag *result = tag;
|
||||||
|
tag = nullptr;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static Tag *
|
static Tag *
|
||||||
input_curl_tag(InputStream *is)
|
input_curl_tag(InputStream *is)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)is;
|
CurlInputStream &c = *(CurlInputStream *)is;
|
||||||
Tag *tag = c->tag;
|
return c.ReadTag();
|
||||||
|
|
||||||
c->tag = nullptr;
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
inline bool
|
||||||
fill_buffer(CurlInputStream *c, Error &error)
|
CurlInputStream::FillBuffer(Error &error)
|
||||||
{
|
{
|
||||||
while (c->easy != nullptr && c->buffers.empty())
|
while (easy != nullptr && buffers.empty())
|
||||||
c->base.cond.wait(c->base.mutex);
|
base.cond.wait(base.mutex);
|
||||||
|
|
||||||
if (c->postponed_error.IsDefined()) {
|
if (postponed_error.IsDefined()) {
|
||||||
error = std::move(c->postponed_error);
|
error = std::move(postponed_error);
|
||||||
c->postponed_error.Clear();
|
postponed_error.Clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !c->buffers.empty();
|
return !buffers.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
|
@ -770,54 +809,47 @@ read_from_buffer(IcyMetaDataParser &icy, std::list<CurlInputBuffer> &buffers,
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
copy_icy_tag(CurlInputStream *c)
|
CurlInputStream::CopyIcyTag()
|
||||||
{
|
{
|
||||||
Tag *tag = c->icy.ReadTag();
|
Tag *new_tag = icy.ReadTag();
|
||||||
|
if (new_tag == nullptr)
|
||||||
if (tag == nullptr)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
delete c->tag;
|
delete tag;
|
||||||
|
|
||||||
if (!c->meta_name.empty() && !tag->HasType(TAG_NAME)) {
|
if (!meta_name.empty() && !new_tag->HasType(TAG_NAME)) {
|
||||||
TagBuilder tag_builder(std::move(*tag));
|
TagBuilder tag_builder(std::move(*new_tag));
|
||||||
tag_builder.AddItem(TAG_NAME, c->meta_name.c_str());
|
tag_builder.AddItem(TAG_NAME, meta_name.c_str());
|
||||||
*tag = tag_builder.Commit();
|
*new_tag = tag_builder.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
c->tag = tag;
|
tag = new_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
input_curl_available(InputStream *is)
|
input_curl_available(InputStream *is)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)is;
|
const CurlInputStream &c = *(const CurlInputStream *)is;
|
||||||
|
return c.IsAvailable();
|
||||||
return c->postponed_error.IsDefined() || c->easy == nullptr ||
|
|
||||||
!c->buffers.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
inline size_t
|
||||||
input_curl_read(InputStream *is, void *ptr, size_t size,
|
CurlInputStream::Read(void *ptr, size_t size, Error &error)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)is;
|
|
||||||
bool success;
|
|
||||||
size_t nbytes = 0;
|
size_t nbytes = 0;
|
||||||
char *dest = (char *)ptr;
|
char *dest = (char *)ptr;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* fill the buffer */
|
/* fill the buffer */
|
||||||
|
|
||||||
success = fill_buffer(c, error);
|
if (!FillBuffer(error))
|
||||||
if (!success)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* send buffer contents */
|
/* send buffer contents */
|
||||||
|
|
||||||
while (size > 0 && !c->buffers.empty()) {
|
while (size > 0 && !buffers.empty()) {
|
||||||
size_t copy = read_from_buffer(c->icy, c->buffers,
|
size_t copy = read_from_buffer(icy, buffers,
|
||||||
dest + nbytes, size);
|
dest + nbytes, size);
|
||||||
|
|
||||||
nbytes += copy;
|
nbytes += copy;
|
||||||
|
@ -825,24 +857,32 @@ input_curl_read(InputStream *is, void *ptr, size_t size,
|
||||||
}
|
}
|
||||||
} while (nbytes == 0);
|
} while (nbytes == 0);
|
||||||
|
|
||||||
if (c->icy.IsDefined())
|
if (icy.IsDefined())
|
||||||
copy_icy_tag(c);
|
CopyIcyTag();
|
||||||
|
|
||||||
is->offset += (InputPlugin::offset_type)nbytes;
|
base.offset += (InputPlugin::offset_type)nbytes;
|
||||||
|
|
||||||
if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) {
|
if (paused && GetTotalBufferSize() < CURL_RESUME_AT) {
|
||||||
c->base.mutex.unlock();
|
base.mutex.unlock();
|
||||||
|
|
||||||
BlockingCall(io_thread_get(), [c](){
|
BlockingCall(io_thread_get(), [this](){
|
||||||
input_curl_resume(c);
|
Resume();
|
||||||
});
|
});
|
||||||
|
|
||||||
c->base.mutex.lock();
|
base.mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
input_curl_read(InputStream *is, void *ptr, size_t size,
|
||||||
|
Error &error)
|
||||||
|
{
|
||||||
|
CurlInputStream &c = *(CurlInputStream *)is;
|
||||||
|
return c.Read(ptr, size, error);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_curl_close(InputStream *is)
|
input_curl_close(InputStream *is)
|
||||||
{
|
{
|
||||||
|
@ -854,23 +894,78 @@ input_curl_close(InputStream *is)
|
||||||
static bool
|
static bool
|
||||||
input_curl_eof(gcc_unused InputStream *is)
|
input_curl_eof(gcc_unused InputStream *is)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)is;
|
const CurlInputStream &c = *(const CurlInputStream *)is;
|
||||||
|
return c.IsEOF();
|
||||||
|
}
|
||||||
|
|
||||||
return c->easy == nullptr && c->buffers.empty();
|
inline void
|
||||||
|
CurlInputStream::HeaderReceived(const char *name,
|
||||||
|
const char *value, const char *end)
|
||||||
|
{
|
||||||
|
if (StringEqualsCaseASCII(name, "accept-ranges")) {
|
||||||
|
/* a stream with icy-metadata is not seekable */
|
||||||
|
if (!icy.IsDefined())
|
||||||
|
base.seekable = true;
|
||||||
|
} else if (StringEqualsCaseASCII(name, "content-length")) {
|
||||||
|
char buffer[64];
|
||||||
|
|
||||||
|
if ((size_t)(end - value) >= sizeof(buffer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memcpy(buffer, value, end - value);
|
||||||
|
buffer[end - value] = 0;
|
||||||
|
|
||||||
|
base.size = base.offset + ParseUint64(buffer);
|
||||||
|
} else if (StringEqualsCaseASCII(name, "content-type")) {
|
||||||
|
base.mime.assign(value, end);
|
||||||
|
} else if (StringEqualsCaseASCII(name, "icy-name") ||
|
||||||
|
StringEqualsCaseASCII(name, "ice-name") ||
|
||||||
|
StringEqualsCaseASCII(name, "x-audiocast-name")) {
|
||||||
|
meta_name.assign(value, end);
|
||||||
|
|
||||||
|
delete tag;
|
||||||
|
|
||||||
|
TagBuilder tag_builder;
|
||||||
|
tag_builder.AddItem(TAG_NAME, meta_name.c_str());
|
||||||
|
|
||||||
|
tag = tag_builder.CommitNew();
|
||||||
|
} else if (StringEqualsCaseASCII(name, "icy-metaint")) {
|
||||||
|
char buffer[64];
|
||||||
|
size_t icy_metaint;
|
||||||
|
|
||||||
|
if ((size_t)(end - value) >= sizeof(buffer) ||
|
||||||
|
icy.IsDefined())
|
||||||
|
return;
|
||||||
|
|
||||||
|
memcpy(buffer, value, end - value);
|
||||||
|
buffer[end - value] = 0;
|
||||||
|
|
||||||
|
icy_metaint = ParseUint64(buffer);
|
||||||
|
FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint);
|
||||||
|
|
||||||
|
if (icy_metaint > 0) {
|
||||||
|
icy.Start(icy_metaint);
|
||||||
|
|
||||||
|
/* a stream with icy-metadata is not
|
||||||
|
seekable */
|
||||||
|
base.seekable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** called by curl when new data is available */
|
/** called by curl when new data is available */
|
||||||
static size_t
|
static size_t
|
||||||
input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)stream;
|
CurlInputStream &c = *(CurlInputStream *)stream;
|
||||||
char name[64];
|
|
||||||
|
|
||||||
size *= nmemb;
|
size *= nmemb;
|
||||||
|
|
||||||
const char *header = (const char *)ptr;
|
const char *header = (const char *)ptr;
|
||||||
const char *end = header + size;
|
const char *end = header + size;
|
||||||
|
|
||||||
|
char name[64];
|
||||||
|
|
||||||
const char *value = (const char *)memchr(header, ':', size);
|
const char *value = (const char *)memchr(header, ':', size);
|
||||||
if (value == nullptr || (size_t)(value - header) >= sizeof(name))
|
if (value == nullptr || (size_t)(value - header) >= sizeof(name))
|
||||||
return size;
|
return size;
|
||||||
|
@ -890,56 +985,25 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||||
while (end > value && IsWhitespaceOrNull(end[-1]))
|
while (end > value && IsWhitespaceOrNull(end[-1]))
|
||||||
--end;
|
--end;
|
||||||
|
|
||||||
if (StringEqualsCaseASCII(name, "accept-ranges")) {
|
c.HeaderReceived(name, value, end);
|
||||||
/* a stream with icy-metadata is not seekable */
|
return size;
|
||||||
if (!c->icy.IsDefined())
|
}
|
||||||
c->base.seekable = true;
|
|
||||||
} else if (StringEqualsCaseASCII(name, "content-length")) {
|
|
||||||
char buffer[64];
|
|
||||||
|
|
||||||
if ((size_t)(end - header) >= sizeof(buffer))
|
inline size_t
|
||||||
return size;
|
CurlInputStream::DataReceived(const void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
assert(size > 0);
|
||||||
|
|
||||||
memcpy(buffer, value, end - value);
|
const ScopeLock protect(base.mutex);
|
||||||
buffer[end - value] = 0;
|
|
||||||
|
|
||||||
c->base.size = c->base.offset + ParseUint64(buffer);
|
if (GetTotalBufferSize() + size >= CURL_MAX_BUFFERED) {
|
||||||
} else if (StringEqualsCaseASCII(name, "content-type")) {
|
paused = true;
|
||||||
c->base.mime.assign(value, end);
|
return CURL_WRITEFUNC_PAUSE;
|
||||||
} else if (StringEqualsCaseASCII(name, "icy-name") ||
|
|
||||||
StringEqualsCaseASCII(name, "ice-name") ||
|
|
||||||
StringEqualsCaseASCII(name, "x-audiocast-name")) {
|
|
||||||
c->meta_name.assign(value, end);
|
|
||||||
|
|
||||||
delete c->tag;
|
|
||||||
|
|
||||||
TagBuilder tag_builder;
|
|
||||||
tag_builder.AddItem(TAG_NAME, c->meta_name.c_str());
|
|
||||||
|
|
||||||
c->tag = tag_builder.CommitNew();
|
|
||||||
} else if (StringEqualsCaseASCII(name, "icy-metaint")) {
|
|
||||||
char buffer[64];
|
|
||||||
size_t icy_metaint;
|
|
||||||
|
|
||||||
if ((size_t)(end - header) >= sizeof(buffer) ||
|
|
||||||
c->icy.IsDefined())
|
|
||||||
return size;
|
|
||||||
|
|
||||||
memcpy(buffer, value, end - value);
|
|
||||||
buffer[end - value] = 0;
|
|
||||||
|
|
||||||
icy_metaint = ParseUint64(buffer);
|
|
||||||
FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint);
|
|
||||||
|
|
||||||
if (icy_metaint > 0) {
|
|
||||||
c->icy.Start(icy_metaint);
|
|
||||||
|
|
||||||
/* a stream with icy-metadata is not
|
|
||||||
seekable */
|
|
||||||
c->base.seekable = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffers.emplace_back(ptr, size);
|
||||||
|
base.ready = true;
|
||||||
|
base.cond.broadcast();
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,24 +1011,13 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||||
static size_t
|
static size_t
|
||||||
input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||||
{
|
{
|
||||||
CurlInputStream *c = (CurlInputStream *)stream;
|
CurlInputStream &c = *(CurlInputStream *)stream;
|
||||||
|
|
||||||
size *= nmemb;
|
size *= nmemb;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const ScopeLock protect(c->base.mutex);
|
return c.DataReceived(ptr, size);
|
||||||
|
|
||||||
if (curl_total_buffer_size(c) + size >= CURL_MAX_BUFFERED) {
|
|
||||||
c->paused = true;
|
|
||||||
return CURL_WRITEFUNC_PAUSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->buffers.emplace_back(ptr, size);
|
|
||||||
c->base.ready = true;
|
|
||||||
|
|
||||||
c->base.cond.broadcast();
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -1091,7 +1144,7 @@ input_curl_seek(InputStream *is, InputPlugin::offset_type offset,
|
||||||
|
|
||||||
c->base.mutex.unlock();
|
c->base.mutex.unlock();
|
||||||
|
|
||||||
input_curl_easy_free_indirect(c);
|
c->FreeEasyIndirect();
|
||||||
c->buffers.clear();
|
c->buffers.clear();
|
||||||
|
|
||||||
is->offset = offset;
|
is->offset = offset;
|
||||||
|
|
Loading…
Reference in New Issue