Merge branch 'libav9'

This commit is contained in:
ejurgensen 2014-01-10 21:54:03 +01:00
commit 6e1cbac18f
10 changed files with 421 additions and 390 deletions

10
INSTALL
View File

@ -19,9 +19,10 @@ Tools required:
- gettext - gettext
- gawk - gawk
- gperf - gperf
- antlr3 (see below)
Libraries: Libraries:
- libantlr3c (ANTLR3 C runtime, version 3.2 for tarball builds) - libantlr3c (ANTLR3 C runtime, use the same version as antlr3, see below)
from <http://www.antlr.org/download/C> from <http://www.antlr.org/download/C>
- Avahi client libraries (avahi-client), 0.6.24 minimum - Avahi client libraries (avahi-client), 0.6.24 minimum
from <http://avahi.org/> from <http://avahi.org/>
@ -73,8 +74,10 @@ multimedia applications. The version of libav you use will potentially have a
great influence on your experience with forked-daapd. 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 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 versions of libav and ffmpeg.
do not work well with forked-daapd.
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 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: The original (now unmaintained) source can be found here:
<http://git.debian.org/?p=users/jblache/forked-daapd.git> <http://git.debian.org/?p=users/jblache/forked-daapd.git>
<http://alioth.debian.org/~jblache/forked-daapd/>
Required tools: Required tools:
- ANTLR v3 is required to build forked-daapd, along with its C runtime - ANTLR v3 is required to build forked-daapd, along with its C runtime

65
README
View File

@ -1,8 +1,8 @@
forked-daapd forked-daapd
------------ ------------
forked-daapd is a DAAP and RSP media server, with support for Linux and forked-daapd is a DAAP (iTunes) and RSP (Roku) media server, with support for
FreeBSD. It is a complete rewrite of mt-daapd (Firefly Media Server). Linux and FreeBSD.
DAAP stands for Digital Audio Access Protocol, and is the protocol used DAAP stands for Digital Audio Access Protocol, and is the protocol used
by iTunes and friends to share/stream media libraries over the network. 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: The original (now unmaintained) source can be found here:
<http://git.debian.org/?p=users/jblache/forked-daapd.git> <http://git.debian.org/?p=users/jblache/forked-daapd.git>
<http://alioth.debian.org/~jblache/forked-daapd/>
forked-daapd is a complete rewrite of mt-daapd (Firefly Media Server).
Supported clients 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; forked-daapd can be paired with Apple's Remote application for iPod/iPhone/iPad;
this is how the pairing process works: this is how the pairing process works:
- start forked-daapd 1. Start forked-daapd
- start Remote, go to Settings, Add Library 2. Start Remote, go to Settings, Add Library
- prepare a text file with a filename ending with .remote; the filename 3. Look in the log file for a message saying:
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 "Discovered remote 'Foobar' (id 71624..."
code 5387, the file content will be:
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 Foobar
5387 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 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 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 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. 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 If you have trouble pairing with forked-daapd, you can use avahi-browse for
receives the mDNS announcement from your iPod/iPhone when the pairing code is troubleshooting:
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:
- in a terminal, run avahi-browse -r -k _touch-remote._tcp - in a terminal, run avahi-browse -r -k _touch-remote._tcp
- start Remote, goto Settings, Add Library - start Remote, goto Settings, Add Library
- after a couple seconds at most, you should get something similar to this: - 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. 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 forked-daapd will discover the AirTunes devices available on your network. For
devices that are password-protected, the device's AirTunes name and password 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 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; 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 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, As for the naming convention, it is quite simple; consider your foo.mp3 song,
residing at /bar/foo.mp3: residing at /bar/foo.mp3:
- if /bar/foo.{png,jpg} exists, this will be used as the artwork for this file; - if it has embedded artwork, 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/foo.{png,jpg} exists, it is used;
- failing that, if /bar/bar.{png,jpg} exists, it will be 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 For "groups" (same album name and album artist), the situation is a bit
different: different:
@ -233,8 +241,9 @@ different:
- failing that, if [directory name].{png,jpg} is found in one of the - 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 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; artwork. The first file found is used, ordering is not guaranteed;
- failing that, individual files are examined and the first artwork found is - failing that, individual files are examined and the first artwork found
used. Here again, ordering is not guaranteed. (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 {artwork,cover,Folder} are the default, you can add other base names in the
configuration file. configuration file.

View File

@ -130,7 +130,13 @@ AC_RUN_IFELSE(
AC_LANG_POP([C]) AC_LANG_POP([C])
LIBS="$save_LIBS" 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) dnl Check for av_lock_manager (ffmpeg >= 0.5.1)
save_LIBS="$LIBS" save_LIBS="$LIBS"
AC_CHECK_LIB([avcodec], [av_lockmgr_register], , AC_MSG_ERROR([libav (ffmpeg) >= 0.5.1 required])) AC_CHECK_LIB([avcodec], [av_lockmgr_register], , AC_MSG_ERROR([libav (ffmpeg) >= 0.5.1 required]))

View File

@ -1,97 +0,0 @@
#!/bin/sh
#
# Patch for ffmpeg so forked-daapd can extract iTunes TV metadata from mp4 video files
# Ace Jones <ace.jones1@yahoo.com>
#
# 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);

View File

@ -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));

View File

@ -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));

View File

@ -95,6 +95,56 @@ artwork_read(char *filename, struct evbuffer *evbuf)
return -1; 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 static int
artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int format, struct evbuffer *evbuf) 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_decoder;
AVCodec *img_encoder; AVCodec *img_encoder;
int64_t pix_fmt_mask;
const enum PixelFormat *pix_fmts;
AVFrame *i_frame; AVFrame *i_frame;
AVFrame *o_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; dst->codec_type = CODEC_TYPE_VIDEO;
#endif #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; pix_fmts = img_encoder->pix_fmts;
while (pix_fmts && (*pix_fmts != -1)) 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); dst->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, src->pix_fmt, 1, NULL);
#endif
if (dst->pix_fmt < 0) 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) artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf)
{ {
AVFormatContext *src_ctx; AVFormatContext *src_ctx;
AVCodecContext *src;
int s; int s;
int target_w; int target_w;
int target_h; int target_h;
int need_rescale;
int format_ok; int format_ok;
int ret; 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; src_ctx = NULL;
@ -588,52 +639,10 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e
return -1; return -1;
} }
src = src_ctx->streams[s]->codec; ret = rescale_needed(src_ctx->streams[s]->codec, max_w, max_h, &target_w, &target_h);
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);
/* Fastpath */ /* Fastpath */
if (!need_rescale && format_ok) if (!ret && format_ok)
{ {
ret = artwork_read(filename, evbuf); ret = artwork_read(filename, evbuf);
if (ret == 0) if (ret == 0)
@ -657,6 +666,115 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e
return ret; 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 static int
artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf) 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 i;
int ret; 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); ret = snprintf(artwork, sizeof(artwork), "%s", path);
if ((ret < 0) || (ret >= sizeof(artwork))) 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; 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); ret = access(artwork, F_OK);
if (ret < 0) 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]))) if (i == (sizeof(cover_extension) / sizeof(cover_extension[0])))
return -1; return -1;
DPRINTF(E_DBG, L_ART, "Found own artwork file %s\n", artwork);
return artwork_get(artwork, max_w, max_h, format, evbuf); 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; cfg_t *lib;
int nbasenames; 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); ret = snprintf(artwork, sizeof(artwork), "%s", path);
if ((ret < 0) || (ret >= sizeof(artwork))) 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; 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); ret = access(artwork, F_OK);
if (ret < 0) 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) if (i == nbasenames)
return -1; return -1;
DPRINTF(E_DBG, L_ART, "Found directory artwork file %s\n", artwork);
return artwork_get(artwork, max_w, max_h, format, evbuf); 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 i;
int ret; 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); ret = snprintf(artwork, sizeof(artwork), "%s", path);
if ((ret < 0) || (ret >= sizeof(artwork))) 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; 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); ret = access(artwork, F_OK);
if (ret < 0) 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]))) if (i == (sizeof(cover_extension) / sizeof(cover_extension[0])))
return -1; 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); 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; 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} */ /* Look for basename(filename).{png,jpg} */
ret = artwork_get_own_image(filename, max_w, max_h, format, evbuf); 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; goto files_art;
} }
got_art = -1; got_art = 0;
while ((got_art < 0) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) 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); got_art = (artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf) > 0)
if (got_art < 0) || (artwork_get_parentdir_image(dir, 1, max_w, max_h, format, evbuf) > 0);
got_art = artwork_get_parentdir_image(dir, 1, max_w, max_h, format, evbuf);
} }
db_query_end(&qp); 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; return -1;
} }
got_art = -1; got_art = 0;
while ((got_art < 0) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) 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); 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) else if (got_art > 0)
return got_art; return got_art;
DPRINTF(E_DBG, L_ART, "No artwork found for group %d\n", id);
return -1; return -1;
} }

View File

@ -384,6 +384,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
case AVMEDIA_TYPE_VIDEO: case AVMEDIA_TYPE_VIDEO:
#else #else
case CODEC_TYPE_VIDEO: 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 #endif
if (!video_stream) if (!video_stream)
{ {

View File

@ -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); ret = artwork_get_item_filename(dbmfi.path, 600, 600, ART_CAN_PNG | ART_CAN_JPEG, rmd->artwork);
if (ret < 0) 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); evbuffer_free(rmd->artwork);
rmd->artwork = NULL; rmd->artwork = NULL;

View File

@ -44,6 +44,10 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/mathematics.h> #include <libavutil/mathematics.h>
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
# include <libavutil/opt.h>
# include <libavresample/avresample.h>
#endif
#include "logger.h" #include "logger.h"
#include "conffile.h" #include "conffile.h"
@ -65,9 +69,13 @@ struct transcode_ctx {
int16_t *abuffer; int16_t *abuffer;
/* Resampling */ /* Resampling */
int need_resample; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
int input_size; AVAudioResampleContext *resample_ctx;
#else
ReSampleContext *resample_ctx; ReSampleContext *resample_ctx;
int input_size;
#endif
int need_resample;
int16_t *re_abuffer; int16_t *re_abuffer;
off_t offset; off_t offset;
@ -147,9 +155,15 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted)
#if BYTE_ORDER == BIG_ENDIAN #if BYTE_ORDER == BIG_ENDIAN
int i; int i;
#endif #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; AVFrame *frame = NULL;
int got_frame = 0; 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 #endif
processed = 0; processed = 0;
@ -167,19 +181,29 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted)
/* Decode data */ /* Decode data */
while (ctx->apacket2.size > 0) while (ctx->apacket2.size > 0)
{ {
buflen = XCODE_BUFFER_SIZE;
#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35)
if (ctx->acodec->get_buffer != avcodec_default_get_buffer) got_frame = 0;
{
DPRINTF(E_WARN, L_XCODE, "Custom get_buffer, not allowed by ffmpeg/libav. Setting to default.\n");
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, used = avcodec_decode_audio4(ctx->acodec,
&frame, &got_frame, frame, &got_frame,
&ctx->apacket2); &ctx->apacket2);
#else #else
buflen = XCODE_BUFFER_SIZE;
used = avcodec_decode_audio3(ctx->acodec, used = avcodec_decode_audio3(ctx->acodec,
ctx->abuffer, &buflen, ctx->abuffer, &buflen,
&ctx->apacket2); &ctx->apacket2);
@ -195,14 +219,20 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted)
ctx->apacket2.data += used; ctx->apacket2.data += used;
ctx->apacket2.size -= used; ctx->apacket2.size -= used;
#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) /* No frame decoded this time around */
/* This part is from the libav wrapper for avcodec_decode_audio3 - it may be useless in this context */ #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
if (got_frame != 0) 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 ch, plane_size;
int planar = av_sample_fmt_is_planar(ctx->acodec->sample_fmt); int planar = av_sample_fmt_is_planar(ctx->acodec->sample_fmt);
int data_size = av_samples_get_buffer_size(&plane_size, ctx->acodec->channels, 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) if (XCODE_BUFFER_SIZE < data_size)
{ {
@ -211,28 +241,54 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted)
continue; 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) if (planar && ctx->acodec->channels > 1)
{ {
uint8_t *out = ((uint8_t *)ctx->abuffer) + plane_size; uint8_t *out = ((uint8_t *)ctx->abuffer) + plane_size;
for (ch = 1; ch < ctx->acodec->channels; ch++) 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; out += plane_size;
} }
} }
buflen = data_size; buflen = data_size;
} }
else
continue;
#else #else
/* No frame decoded this time around */
if (buflen == 0) if (buflen == 0)
continue; continue;
#endif #endif
if (ctx->need_resample) 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); 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 else
buf = ctx->abuffer; buf = ctx->abuffer;
#endif
#if BYTE_ORDER == BIG_ENDIAN #if BYTE_ORDER == BIG_ENDIAN
/* swap buffer, LE16 */ /* swap buffer, LE16 */
@ -290,6 +347,14 @@ transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted)
ctx->offset += processed; 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; return processed;
} }
@ -385,7 +450,6 @@ struct transcode_ctx *
transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr) transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr)
{ {
struct transcode_ctx *ctx; struct transcode_ctx *ctx;
int i;
int ret; int ret;
ctx = (struct transcode_ctx *)malloc(sizeof(struct transcode_ctx)); 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; 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; ctx->astream = -1;
for (i = 0; i < ctx->fmtctx->nb_streams; i++) 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) if (ctx->fmtctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
#endif
{ {
ctx->astream = i; ctx->astream = i;
@ -444,20 +515,23 @@ transcode_setup(struct media_file_info *mfi, off_t *est_size, int wavhdr)
goto setup_fail; goto setup_fail;
} }
ctx->acodec = ctx->fmtctx->streams[ctx->astream]->codec; ctx->adecoder = avcodec_find_decoder(ctx->fmtctx->streams[ctx->astream]->codec->codec_id);
ctx->adecoder = avcodec_find_decoder(ctx->acodec->codec_id);
if (!ctx->adecoder) if (!ctx->adecoder)
{ {
DPRINTF(E_WARN, L_XCODE, "No suitable decoder found for codec\n"); DPRINTF(E_WARN, L_XCODE, "No suitable decoder found for codec\n");
goto setup_fail; goto setup_fail;
} }
#endif
ctx->acodec = ctx->fmtctx->streams[ctx->astream]->codec;
if (ctx->adecoder->capabilities & CODEC_CAP_TRUNCATED) if (ctx->adecoder->capabilities & CODEC_CAP_TRUNCATED)
ctx->acodec->flags |= CODEC_FLAG_TRUNCATED; ctx->acodec->flags |= CODEC_FLAG_TRUNCATED;
#if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 6) #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); ret = avcodec_open2(ctx->acodec, ctx->adecoder, NULL);
#else #else
ret = avcodec_open(ctx->acodec, ctx->adecoder); 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; goto setup_fail_codec;
} }
if ((ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16) ctx->need_resample = (ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16)
|| (ctx->acodec->channels != 2) || (ctx->acodec->channels != 2)
|| (ctx->acodec->sample_rate != 44100)) || (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); 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, 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); audio_resample_close(ctx->resample_ctx);
goto setup_fail_codec; goto setup_fail_codec;
} }
#endif
ctx->need_resample = 1; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4)
#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); ctx->input_size = ctx->acodec->channels * av_get_bytes_per_sample(ctx->acodec->sample_fmt);
#elif LIBAVCODEC_VERSION_MAJOR >= 53 #elif LIBAVCODEC_VERSION_MAJOR >= 53
ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_fmt(ctx->acodec->sample_fmt) / 8; 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 (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); audio_resample_close(ctx->resample_ctx);
#endif
av_free(ctx->re_abuffer); av_free(ctx->re_abuffer);
} }