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. diff --git a/configure.in b/configure.in index aea17e6f..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 ]) +_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/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)); diff --git a/src/artwork.c b/src/artwork.c index 593a58b6..501c5b58 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) { @@ -111,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; @@ -244,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)) { @@ -253,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) { @@ -520,15 +573,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, "Getting artwork (max destination width %d height %d)\n", max_w, max_h); src_ctx = NULL; @@ -588,52 +639,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 +666,115 @@ 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) +{ + AVFormatContext *src_ctx; + AVStream *src_st; + int s; + int target_w; + int target_h; + int format_ok; + int ret; + + /* 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; + + 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_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]; + + 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 not too large, using original image\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 + { + 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); + + if (ret < 0) + { + if (EVBUFFER_LENGTH(evbuf) > 0) + evbuffer_drain(evbuf, EVBUFFER_LENGTH(evbuf)); + } + + return ret; +} +#endif static int artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf) @@ -667,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))) { @@ -691,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) @@ -703,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); } @@ -718,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))) { @@ -753,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) @@ -769,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); } @@ -782,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))) { @@ -814,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) @@ -826,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); } @@ -836,7 +972,12 @@ artwork_get_item_filename(char *filename, int max_w, int max_h, int format, stru { int ret; - /* FUTURE: look at embedded artwork */ +#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); @@ -903,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); @@ -934,10 +1074,15 @@ 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); +#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); @@ -947,5 +1092,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; } diff --git a/src/filescanner_ffmpeg.c b/src/filescanner_ffmpeg.c index dbbb3207..b276a0ca 100644 --- a/src/filescanner_ffmpeg.c +++ b/src/filescanner_ffmpeg.c @@ -384,6 +384,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) case AVMEDIA_TYPE_VIDEO: #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) { diff --git a/src/raop.c b/src/raop.c index 9e6b62d9..7e2fa76b 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; diff --git a/src/transcode.c b/src/transcode.c index 35404e48..a92477af 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -44,6 +44,10 @@ #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" @@ -65,9 +69,13 @@ struct transcode_ctx { int16_t *abuffer; /* Resampling */ - int need_resample; - int input_size; +#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; @@ -147,9 +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) - AVFrame frame; - int got_frame = 0; +#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; @@ -167,19 +181,29 @@ 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); @@ -195,14 +219,20 @@ 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) - /* This part is from the libav wrapper for avcodec_decode_audio3 - it may be useless in this context */ - if (got_frame != 0) + /* 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); + frame->nb_samples, ctx->acodec->sample_fmt, 1); if (XCODE_BUFFER_SIZE < data_size) { @@ -211,28 +241,54 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) continue; } - memcpy(ctx->abuffer, frame.extended_data[0], plane_size); + 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); + memcpy(out, frame->extended_data[ch], plane_size); out += plane_size; } } buflen = data_size; } - else - continue; #else - /* No frame decoded this time around */ 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); + + 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 = 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); @@ -247,6 +303,7 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) } else buf = ctx->abuffer; +#endif #if BYTE_ORDER == BIG_ENDIAN /* swap buffer, LE16 */ @@ -290,6 +347,14 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) ctx->offset += processed; +#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; } @@ -385,7 +450,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)); @@ -422,14 +486,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; @@ -444,20 +515,23 @@ 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; #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,10 +551,48 @@ 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->channels != 2) + || (ctx->acodec->sample_rate != 44100); + + 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"); + + 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", + 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) + { + DPRINTF(E_LOG, 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_LOG, L_XCODE, "Could not open resample context\n"); + + 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, @@ -503,9 +615,11 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) audio_resample_close(ctx->resample_ctx); goto setup_fail_codec; } +#endif - ctx->need_resample = 1; -#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) +#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; @@ -554,7 +668,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); }