http: use libcurl
MPD's HTTP client code has always been broken, no matter how effort was put into fixing it. Replace it with libcurl, which is known to be quite stable. This adds a fat library dependency, but only for people who need streaming.
This commit is contained in:
parent
6b09e4daef
commit
3609de8685
4
INSTALL
4
INSTALL
|
@ -70,6 +70,10 @@ For Zeroconf support.
|
|||
libsamplerate - http://www.mega-nerd.com/SRC/
|
||||
For advanced samplerate conversions.
|
||||
|
||||
libcurl - http://curl.haxx.se/
|
||||
For playing HTTP streams.
|
||||
|
||||
|
||||
Download
|
||||
--------
|
||||
|
||||
|
|
18
configure.ac
18
configure.ac
|
@ -102,6 +102,17 @@ AC_ARG_ENABLE(un,
|
|||
[enable_un=yes])
|
||||
|
||||
|
||||
dnl
|
||||
dnl input options
|
||||
dnl
|
||||
|
||||
AC_ARG_ENABLE(curl,
|
||||
AS_HELP_STRING([--disable-curl],
|
||||
[enable support obtaining song data via HTTP (default: enable)]),
|
||||
[enable_curl=$enableval],
|
||||
[enable_curl=yes])
|
||||
|
||||
|
||||
dnl
|
||||
dnl audio output plugins
|
||||
dnl
|
||||
|
@ -316,6 +327,13 @@ case $host in
|
|||
enable_osx=yes ;;
|
||||
esac
|
||||
|
||||
if test x$enable_curl = xyes; then
|
||||
PKG_CHECK_MODULES(CURL, [libcurl],
|
||||
AC_DEFINE(HAVE_CURL, 1, [Define when libcurl is used for HTTP streaming]),
|
||||
enable_curl=no)
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_CURL, test x$enable_curl = xyes)
|
||||
|
||||
if test x$enable_shout_ogg = xyes || x$enable_shout_mp3 = xyes; then
|
||||
enable_shout=yes
|
||||
PKG_CHECK_MODULES([SHOUT], [shout],
|
||||
|
|
|
@ -49,8 +49,6 @@ mpd_headers = \
|
|||
decoder/_ogg_common.h \
|
||||
inputStream.h \
|
||||
inputStream_file.h \
|
||||
inputStream_http.h \
|
||||
inputStream_http_auth.h \
|
||||
client.h \
|
||||
list.h \
|
||||
dlist.h \
|
||||
|
@ -132,7 +130,6 @@ mpd_SOURCES = \
|
|||
decoder_list.c \
|
||||
inputStream.c \
|
||||
inputStream_file.c \
|
||||
inputStream_http.c \
|
||||
client.c \
|
||||
ioops.c \
|
||||
list.c \
|
||||
|
@ -246,8 +243,14 @@ mpd_SOURCES += zeroconf.c
|
|||
endif
|
||||
|
||||
|
||||
if HAVE_CURL
|
||||
mpd_SOURCES += input_curl.c
|
||||
endif
|
||||
|
||||
|
||||
mpd_CFLAGS = $(MPD_CFLAGS)
|
||||
mpd_CPPFLAGS = \
|
||||
$(CURL_CFLAGS) \
|
||||
$(AO_CFLAGS) $(ALSA_CFLAGS) \
|
||||
$(SHOUT_CFLAGS) \
|
||||
$(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \
|
||||
|
@ -258,6 +261,7 @@ mpd_CPPFLAGS = \
|
|||
$(FFMPEG_CFLAGS) \
|
||||
$(GLIB_CFLAGS)
|
||||
mpd_LDADD = $(MPD_LIBS) \
|
||||
$(CURL_LIBS) \
|
||||
$(AO_LIBS) $(ALSA_LIBS) \
|
||||
$(SHOUT_LIBS) \
|
||||
$(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \
|
||||
|
|
|
@ -17,20 +17,29 @@
|
|||
*/
|
||||
|
||||
#include "inputStream.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "inputStream_file.h"
|
||||
#include "inputStream_http.h"
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
#include "input_curl.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void initInputStream(void)
|
||||
{
|
||||
inputStream_initFile();
|
||||
inputStream_initHttp();
|
||||
#ifdef HAVE_CURL
|
||||
input_curl_global_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void input_stream_global_finish(void)
|
||||
{
|
||||
#ifdef HAVE_CURL
|
||||
input_curl_global_finish();
|
||||
#endif
|
||||
}
|
||||
|
||||
int openInputStream(struct input_stream *inStream, char *url)
|
||||
|
@ -46,8 +55,11 @@ int openInputStream(struct input_stream *inStream, char *url)
|
|||
|
||||
if (inputStream_fileOpen(inStream, url) == 0)
|
||||
return 0;
|
||||
if (inputStream_httpOpen(inStream, url) == 0)
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
if (input_curl_open(inStream, url))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +0,0 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
||||
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* This file is only included by inputStream_http.c */
|
||||
#ifndef INPUT_STREAM_HTTP_AUTH_H
|
||||
#define INPUT_STREAM_HTTP_AUTH_H
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* base64 code taken from xmms */
|
||||
|
||||
#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
|
||||
|
||||
static char *base64dup(char *s)
|
||||
{
|
||||
int i;
|
||||
int len = strlen(s);
|
||||
char *ret = xcalloc(BASE64_LENGTH(len) + 1, 1);
|
||||
unsigned char *p = (unsigned char *)ret;
|
||||
|
||||
static const char tbl[64] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
|
||||
for (i = 0; i < len; i += 3) {
|
||||
*p++ = tbl[s[0] >> 2];
|
||||
*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
|
||||
*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
|
||||
*p++ = tbl[s[2] & 0x3f];
|
||||
s += 3;
|
||||
}
|
||||
/* Pad the result if necessary... */
|
||||
if (i == len + 1)
|
||||
*(p - 1) = '=';
|
||||
else if (i == len + 2)
|
||||
*(p - 1) = *(p - 2) = '=';
|
||||
/* ...and zero-terminate it. */
|
||||
*p = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *auth_string(const char *header,
|
||||
const char *user, const char *password)
|
||||
{
|
||||
char *ret = NULL;
|
||||
int templen;
|
||||
char *temp;
|
||||
char *temp64;
|
||||
|
||||
if (!user || !password)
|
||||
return NULL;
|
||||
|
||||
templen = strlen(user) + strlen(password) + 2;
|
||||
temp = xmalloc(templen);
|
||||
strcpy(temp, user);
|
||||
strcat(temp, ":");
|
||||
strcat(temp, password);
|
||||
temp64 = base64dup(temp);
|
||||
free(temp);
|
||||
|
||||
ret = xmalloc(strlen(temp64) + strlen(header) + 3);
|
||||
strcpy(ret, header);
|
||||
strcat(ret, temp64);
|
||||
strcat(ret, "\r\n");
|
||||
free(temp64);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define PROXY_AUTH_HEADER "Proxy-Authorization: Basic "
|
||||
#define HTTP_AUTH_HEADER "Authorization: Basic "
|
||||
|
||||
#define proxy_auth_string(x, y) auth_string(PROXY_AUTH_HEADER, x, y)
|
||||
#define http_auth_string(x, y) auth_string(HTTP_AUTH_HEADER, x, y)
|
||||
|
||||
#endif /* INPUT_STREAM_HTTP_AUTH_H */
|
|
@ -0,0 +1,493 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
|
||||
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "input_curl.h"
|
||||
#include "inputStream.h"
|
||||
#include "gcc.h"
|
||||
#include "dlist.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* Buffers created by input_curl_writefunction().
|
||||
*/
|
||||
struct buffer {
|
||||
struct list_head siblings;
|
||||
|
||||
/** size of the payload */
|
||||
size_t size;
|
||||
|
||||
/** how much has been consumed yet? */
|
||||
size_t consumed;
|
||||
|
||||
/** the payload */
|
||||
unsigned char data[sizeof(long)];
|
||||
};
|
||||
|
||||
struct input_curl {
|
||||
/* some buffers which were passed to libcurl, which we have
|
||||
too free */
|
||||
char *url, *range;
|
||||
struct curl_slist *request_headers;
|
||||
|
||||
/** the curl handles */
|
||||
CURL *easy;
|
||||
CURLM *multi;
|
||||
|
||||
/** list of buffers, where input_curl_writefunction() appends
|
||||
to, and input_curl_read() reads from them */
|
||||
struct list_head buffers;
|
||||
|
||||
/** has something been added to the buffers list? */
|
||||
bool buffered;
|
||||
|
||||
/** did libcurl tell us the we're at the end of the response body? */
|
||||
bool eof;
|
||||
};
|
||||
|
||||
/** libcurl should accept "ICY 200 OK" */
|
||||
static struct curl_slist *http_200_aliases;
|
||||
|
||||
void input_curl_global_init(void)
|
||||
{
|
||||
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (code != CURLE_OK)
|
||||
g_warning("curl_global_init() failed: %s\n",
|
||||
curl_easy_strerror(code));
|
||||
|
||||
http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK");
|
||||
}
|
||||
|
||||
void input_curl_global_finish(void)
|
||||
{
|
||||
curl_slist_free_all(http_200_aliases);
|
||||
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the current "libcurl easy" handle, and everything associated
|
||||
* with it.
|
||||
*/
|
||||
static void
|
||||
input_curl_easy_free(struct input_curl *c)
|
||||
{
|
||||
if (c->easy != NULL) {
|
||||
curl_multi_remove_handle(c->multi, c->easy);
|
||||
curl_easy_cleanup(c->easy);
|
||||
c->easy = NULL;
|
||||
}
|
||||
|
||||
curl_slist_free_all(c->request_headers);
|
||||
c->request_headers = NULL;
|
||||
|
||||
g_free(c->range);
|
||||
c->range = NULL;
|
||||
|
||||
while (!list_empty(&c->buffers)) {
|
||||
struct buffer *buffer = (struct buffer *)c->buffers.next;
|
||||
list_del(&buffer->siblings);
|
||||
|
||||
g_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees this stream (but not the input_stream struct itself).
|
||||
*/
|
||||
static void
|
||||
input_curl_free(struct input_stream *is)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
|
||||
input_curl_easy_free(c);
|
||||
|
||||
if (c->multi != NULL)
|
||||
curl_multi_cleanup(c->multi);
|
||||
|
||||
g_free(c->url);
|
||||
g_free(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the libcurl socket.
|
||||
*
|
||||
* @return -1 on error, 0 if no data is available yet, 1 if data is
|
||||
* available
|
||||
*/
|
||||
static int
|
||||
input_curl_select(struct input_curl *c)
|
||||
{
|
||||
fd_set rfds, wfds, efds;
|
||||
int max_fd, ret;
|
||||
CURLMcode mcode;
|
||||
/* XXX hard coded timeout value.. */
|
||||
struct timeval timeout = {
|
||||
.tv_sec = 1,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&efds);
|
||||
|
||||
mcode = curl_multi_fdset(c->multi, &rfds, &wfds, &efds, &max_fd);
|
||||
if (mcode != CURLM_OK) {
|
||||
g_warning("curl_multi_fdset() failed: %s\n",
|
||||
curl_multi_strerror(mcode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(max_fd >= 0);
|
||||
|
||||
ret = select(max_fd + 1, &rfds, &wfds, &efds, &timeout);
|
||||
if (ret < 0)
|
||||
g_warning("select() failed: %s\n", strerror(errno));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t
|
||||
read_from_buffer(struct buffer *buffer, void *dest, size_t length)
|
||||
{
|
||||
assert(buffer->size > 0);
|
||||
assert(buffer->consumed < buffer->size);
|
||||
|
||||
if (length > buffer->size - buffer->consumed)
|
||||
length = buffer->size - buffer->consumed;
|
||||
|
||||
memcpy(dest, buffer->data + buffer->consumed, length);
|
||||
|
||||
buffer->consumed += length;
|
||||
if (buffer->consumed == buffer->size) {
|
||||
list_del(&buffer->siblings);
|
||||
g_free(buffer);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static size_t
|
||||
input_curl_read(struct input_stream *is, void *ptr, size_t size)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
CURLMcode mcode = CURLM_CALL_MULTI_PERFORM;
|
||||
size_t nbytes = 0;
|
||||
char *dest = ptr;
|
||||
|
||||
/* fill the buffer */
|
||||
|
||||
while (!c->eof && list_empty(&c->buffers)) {
|
||||
int running_handles;
|
||||
|
||||
if (mcode != CURLM_CALL_MULTI_PERFORM) {
|
||||
/* if we're still here, there is no input yet
|
||||
- wait for input */
|
||||
int ret = input_curl_select(c);
|
||||
if (ret <= 0)
|
||||
/* no data yet or error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
mcode = curl_multi_perform(c->multi, &running_handles);
|
||||
if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) {
|
||||
g_warning("curl_multi_perform() failed: %s\n",
|
||||
curl_multi_strerror(mcode));
|
||||
c->eof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->eof = running_handles == 0;
|
||||
}
|
||||
|
||||
/* send buffer contents */
|
||||
|
||||
while (size > 0 && !list_empty(&c->buffers)) {
|
||||
struct buffer *buffer = (struct buffer *)c->buffers.next;
|
||||
size_t copy = read_from_buffer(buffer, dest + nbytes, size);
|
||||
|
||||
nbytes += copy;
|
||||
size -= copy;
|
||||
}
|
||||
|
||||
is->offset += (off_t)nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static int
|
||||
input_curl_close(struct input_stream *is)
|
||||
{
|
||||
input_curl_free(is);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
input_curl_eof(mpd_unused struct input_stream *is)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
|
||||
return c->eof && list_empty(&c->buffers);
|
||||
}
|
||||
|
||||
static int
|
||||
input_curl_buffer(mpd_unused struct input_stream *is)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
CURLMcode mcode;
|
||||
int running_handles;
|
||||
|
||||
c->buffered = false;
|
||||
|
||||
do {
|
||||
mcode = curl_multi_perform(c->multi, &running_handles);
|
||||
} while (mcode == CURLM_CALL_MULTI_PERFORM && list_empty(&c->buffers));
|
||||
|
||||
if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) {
|
||||
g_warning("curl_multi_perform() failed: %s\n",
|
||||
curl_multi_strerror(mcode));
|
||||
c->eof = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->eof = running_handles == 0;
|
||||
|
||||
return c->buffered;
|
||||
}
|
||||
|
||||
/** called by curl when new data is available */
|
||||
static size_t
|
||||
input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
struct input_stream *is = stream;
|
||||
const char *header = ptr, *end, *colon;
|
||||
char name[64];
|
||||
|
||||
size *= nmemb;
|
||||
end = header + size;
|
||||
|
||||
colon = memchr(header, ':', size);
|
||||
if (colon == NULL || (size_t)(colon - header) >= sizeof(name))
|
||||
return size;
|
||||
|
||||
memcpy(name, header, colon - header);
|
||||
name[colon - header] = 0;
|
||||
|
||||
if (strcasecmp(name, "accept-ranges") == 0)
|
||||
is->seekable = true;
|
||||
else if (strcasecmp(name, "content-length") == 0) {
|
||||
char value[64];
|
||||
|
||||
header = colon + 1;
|
||||
while (header < end && header[0] == ' ')
|
||||
++header;
|
||||
|
||||
if ((size_t)(end - header) >= sizeof(value))
|
||||
return size;
|
||||
|
||||
memcpy(value, header, end - header);
|
||||
value[end - header] = 0;
|
||||
|
||||
is->size = is->offset + g_ascii_strtoull(value, NULL, 10);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/** called by curl when new data is available */
|
||||
static size_t
|
||||
input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
struct input_stream *is = stream;
|
||||
struct input_curl *c = is->data;
|
||||
struct buffer *buffer;
|
||||
|
||||
size *= nmemb;
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size);
|
||||
buffer->size = size;
|
||||
buffer->consumed = 0;
|
||||
memcpy(buffer->data, ptr, size);
|
||||
list_add_tail(&buffer->siblings, &c->buffers);
|
||||
|
||||
c->buffered = true;
|
||||
is->ready = true;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool
|
||||
input_curl_easy_init(struct input_stream *is)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
CURLcode code;
|
||||
CURLMcode mcode;
|
||||
|
||||
c->eof = false;
|
||||
|
||||
c->easy = curl_easy_init();
|
||||
if (c->easy == NULL) {
|
||||
g_warning("curl_easy_init() failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mcode = curl_multi_add_handle(c->multi, c->easy);
|
||||
if (mcode != CURLM_OK)
|
||||
return false;
|
||||
|
||||
curl_easy_setopt(c->easy, CURLOPT_HEADERFUNCTION,
|
||||
input_curl_headerfunction);
|
||||
curl_easy_setopt(c->easy, CURLOPT_WRITEHEADER, is);
|
||||
curl_easy_setopt(c->easy, CURLOPT_WRITEFUNCTION,
|
||||
input_curl_writefunction);
|
||||
curl_easy_setopt(c->easy, CURLOPT_WRITEDATA, is);
|
||||
curl_easy_setopt(c->easy, CURLOPT_HTTP200ALIASES, http_200_aliases);
|
||||
|
||||
code = curl_easy_setopt(c->easy, CURLOPT_URL, c->url);
|
||||
if (code != CURLE_OK)
|
||||
return false;
|
||||
|
||||
c->request_headers = NULL;
|
||||
c->request_headers = curl_slist_append(c->request_headers,
|
||||
"Icy-Metadata: 1");
|
||||
curl_easy_setopt(c->easy, CURLOPT_HTTPHEADER, c->request_headers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
input_curl_send_request(struct input_curl *c)
|
||||
{
|
||||
CURLMcode mcode;
|
||||
int running_handles;
|
||||
|
||||
do {
|
||||
mcode = curl_multi_perform(c->multi, &running_handles);
|
||||
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
||||
|
||||
c->eof = running_handles == 0;
|
||||
|
||||
if (mcode != CURLM_OK) {
|
||||
g_warning("curl_multi_perform() failed: %s\n",
|
||||
curl_multi_strerror(mcode));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
input_curl_seek(struct input_stream *is, mpd_unused long offset,
|
||||
mpd_unused int whence)
|
||||
{
|
||||
struct input_curl *c = is->data;
|
||||
bool ret;
|
||||
|
||||
if (!is->seekable)
|
||||
return -1;
|
||||
|
||||
/* calculate the absolute offset */
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
is->offset = (off_t)offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
is->offset += (off_t)offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
is->offset = (off_t)is->size + (off_t)offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is->offset < 0)
|
||||
return -1;
|
||||
|
||||
/* close the old connection and open a new one */
|
||||
|
||||
input_curl_easy_free(c);
|
||||
|
||||
ret = input_curl_easy_init(is);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
/* send the "Range" header */
|
||||
|
||||
if (is->offset > 0) {
|
||||
c->range = g_strdup_printf("%ld-", is->offset); /* XXX 64 bit safety */
|
||||
curl_easy_setopt(c->easy, CURLOPT_RANGE, c->range);
|
||||
}
|
||||
|
||||
ret = input_curl_send_request(c);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool input_curl_open(struct input_stream *is, char *url)
|
||||
{
|
||||
struct input_curl *c;
|
||||
bool ret;
|
||||
|
||||
c = g_new0(struct input_curl, 1);
|
||||
c->url = g_strdup(url);
|
||||
INIT_LIST_HEAD(&c->buffers);
|
||||
|
||||
is->data = c;
|
||||
|
||||
c->multi = curl_multi_init();
|
||||
if (c->multi == NULL) {
|
||||
g_warning("curl_multi_init() failed\n");
|
||||
|
||||
input_curl_free(is);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = input_curl_easy_init(is);
|
||||
if (!ret) {
|
||||
input_curl_free(is);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = input_curl_send_request(c);
|
||||
if (!ret) {
|
||||
input_curl_free(is);
|
||||
return false;
|
||||
}
|
||||
|
||||
is->seekFunc = input_curl_seek;
|
||||
is->closeFunc = input_curl_close;
|
||||
is->readFunc = input_curl_read;
|
||||
is->atEOFFunc = input_curl_eof;
|
||||
is->bufferFunc = input_curl_buffer;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
||||
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -16,23 +16,17 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef INPUT_STREAM_HTTP_H
|
||||
#define INPUT_STREAM_HTTP_H
|
||||
#ifndef MPD_INPUT_CURL_H
|
||||
#define MPD_INPUT_CURL_H
|
||||
|
||||
#include "inputStream.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
void inputStream_initHttp(void);
|
||||
struct input_stream;
|
||||
|
||||
int inputStream_httpOpen(InputStream * inStream, char *filename);
|
||||
void input_curl_global_init(void);
|
||||
|
||||
int inputStream_httpSeek(InputStream * inStream, long offset, int whence);
|
||||
void input_curl_global_finish(void);
|
||||
|
||||
size_t inputStream_httpRead(InputStream * inStream, void *ptr, size_t size);
|
||||
|
||||
int inputStream_httpClose(InputStream * inStream);
|
||||
|
||||
int inputStream_httpAtEOF(InputStream * inStream);
|
||||
|
||||
int inputStream_httpBuffer(InputStream * inStream);
|
||||
bool input_curl_open(struct input_stream *is, char *url);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue