From b8d8df132b8e50b754f1e579c5b18f64d543717f Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Fri, 20 Mar 2015 23:40:42 +0100 Subject: [PATCH] Support for remote m3u playlists (ref pr #79) --- src/Makefile.am | 2 +- src/filescanner_ffmpeg.c | 24 +- src/http.c | 472 +++++++++++++++++++++++++++++++++++++++ src/http.h | 89 ++++++++ src/icy.c | 219 ------------------ src/icy.h | 28 --- src/logger.c | 2 +- src/logger.h | 43 ++-- src/player.c | 21 +- src/raop.c | 6 +- src/transcode.c | 10 +- src/transcode.h | 4 +- 12 files changed, 628 insertions(+), 292 deletions(-) create mode 100644 src/http.c create mode 100644 src/http.h delete mode 100644 src/icy.c delete mode 100644 src/icy.h diff --git a/src/Makefile.am b/src/Makefile.am index 821e214d..8685f87c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,11 +110,11 @@ forked_daapd_SOURCES = main.c \ httpd_rsp.c httpd_rsp.h \ httpd_daap.c httpd_daap.h \ httpd_dacp.c httpd_dacp.h \ + http.c http.h \ dmap_common.c dmap_common.h \ transcode.c transcode.h \ pipe.c pipe.h \ artwork.c artwork.h \ - icy.h icy.c \ misc.c misc.h \ rng.c rng.h \ rsp_query.c rsp_query.h \ diff --git a/src/filescanner_ffmpeg.c b/src/filescanner_ffmpeg.c index 06560bfd..3c8637f9 100644 --- a/src/filescanner_ffmpeg.c +++ b/src/filescanner_ffmpeg.c @@ -37,7 +37,7 @@ #include "logger.h" #include "filescanner.h" #include "misc.h" -#include "icy.h" +#include "http.h" /* Legacy format-specific scanners */ @@ -321,7 +321,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) AVFormatContext *ctx; AVDictionary *options; const struct metadata_map *extra_md_map; - struct icy_metadata *icy_metadata; + struct http_icy_metadata *icy_metadata; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) enum AVCodecID codec_id; enum AVCodecID video_codec_id; @@ -333,12 +333,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) #endif AVStream *video_stream; AVStream *audio_stream; + char *path; int mdcount; int i; int ret; ctx = NULL; options = NULL; + path = strdup(file); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) # ifndef HAVE_FFMPEG @@ -352,21 +354,29 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) if (mfi->data_kind == 1) { + free(path); + ret = http_stream_setup(&path, file); + if (ret < 0) + return -1; + av_dict_set(&options, "icy", "1", 0); mfi->artwork = ARTWORK_HTTP; } - ret = avformat_open_input(&ctx, file, NULL, &options); + ret = avformat_open_input(&ctx, path, NULL, &options); #else - ret = av_open_input_file(&ctx, file, NULL, 0, NULL); + ret = av_open_input_file(&ctx, path, NULL, 0, NULL); #endif if (ret != 0) { - DPRINTF(E_WARN, L_SCAN, "Cannot open media file '%s': %s\n", file, strerror(AVUNERROR(ret))); + DPRINTF(E_WARN, L_SCAN, "Cannot open media file '%s': %s\n", path, strerror(AVUNERROR(ret))); + free(path); return -1; } + free(path); + #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) ret = avformat_find_stream_info(ctx, NULL); #else @@ -485,7 +495,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) /* Try to extract ICY metadata if url/stream */ if (mfi->data_kind == 1) { - icy_metadata = icy_metadata_get(ctx, 0); + icy_metadata = http_icy_metadata_get(ctx, 0); if (icy_metadata && icy_metadata->name) { DPRINTF(E_DBG, L_SCAN, "libav/ffmpeg found ICY metadata, name is '%s'\n", icy_metadata->name); @@ -520,7 +530,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) mfi->genre = strdup(icy_metadata->genre); } if (icy_metadata) - icy_metadata_free(icy_metadata); + http_icy_metadata_free(icy_metadata); } /* Get some more information on the audio stream */ diff --git a/src/http.c b/src/http.c new file mode 100644 index 00000000..cb71fd39 --- /dev/null +++ b/src/http.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2015 Espen Jürgensen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http.h" +#include "logger.h" +#include "misc.h" + +/* ======================= libevent HTTP client =============================*/ + +// Number of seconds the client will wait for a response before aborting +#define HTTP_CLIENT_TIMEOUT 5 + +static void +request_cb(struct evhttp_request *req, void *arg) +{ + struct http_client_ctx *ctx; + const char *response_code_line; + int response_code; + + ctx = (struct http_client_ctx *)arg; + + response_code = evhttp_request_get_response_code(req); + response_code_line = evhttp_request_get_response_code_line(req); + + if (req == NULL) + { + DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection timed out\n", ctx->url); + goto connection_error; + } + else if (response_code == 0) + { + DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url); + goto connection_error; + } + else if (response_code != 200) + { + DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: %s (error %d)\n", ctx->url, response_code_line, response_code); + goto connection_error; + } + + /* Async: we make a callback to caller, Sync: we move the body into the callers evbuf */ + ctx->ret = 0; + + if (ctx->async) + ctx->cb(req, arg); + else + evbuffer_add_buffer(ctx->evbuf, evhttp_request_get_input_buffer(req)); + + event_base_loopbreak(ctx->evbase); + + if (ctx->async) + free(ctx); + + return; + + connection_error: + + ctx->ret = -1; + + event_base_loopbreak(ctx->evbase); + + if (ctx->async) + free(ctx); + + return; +} + +static void * +request_make(void *arg) +{ + struct http_client_ctx *ctx; + struct evhttp_connection *evcon; + struct evhttp_request *req; + struct evkeyvalq *headers; + char hostname[PATH_MAX]; + char path[PATH_MAX]; + char s[PATH_MAX]; + int port; + int ret; + + ctx = (struct http_client_ctx *)arg; + + ctx->ret = -1; + + av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), ctx->url); + if (strlen(hostname) == 0) + { + DPRINTF(E_LOG, L_HTTP, "Error extracting hostname from URL: %s\n", ctx->url); + + return NULL; + } + + if (port <= 0) + port = 80; + + if (strlen(path) == 0) + { + path[0] = '/'; + path[1] = '\0'; + } + + ctx->evbase = event_base_new(); + if (!ctx->evbase) + { + DPRINTF(E_LOG, L_HTTP, "Could not create or find http client event base\n"); + + return NULL; + } + + evcon = evhttp_connection_base_new(ctx->evbase, NULL, hostname, (unsigned short)port); + if (!evcon) + { + DPRINTF(E_LOG, L_HTTP, "Could not create connection to %s\n", hostname); + + event_base_free(ctx->evbase); + return NULL; + } + + evhttp_connection_set_timeout(evcon, HTTP_CLIENT_TIMEOUT); + + /* Set up request */ + req = evhttp_request_new(request_cb, ctx); + if (!req) + { + DPRINTF(E_LOG, L_HTTP, "Could not create request to %s\n", hostname); + + evhttp_connection_free(evcon); + event_base_free(ctx->evbase); + return NULL; + } + + headers = evhttp_request_get_output_headers(req); + snprintf(s, PATH_MAX, "%s:%d", hostname, port); + evhttp_add_header(headers, "Host", s); + evhttp_add_header(headers, "Content-Length", "0"); + evhttp_add_header(headers, "User-Agent", "forked-daapd/" VERSION); + + /* Make request */ + DPRINTF(E_INFO, L_HTTP, "Making request to %s asking for playlist\n", hostname); + + ret = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, path); + if (ret < 0) + { + DPRINTF(E_LOG, L_HTTP, "Error making http request to %s\n", hostname); + + evhttp_connection_free(evcon); + event_base_free(ctx->evbase); + return NULL; + } + + event_base_dispatch(ctx->evbase); + + evhttp_connection_free(evcon); + event_base_free(ctx->evbase); + + return NULL; +} + +int +http_client_request(struct http_client_ctx *ctx) +{ + pthread_t tid; + pthread_attr_t attr; + int ret; + + /* If async make the request in a spawned thread, otherwise just make it */ + if (ctx->async) + { + ret = pthread_attr_init(&attr); + if (ret != 0) + { + DPRINTF(E_LOG, L_HTTP, "Error in http_client_request: Could not init attributes\n"); + return -1; + } + + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ret = pthread_create(&tid, &attr, request_make, ctx); + if (ret != 0) + { + DPRINTF(E_LOG, L_HTTP, "Error in http_client_request: Could not create thread\n"); + return -1; + } + } + else + { + request_make(ctx); + ret = ctx->ret; + } + + return ret; +} + +int +http_stream_setup(char **stream, const char *url) +{ + struct http_client_ctx ctx; + struct evbuffer *evbuf; + const char *ext; + char *line; + int ret; + int n; + + *stream = NULL; + + ext = strrchr(url, '.'); + if (strcasecmp(ext, ".m3u") != 0) + { + *stream = strdup(url); + return 0; + } + + // It was a m3u playlist, so now retrieve it + memset(&ctx, 0, sizeof(struct http_client_ctx)); + + evbuf = evbuffer_new(); + if (!evbuf) + return -1; + + ctx.async = 0; + ctx.url = url; + ctx.evbuf = evbuf; + + ret = http_client_request(&ctx); + if (ret < 0) + { + DPRINTF(E_LOG, L_HTTP, "Couldn't fetch internet playlist: %s\n", url); + + evbuffer_free(evbuf); + return -1; + } + + /* Read the playlist until the first stream link is found, but give up if + * nothing is found in the first 10 lines + */ + n = 0; + while ((line = evbuffer_readln(ctx.evbuf, NULL, EVBUFFER_EOL_ANY)) && (n < 10)) + { + n++; + if (strncasecmp(line, "http://", strlen("http://")) == 0) + { + DPRINTF(E_DBG, L_HTTP, "Found internet playlist stream (line %d): %s\n", n, line); + + n = -1; + break; + } + + free(line); + } + + evbuffer_free(ctx.evbuf); + + if (n != -1) + { + DPRINTF(E_LOG, L_HTTP, "Couldn't find stream in internet playlist: %s\n", url); + + return -1; + } + + *stream = line; + + return 0; +} + + +/* ======================= ICY metadata handling =============================*/ + +static int +metadata_packet_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx) +{ + uint8_t *buffer; + char *icy_token; + char *ptr; + char *end; + + av_opt_get(fmtctx, "icy_metadata_packet", AV_OPT_SEARCH_CHILDREN, &buffer); + if (!buffer) + return -1; + + icy_token = strtok((char *)buffer, ";"); + while (icy_token != NULL) + { + ptr = strchr(icy_token, '='); + if (!ptr || (ptr[1] == '\0')) + { + icy_token = strtok(NULL, ";"); + continue; + } + + ptr++; + if (ptr[0] == '\'') + ptr++; + + end = strrchr(ptr, '\''); + if (end) + *end = '\0'; + + if ((strncmp(icy_token, "StreamTitle", strlen("StreamTitle")) == 0) && !metadata->title) + { + metadata->title = ptr; + + /* Dash separates artist from title, if no dash assume all is title */ + ptr = strstr(ptr, " - "); + if (ptr) + { + *ptr = '\0'; + metadata->title = strdup(metadata->title); + *ptr = ' '; + + metadata->artist = strdup(ptr + 3); + } + else + metadata->title = strdup(metadata->title); + } + else if ((strncmp(icy_token, "StreamUrl", strlen("StreamUrl")) == 0) && !metadata->artwork_url) + { + metadata->artwork_url = strdup(ptr); + } + + if (end) + *end = '\''; + + icy_token = strtok(NULL, ";"); + } + av_free(buffer); + + if (metadata->title) + metadata->hash = djb_hash(metadata->title, strlen(metadata->title)); + + return 0; +} + +static int +metadata_header_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx) +{ + uint8_t *buffer; + char *icy_token; + char *ptr; + + av_opt_get(fmtctx, "icy_metadata_headers", AV_OPT_SEARCH_CHILDREN, &buffer); + if (!buffer) + return -1; + + icy_token = strtok((char *)buffer, "\r\n"); + while (icy_token != NULL) + { + ptr = strchr(icy_token, ':'); + if (!ptr || (ptr[1] == '\0')) + { + icy_token = strtok(NULL, "\r\n"); + continue; + } + + ptr++; + if (ptr[0] == ' ') + ptr++; + + if ((strncmp(icy_token, "icy-name", strlen("icy-name")) == 0) && !metadata->name) + metadata->name = strdup(ptr); + else if ((strncmp(icy_token, "icy-description", strlen("icy-description")) == 0) && !metadata->description) + metadata->description = strdup(ptr); + else if ((strncmp(icy_token, "icy-genre", strlen("icy-genre")) == 0) && !metadata->genre) + metadata->genre = strdup(ptr); + + icy_token = strtok(NULL, "\r\n"); + } + av_free(buffer); + + return 0; +} + +void +http_icy_metadata_free(struct http_icy_metadata *metadata) +{ + if (metadata->name) + free(metadata->name); + + if (metadata->description) + free(metadata->description); + + if (metadata->genre) + free(metadata->genre); + + if (metadata->title) + free(metadata->title); + + if (metadata->artist) + free(metadata->artist); + + if (metadata->artwork_url) + free(metadata->artwork_url); + + free(metadata); +} + +#if LIBAVFORMAT_VERSION_MAJOR >= 56 || (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 13) +struct http_icy_metadata * +http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only) +{ + struct http_icy_metadata *metadata; + int got_packet; + int got_header; + + metadata = malloc(sizeof(struct http_icy_metadata)); + if (!metadata) + return NULL; + memset(metadata, 0, sizeof(struct http_icy_metadata)); + + got_packet = (metadata_packet_get(metadata, fmtctx) == 0); + got_header = (!packet_only) && (metadata_header_get(metadata, fmtctx) == 0); + + if (!got_packet && !got_header) + { + free(metadata); + return NULL; + } + +/* DPRINTF(E_DBG, L_HTTP, "Found ICY: N %s, D %s, G %s, T %s, A %s, U %s, I %" PRIu32 "\n", + metadata->name, + metadata->description, + metadata->genre, + metadata->title, + metadata->artist, + metadata->artwork_url, + metadata->hash + ); +*/ + return metadata; +} +#else +struct http_icy_metadata * +http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only) +{ + return NULL; +} +#endif + diff --git a/src/http.h b/src/http.h new file mode 100644 index 00000000..70aa83f9 --- /dev/null +++ b/src/http.h @@ -0,0 +1,89 @@ + +#ifndef __HTTP_H__ +#define __HTTP_H__ + +#include +#include + +#include + +struct http_client_ctx +{ + int async; + const char *url; + int ret; + + /* For sync mode */ + struct evbuffer *evbuf; + + /* For async mode */ + void (*cb)(struct evhttp_request *, void *); + + /* Private */ + void *evbase; +}; + +struct http_icy_metadata +{ + /* Static stream metadata from icy_metadata_headers */ + char *name; + char *description; + char *genre; + + /* Track specific, comes from icy_metadata_packet */ + char *title; + char *artist; + char *artwork_url; + + uint32_t hash; +}; + + +/* Generic HTTP client + * Can be called blocking or non-blocking. No support for https. + * + * @param ctx HTTP request params, see above + * @return 0 if successful, -1 if an error occurred + */ +int +http_client_request(struct http_client_ctx *ctx); + + +/* Returns a newly allocated string with the first stream in the m3u given in + * url. If url is not a m3u, the string will be a copy of url. + * + * @param stream the newly allocated string with link to stream (NULL on error) + * @param url link to either stream or m3u + * @return 0 if successful, -1 if an error occurred + */ +int +http_stream_setup(char **stream, const char *url); + + +/* Frees a ICY metadata struct + * + * @param metadata struct to free + */ +void +http_icy_metadata_free(struct http_icy_metadata *metadata); + + +/* Extracts ICY header and packet metadata (requires libav 10) + * + * example header metadata (standard http header format): + * icy-name: Rock On Radio + * example packet metadata (track currently being played): + * StreamTitle='Robert Miles - Black Rubber';StreamUrl=''; + * + * The extraction is straight from the stream and done in the player thread, so + * it must not produce significant delay. + * + * @param fmtctx the libav/ffmpeg AVFormatContext containing the stream + * @param packet_only only get currently playing info (see struct above) + * @return metadata struct if successful, NULL on error or nothing found + */ +struct http_icy_metadata * +http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only); + + +#endif /* !__HTTP_H__ */ diff --git a/src/icy.c b/src/icy.c deleted file mode 100644 index a5f1e81b..00000000 --- a/src/icy.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2015 Espen Jürgensen - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "icy.h" -#include "logger.h" -#include "misc.h" - -#include - -static int -metadata_packet_get(struct icy_metadata *metadata, AVFormatContext *fmtctx) -{ - uint8_t *buffer; - char *icy_token; - char *ptr; - char *end; - - av_opt_get(fmtctx, "icy_metadata_packet", AV_OPT_SEARCH_CHILDREN, &buffer); - if (!buffer) - return -1; - - icy_token = strtok((char *)buffer, ";"); - while (icy_token != NULL) - { - ptr = strchr(icy_token, '='); - if (!ptr || (ptr[1] == '\0')) - { - icy_token = strtok(NULL, ";"); - continue; - } - - ptr++; - if (ptr[0] == '\'') - ptr++; - - end = strrchr(ptr, '\''); - if (end) - *end = '\0'; - - if ((strncmp(icy_token, "StreamTitle", strlen("StreamTitle")) == 0) && !metadata->title) - { - metadata->title = ptr; - - /* Dash separates artist from title, if no dash assume all is title */ - ptr = strstr(ptr, " - "); - if (ptr) - { - *ptr = '\0'; - metadata->title = strdup(metadata->title); - *ptr = ' '; - - metadata->artist = strdup(ptr + 3); - } - else - metadata->title = strdup(metadata->title); - } - else if ((strncmp(icy_token, "StreamUrl", strlen("StreamUrl")) == 0) && !metadata->artwork_url) - { - metadata->artwork_url = strdup(ptr); - } - - if (end) - *end = '\''; - - icy_token = strtok(NULL, ";"); - } - av_free(buffer); - - if (metadata->title) - metadata->hash = djb_hash(metadata->title, strlen(metadata->title)); - - return 0; -} - -static int -metadata_header_get(struct icy_metadata *metadata, AVFormatContext *fmtctx) -{ - uint8_t *buffer; - char *icy_token; - char *ptr; - - av_opt_get(fmtctx, "icy_metadata_headers", AV_OPT_SEARCH_CHILDREN, &buffer); - if (!buffer) - return -1; - - icy_token = strtok((char *)buffer, "\r\n"); - while (icy_token != NULL) - { - ptr = strchr(icy_token, ':'); - if (!ptr || (ptr[1] == '\0')) - { - icy_token = strtok(NULL, "\r\n"); - continue; - } - - ptr++; - if (ptr[0] == ' ') - ptr++; - - if ((strncmp(icy_token, "icy-name", strlen("icy-name")) == 0) && !metadata->name) - metadata->name = strdup(ptr); - else if ((strncmp(icy_token, "icy-description", strlen("icy-description")) == 0) && !metadata->description) - metadata->description = strdup(ptr); - else if ((strncmp(icy_token, "icy-genre", strlen("icy-genre")) == 0) && !metadata->genre) - metadata->genre = strdup(ptr); - - icy_token = strtok(NULL, "\r\n"); - } - av_free(buffer); - - return 0; -} - -void -icy_metadata_free(struct icy_metadata *metadata) -{ - if (metadata->name) - free(metadata->name); - - if (metadata->description) - free(metadata->description); - - if (metadata->genre) - free(metadata->genre); - - if (metadata->title) - free(metadata->title); - - if (metadata->artist) - free(metadata->artist); - - if (metadata->artwork_url) - free(metadata->artwork_url); - - free(metadata); -} - -#if LIBAVFORMAT_VERSION_MAJOR >= 56 || (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 13) -/* Extracts ICY header and packet metadata (requires libav 10) - * - * example header metadata (standard http header format): - * icy-name: Rock On Radio - * example packet metadata (track currently being played): - * StreamTitle='Robert Miles - Black Rubber';StreamUrl=''; - * - * The extraction is straight from the stream and done in the player thread, so - * it must not produce significant delay. - */ -struct icy_metadata * -icy_metadata_get(AVFormatContext *fmtctx, int packet_only) -{ - struct icy_metadata *metadata; - int got_packet; - int got_header; - - metadata = malloc(sizeof(struct icy_metadata)); - if (!metadata) - return NULL; - memset(metadata, 0, sizeof(struct icy_metadata)); - - got_packet = (metadata_packet_get(metadata, fmtctx) == 0); - got_header = (!packet_only) && (metadata_header_get(metadata, fmtctx) == 0); - - if (!got_packet && !got_header) - { - free(metadata); - return NULL; - } - -/* DPRINTF(E_DBG, L_MISC, "Found ICY: N %s, D %s, G %s, T %s, A %s, U %s, I %" PRIu32 "\n", - metadata->name, - metadata->description, - metadata->genre, - metadata->title, - metadata->artist, - metadata->artwork_url, - metadata->hash - ); -*/ - return metadata; -} -#else -struct icy_metadata * -icy_metadata_get(AVFormatContext *fmtctx, int packet_only) -{ - return NULL; -} -#endif - diff --git a/src/icy.h b/src/icy.h deleted file mode 100644 index f909bf17..00000000 --- a/src/icy.h +++ /dev/null @@ -1,28 +0,0 @@ - -#ifndef __ICY_H__ -#define __ICY_H__ - -#include - -struct icy_metadata -{ - /* Static stream metadata from icy_metadata_headers */ - char *name; - char *description; - char *genre; - - /* Track specific, comes from icy_metadata_packet */ - char *title; - char *artist; - char *artwork_url; - - uint32_t hash; -}; - -void -icy_metadata_free(struct icy_metadata *metadata); - -struct icy_metadata * -icy_metadata_get(AVFormatContext *fmtctx, int packet_only); - -#endif /* !__ICY_H__ */ diff --git a/src/logger.c b/src/logger.c index 80accc9c..273b4ad7 100644 --- a/src/logger.c +++ b/src/logger.c @@ -43,7 +43,7 @@ static int threshold; static int console; static char *logfilename; static FILE *logfile; -static char *labels[] = { "config", "daap", "db", "httpd", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd" }; +static char *labels[] = { "config", "daap", "db", "httpd", "http", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd" }; static char *severities[] = { "FATAL", "LOG", "WARN", "INFO", "DEBUG", "SPAM" }; diff --git a/src/logger.h b/src/logger.h index e643f070..73e9424a 100644 --- a/src/logger.h +++ b/src/logger.h @@ -9,29 +9,30 @@ #define L_DAAP 1 #define L_DB 2 #define L_HTTPD 3 -#define L_MAIN 4 -#define L_MDNS 5 -#define L_MISC 6 -#define L_RSP 7 -#define L_SCAN 8 -#define L_XCODE 9 +#define L_HTTP 4 +#define L_MAIN 5 +#define L_MDNS 6 +#define L_MISC 7 +#define L_RSP 8 +#define L_SCAN 9 +#define L_XCODE 10 /* libevent logging */ -#define L_EVENT 10 -#define L_REMOTE 11 -#define L_DACP 12 -#define L_FFMPEG 13 -#define L_ART 14 -#define L_PLAYER 15 -#define L_RAOP 16 -#define L_LAUDIO 17 -#define L_DMAP 18 -#define L_DBPERF 19 -#define L_SPOTIFY 20 -#define L_LASTFM 21 -#define L_CACHE 22 -#define L_MPD 23 +#define L_EVENT 11 +#define L_REMOTE 12 +#define L_DACP 13 +#define L_FFMPEG 14 +#define L_ART 15 +#define L_PLAYER 16 +#define L_RAOP 17 +#define L_LAUDIO 18 +#define L_DMAP 19 +#define L_DBPERF 20 +#define L_SPOTIFY 21 +#define L_LASTFM 22 +#define L_CACHE 23 +#define L_MPD 24 -#define N_LOGDOMAINS 24 +#define N_LOGDOMAINS 25 /* Severities */ #define E_FATAL 0 diff --git a/src/player.c b/src/player.c index 694a2640..fbda3acc 100644 --- a/src/player.c +++ b/src/player.c @@ -65,7 +65,7 @@ /* These handle getting the media data */ #include "transcode.h" #include "pipe.h" -#include "icy.h" +#include "http.h" #ifdef HAVE_SPOTIFY_H # include "spotify.h" #endif @@ -677,7 +677,7 @@ static void metadata_icy_poll_cb(int fd, short what, void *arg) { struct timeval tv = { METADATA_ICY_POLL, 0 }; - struct icy_metadata *metadata; + struct http_icy_metadata *metadata; int changed; /* Playback of stream has stopped, so stop polling */ @@ -703,7 +703,7 @@ metadata_icy_poll_cb(int fd, short what, void *arg) metadata_send(cur_streaming, 0); no_update: - icy_metadata_free(metadata); + http_icy_metadata_free(metadata); no_metadata: evtimer_add(metaev, &tv); @@ -1312,6 +1312,7 @@ static int source_open(struct player_source *ps, int no_md) { struct media_file_info *mfi; + char *url; int ret; ps->setup_done = 0; @@ -1343,9 +1344,19 @@ source_open(struct player_source *ps, int no_md) { case 1: ps->type = SOURCE_HTTP; + + ret = http_stream_setup(&url, mfi->path); + if (ret < 0) + break; + + free(mfi->path); + mfi->path = url; + ret = transcode_setup(&ps->ctx, mfi, NULL, 0); - if (ret >= 0) - metadata_icy_poll_start(); + if (ret < 0) + break; + + metadata_icy_poll_start(); break; case 2: diff --git a/src/raop.c b/src/raop.c index 745cf98b..073c2ae9 100644 --- a/src/raop.c +++ b/src/raop.c @@ -2183,7 +2183,7 @@ raop_metadata_send_thread(void *arg) ret = db_perthread_init(); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Error in raop_metadata_send_thread: Could not init thread\n"); + DPRINTF(E_LOG, L_RAOP, "Error in raop_metadata_send_thread: Could not init thread\n"); return NULL; } @@ -2239,7 +2239,7 @@ raop_metadata_send(int id, uint64_t rtptime, uint64_t offset, int startup) ret = pthread_attr_init(&attr); if (ret != 0) { - DPRINTF(E_LOG, L_DB, "Error in raop_metadata_send: Could not init attributes\n"); + DPRINTF(E_LOG, L_RAOP, "Error in raop_metadata_send: Could not init attributes\n"); return; } @@ -2247,7 +2247,7 @@ raop_metadata_send(int id, uint64_t rtptime, uint64_t offset, int startup) ret = pthread_create(&tid, &attr, raop_metadata_send_thread, ctx); if (ret != 0) { - DPRINTF(E_LOG, L_DB, "Error in raop_metadata_send: Could not create thread\n"); + DPRINTF(E_LOG, L_RAOP, "Error in raop_metadata_send: Could not create thread\n"); } pthread_attr_destroy(&attr); diff --git a/src/transcode.c b/src/transcode.c index 678d5893..ee112f5d 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -897,16 +897,16 @@ transcode_needed(const char *user_agent, const char *client_codecs, char *file_c } void -transcode_metadata(struct transcode_ctx *ctx, struct icy_metadata **metadata, int *changed) +transcode_metadata(struct transcode_ctx *ctx, struct http_icy_metadata **metadata, int *changed) { - struct icy_metadata *m; + struct http_icy_metadata *m; *metadata = NULL; if (!ctx->fmtctx) return; - m = icy_metadata_get(ctx->fmtctx, 1); + m = http_icy_metadata_get(ctx->fmtctx, 1); *changed = (m->hash != ctx->icy_hash); @@ -918,7 +918,7 @@ transcode_metadata(struct transcode_ctx *ctx, struct icy_metadata **metadata, in void transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, char *stream_url) { - struct icy_metadata *m; + struct http_icy_metadata *m; *artwork_url = NULL; @@ -928,7 +928,7 @@ transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, ch if (strcmp(ctx->fmtctx->filename, stream_url) != 0) return; - m = icy_metadata_get(ctx->fmtctx, 1); + m = http_icy_metadata_get(ctx->fmtctx, 1); if (m->artwork_url) *artwork_url = strdup(m->artwork_url); diff --git a/src/transcode.h b/src/transcode.h index e781802c..5e1f8d9c 100644 --- a/src/transcode.h +++ b/src/transcode.h @@ -7,7 +7,7 @@ #else # include #endif -#include "icy.h" +#include "http.h" struct transcode_ctx; @@ -27,7 +27,7 @@ int transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype); void -transcode_metadata(struct transcode_ctx *ctx, struct icy_metadata **metadata, int *changed); +transcode_metadata(struct transcode_ctx *ctx, struct http_icy_metadata **metadata, int *changed); void transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, char *stream_url);