diff --git a/src/filescanner.c b/src/filescanner.c index 75069169..5a6e9248 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -525,7 +525,11 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct else if (type & F_SCAN_TYPE_URL) { 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); +#else ret = scan_metadata_icy(path, mfi); +#endif } else if (type & F_SCAN_TYPE_SPOTIFY) { diff --git a/src/filescanner_ffmpeg.c b/src/filescanner_ffmpeg.c index a57340c5..c4bb086b 100644 --- a/src/filescanner_ffmpeg.c +++ b/src/filescanner_ffmpeg.c @@ -32,6 +32,7 @@ #include #include +#include #include "logger.h" #include "filescanner.h" @@ -313,10 +314,82 @@ extract_metadata(struct media_file_info *mfi, AVFormatContext *ctx, AVStream *au return mdcount; } +/* Extracts ICY metadata (requires libav 10: libavformat 55.13) */ +static void +extract_metadata_icy(struct media_file_info *mfi, AVFormatContext *ctx) +{ + uint8_t *icy_meta; + char *icy_token; + char *icy_str; + char *ptr; + + icy_meta = NULL; + // TODO Also get icy_metadata_packet to show current track + av_opt_get(ctx, "icy_metadata_headers", AV_OPT_SEARCH_CHILDREN, &icy_meta); + + if (!icy_meta) + return; + + icy_str = strdup((char *)icy_meta); + icy_token = strtok(icy_str, "\r\n"); + + while (icy_token != NULL) + { + ptr = strchr(icy_token, ':'); + if (!ptr || (strlen(ptr) < 3)) + icy_token = strtok(NULL, "\r\n"); + + ptr++; + if (ptr[0] == ' ') + ptr++; + + if (strstr(icy_token, "icy-name")) + { + DPRINTF(E_DBG, L_SCAN, "Libav/ffmpeg found ICY metadata, name is '%s'\n", ptr); + + if (mfi->title) + free(mfi->title); + if (mfi->artist) + free(mfi->artist); + if (mfi->album_artist) + free(mfi->album_artist); + + mfi->title = strdup(ptr); + mfi->artist = strdup(ptr); + mfi->album_artist = strdup(ptr); + } + + if (strstr(icy_token, "icy-description")) + { + DPRINTF(E_DBG, L_SCAN, "Libav/ffmpeg found ICY metadata, description is '%s'\n", ptr); + + if (mfi->album) + free(mfi->album); + + mfi->album = strdup(ptr); + } + + if (strstr(icy_token, "icy-genre")) + { + DPRINTF(E_DBG, L_SCAN, "Libav/ffmpeg found ICY metadata, genre is '%s'\n", ptr); + + if (mfi->genre) + free(mfi->genre); + + mfi->genre = strdup(ptr); + } + + icy_token = strtok(NULL, "\r\n"); + } + av_free(icy_meta); + free(icy_str); +} + int scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) { AVFormatContext *ctx; + AVDictionary *options; const struct metadata_map *extra_md_map; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) enum AVCodecID codec_id; @@ -334,9 +407,13 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) int ret; ctx = NULL; + options = NULL; #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) - ret = avformat_open_input(&ctx, file, NULL, NULL); + if (mfi->data_kind == 1) + av_dict_set(&options, "icy", "1", 0); + + ret = avformat_open_input(&ctx, file, NULL, &options); #else ret = av_open_input_file(&ctx, file, NULL, 0, NULL); #endif @@ -462,6 +539,10 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) DPRINTF(E_DBG, L_SCAN, "Duration %d ms, bitrate %d kbps\n", mfi->song_length, mfi->bitrate); + /* Try to extract ICY metadata if url/stream */ + if (mfi->data_kind == 1) + extract_metadata_icy(mfi, ctx); + /* Get some more information on the audio stream */ if (audio_stream) { diff --git a/src/filescanner_icy.c b/src/filescanner_icy.c index 08f6d6d0..7a2fa88e 100644 --- a/src/filescanner_icy.c +++ b/src/filescanner_icy.c @@ -244,7 +244,7 @@ scan_metadata_icy(char *url, struct media_file_info *mfi) #endif #ifdef HAVE_LIBEVENT2_OLD - DPRINTF(E_LOG, L_SCAN, "Skipping Shoutcast metadata request for %s (requires libevent>=2.1.4)\n", ctx->hostname); + 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);