From b42b1f98d75a6034406f18a84a36b17196228ec8 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sun, 29 Dec 2013 20:44:32 +0100 Subject: [PATCH 01/15] Adjust filescanner to ignore streams with embedded artwork --- src/filescanner_ffmpeg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/filescanner_ffmpeg.c b/src/filescanner_ffmpeg.c index dbbb3207..69ef2ded 100644 --- a/src/filescanner_ffmpeg.c +++ b/src/filescanner_ffmpeg.c @@ -385,6 +385,12 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) #else case CODEC_TYPE_VIDEO: #endif + if (ctx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) + { + DPRINTF(E_DBG, L_SCAN, "Found embedded artwork (stream %d)\n", i); + + break; + } if (!video_stream) { DPRINTF(E_DBG, L_SCAN, "File has video (stream %d)\n", i); From 73b2d08400b4180e719bc26811ecb3181f9b77f3 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 30 Dec 2013 00:40:16 +0100 Subject: [PATCH 02/15] Rework of transcode.c for libav 9. Resampling is broken in this commit. --- configure.in | 2 +- src/transcode.c | 121 +++++++++++++++++++++++++++--------------------- 2 files changed, 68 insertions(+), 55 deletions(-) diff --git a/configure.in b/configure.in index aea17e6f..e76d4d30 100644 --- a/configure.in +++ b/configure.in @@ -130,7 +130,7 @@ AC_RUN_IFELSE( AC_LANG_POP([C]) LIBS="$save_LIBS" -PKG_CHECK_MODULES(LIBAV, [ libavcodec libavformat libswscale libavutil ]) +PKG_CHECK_MODULES(LIBAV, [ libavcodec libavformat libswscale libavutil libavresample ]) dnl Check for av_lock_manager (ffmpeg >= 0.5.1) save_LIBS="$LIBS" AC_CHECK_LIB([avcodec], [av_lockmgr_register], , AC_MSG_ERROR([libav (ffmpeg) >= 0.5.1 required])) diff --git a/src/transcode.c b/src/transcode.c index 35404e48..ac19df0f 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -43,7 +43,9 @@ #include #include +#include #include +#include #include "logger.h" #include "conffile.h" @@ -67,7 +69,7 @@ struct transcode_ctx { /* Resampling */ int need_resample; int input_size; - ReSampleContext *resample_ctx; + AVAudioResampleContext *resample_ctx; int16_t *re_abuffer; off_t offset; @@ -148,8 +150,8 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) int i; #endif #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) - AVFrame frame; - int got_frame = 0; + AVFrame *frame = NULL; + int got_frame; #endif processed = 0; @@ -167,19 +169,28 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) /* Decode data */ while (ctx->apacket2.size > 0) { - buflen = XCODE_BUFFER_SIZE; - #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) - if (ctx->acodec->get_buffer != avcodec_default_get_buffer) - { - DPRINTF(E_WARN, L_XCODE, "Custom get_buffer, not allowed by ffmpeg/libav. Setting to default.\n"); + got_frame = 0; - ctx->acodec->get_buffer = avcodec_default_get_buffer; + if (!frame) + { + frame = avcodec_alloc_frame(); + if (!frame) + { + DPRINTF(E_LOG, L_XCODE, "Out of memory for decoded frame\n"); + + return -1; + } } + else + avcodec_get_frame_defaults(frame); + + used = avcodec_decode_audio4(ctx->acodec, - &frame, &got_frame, + frame, &got_frame, &ctx->apacket2); #else + buflen = XCODE_BUFFER_SIZE; used = avcodec_decode_audio3(ctx->acodec, ctx->abuffer, &buflen, &ctx->apacket2); @@ -196,36 +207,20 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->apacket2.size -= used; #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) - /* This part is from the libav wrapper for avcodec_decode_audio3 - it may be useless in this context */ - if (got_frame != 0) - { - int ch, plane_size; - int planar = av_sample_fmt_is_planar(ctx->acodec->sample_fmt); - int data_size = av_samples_get_buffer_size(&plane_size, ctx->acodec->channels, - frame.nb_samples, ctx->acodec->sample_fmt, 1); - - if (XCODE_BUFFER_SIZE < data_size) - { - DPRINTF(E_WARN, L_XCODE, "Output buffer too small for frame (%d < %d)\n", XCODE_BUFFER_SIZE, data_size); - - continue; - } - - memcpy(ctx->abuffer, frame.extended_data[0], plane_size); - - if (planar && ctx->acodec->channels > 1) - { - uint8_t *out = ((uint8_t *)ctx->abuffer) + plane_size; - for (ch = 1; ch < ctx->acodec->channels; ch++) - { - memcpy(out, frame.extended_data[ch], plane_size); - out += plane_size; - } - } - buflen = data_size; - } - else + /* No frame decoded this time around */ + if (!got_frame) continue; + + buflen = av_samples_get_buffer_size(NULL, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); + + if (buflen > XCODE_BUFFER_SIZE) + { + DPRINTF(E_LOG, L_XCODE, "Output buffer too small for frame (%d > %d)\n", buflen, XCODE_BUFFER_SIZE); + + return -1; + } + + memcpy(ctx->abuffer, frame->data[0], buflen); #else /* No frame decoded this time around */ if (buflen == 0) @@ -234,12 +229,14 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) if (ctx->need_resample) { - buflen = audio_resample(ctx->resample_ctx, ctx->re_abuffer, ctx->abuffer, buflen / ctx->input_size); +//FIXME buflen = audio_resample(ctx->resample_ctx, ctx->re_abuffer, ctx->abuffer, buflen / ctx->input_size); +buflen = 0; if (buflen == 0) { - DPRINTF(E_WARN, L_XCODE, "Resample returned no samples!\n"); - continue; + DPRINTF(E_LOG, L_XCODE, "Resample returned no samples!\n"); + + return -1; } buflen = buflen * 2 * 2; /* 16bit samples, 2 channels */ @@ -458,6 +455,9 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) ctx->acodec->flags |= CODEC_FLAG_TRUNCATED; #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 6) + ctx->acodec->request_sample_fmt = AV_SAMPLE_FMT_S16; + ctx->acodec->request_channel_layout = AV_CH_LAYOUT_STEREO; + ret = avcodec_open2(ctx->acodec, ctx->adecoder, NULL); #else ret = avcodec_open(ctx->acodec, ctx->adecoder); @@ -477,34 +477,47 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) goto setup_fail_codec; } - if ((ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16) - || (ctx->acodec->channels != 2) - || (ctx->acodec->sample_rate != 44100)) + ctx->need_resample = (ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16) + || (ctx->acodec->channel_layout != AV_CH_LAYOUT_STEREO) + || (ctx->acodec->sample_rate != 44100); + + if (ctx->need_resample) { DPRINTF(E_DBG, L_XCODE, "Setting up resampling (%d@%d)\n", ctx->acodec->channels, ctx->acodec->sample_rate); - ctx->resample_ctx = av_audio_resample_init(2, ctx->acodec->channels, - 44100, ctx->acodec->sample_rate, - AV_SAMPLE_FMT_S16, ctx->acodec->sample_fmt, - 16, 10, 0, 0.8); - + ctx->resample_ctx = avresample_alloc_context(); if (!ctx->resample_ctx) { - DPRINTF(E_WARN, L_XCODE, "Could not init resample from %d@%d to 2@44100\n", ctx->acodec->channels, ctx->acodec->sample_rate); + DPRINTF(E_WARN, L_XCODE, "Out of memory for resample context\n"); goto setup_fail_codec; } + av_opt_set_int(ctx->resample_ctx, "in_sample_fmt", ctx->acodec->sample_fmt, 0); + av_opt_set_int(ctx->resample_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + av_opt_set_int(ctx->resample_ctx, "in_channel_layout", ctx->acodec->channel_layout, 0); + av_opt_set_int(ctx->resample_ctx, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); + av_opt_set_int(ctx->resample_ctx, "in_sample_rate", ctx->acodec->sample_rate, 0); + av_opt_set_int(ctx->resample_ctx, "out_sample_rate", 44100, 0); + + ret = avresample_open(ctx->resample_ctx); + if (ret < 0) + { + DPRINTF(E_WARN, L_XCODE, "Out of memory for resample context\n"); + + avresample_free(&ctx->resample_ctx); + goto setup_fail_codec; + } + ctx->re_abuffer = (int16_t *)av_malloc(XCODE_BUFFER_SIZE * 2); if (!ctx->re_abuffer) { DPRINTF(E_WARN, L_XCODE, "Could not allocate resample buffer\n"); - audio_resample_close(ctx->resample_ctx); + avresample_free(&ctx->resample_ctx); goto setup_fail_codec; } - ctx->need_resample = 1; #if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) ctx->input_size = ctx->acodec->channels * av_get_bytes_per_sample(ctx->acodec->sample_fmt); #elif LIBAVCODEC_VERSION_MAJOR >= 53 @@ -554,7 +567,7 @@ transcode_cleanup(struct transcode_ctx *ctx) if (ctx->need_resample) { - audio_resample_close(ctx->resample_ctx); + avresample_free(&ctx->resample_ctx); av_free(ctx->re_abuffer); } From 3a8936cd261ebc4838f723e4f334dddd072751c4 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 30 Dec 2013 13:03:53 +0100 Subject: [PATCH 03/15] Change to libav 9 resampling (avresample) Some of the previous libav stuff removed, re-add later --- src/transcode.c | 74 +++++++++++++++---------------------------------- 1 file changed, 23 insertions(+), 51 deletions(-) diff --git a/src/transcode.c b/src/transcode.c index ac19df0f..ee4ce7ab 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -68,7 +68,6 @@ struct transcode_ctx { /* Resampling */ int need_resample; - int input_size; AVAudioResampleContext *resample_ctx; int16_t *re_abuffer; @@ -152,6 +151,9 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) AVFrame *frame = NULL; int got_frame; + int out_size; + int out_linesize; + int out_samples; #endif processed = 0; @@ -169,7 +171,6 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) /* Decode data */ while (ctx->apacket2.size > 0) { -#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) got_frame = 0; if (!frame) @@ -189,12 +190,6 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) used = avcodec_decode_audio4(ctx->acodec, frame, &got_frame, &ctx->apacket2); -#else - buflen = XCODE_BUFFER_SIZE; - used = avcodec_decode_audio3(ctx->acodec, - ctx->abuffer, &buflen, - &ctx->apacket2); -#endif if (used < 0) { @@ -206,44 +201,38 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->apacket2.data += used; ctx->apacket2.size -= used; -#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) /* No frame decoded this time around */ if (!got_frame) continue; - buflen = av_samples_get_buffer_size(NULL, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); - - if (buflen > XCODE_BUFFER_SIZE) - { - DPRINTF(E_LOG, L_XCODE, "Output buffer too small for frame (%d > %d)\n", buflen, XCODE_BUFFER_SIZE); - - return -1; - } - - memcpy(ctx->abuffer, frame->data[0], buflen); -#else - /* No frame decoded this time around */ - if (buflen == 0) - continue; -#endif - if (ctx->need_resample) { -//FIXME buflen = audio_resample(ctx->resample_ctx, ctx->re_abuffer, ctx->abuffer, buflen / ctx->input_size); -buflen = 0; + out_size = av_samples_get_buffer_size(&out_linesize, 2, frame->nb_samples, AV_SAMPLE_FMT_S16, 0); - if (buflen == 0) + buf = av_realloc(ctx->re_abuffer, out_size); + if (!buf) + { + DPRINTF(E_LOG, L_XCODE, "Out of memory for resample buffer!\n"); + + return -1; + } + + out_samples = avresample_convert(ctx->resample_ctx, (uint8_t **)&buf, out_linesize, frame->nb_samples, + (uint8_t **)frame->data, frame->linesize[0], frame->nb_samples); + if (out_samples < 0) { DPRINTF(E_LOG, L_XCODE, "Resample returned no samples!\n"); return -1; } - buflen = buflen * 2 * 2; /* 16bit samples, 2 channels */ - buf = ctx->re_abuffer; + buflen = out_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * 2; /* 16bit samples, 2 channels */ } else - buf = ctx->abuffer; + { + buf = (int16_t *)frame->data[0]; + buflen = av_samples_get_buffer_size(NULL, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); + } #if BYTE_ORDER == BIG_ENDIAN /* swap buffer, LE16 */ @@ -478,7 +467,7 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) } ctx->need_resample = (ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16) - || (ctx->acodec->channel_layout != AV_CH_LAYOUT_STEREO) + || (ctx->acodec->channels != 2) || (ctx->acodec->sample_rate != 44100); if (ctx->need_resample) @@ -488,7 +477,7 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) ctx->resample_ctx = avresample_alloc_context(); if (!ctx->resample_ctx) { - DPRINTF(E_WARN, L_XCODE, "Out of memory for resample context\n"); + DPRINTF(E_LOG, L_XCODE, "Out of memory for resample context\n"); goto setup_fail_codec; } @@ -503,28 +492,11 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) ret = avresample_open(ctx->resample_ctx); if (ret < 0) { - DPRINTF(E_WARN, L_XCODE, "Out of memory for resample context\n"); + DPRINTF(E_LOG, L_XCODE, "Could not open resample context\n"); avresample_free(&ctx->resample_ctx); goto setup_fail_codec; } - - ctx->re_abuffer = (int16_t *)av_malloc(XCODE_BUFFER_SIZE * 2); - if (!ctx->re_abuffer) - { - DPRINTF(E_WARN, L_XCODE, "Could not allocate resample buffer\n"); - - avresample_free(&ctx->resample_ctx); - goto setup_fail_codec; - } - -#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) - ctx->input_size = ctx->acodec->channels * av_get_bytes_per_sample(ctx->acodec->sample_fmt); -#elif LIBAVCODEC_VERSION_MAJOR >= 53 - ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_fmt(ctx->acodec->sample_fmt) / 8; -#else - ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_format(ctx->acodec->sample_fmt) / 8; -#endif } ctx->duration = mfi->song_length; From 8663641e843db87ca01ca7e01de8c9164963b987 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 30 Dec 2013 23:16:30 +0100 Subject: [PATCH 04/15] Account for no channel_layout (resample) + add free decode frame --- src/transcode.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/transcode.c b/src/transcode.c index ee4ce7ab..9d366a7a 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -276,6 +276,9 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->offset += processed; + if (frame) + avcodec_free_frame(&frame); + return processed; } @@ -472,7 +475,16 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) if (ctx->need_resample) { - DPRINTF(E_DBG, L_XCODE, "Setting up resampling (%d@%d)\n", ctx->acodec->channels, ctx->acodec->sample_rate); + if (!ctx->acodec->channel_layout) + { + ctx->acodec->channel_layout = av_get_default_channel_layout(ctx->acodec->channels); + + DPRINTF(E_DBG, L_XCODE, "Resample requires channel_layout, but none from ffmpeg. Setting to default.\n"); + } + + DPRINTF(E_DBG, L_XCODE, "Will resample, decoded stream is: %s, %d channels (layout %" PRIu64 "), %d Hz\n", + av_get_sample_fmt_name(ctx->acodec->sample_fmt), ctx->acodec->channels, + ctx->acodec->channel_layout, ctx->acodec->sample_rate); ctx->resample_ctx = avresample_alloc_context(); if (!ctx->resample_ctx) From 21584fa1fff12f415f83d6c167026d9caf84a25e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 30 Dec 2013 23:47:41 +0100 Subject: [PATCH 05/15] Minor adjustment of log message in transcode.c --- src/transcode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transcode.c b/src/transcode.c index 9d366a7a..1530c3c4 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -477,9 +477,9 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) { if (!ctx->acodec->channel_layout) { - ctx->acodec->channel_layout = av_get_default_channel_layout(ctx->acodec->channels); - DPRINTF(E_DBG, L_XCODE, "Resample requires channel_layout, but none from ffmpeg. Setting to default.\n"); + + ctx->acodec->channel_layout = av_get_default_channel_layout(ctx->acodec->channels); } DPRINTF(E_DBG, L_XCODE, "Will resample, decoded stream is: %s, %d channels (layout %" PRIu64 "), %d Hz\n", From edaa8fe4f2893e90dbc43e42af9d43246cb53a42 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 2 Jan 2014 22:49:18 +0100 Subject: [PATCH 06/15] Add libav version conditions --- configure.in | 8 ++- src/transcode.c | 144 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 14 deletions(-) diff --git a/configure.in b/configure.in index e76d4d30..a27a2338 100644 --- a/configure.in +++ b/configure.in @@ -130,7 +130,13 @@ AC_RUN_IFELSE( AC_LANG_POP([C]) LIBS="$save_LIBS" -PKG_CHECK_MODULES(LIBAV, [ libavcodec libavformat libswscale libavutil libavresample ]) +_PKG_CONFIG([libavcodec_VERSION], [atleast-version=54.35], [libavcodec]) +if test $pkg_failed = yes; then + PKG_CHECK_MODULES(LIBAV, [ libavcodec libavformat libswscale libavutil ]) +else + PKG_CHECK_MODULES(LIBAV, [ libavcodec libavformat libswscale libavutil libavresample ]) +fi + dnl Check for av_lock_manager (ffmpeg >= 0.5.1) save_LIBS="$LIBS" AC_CHECK_LIB([avcodec], [av_lockmgr_register], , AC_MSG_ERROR([libav (ffmpeg) >= 0.5.1 required])) diff --git a/src/transcode.c b/src/transcode.c index 1530c3c4..b8ad74d7 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -43,9 +43,11 @@ #include #include -#include #include -#include +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) +# include +# include +#endif #include "logger.h" #include "conffile.h" @@ -67,8 +69,13 @@ struct transcode_ctx { int16_t *abuffer; /* Resampling */ - int need_resample; +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) AVAudioResampleContext *resample_ctx; +#else + ReSampleContext *resample_ctx; + int input_size; +#endif + int need_resample; int16_t *re_abuffer; off_t offset; @@ -148,12 +155,15 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) #if BYTE_ORDER == BIG_ENDIAN int i; #endif -#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) AVFrame *frame = NULL; int got_frame; int out_size; int out_linesize; int out_samples; +#elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) + AVFrame *frame = NULL; + int got_frame; #endif processed = 0; @@ -171,6 +181,7 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) /* Decode data */ while (ctx->apacket2.size > 0) { +#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) got_frame = 0; if (!frame) @@ -190,6 +201,13 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) used = avcodec_decode_audio4(ctx->acodec, frame, &got_frame, &ctx->apacket2); +#else + buflen = XCODE_BUFFER_SIZE; + + used = avcodec_decode_audio3(ctx->acodec, + ctx->abuffer, &buflen, + &ctx->apacket2); +#endif if (used < 0) { @@ -202,10 +220,47 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->apacket2.size -= used; /* No frame decoded this time around */ +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (!got_frame) continue; +#elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) + if (!got_frame) + continue; + else + { + /* This part is from the libav wrapper for avcodec_decode_audio3 */ + int ch, plane_size; + int planar = av_sample_fmt_is_planar(ctx->acodec->sample_fmt); + int data_size = av_samples_get_buffer_size(&plane_size, ctx->acodec->channels, + frame->nb_samples, ctx->acodec->sample_fmt, 1); + + if (XCODE_BUFFER_SIZE < data_size) + { + DPRINTF(E_WARN, L_XCODE, "Output buffer too small for frame (%d < %d)\n", XCODE_BUFFER_SIZE, data_size); + + continue; + } + + memcpy(ctx->abuffer, frame->extended_data[0], plane_size); + + if (planar && ctx->acodec->channels > 1) + { + uint8_t *out = ((uint8_t *)ctx->abuffer) + plane_size; + for (ch = 1; ch < ctx->acodec->channels; ch++) + { + memcpy(out, frame->extended_data[ch], plane_size); + out += plane_size; + } + } + buflen = data_size; + } +#else + if (buflen == 0) + continue; +#endif if (ctx->need_resample) +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) { out_size = av_samples_get_buffer_size(&out_linesize, 2, frame->nb_samples, AV_SAMPLE_FMT_S16, 0); @@ -226,13 +281,29 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) return -1; } - buflen = out_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * 2; /* 16bit samples, 2 channels */ + buflen = out_samples * 2 * 2; /* 16bit samples, 2 channels */ } else { buf = (int16_t *)frame->data[0]; buflen = av_samples_get_buffer_size(NULL, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); } +#else + { + buflen = audio_resample(ctx->resample_ctx, ctx->re_abuffer, ctx->abuffer, buflen / ctx->input_size); + + if (buflen == 0) + { + DPRINTF(E_WARN, L_XCODE, "Resample returned no samples!\n"); + continue; + } + + buflen = buflen * 2 * 2; /* 16bit samples, 2 channels */ + buf = ctx->re_abuffer; + } + else + buf = ctx->abuffer; +#endif #if BYTE_ORDER == BIG_ENDIAN /* swap buffer, LE16 */ @@ -276,8 +347,10 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->offset += processed; +#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) if (frame) avcodec_free_frame(&frame); +#endif return processed; } @@ -374,7 +447,6 @@ struct transcode_ctx * transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) { struct transcode_ctx *ctx; - int i; int ret; ctx = (struct transcode_ctx *)malloc(sizeof(struct transcode_ctx)); @@ -411,14 +483,21 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) goto setup_fail; } +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) + ctx->astream = av_find_best_stream(ctx->fmtctx, AVMEDIA_TYPE_AUDIO, -1, -1, &ctx->adecoder, 0); + + if (ctx->astream < 0) + { + DPRINTF(E_WARN, L_XCODE, "Did not find audio stream or suitable decoder for %s\n", mfi->fname); + + goto setup_fail; + } +#else + int i; ctx->astream = -1; for (i = 0; i < ctx->fmtctx->nb_streams; i++) { -#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) - if (ctx->fmtctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) -#else if (ctx->fmtctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) -#endif { ctx->astream = i; @@ -433,15 +512,15 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) goto setup_fail; } - ctx->acodec = ctx->fmtctx->streams[ctx->astream]->codec; - - ctx->adecoder = avcodec_find_decoder(ctx->acodec->codec_id); + ctx->adecoder = avcodec_find_decoder(ctx->fmtctx->streams[ctx->astream]->codec->codec_id); if (!ctx->adecoder) { DPRINTF(E_WARN, L_XCODE, "No suitable decoder found for codec\n"); goto setup_fail; } +#endif + ctx->acodec = ctx->fmtctx->streams[ctx->astream]->codec; if (ctx->adecoder->capabilities & CODEC_CAP_TRUNCATED) ctx->acodec->flags |= CODEC_FLAG_TRUNCATED; @@ -475,6 +554,7 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) if (ctx->need_resample) { +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (!ctx->acodec->channel_layout) { DPRINTF(E_DBG, L_XCODE, "Resample requires channel_layout, but none from ffmpeg. Setting to default.\n"); @@ -509,6 +589,40 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) avresample_free(&ctx->resample_ctx); goto setup_fail_codec; } +#else + DPRINTF(E_DBG, L_XCODE, "Setting up resampling (%d@%d)\n", ctx->acodec->channels, ctx->acodec->sample_rate); + + ctx->resample_ctx = av_audio_resample_init(2, ctx->acodec->channels, + 44100, ctx->acodec->sample_rate, + AV_SAMPLE_FMT_S16, ctx->acodec->sample_fmt, + 16, 10, 0, 0.8); + + if (!ctx->resample_ctx) + { + DPRINTF(E_WARN, L_XCODE, "Could not init resample from %d@%d to 2@44100\n", ctx->acodec->channels, ctx->acodec->sample_rate); + + goto setup_fail_codec; + } + + ctx->re_abuffer = (int16_t *)av_malloc(XCODE_BUFFER_SIZE * 2); + if (!ctx->re_abuffer) + { + DPRINTF(E_WARN, L_XCODE, "Could not allocate resample buffer\n"); + + audio_resample_close(ctx->resample_ctx); + goto setup_fail_codec; + } +#endif + +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) + +#elif LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) + ctx->input_size = ctx->acodec->channels * av_get_bytes_per_sample(ctx->acodec->sample_fmt); +#elif LIBAVCODEC_VERSION_MAJOR >= 53 + ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_fmt(ctx->acodec->sample_fmt) / 8; +#else + ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_format(ctx->acodec->sample_fmt) / 8; +#endif } ctx->duration = mfi->song_length; @@ -551,7 +665,11 @@ transcode_cleanup(struct transcode_ctx *ctx) if (ctx->need_resample) { +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) avresample_free(&ctx->resample_ctx); +#else + audio_resample_close(ctx->resample_ctx); +#endif av_free(ctx->re_abuffer); } From 92022d82f78f7104ec890afd2474d4ad99f33027 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sun, 5 Jan 2014 13:49:26 +0100 Subject: [PATCH 07/15] Remove patches for ancient versions of ffmpeg --- ffmpeg/ffmpeg-0.5.tv.patch.sh | 97 ----------------------------------- ffmpeg/ffmpeg-0.6.patch | 81 ----------------------------- ffmpeg/libav-0.7.patch | 81 ----------------------------- 3 files changed, 259 deletions(-) delete mode 100755 ffmpeg/ffmpeg-0.5.tv.patch.sh delete mode 100644 ffmpeg/ffmpeg-0.6.patch delete mode 100644 ffmpeg/libav-0.7.patch diff --git a/ffmpeg/ffmpeg-0.5.tv.patch.sh b/ffmpeg/ffmpeg-0.5.tv.patch.sh deleted file mode 100755 index e46ccbd9..00000000 --- a/ffmpeg/ffmpeg-0.5.tv.patch.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/sh -# -# Patch for ffmpeg so forked-daapd can extract iTunes TV metadata from mp4 video files -# Ace Jones -# -# Usage: chmod +x this file, and run it from the directory above where you want ffmpeg -# -svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg -patch -p0 < $0 -exit -Index: ffmpeg/libavformat/mov.c -=================================================================== ---- ffmpeg/libavformat/mov.c (revision 20790) -+++ ffmpeg/libavformat/mov.c (working copy) -@@ -80,19 +80,45 @@ - - static const MOVParseTableEntry mov_default_parse_table[]; - --static int mov_metadata_trkn(MOVContext *c, ByteIOContext *pb, unsigned len) -+static int mov_metadata_trkn(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) - { - char buf[16]; - - get_be16(pb); // unknown - snprintf(buf, sizeof(buf), "%d", get_be16(pb)); -- av_metadata_set(&c->fc->metadata, "track", buf); -+ av_metadata_set(&c->fc->metadata, key, buf); - - get_be16(pb); // total tracks - - return 0; - } - -+static int mov_metadata_int8(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ /* bypass padding bytes */ -+ get_byte(pb); -+ get_byte(pb); -+ get_byte(pb); -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set(&c->fc->metadata, key, buf); -+ -+ return 0; -+} -+ -+static int mov_metadata_stik(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set(&c->fc->metadata, key, buf); -+ -+ return 0; -+} - static int mov_read_udta_string(MOVContext *c, ByteIOContext *pb, MOVAtom atom) - { - #ifdef MOV_EXPORT_ALL_METADATA -@@ -101,7 +127,7 @@ - char str[1024], key2[16], language[4] = {0}; - const char *key = NULL; - uint16_t str_size; -- int (*parse)(MOVContext*, ByteIOContext*, unsigned) = NULL; -+ int (*parse)(MOVContext*, ByteIOContext*, unsigned, const char*) = NULL; - - switch (atom.type) { - case MKTAG(0xa9,'n','a','m'): key = "title"; break; -@@ -122,6 +148,12 @@ - case MKTAG( 't','v','s','h'): key = "show"; break; - case MKTAG( 't','v','e','n'): key = "episode_id";break; - case MKTAG( 't','v','n','n'): key = "network"; break; -+ case MKTAG( 't','v','e','s'): key = "episode_sort"; -+ parse = mov_metadata_int8; break; -+ case MKTAG( 't','v','s','n'): key = "season_number"; -+ parse = mov_metadata_int8; break; -+ case MKTAG( 's','t','i','k'): key = "stik"; -+ parse = mov_metadata_stik; break; - case MKTAG( 't','r','k','n'): key = "track"; - parse = mov_metadata_trkn; break; - } -@@ -157,10 +189,11 @@ - str_size = FFMIN3(sizeof(str)-1, str_size, atom.size); - - if (parse) -- parse(c, pb, str_size); -+ parse(c, pb, str_size, key); - else { - get_buffer(pb, str, str_size); - str[str_size] = 0; -+ - av_metadata_set(&c->fc->metadata, key, str); - if (*language && strcmp(language, "und")) { - snprintf(key2, sizeof(key2), "%s-%s", key, language); diff --git a/ffmpeg/ffmpeg-0.6.patch b/ffmpeg/ffmpeg-0.6.patch deleted file mode 100644 index 0971f602..00000000 --- a/ffmpeg/ffmpeg-0.6.patch +++ /dev/null @@ -1,81 +0,0 @@ ---- ffmpeg/libavformat/mov.c 2010-12-13 17:10:32.347224001 -0800 -+++ ffmpeg/libavformat/mov.c 2010-12-13 17:03:05.078969987 -0800 -@@ -78,19 +78,46 @@ - - static const MOVParseTableEntry mov_default_parse_table[]; - --static int mov_metadata_trkn(MOVContext *c, ByteIOContext *pb, unsigned len) -+static int mov_metadata_trkn(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) - { - char buf[16]; - - get_be16(pb); // unknown - snprintf(buf, sizeof(buf), "%d", get_be16(pb)); -- av_metadata_set2(&c->fc->metadata, "track", buf, 0); -+ av_metadata_set(&c->fc->metadata, key, buf); - - get_be16(pb); // total tracks - - return 0; - } - -+static int mov_metadata_int8(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ /* bypass padding bytes */ -+ get_byte(pb); -+ get_byte(pb); -+ get_byte(pb); -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set(&c->fc->metadata, key, buf); -+ -+ return 0; -+} -+ -+static int mov_metadata_stik(MOVContext *c, ByteIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set(&c->fc->metadata, key, buf); -+ -+ return 0; -+} -+ - static const uint32_t mac_to_unicode[128] = { - 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, - 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, -@@ -137,7 +164,7 @@ - const char *key = NULL; - uint16_t str_size, langcode = 0; - uint32_t data_type = 0; -- int (*parse)(MOVContext*, ByteIOContext*, unsigned) = NULL; -+ int (*parse)(MOVContext*, ByteIOContext*, unsigned, const char *) = NULL; - - switch (atom.type) { - case MKTAG(0xa9,'n','a','m'): key = "title"; break; -@@ -159,6 +186,11 @@ - case MKTAG( 't','v','s','h'): key = "show"; break; - case MKTAG( 't','v','e','n'): key = "episode_id";break; - case MKTAG( 't','v','n','n'): key = "network"; break; -+ case MKTAG( 't','v','e','s'): key = "episode_sort"; -+ case MKTAG( 't','v','s','n'): key = "season_number"; -+ parse = mov_metadata_int8; break; -+ case MKTAG( 's','t','i','k'): key = "stik"; -+ parse = mov_metadata_stik; break; - case MKTAG( 't','r','k','n'): key = "track"; - parse = mov_metadata_trkn; break; - } -@@ -195,7 +227,7 @@ - str_size = FFMIN3(sizeof(str)-1, str_size, atom.size); - - if (parse) -- parse(c, pb, str_size); -+ parse(c, pb, str_size, key); - else { - if (data_type == 3 || (data_type == 0 && langcode < 0x800)) { // MAC Encoded - mov_read_mac_string(c, pb, str_size, str, sizeof(str)); diff --git a/ffmpeg/libav-0.7.patch b/ffmpeg/libav-0.7.patch deleted file mode 100644 index 2a54849b..00000000 --- a/ffmpeg/libav-0.7.patch +++ /dev/null @@ -1,81 +0,0 @@ ---- libav/libavformat/mov.c 2011-08-09 14:03:47.622688230 -0700 -+++ libav/libavformat/mov.c 2011-08-09 14:09:33.181475099 -0700 -@@ -81,19 +81,46 @@ - - static const MOVParseTableEntry mov_default_parse_table[]; - --static int mov_metadata_trkn(MOVContext *c, AVIOContext *pb, unsigned len) -+static int mov_metadata_trkn(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) - { - char buf[16]; - - avio_rb16(pb); // unknown - snprintf(buf, sizeof(buf), "%d", avio_rb16(pb)); -- av_dict_set(&c->fc->metadata, "track", buf, 0); -+ av_dict_set(&c->fc->metadata, key, buf, 0); - - avio_rb16(pb); // total tracks - - return 0; - } - -+static int mov_metadata_int8(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ /* bypass padding bytes */ -+ get_byte(pb); -+ get_byte(pb); -+ get_byte(pb); -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set2(&c->fc->metadata, key, buf, 0); -+ -+ return 0; -+} -+ -+static int mov_metadata_stik(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) -+{ -+ char buf[16]; -+ -+ snprintf(buf, sizeof(buf-1), "%hu", get_byte(pb)); -+ buf[sizeof(buf)-1] = 0; -+ av_metadata_set2(&c->fc->metadata, key, buf, 0); -+ -+ return 0; -+} -+ - static const uint32_t mac_to_unicode[128] = { - 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, - 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, -@@ -140,7 +167,7 @@ - const char *key = NULL; - uint16_t str_size, langcode = 0; - uint32_t data_type = 0; -- int (*parse)(MOVContext*, AVIOContext*, unsigned) = NULL; -+ int (*parse)(MOVContext*, AVIOContext*, unsigned, const char *) = NULL; - - switch (atom.type) { - case MKTAG(0xa9,'n','a','m'): key = "title"; break; -@@ -162,6 +189,11 @@ - case MKTAG( 't','v','s','h'): key = "show"; break; - case MKTAG( 't','v','e','n'): key = "episode_id";break; - case MKTAG( 't','v','n','n'): key = "network"; break; -+ case MKTAG( 't','v','e','s'): key = "episode_sort"; -+ case MKTAG( 't','v','s','n'): key = "season_number"; -+ parse = mov_metadata_int8; break; -+ case MKTAG( 's','t','i','k'): key = "stik"; -+ parse = mov_metadata_stik; break; - case MKTAG( 't','r','k','n'): key = "track"; - parse = mov_metadata_trkn; break; - } -@@ -198,7 +230,7 @@ - str_size = FFMIN3(sizeof(str)-1, str_size, atom.size); - - if (parse) -- parse(c, pb, str_size); -+ parse(c, pb, str_size, key); - else { - if (data_type == 3 || (data_type == 0 && langcode < 0x800)) { // MAC Encoded - mov_read_mac_string(c, pb, str_size, str, sizeof(str)); From efd4d56de515cb167625ac730e814d265383aa5e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 6 Jan 2014 21:41:30 +0100 Subject: [PATCH 08/15] Fix a few missing libav conditions --- src/filescanner_ffmpeg.c | 2 ++ src/transcode.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/filescanner_ffmpeg.c b/src/filescanner_ffmpeg.c index 69ef2ded..b276a0ca 100644 --- a/src/filescanner_ffmpeg.c +++ b/src/filescanner_ffmpeg.c @@ -385,12 +385,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) #else case CODEC_TYPE_VIDEO: #endif +#if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 20) if (ctx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) { DPRINTF(E_DBG, L_SCAN, "Found embedded artwork (stream %d)\n", i); break; } +#endif if (!video_stream) { DPRINTF(E_DBG, L_SCAN, "File has video (stream %d)\n", i); diff --git a/src/transcode.c b/src/transcode.c index b8ad74d7..a92477af 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -347,9 +347,12 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->offset += processed; -#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (frame) avcodec_free_frame(&frame); +#elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) + if (frame) + av_free(frame); #endif return processed; From a1236f440014ba4687892649cceec2353cafad3e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Wed, 8 Jan 2014 22:13:20 +0100 Subject: [PATCH 09/15] Lower log level of message "Failed to retrieve artwork..." --- src/raop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raop.c b/src/raop.c index cf08c41f..55b073dd 100644 --- a/src/raop.c +++ b/src/raop.c @@ -851,7 +851,7 @@ raop_metadata_prepare(int id, uint64_t rtptime) ret = artwork_get_item_filename(dbmfi.path, 600, 600, ART_CAN_PNG | ART_CAN_JPEG, rmd->artwork); if (ret < 0) { - DPRINTF(E_LOG, L_RAOP, "Failed to retrieve artwork for '%s' (%d); no artwork will be sent\n", dbmfi.title, id); + DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for '%s' (%d); no artwork will be sent\n", dbmfi.title, id); evbuffer_free(rmd->artwork); rmd->artwork = NULL; From a6b586a0ce44327fb83f5dbe9f7a865caab3c470 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 21:07:27 +0100 Subject: [PATCH 10/15] Support for embedded artwork (items only) --- src/artwork.c | 208 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 48 deletions(-) diff --git a/src/artwork.c b/src/artwork.c index 593a58b6..9214abbb 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -95,6 +95,56 @@ artwork_read(char *filename, struct evbuffer *evbuf) return -1; } +static int +rescale_needed(AVCodecContext *src, int max_w, int max_h, int *target_w, int *target_h) +{ + int need_rescale; + + DPRINTF(E_DBG, L_ART, "Original image dimensions: w %d h %d\n", src->width, src->height); + + need_rescale = 1; + + if ((max_w <= 0) || (max_h <= 0)) /* No valid dimensions, use original */ + { + need_rescale = 0; + + *target_w = src->width; + *target_h = src->height; + } + else if ((src->width <= max_w) && (src->height <= max_h)) /* Smaller than target */ + { + need_rescale = 0; + + *target_w = src->width; + *target_h = src->height; + } + else if (src->width * max_h > src->height * max_w) /* Wider aspect ratio than target */ + { + *target_w = max_w; + *target_h = (double)max_w * ((double)src->height / (double)src->width); + } + else /* Taller or equal aspect ratio */ + { + *target_w = (double)max_h * ((double)src->width / (double)src->height); + *target_h = max_h; + } + + DPRINTF(E_DBG, L_ART, "Raw destination width %d height %d\n", *target_w, *target_h); + + if ((*target_h > max_h) && (max_h > 0)) + *target_h = max_h; + + /* PNG prefers even row count */ + *target_w += *target_w % 2; + + if ((*target_w > max_w) && (max_w > 0)) + *target_w = max_w - (max_w % 2); + + DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", *target_w, *target_h); + + return need_rescale; +} + static int artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int format, struct evbuffer *evbuf) { @@ -520,15 +570,13 @@ static int artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf) { AVFormatContext *src_ctx; - AVCodecContext *src; int s; int target_w; int target_h; - int need_rescale; int format_ok; int ret; - DPRINTF(E_DBG, L_ART, "Artwork request parameters: max w = %d, max h = %d\n", max_w, max_h); + DPRINTF(E_DBG, L_ART, "Artwork request for %s, max w = %d, max h = %d\n", filename, max_w, max_h); src_ctx = NULL; @@ -588,52 +636,10 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e return -1; } - src = src_ctx->streams[s]->codec; - - DPRINTF(E_DBG, L_ART, "Original image '%s': w %d h %d\n", filename, src->width, src->height); - - need_rescale = 1; - - if ((max_w <= 0) || (max_h <= 0)) /* No valid dimensions, use original */ - { - need_rescale = 0; - - target_w = src->width; - target_h = src->height; - } - else if ((src->width <= max_w) && (src->height <= max_h)) /* Smaller than target */ - { - need_rescale = 0; - - target_w = src->width; - target_h = src->height; - } - else if (src->width * max_h > src->height * max_w) /* Wider aspect ratio than target */ - { - target_w = max_w; - target_h = (double)max_w * ((double)src->height / (double)src->width); - } - else /* Taller or equal aspect ratio */ - { - target_w = (double)max_h * ((double)src->width / (double)src->height); - target_h = max_h; - } - - DPRINTF(E_DBG, L_ART, "Raw destination width %d height %d\n", target_w, target_h); - - if ((target_h > max_h) && (max_h > 0)) - target_h = max_h; - - /* PNG prefers even row count */ - target_w += target_w % 2; - - if ((target_w > max_w) && (max_w > 0)) - target_w = max_w - (max_w % 2); - - DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", target_w, target_h); + ret = rescale_needed(src_ctx->streams[s]->codec, max_w, max_h, &target_w, &target_h); /* Fastpath */ - if (!need_rescale && format_ok) + if (!ret && format_ok) { ret = artwork_read(filename, evbuf); if (ret == 0) @@ -657,6 +663,103 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e return ret; } +static int +artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf) +{ + AVFormatContext *src_ctx; + AVStream *src_st; + int s; + int target_w; + int target_h; + int format_ok; + int ret; + + DPRINTF(E_DBG, L_ART, "Artwork request for %s, max w = %d, max h = %d\n", filename, max_w, max_h); + + src_ctx = NULL; + + ret = avformat_open_input(&src_ctx, filename, NULL, NULL); + if (ret < 0) + { + DPRINTF(E_WARN, L_ART, "Cannot open media file '%s': %s\n", filename, strerror(AVUNERROR(ret))); + + return -1; + } + + ret = avformat_find_stream_info(src_ctx, NULL); + if (ret < 0) + { + DPRINTF(E_WARN, L_ART, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret))); + + avformat_close_input(&src_ctx); + return -1; + } + + format_ok = 0; + for (s = 0; s < src_ctx->nb_streams; s++) + { + if (src_ctx->streams[s]->disposition & AV_DISPOSITION_ATTACHED_PIC) + { + if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_PNG) + { + format_ok = (format & ART_CAN_PNG) ? ART_FMT_PNG : 0; + break; + } + else if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_MJPEG) + { + format_ok = (format & ART_CAN_JPEG) ? ART_FMT_JPEG : 0; + break; + } + } + } + + if (s == src_ctx->nb_streams) + { + DPRINTF(E_DBG, L_ART, "Did not find embedded artwork in '%s'\n", filename); + + avformat_close_input(&src_ctx); + return -1; + } + + src_st = src_ctx->streams[s]; + + ret = rescale_needed(src_st->codec, max_w, max_h, &target_w, &target_h); + + /* Fastpath */ + if (!ret && format_ok) + { + DPRINTF(E_DBG, L_ART, "Artwork does not need rescale, sending original picture\n"); + + ret = evbuffer_expand(evbuf, src_st->attached_pic.size); + if (ret < 0) + { + DPRINTF(E_LOG, L_ART, "Out of memory for artwork\n"); + + avformat_close_input(&src_ctx); + return -1; + } + + ret = evbuffer_add(evbuf, src_st->attached_pic.data, src_st->attached_pic.size); + if (ret < 0) + { + DPRINTF(E_LOG, L_ART, "Could not add embedded image to event buffer\n"); + } + else + ret = format_ok; + } + else + ret = artwork_rescale(src_ctx, s, target_w, target_h, format, evbuf); + + avformat_close_input(&src_ctx); + + if (ret < 0) + { + if (EVBUFFER_LENGTH(evbuf) > 0) + evbuffer_drain(evbuf, EVBUFFER_LENGTH(evbuf)); + } + + return ret; +} static int artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf) @@ -836,7 +939,16 @@ artwork_get_item_filename(char *filename, int max_w, int max_h, int format, stru { int ret; - /* FUTURE: look at embedded artwork */ + /* If item is an internet stream don't look for artwork */ + if (strncmp(filename, "http://", strlen("http://")) == 0) + return -1; + +#if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 20) + /* Look for embedded artwork */ + ret = artwork_get_embedded_image(filename, max_w, max_h, format, evbuf); + if (ret > 0) + return ret; +#endif /* Look for basename(filename).{png,jpg} */ ret = artwork_get_own_image(filename, max_w, max_h, format, evbuf); From 95b67668b410a4ec1af1ac0d3a03da8ee1a46aa7 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 21:11:01 +0100 Subject: [PATCH 11/15] Forgot libav preprocessor condition --- src/artwork.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/artwork.c b/src/artwork.c index 9214abbb..b517cd1f 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -663,6 +663,7 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e return ret; } +#if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 20) static int artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf) { @@ -760,6 +761,7 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, str return ret; } +#endif static int artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf) From 416b89f51a21622e2b7446fbbbf47acd1898f6a1 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 21:39:05 +0100 Subject: [PATCH 12/15] Use avcodec_find_best_pix_fmt2 (avcodec_find_best_pix_fmt deprecated in libav9) --- src/artwork.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/artwork.c b/src/artwork.c index b517cd1f..861fa6bb 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -161,9 +161,6 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int forma AVCodec *img_decoder; AVCodec *img_encoder; - int64_t pix_fmt_mask; - const enum PixelFormat *pix_fmts; - AVFrame *i_frame; AVFrame *o_frame; @@ -294,7 +291,12 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int forma dst->codec_type = CODEC_TYPE_VIDEO; #endif - pix_fmt_mask = 0; +#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) + dst->pix_fmt = avcodec_find_best_pix_fmt2((enum AVPixelFormat *)img_encoder->pix_fmts, src->pix_fmt, 1, NULL); +#else + const enum PixelFormat *pix_fmts; + int64_t pix_fmt_mask = 0; + pix_fmts = img_encoder->pix_fmts; while (pix_fmts && (*pix_fmts != -1)) { @@ -303,6 +305,7 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int forma } dst->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, src->pix_fmt, 1, NULL); +#endif if (dst->pix_fmt < 0) { From da59d0c5785813ebaf236de4cb2f8a9539aa9741 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 22:58:11 +0100 Subject: [PATCH 13/15] Support for embedded artwork (groups) and misc cleaning up --- src/artwork.c | 66 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/src/artwork.c b/src/artwork.c index 861fa6bb..e244fc42 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -579,7 +579,7 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e int format_ok; int ret; - DPRINTF(E_DBG, L_ART, "Artwork request for %s, max w = %d, max h = %d\n", filename, max_w, max_h); + DPRINTF(E_DBG, L_ART, "Getting artwork (parameters: max w %d, max h = %d)\n", max_w, max_h); src_ctx = NULL; @@ -678,7 +678,11 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, str int format_ok; int ret; - DPRINTF(E_DBG, L_ART, "Artwork request for %s, max w = %d, max h = %d\n", filename, max_w, max_h); + /* If item is an internet stream don't look for artwork */ + if (strncmp(filename, "http://", strlen("http://")) == 0) + return -1; + + DPRINTF(E_SPAM, L_ART, "Trying embedded artwork in %s\n", filename); src_ctx = NULL; @@ -719,11 +723,13 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, str if (s == src_ctx->nb_streams) { - DPRINTF(E_DBG, L_ART, "Did not find embedded artwork in '%s'\n", filename); + DPRINTF(E_SPAM, L_ART, "Did not find embedded artwork in '%s'\n", filename); avformat_close_input(&src_ctx); return -1; } + else + DPRINTF(E_DBG, L_ART, "Found embedded artwork in '%s'\n", filename); src_st = src_ctx->streams[s]; @@ -732,7 +738,7 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, str /* Fastpath */ if (!ret && format_ok) { - DPRINTF(E_DBG, L_ART, "Artwork does not need rescale, sending original picture\n"); + DPRINTF(E_DBG, L_ART, "Artwork not too large, using original image\n"); ret = evbuffer_expand(evbuf, src_st->attached_pic.size); if (ret < 0) @@ -752,7 +758,11 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, int format, str ret = format_ok; } else - ret = artwork_rescale(src_ctx, s, target_w, target_h, format, evbuf); + { + DPRINTF(E_DBG, L_ART, "Artwork too large, rescaling image\n"); + + ret = artwork_rescale(src_ctx, s, target_w, target_h, format, evbuf); + } avformat_close_input(&src_ctx); @@ -775,6 +785,10 @@ artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuf int i; int ret; + /* If item is an internet stream don't look for artwork */ + if (strncmp(path, "http://", strlen("http://")) == 0) + return -1; + ret = snprintf(artwork, sizeof(artwork), "%s", path); if ((ret < 0) || (ret >= sizeof(artwork))) { @@ -799,7 +813,7 @@ artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuf continue; } - DPRINTF(E_DBG, L_ART, "Trying own artwork file %s\n", artwork); + DPRINTF(E_SPAM, L_ART, "Trying own artwork file %s\n", artwork); ret = access(artwork, F_OK); if (ret < 0) @@ -811,6 +825,8 @@ artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuf if (i == (sizeof(cover_extension) / sizeof(cover_extension[0]))) return -1; + DPRINTF(E_DBG, L_ART, "Found own artwork file %s\n", artwork); + return artwork_get(artwork, max_w, max_h, format, evbuf); } @@ -826,6 +842,10 @@ artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, int format, s cfg_t *lib; int nbasenames; + /* If item is an internet stream don't look for artwork */ + if (strncmp(path, "http://", strlen("http://")) == 0) + return -1; + ret = snprintf(artwork, sizeof(artwork), "%s", path); if ((ret < 0) || (ret >= sizeof(artwork))) { @@ -861,7 +881,7 @@ artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, int format, s continue; } - DPRINTF(E_DBG, L_ART, "Trying directory artwork file %s\n", artwork); + DPRINTF(E_SPAM, L_ART, "Trying directory artwork file %s\n", artwork); ret = access(artwork, F_OK); if (ret < 0) @@ -877,6 +897,8 @@ artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, int format, s if (i == nbasenames) return -1; + DPRINTF(E_DBG, L_ART, "Found directory artwork file %s\n", artwork); + return artwork_get(artwork, max_w, max_h, format, evbuf); } @@ -890,6 +912,10 @@ artwork_get_parentdir_image(char *path, int isdir, int max_w, int max_h, int for int i; int ret; + /* If item is an internet stream don't look for artwork */ + if (strncmp(path, "http://", strlen("http://")) == 0) + return -1; + ret = snprintf(artwork, sizeof(artwork), "%s", path); if ((ret < 0) || (ret >= sizeof(artwork))) { @@ -922,7 +948,7 @@ artwork_get_parentdir_image(char *path, int isdir, int max_w, int max_h, int for continue; } - DPRINTF(E_DBG, L_ART, "Trying parent directory artwork file %s\n", artwork); + DPRINTF(E_SPAM, L_ART, "Trying parent directory artwork file %s\n", artwork); ret = access(artwork, F_OK); if (ret < 0) @@ -934,6 +960,8 @@ artwork_get_parentdir_image(char *path, int isdir, int max_w, int max_h, int for if (i == (sizeof(cover_extension) / sizeof(cover_extension[0]))) return -1; + DPRINTF(E_DBG, L_ART, "Found parent directory artwork file %s\n", artwork); + return artwork_get(artwork, max_w, max_h, format, evbuf); } @@ -944,10 +972,6 @@ artwork_get_item_filename(char *filename, int max_w, int max_h, int format, stru { int ret; - /* If item is an internet stream don't look for artwork */ - if (strncmp(filename, "http://", strlen("http://")) == 0) - return -1; - #if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 20) /* Look for embedded artwork */ ret = artwork_get_embedded_image(filename, max_w, max_h, format, evbuf); @@ -1020,12 +1044,11 @@ artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evb goto files_art; } - got_art = -1; - while ((got_art < 0) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) + got_art = 0; + while (!got_art && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) { - got_art = artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf); - if (got_art < 0) - got_art = artwork_get_parentdir_image(dir, 1, max_w, max_h, format, evbuf); + got_art = (artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf) > 0) + || (artwork_get_parentdir_image(dir, 1, max_w, max_h, format, evbuf) > 0); } db_query_end(&qp); @@ -1051,10 +1074,11 @@ artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evb return -1; } - got_art = -1; - while ((got_art < 0) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) + got_art = 0; + while (!got_art && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { - got_art = artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf); + got_art = (artwork_get_embedded_image(dbmfi.path, max_w, max_h, format, evbuf) > 0) + || (artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf) > 0); } db_query_end(&qp); @@ -1064,5 +1088,7 @@ artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evb else if (got_art > 0) return got_art; + DPRINTF(E_DBG, L_ART, "No artwork found for group %d\n", id); + return -1; } From 54fa5b9104068de0f0b96a35c7177a5ab54967d7 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 22:58:58 +0100 Subject: [PATCH 14/15] Update artwork documentation and misc doc improvements --- INSTALL | 10 +++++---- README | 65 ++++++++++++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/INSTALL b/INSTALL index db1cf1bd..70bfdbfc 100644 --- a/INSTALL +++ b/INSTALL @@ -19,9 +19,10 @@ Tools required: - gettext - gawk - gperf + - antlr3 (see below) Libraries: - - libantlr3c (ANTLR3 C runtime, version 3.2 for tarball builds) + - libantlr3c (ANTLR3 C runtime, use the same version as antlr3, see below) from - Avahi client libraries (avahi-client), 0.6.24 minimum from @@ -73,8 +74,10 @@ multimedia applications. The version of libav you use will potentially have a great influence on your experience with forked-daapd. forked-daapd is known to be working with libav 0.8.x, but it also supports older -versions of libav and ffmpeg. Versions of libav/ffmpeg newer than 0.8.x/0.11.x -do not work well with forked-daapd. +versions of libav and ffmpeg. + +Embedded artwork is only supported if your version of forked-daapd is built +with libav 9+ or ffmpeg 0.11+. Building from the git tree @@ -87,7 +90,6 @@ The source for this version of forked-daapd can be found here: The original (now unmaintained) source can be found here: - Required tools: - ANTLR v3 is required to build forked-daapd, along with its C runtime diff --git a/README b/README index 0852e8b6..35967967 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ forked-daapd ------------ -forked-daapd is a DAAP and RSP media server, with support for Linux and -FreeBSD. It is a complete rewrite of mt-daapd (Firefly Media Server). +forked-daapd is a DAAP (iTunes) and RSP (Roku) media server, with support for +Linux and FreeBSD. DAAP stands for Digital Audio Access Protocol, and is the protocol used by iTunes and friends to share/stream media libraries over the network. @@ -17,7 +17,8 @@ The source for this version of forked-daapd can be found here: The original (now unmaintained) source can be found here: - + +forked-daapd is a complete rewrite of mt-daapd (Firefly Media Server). Supported clients @@ -54,20 +55,30 @@ some do not require pairing. forked-daapd can be paired with Apple's Remote application for iPod/iPhone/iPad; this is how the pairing process works: - - start forked-daapd - - start Remote, go to Settings, Add Library - - prepare a text file with a filename ending with .remote; the filename - doesn't matter, only the .remote ending does. This file must contain - two lines: the first line is the name of your iPod/iPhone/iPad, the second - is the 4-digit pairing code displayed by Remote. + 1. Start forked-daapd + 2. Start Remote, go to Settings, Add Library + 3. Look in the log file for a message saying: - If your iPod/iPhone/iPad is named "Foobar" and Remote gives you the pairing - code 5387, the file content will be: + "Discovered remote 'Foobar' (id 71624..." + + This tells you the name of your device (Foobar in this example). + + If you cannot find this message, it means that forked-daapd did not receive + a mDNS announcement from your Remote. You have a network issue and mDNS + doesn't work properly on your network. + + 4. Prepare a text file with a filename ending with .remote; the filename + doesn't matter, only the .remote ending does. This file must contain + two lines: the first line is the name of your iPod/iPhone/iPad, the second + is the 4-digit pairing code displayed by Remote. + + If your iPod/iPhone/iPad is named "Foobar" and Remote gives you the pairing + code 5387, the file content must be: Foobar 5387 - - move this file somewhere in your library + 5. Move this file somewhere in your library At this point, you should be done with the pairing process and Remote should display the name of your forked-daapd library. You can delete the .remote file @@ -79,13 +90,8 @@ the pairing process failed. This will usually be because the .remote file did not contain the correct name or pairing code. Start over the pairing process and try again. -If in doubt, enable a more verbose level of logging and check that forked-daapd -receives the mDNS announcement from your iPod/iPhone when the pairing code is -displayed by Remote (you can also use avahi-browse for this purpose, see below). -If not, you have a network issue and mDNS doesn't work properly on your network. - -If you are unsure about your iPod/iPhone/iPad's name, here's how you can check -for the correct value: +If you have trouble pairing with forked-daapd, you can use avahi-browse for +troubleshooting: - in a terminal, run avahi-browse -r -k _touch-remote._tcp - start Remote, goto Settings, Add Library - after a couple seconds at most, you should get something similar to this: @@ -128,8 +134,8 @@ server startup, provided they appear in the 5 minutes following the startup and no playback has occured yet. Again, principle of least surprise. -AirTunes devices ----------------- +AirTunes devices (AirPlay speakers) +----------------------------------- forked-daapd will discover the AirTunes devices available on your network. For devices that are password-protected, the device's AirTunes name and password @@ -210,9 +216,10 @@ Smart playlists are not supported at the moment. Artwork ------- -forked-daapd has /some/ support for artwork, with a number of limitations. +forked-daapd has support for artwork. -Embedded artwork is not currently supported. +Embedded artwork is only supported if your version of forked-daapd was built +with libav 9+ or ffmpeg 0.11+. Your artwork must be in PNG or JPEG format, dimensions do not matter; forked-daapd scales down (never up) the artwork on-the-fly to match the @@ -221,9 +228,10 @@ the more time and resources it takes to perform the scaling operation. As for the naming convention, it is quite simple; consider your foo.mp3 song, residing at /bar/foo.mp3: - - if /bar/foo.{png,jpg} exists, this will be used as the artwork for this file; - - failing that, if /bar/{artwork,cover,Folder}.{png,jpg} exists, it is used. - - failing that, if /bar/bar.{png,jpg} exists, it will be used + - if it has embedded artwork, this will be used as the artwork for this file; + - failing that, if /bar/foo.{png,jpg} exists, it is used; + - failing that, if /bar/{artwork,cover,Folder}.{png,jpg} exists, it is used; + - failing that, if /bar/bar.{png,jpg} exists, it is used For "groups" (same album name and album artist), the situation is a bit different: @@ -233,8 +241,9 @@ different: - failing that, if [directory name].{png,jpg} is found in one of the directories containing files that are part of the group, it is used as the artwork. The first file found is used, ordering is not guaranteed; - - failing that, individual files are examined and the first artwork found is - used. Here again, ordering is not guaranteed. + - failing that, individual files are examined and the first artwork found + (embedded or in the same dir and named the same as the file) is used. Here + again, ordering is not guaranteed. {artwork,cover,Folder} are the default, you can add other base names in the configuration file. From c7c7f835b72c7599371ffdbc55c932337b55f10e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Thu, 9 Jan 2014 23:13:40 +0100 Subject: [PATCH 15/15] Another libav preprocessor condition --- src/artwork.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/artwork.c b/src/artwork.c index e244fc42..501c5b58 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -579,7 +579,7 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e int format_ok; int ret; - DPRINTF(E_DBG, L_ART, "Getting artwork (parameters: max w %d, max h = %d)\n", max_w, max_h); + DPRINTF(E_DBG, L_ART, "Getting artwork (max destination width %d height %d)\n", max_w, max_h); src_ctx = NULL; @@ -1077,8 +1077,12 @@ artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evb got_art = 0; while (!got_art && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { +#if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 20) got_art = (artwork_get_embedded_image(dbmfi.path, max_w, max_h, format, evbuf) > 0) || (artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf) > 0); +#else + got_art = (artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf) > 0); +#endif } db_query_end(&qp);