mirror of
https://github.com/owntone/owntone-server.git
synced 2025-05-23 10:31:56 -04:00
Support for remote m3u playlists (ref pr #79)
This commit is contained in:
parent
cde8441493
commit
b8d8df132b
@ -110,11 +110,11 @@ forked_daapd_SOURCES = main.c \
|
|||||||
httpd_rsp.c httpd_rsp.h \
|
httpd_rsp.c httpd_rsp.h \
|
||||||
httpd_daap.c httpd_daap.h \
|
httpd_daap.c httpd_daap.h \
|
||||||
httpd_dacp.c httpd_dacp.h \
|
httpd_dacp.c httpd_dacp.h \
|
||||||
|
http.c http.h \
|
||||||
dmap_common.c dmap_common.h \
|
dmap_common.c dmap_common.h \
|
||||||
transcode.c transcode.h \
|
transcode.c transcode.h \
|
||||||
pipe.c pipe.h \
|
pipe.c pipe.h \
|
||||||
artwork.c artwork.h \
|
artwork.c artwork.h \
|
||||||
icy.h icy.c \
|
|
||||||
misc.c misc.h \
|
misc.c misc.h \
|
||||||
rng.c rng.h \
|
rng.c rng.h \
|
||||||
rsp_query.c rsp_query.h \
|
rsp_query.c rsp_query.h \
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "filescanner.h"
|
#include "filescanner.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "icy.h"
|
#include "http.h"
|
||||||
|
|
||||||
|
|
||||||
/* Legacy format-specific scanners */
|
/* Legacy format-specific scanners */
|
||||||
@ -321,7 +321,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
|||||||
AVFormatContext *ctx;
|
AVFormatContext *ctx;
|
||||||
AVDictionary *options;
|
AVDictionary *options;
|
||||||
const struct metadata_map *extra_md_map;
|
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)
|
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
|
||||||
enum AVCodecID codec_id;
|
enum AVCodecID codec_id;
|
||||||
enum AVCodecID video_codec_id;
|
enum AVCodecID video_codec_id;
|
||||||
@ -333,12 +333,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
|||||||
#endif
|
#endif
|
||||||
AVStream *video_stream;
|
AVStream *video_stream;
|
||||||
AVStream *audio_stream;
|
AVStream *audio_stream;
|
||||||
|
char *path;
|
||||||
int mdcount;
|
int mdcount;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
options = NULL;
|
options = NULL;
|
||||||
|
path = strdup(file);
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
||||||
# ifndef HAVE_FFMPEG
|
# ifndef HAVE_FFMPEG
|
||||||
@ -352,21 +354,29 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
|||||||
|
|
||||||
if (mfi->data_kind == 1)
|
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);
|
av_dict_set(&options, "icy", "1", 0);
|
||||||
mfi->artwork = ARTWORK_HTTP;
|
mfi->artwork = ARTWORK_HTTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = avformat_open_input(&ctx, file, NULL, &options);
|
ret = avformat_open_input(&ctx, path, NULL, &options);
|
||||||
#else
|
#else
|
||||||
ret = av_open_input_file(&ctx, file, NULL, 0, NULL);
|
ret = av_open_input_file(&ctx, path, NULL, 0, NULL);
|
||||||
#endif
|
#endif
|
||||||
if (ret != 0)
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
||||||
ret = avformat_find_stream_info(ctx, NULL);
|
ret = avformat_find_stream_info(ctx, NULL);
|
||||||
#else
|
#else
|
||||||
@ -485,7 +495,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
|||||||
/* Try to extract ICY metadata if url/stream */
|
/* Try to extract ICY metadata if url/stream */
|
||||||
if (mfi->data_kind == 1)
|
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)
|
if (icy_metadata && icy_metadata->name)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_SCAN, "libav/ffmpeg found ICY metadata, name is '%s'\n", 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);
|
mfi->genre = strdup(icy_metadata->genre);
|
||||||
}
|
}
|
||||||
if (icy_metadata)
|
if (icy_metadata)
|
||||||
icy_metadata_free(icy_metadata);
|
http_icy_metadata_free(icy_metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get some more information on the audio stream */
|
/* Get some more information on the audio stream */
|
||||||
|
472
src/http.c
Normal file
472
src/http.c
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Espen Jürgensen <espenjurgensen@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
|
||||||
|
#include <event2/event.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
89
src/http.h
Normal file
89
src/http.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
#ifndef __HTTP_H__
|
||||||
|
#define __HTTP_H__
|
||||||
|
|
||||||
|
#include <event2/buffer.h>
|
||||||
|
#include <event2/http.h>
|
||||||
|
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
|
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__ */
|
219
src/icy.c
219
src/icy.c
@ -1,219 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Espen Jürgensen <espenjurgensen@gmail.com>
|
|
||||||
*
|
|
||||||
* 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 <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "icy.h"
|
|
||||||
#include "logger.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#include <libavutil/opt.h>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
28
src/icy.h
28
src/icy.h
@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
#ifndef __ICY_H__
|
|
||||||
#define __ICY_H__
|
|
||||||
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
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__ */
|
|
@ -43,7 +43,7 @@ static int threshold;
|
|||||||
static int console;
|
static int console;
|
||||||
static char *logfilename;
|
static char *logfilename;
|
||||||
static FILE *logfile;
|
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" };
|
static char *severities[] = { "FATAL", "LOG", "WARN", "INFO", "DEBUG", "SPAM" };
|
||||||
|
|
||||||
|
|
||||||
|
43
src/logger.h
43
src/logger.h
@ -9,29 +9,30 @@
|
|||||||
#define L_DAAP 1
|
#define L_DAAP 1
|
||||||
#define L_DB 2
|
#define L_DB 2
|
||||||
#define L_HTTPD 3
|
#define L_HTTPD 3
|
||||||
#define L_MAIN 4
|
#define L_HTTP 4
|
||||||
#define L_MDNS 5
|
#define L_MAIN 5
|
||||||
#define L_MISC 6
|
#define L_MDNS 6
|
||||||
#define L_RSP 7
|
#define L_MISC 7
|
||||||
#define L_SCAN 8
|
#define L_RSP 8
|
||||||
#define L_XCODE 9
|
#define L_SCAN 9
|
||||||
|
#define L_XCODE 10
|
||||||
/* libevent logging */
|
/* libevent logging */
|
||||||
#define L_EVENT 10
|
#define L_EVENT 11
|
||||||
#define L_REMOTE 11
|
#define L_REMOTE 12
|
||||||
#define L_DACP 12
|
#define L_DACP 13
|
||||||
#define L_FFMPEG 13
|
#define L_FFMPEG 14
|
||||||
#define L_ART 14
|
#define L_ART 15
|
||||||
#define L_PLAYER 15
|
#define L_PLAYER 16
|
||||||
#define L_RAOP 16
|
#define L_RAOP 17
|
||||||
#define L_LAUDIO 17
|
#define L_LAUDIO 18
|
||||||
#define L_DMAP 18
|
#define L_DMAP 19
|
||||||
#define L_DBPERF 19
|
#define L_DBPERF 20
|
||||||
#define L_SPOTIFY 20
|
#define L_SPOTIFY 21
|
||||||
#define L_LASTFM 21
|
#define L_LASTFM 22
|
||||||
#define L_CACHE 22
|
#define L_CACHE 23
|
||||||
#define L_MPD 23
|
#define L_MPD 24
|
||||||
|
|
||||||
#define N_LOGDOMAINS 24
|
#define N_LOGDOMAINS 25
|
||||||
|
|
||||||
/* Severities */
|
/* Severities */
|
||||||
#define E_FATAL 0
|
#define E_FATAL 0
|
||||||
|
21
src/player.c
21
src/player.c
@ -65,7 +65,7 @@
|
|||||||
/* These handle getting the media data */
|
/* These handle getting the media data */
|
||||||
#include "transcode.h"
|
#include "transcode.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "icy.h"
|
#include "http.h"
|
||||||
#ifdef HAVE_SPOTIFY_H
|
#ifdef HAVE_SPOTIFY_H
|
||||||
# include "spotify.h"
|
# include "spotify.h"
|
||||||
#endif
|
#endif
|
||||||
@ -677,7 +677,7 @@ static void
|
|||||||
metadata_icy_poll_cb(int fd, short what, void *arg)
|
metadata_icy_poll_cb(int fd, short what, void *arg)
|
||||||
{
|
{
|
||||||
struct timeval tv = { METADATA_ICY_POLL, 0 };
|
struct timeval tv = { METADATA_ICY_POLL, 0 };
|
||||||
struct icy_metadata *metadata;
|
struct http_icy_metadata *metadata;
|
||||||
int changed;
|
int changed;
|
||||||
|
|
||||||
/* Playback of stream has stopped, so stop polling */
|
/* 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);
|
metadata_send(cur_streaming, 0);
|
||||||
|
|
||||||
no_update:
|
no_update:
|
||||||
icy_metadata_free(metadata);
|
http_icy_metadata_free(metadata);
|
||||||
|
|
||||||
no_metadata:
|
no_metadata:
|
||||||
evtimer_add(metaev, &tv);
|
evtimer_add(metaev, &tv);
|
||||||
@ -1312,6 +1312,7 @@ static int
|
|||||||
source_open(struct player_source *ps, int no_md)
|
source_open(struct player_source *ps, int no_md)
|
||||||
{
|
{
|
||||||
struct media_file_info *mfi;
|
struct media_file_info *mfi;
|
||||||
|
char *url;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ps->setup_done = 0;
|
ps->setup_done = 0;
|
||||||
@ -1343,9 +1344,19 @@ source_open(struct player_source *ps, int no_md)
|
|||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
ps->type = SOURCE_HTTP;
|
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);
|
ret = transcode_setup(&ps->ctx, mfi, NULL, 0);
|
||||||
if (ret >= 0)
|
if (ret < 0)
|
||||||
metadata_icy_poll_start();
|
break;
|
||||||
|
|
||||||
|
metadata_icy_poll_start();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -2183,7 +2183,7 @@ raop_metadata_send_thread(void *arg)
|
|||||||
ret = db_perthread_init();
|
ret = db_perthread_init();
|
||||||
if (ret < 0)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2239,7 +2239,7 @@ raop_metadata_send(int id, uint64_t rtptime, uint64_t offset, int startup)
|
|||||||
ret = pthread_attr_init(&attr);
|
ret = pthread_attr_init(&attr);
|
||||||
if (ret != 0)
|
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;
|
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);
|
ret = pthread_create(&tid, &attr, raop_metadata_send_thread, ctx);
|
||||||
if (ret != 0)
|
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);
|
pthread_attr_destroy(&attr);
|
||||||
|
@ -897,16 +897,16 @@ transcode_needed(const char *user_agent, const char *client_codecs, char *file_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
*metadata = NULL;
|
||||||
|
|
||||||
if (!ctx->fmtctx)
|
if (!ctx->fmtctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m = icy_metadata_get(ctx->fmtctx, 1);
|
m = http_icy_metadata_get(ctx->fmtctx, 1);
|
||||||
|
|
||||||
*changed = (m->hash != ctx->icy_hash);
|
*changed = (m->hash != ctx->icy_hash);
|
||||||
|
|
||||||
@ -918,7 +918,7 @@ transcode_metadata(struct transcode_ctx *ctx, struct icy_metadata **metadata, in
|
|||||||
void
|
void
|
||||||
transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, char *stream_url)
|
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;
|
*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)
|
if (strcmp(ctx->fmtctx->filename, stream_url) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m = icy_metadata_get(ctx->fmtctx, 1);
|
m = http_icy_metadata_get(ctx->fmtctx, 1);
|
||||||
|
|
||||||
if (m->artwork_url)
|
if (m->artwork_url)
|
||||||
*artwork_url = strdup(m->artwork_url);
|
*artwork_url = strdup(m->artwork_url);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#else
|
#else
|
||||||
# include <event.h>
|
# include <event.h>
|
||||||
#endif
|
#endif
|
||||||
#include "icy.h"
|
#include "http.h"
|
||||||
|
|
||||||
struct transcode_ctx;
|
struct transcode_ctx;
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ int
|
|||||||
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype);
|
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype);
|
||||||
|
|
||||||
void
|
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
|
void
|
||||||
transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, char *stream_url);
|
transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url, char *stream_url);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user