Remove filescanner_icy.c and consolidate in http.c.

Libav 9 does not support ICY metadata, so in that case we must be able to get it outselves.
This commit is contained in:
ejurgensen 2015-03-29 00:29:06 +01:00
parent b8d8df132b
commit ea29a8d988
6 changed files with 229 additions and 404 deletions

View File

@ -101,7 +101,7 @@ forked_daapd_SOURCES = main.c \
conffile.c conffile.h \ conffile.c conffile.h \
cache.c cache.h \ cache.c cache.h \
filescanner.c filescanner.h \ filescanner.c filescanner.h \
filescanner_ffmpeg.c filescanner_playlist.c filescanner_icy.c $(ITUNES_SRC) \ filescanner_ffmpeg.c filescanner_playlist.c $(ITUNES_SRC) \
mdns_avahi.c mdns.h \ mdns_avahi.c mdns.h \
remote_pairing.c remote_pairing.h \ remote_pairing.c remote_pairing.h \
$(EVHTTP_SRC) \ $(EVHTTP_SRC) \

View File

@ -693,11 +693,7 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
else if (type & F_SCAN_TYPE_URL) else if (type & F_SCAN_TYPE_URL)
{ {
mfi->data_kind = 1; /* url/stream */ mfi->data_kind = 1; /* url/stream */
#if LIBAVFORMAT_VERSION_MAJOR >= 56 || (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 13)
ret = scan_metadata_ffmpeg(path, mfi); ret = scan_metadata_ffmpeg(path, mfi);
#else
ret = scan_metadata_icy(path, mfi);
#endif
} }
else if (type & F_SCAN_TYPE_SPOTIFY) else if (type & F_SCAN_TYPE_SPOTIFY)
{ {

View File

@ -498,7 +498,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
icy_metadata = http_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, "Found ICY metadata, name is '%s'\n", icy_metadata->name);
if (mfi->title) if (mfi->title)
free(mfi->title); free(mfi->title);
@ -513,7 +513,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
} }
if (icy_metadata && icy_metadata->description) if (icy_metadata && icy_metadata->description)
{ {
DPRINTF(E_DBG, L_SCAN, "libav/ffmpeg found ICY metadata, description is '%s'\n", icy_metadata->description); DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, description is '%s'\n", icy_metadata->description);
if (mfi->album) if (mfi->album)
free(mfi->album); free(mfi->album);
@ -522,7 +522,7 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
} }
if (icy_metadata && icy_metadata->genre) if (icy_metadata && icy_metadata->genre)
{ {
DPRINTF(E_DBG, L_SCAN, "libav/ffmpeg found ICY metadata, genre is '%s'\n", icy_metadata->genre); DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, genre is '%s'\n", icy_metadata->genre);
if (mfi->genre) if (mfi->genre)
free(mfi->genre); free(mfi->genre);

View File

@ -1,348 +0,0 @@
/*
* Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.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
*/
#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 <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <netinet/in.h>
#endif
#include <event.h>
#if defined HAVE_LIBEVENT2
# include <event2/http.h>
#else
# include "evhttp/evhttp_compat.h"
#endif
#include <libavformat/avformat.h>
#include "logger.h"
#include "filescanner.h"
#include "misc.h"
#define ICY_TIMEOUT 3
enum icy_request_status { ICY_INIT, ICY_WAITING, ICY_DONE };
static enum icy_request_status status;
/* TODO why doesn't evbase_scan work... */
extern struct event_base *evbase_main;
struct icy_ctx
{
char *url;
char address[INET6_ADDRSTRLEN];
char hostname[PATH_MAX];
char path[PATH_MAX];
int port;
char *icy_name;
char *icy_description;
char *icy_genre;
pthread_mutex_t lck;
pthread_cond_t cond;
};
#ifndef HAVE_LIBEVENT2
static int
resolve_address(char *hostname, char *s, size_t maxlen)
{
struct addrinfo *result;
int ret;
ret = getaddrinfo(hostname, NULL, NULL, &result);
if (ret != 0)
return -1;
switch(result->ai_addr->sa_family)
{
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in *)result->ai_addr)->sin_addr), s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)result->ai_addr)->sin6_addr), s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
freeaddrinfo(result);
return -1;
}
freeaddrinfo(result);
return 0;
}
#endif
#ifndef HAVE_LIBEVENT2_OLD
static void
scan_icy_request_cb(struct evhttp_request *req, void *arg)
{
struct icy_ctx *ctx;
ctx = (struct icy_ctx *)arg;
pthread_mutex_lock(&ctx->lck);
DPRINTF(E_DBG, L_SCAN, "ICY metadata request: Signal callback\n");
status = ICY_DONE;
pthread_cond_signal(&ctx->cond);
pthread_mutex_unlock(&ctx->lck);
}
/* Will always return -1 to make evhttp close the connection - we only need the http headers */
static int
scan_icy_header_cb(struct evhttp_request *req, void *arg)
{
struct icy_ctx *ctx;
struct evkeyvalq *headers;
const char *ptr;
ctx = (struct icy_ctx *)arg;
DPRINTF(E_DBG, L_SCAN, "ICY metadata request: Headers received\n");
headers = evhttp_request_get_input_headers(req);
if ( (ptr = evhttp_find_header(headers, "icy-name")) )
{
ctx->icy_name = strdup(ptr);
DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, name is %s\n", ctx->icy_name);
}
if ( (ptr = evhttp_find_header(headers, "icy-description")) )
{
ctx->icy_description = strdup(ptr);
DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, description is %s\n", ctx->icy_description);
}
if ( (ptr = evhttp_find_header(headers, "icy-genre")) )
{
ctx->icy_genre = strdup(ptr);
DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, genre is %s\n", ctx->icy_genre);
}
return -1;
}
#endif
int
scan_metadata_icy(char *url, struct media_file_info *mfi)
{
struct icy_ctx *ctx;
struct evhttp_connection *evcon;
#ifndef HAVE_LIBEVENT2_OLD
struct evhttp_request *req;
struct evkeyvalq *headers;
char s[PATH_MAX];
#endif
time_t start;
time_t end;
int ret;
status = ICY_INIT;
start = time(NULL);
/* We can set this straight away */
mfi->url = strdup(url);
ctx = (struct icy_ctx *)malloc(sizeof(struct icy_ctx));
if (!ctx)
{
DPRINTF(E_LOG, L_SCAN, "Out of memory for ICY metadata context\n");
return -1;
}
memset(ctx, 0, sizeof(struct icy_ctx));
pthread_mutex_init(&ctx->lck, NULL);
pthread_cond_init(&ctx->cond, NULL);
ctx->url = url;
/* TODO https */
av_url_split(NULL, 0, NULL, 0, ctx->hostname, sizeof(ctx->hostname), &ctx->port, ctx->path, sizeof(ctx->path), ctx->url);
if ((!ctx->hostname) || (strlen(ctx->hostname) == 0))
{
DPRINTF(E_LOG, L_SCAN, "Error extracting hostname from playlist URL: %s\n", ctx->url);
return -1;
}
if (ctx->port < 0)
ctx->port = 80;
if (strlen(ctx->path) == 0)
{
ctx->path[0] = '/';
ctx->path[1] = '\0';
}
#ifdef HAVE_LIBEVENT2
evcon = evhttp_connection_base_new(evbase_main, NULL, ctx->hostname, (unsigned short)ctx->port);
if (!evcon)
{
DPRINTF(E_LOG, L_SCAN, "Could not create connection to %s\n", ctx->hostname);
goto no_icy;
}
#else
/* Resolve IP address */
ret = resolve_address(ctx->hostname, ctx->address, sizeof(ctx->address));
if (ret < 0)
{
DPRINTF(E_LOG, L_SCAN, "Could not find IP address of %s\n", ctx->hostname);
return -1;
}
DPRINTF(E_DBG, L_SCAN, "URL %s converted to hostname %s, port %d, path %s, IP %s\n", ctx->url, ctx->hostname, ctx->port, ctx->path, ctx->address);
/* Set up connection */
evcon = evhttp_connection_new(ctx->address, (unsigned short)ctx->port);
if (!evcon)
{
DPRINTF(E_LOG, L_SCAN, "Could not create connection to %s\n", ctx->hostname);
goto no_icy;
}
evhttp_connection_set_base(evcon, evbase_main);
#endif
#ifdef HAVE_LIBEVENT2_OLD
DPRINTF(E_LOG, L_SCAN, "Skipping Shoutcast metadata request for %s (requires libevent>=2.1.4 or libav 10)\n", ctx->hostname);
#else
evhttp_connection_set_timeout(evcon, ICY_TIMEOUT);
/* Set up request */
req = evhttp_request_new(scan_icy_request_cb, ctx);
if (!req)
{
DPRINTF(E_LOG, L_SCAN, "Could not create request to %s\n", ctx->hostname);
goto no_icy;
}
evhttp_request_set_header_cb(req, scan_icy_header_cb);
headers = evhttp_request_get_output_headers(req);
snprintf(s, PATH_MAX, "%s:%d", ctx->hostname, ctx->port);
evhttp_add_header(headers, "Host", s);
evhttp_add_header(headers, "Icy-MetaData", "1");
/* Make request */
DPRINTF(E_INFO, L_SCAN, "Making request to %s asking for ICY (Shoutcast) metadata\n", ctx->hostname);
status = ICY_WAITING;
ret = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, ctx->path);
if (ret < 0)
{
DPRINTF(E_LOG, L_SCAN, "Error making request to %s\n", ctx->hostname);
status = ICY_DONE;
goto no_icy;
}
#endif
/* Can't count on server support for ICY metadata, so
* while waiting for a reply make a parallel call to scan_metadata_ffmpeg.
*/
no_icy:
ret = scan_metadata_ffmpeg(url, mfi);
if (ret < 0)
{
DPRINTF(E_LOG, L_SCAN, "Playlist URL is unavailable for probe/metadata, assuming MP3 encoding\n");
mfi->type = strdup("mp3");
mfi->codectype = strdup("mpeg");
mfi->description = strdup("MPEG audio file");
}
/* Wait for ICY request to complete or timeout */
pthread_mutex_lock(&ctx->lck);
if (status == ICY_WAITING)
pthread_cond_wait(&ctx->cond, &ctx->lck);
pthread_mutex_unlock(&ctx->lck);
/* Copy result to mfi */
if (ctx->icy_name)
{
if (mfi->title)
free(mfi->title);
if (mfi->artist)
free(mfi->artist);
if (mfi->album_artist)
free(mfi->album_artist);
mfi->title = strdup(ctx->icy_name);
mfi->artist = strdup(ctx->icy_name);
mfi->album_artist = strdup(ctx->icy_name);
free(ctx->icy_name);
}
if (ctx->icy_description)
{
if (mfi->album)
free(mfi->album);
mfi->album = ctx->icy_description;
}
if (ctx->icy_genre)
{
if (mfi->genre)
free(mfi->genre);
mfi->genre = ctx->icy_genre;
}
/* Clean up */
if (evcon)
evhttp_connection_free(evcon);
pthread_cond_destroy(&ctx->cond);
pthread_mutex_destroy(&ctx->lck);
free(ctx);
end = time(NULL);
DPRINTF(E_DBG, L_SCAN, "ICY metadata scan of %s completed in %.f sec\n", url, difftime(end, start));
return 1;
}

View File

@ -46,6 +46,38 @@
// Number of seconds the client will wait for a response before aborting // Number of seconds the client will wait for a response before aborting
#define HTTP_CLIENT_TIMEOUT 5 #define HTTP_CLIENT_TIMEOUT 5
/* The strict libevent api does not permit walking through an evkeyvalq and saving
* all the http headers, so we predefine what we are looking for. You can add
* extra headers here that you would like to save.
*/
static char *header_list[] =
{
"icy-name",
"icy-description",
"icy-metaint",
"icy-genre",
};
/* Copies headers we are searching for from one keyval struct to another
*
*/
static void
headers_save(struct keyval *kv, struct evkeyvalq *headers)
{
const char *value;
int i;
if (!kv || !headers)
return;
for (i = 0; i < (sizeof(header_list) / sizeof(header_list[0])); i++)
{
if ( (value = evhttp_find_header(headers, header_list[i])) )
keyval_add(kv, header_list[i], value);
}
}
static void static void
request_cb(struct evhttp_request *req, void *arg) request_cb(struct evhttp_request *req, void *arg)
{ {
@ -55,15 +87,28 @@ request_cb(struct evhttp_request *req, void *arg)
ctx = (struct http_client_ctx *)arg; ctx = (struct http_client_ctx *)arg;
response_code = evhttp_request_get_response_code(req); if (ctx->headers_only)
response_code_line = evhttp_request_get_response_code_line(req); {
ctx->ret = 0;
if (req == NULL) event_base_loopbreak(ctx->evbase);
if (ctx->async)
free(ctx);
return;
}
if (!req)
{ {
DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection timed out\n", ctx->url); DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection timed out\n", ctx->url);
goto connection_error; goto connection_error;
} }
else if (response_code == 0)
response_code = evhttp_request_get_response_code(req);
response_code_line = evhttp_request_get_response_code_line(req);
if (response_code == 0)
{ {
DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url); DPRINTF(E_LOG, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url);
goto connection_error; goto connection_error;
@ -77,10 +122,15 @@ request_cb(struct evhttp_request *req, void *arg)
/* Async: we make a callback to caller, Sync: we move the body into the callers evbuf */ /* Async: we make a callback to caller, Sync: we move the body into the callers evbuf */
ctx->ret = 0; ctx->ret = 0;
if (ctx->async) if (!ctx->async)
ctx->cb(req, arg); {
if (ctx->headers)
headers_save(ctx->headers, evhttp_request_get_input_headers(req));
if (ctx->body)
evbuffer_add_buffer(ctx->body, evhttp_request_get_input_buffer(req));
}
else else
evbuffer_add_buffer(ctx->evbuf, evhttp_request_get_input_buffer(req)); ctx->cb(req, arg);
event_base_loopbreak(ctx->evbase); event_base_loopbreak(ctx->evbase);
@ -101,6 +151,31 @@ request_cb(struct evhttp_request *req, void *arg)
return; return;
} }
/* This callback is only invoked if ctx->headers_only is set. Since that means
* we only want headers, it will always return -1 to make evhttp close the
* connection. The headers will be saved in a keyval struct in ctx, since we
* cannot address the *evkeyvalq after the connection is free'd.
*/
#ifndef HAVE_LIBEVENT2_OLD
static int
request_header_cb(struct evhttp_request *req, void *arg)
{
struct http_client_ctx *ctx;
ctx = (struct http_client_ctx *)arg;
if (!ctx->headers)
{
DPRINTF(E_LOG, L_HTTP, "BUG: Header callback invoked but caller did not say where to save the headers\n");
return -1;
}
headers_save(ctx->headers, evhttp_request_get_input_headers(req));
return -1;
}
#endif
static void * static void *
request_make(void *arg) request_make(void *arg)
{ {
@ -165,19 +240,25 @@ request_make(void *arg)
return NULL; return NULL;
} }
#ifndef HAVE_LIBEVENT2_OLD
if (ctx->headers_only)
evhttp_request_set_header_cb(req, request_header_cb);
#endif
headers = evhttp_request_get_output_headers(req); headers = evhttp_request_get_output_headers(req);
snprintf(s, PATH_MAX, "%s:%d", hostname, port); snprintf(s, PATH_MAX, "%s:%d", hostname, port);
evhttp_add_header(headers, "Host", s); evhttp_add_header(headers, "Host", s);
evhttp_add_header(headers, "Content-Length", "0"); evhttp_add_header(headers, "Content-Length", "0");
evhttp_add_header(headers, "User-Agent", "forked-daapd/" VERSION); evhttp_add_header(headers, "User-Agent", "forked-daapd/" VERSION);
evhttp_add_header(headers, "Icy-MetaData", "1");
/* Make request */ /* Make request */
DPRINTF(E_INFO, L_HTTP, "Making request to %s asking for playlist\n", hostname); DPRINTF(E_INFO, L_HTTP, "Making request to %s:%d\n", hostname, port);
ret = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, path); ret = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, path);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_HTTP, "Error making http request to %s\n", hostname); DPRINTF(E_LOG, L_HTTP, "Error making http request to %s:%d\n", hostname, port);
evhttp_connection_free(evcon); evhttp_connection_free(evcon);
event_base_free(ctx->evbase); event_base_free(ctx->evbase);
@ -254,7 +335,7 @@ http_stream_setup(char **stream, const char *url)
ctx.async = 0; ctx.async = 0;
ctx.url = url; ctx.url = url;
ctx.evbuf = evbuf; ctx.body = evbuf;
ret = http_client_request(&ctx); ret = http_client_request(&ctx);
if (ret < 0) if (ret < 0)
@ -269,7 +350,7 @@ http_stream_setup(char **stream, const char *url)
* nothing is found in the first 10 lines * nothing is found in the first 10 lines
*/ */
n = 0; n = 0;
while ((line = evbuffer_readln(ctx.evbuf, NULL, EVBUFFER_EOL_ANY)) && (n < 10)) while ((line = evbuffer_readln(ctx.body, NULL, EVBUFFER_EOL_ANY)) && (n < 10))
{ {
n++; n++;
if (strncasecmp(line, "http://", strlen("http://")) == 0) if (strncasecmp(line, "http://", strlen("http://")) == 0)
@ -283,7 +364,7 @@ http_stream_setup(char **stream, const char *url)
free(line); free(line);
} }
evbuffer_free(ctx.evbuf); evbuffer_free(ctx.body);
if (n != -1) if (n != -1)
{ {
@ -300,6 +381,8 @@ http_stream_setup(char **stream, const char *url)
/* ======================= ICY metadata handling =============================*/ /* ======================= ICY metadata handling =============================*/
#if LIBAVFORMAT_VERSION_MAJOR >= 56 || (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 13)
static int static int
metadata_packet_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx) metadata_packet_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx)
{ {
@ -404,31 +487,6 @@ metadata_header_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx)
return 0; 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 * struct http_icy_metadata *
http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only) http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only)
{ {
@ -462,11 +520,121 @@ http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only)
*/ */
return metadata; return metadata;
} }
#else
#elif defined(HAVE_LIBEVENT2_OLD)
struct http_icy_metadata * struct http_icy_metadata *
http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only) http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only)
{ {
DPRINTF(E_INFO, L_HTTP, "Skipping Shoutcast metadata request for %s (requires libevent>=2.1.4 or libav 10)\n", fmtctx->filename);
return NULL; return NULL;
} }
#else
/* Earlier versions of ffmpeg/libav do not seem to allow access to the http
* headers, so we must instead open the stream ourselves to get the metadata.
* Sorry about the extra connections, you radio streaming people!
*
* TODO: Get packet metadata from fmtctx->packet_buffer
*/
struct http_icy_metadata *
http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only)
{
struct http_icy_metadata *metadata;
struct http_client_ctx ctx;
struct keyval *kv;
const char *value;
int got_header;
int ret;
/* Can only get header metadata at the moment */
if (packet_only)
return NULL;
kv = keyval_alloc();
if (!kv)
return NULL;
memset(&ctx, 0, sizeof(struct http_client_ctx));
ctx.async = 0;
ctx.url = fmtctx->filename;
ctx.headers = kv;
ctx.headers_only = 1;
ctx.body = NULL;
ret = http_client_request(&ctx);
if (ret < 0)
{
DPRINTF(E_LOG, L_HTTP, "Error fetching %s\n", fmtctx->filename);
free(kv);
return NULL;
}
metadata = malloc(sizeof(struct http_icy_metadata));
if (!metadata)
return NULL;
memset(metadata, 0, sizeof(struct http_icy_metadata));
got_header = 0;
if ( (value = keyval_get(ctx.headers, "icy-name")) )
{
metadata->name = strdup(value);
got_header = 1;
}
if ( (value = keyval_get(ctx.headers, "icy-description")) )
{
metadata->description = strdup(value);
got_header = 1;
}
if ( (value = keyval_get(ctx.headers, "icy-genre")) )
{
metadata->genre = strdup(value);
got_header = 1;
}
keyval_clear(kv);
free(kv);
if (!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;
}
#endif #endif
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);
}

View File

@ -4,6 +4,7 @@
#include <event2/buffer.h> #include <event2/buffer.h>
#include <event2/http.h> #include <event2/http.h>
#include "misc.h"
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
@ -13,8 +14,17 @@ struct http_client_ctx
const char *url; const char *url;
int ret; int ret;
/* For sync mode */ /* For sync mode, a keyval/evbuf to store response headers and body
struct evbuffer *evbuf; * Can be set to NULL to ignore that part of the response
*/
struct keyval *headers;
struct evbuffer *body;
/* Cut the connection after the headers have been received
* Used for getting Shoutcast/ICY headers for old versions of libav/ffmpeg
* (requires libevent 1 or 2.1.4+)
*/
int headers_only;
/* For async mode */ /* For async mode */
void (*cb)(struct evhttp_request *, void *); void (*cb)(struct evhttp_request *, void *);
@ -60,14 +70,6 @@ int
http_stream_setup(char **stream, const char *url); 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) /* Extracts ICY header and packet metadata (requires libav 10)
* *
* example header metadata (standard http header format): * example header metadata (standard http header format):
@ -86,4 +88,11 @@ struct http_icy_metadata *
http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only); http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only);
/* Frees an ICY metadata struct
*
* @param metadata struct to free
*/
void
http_icy_metadata_free(struct http_icy_metadata *metadata);
#endif /* !__HTTP_H__ */ #endif /* !__HTTP_H__ */