mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
Merge branch 'libav9'
This commit is contained in:
commit
6e1cbac18f
10
INSTALL
10
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 <http://www.antlr.org/download/C>
|
||||
- Avahi client libraries (avahi-client), 0.6.24 minimum
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
<http://git.debian.org/?p=users/jblache/forked-daapd.git>
|
||||
<http://alioth.debian.org/~jblache/forked-daapd/>
|
||||
|
||||
Required tools:
|
||||
- ANTLR v3 is required to build forked-daapd, along with its C runtime
|
||||
|
57
README
57
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:
|
||||
|
||||
<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
|
||||
@ -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
|
||||
1. Start forked-daapd
|
||||
2. Start Remote, go to Settings, Add Library
|
||||
3. Look in the log file for a message saying:
|
||||
|
||||
"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 will be:
|
||||
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.
|
||||
|
@ -130,7 +130,13 @@ AC_RUN_IFELSE(
|
||||
AC_LANG_POP([C])
|
||||
LIBS="$save_LIBS"
|
||||
|
||||
_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]))
|
||||
|
@ -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);
|
@ -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));
|
@ -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));
|
273
src/artwork.c
273
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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
184
src/transcode.c
184
src/transcode.c
@ -44,6 +44,10 @@
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.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 "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->need_resample = (ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16)
|
||||
|| (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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user