merge with mpd/trunk up to r3925
git-svn-id: https://svn.musicpd.org/mpd/trunk@3926 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
parent
d7e846bdc2
commit
6963502213
46
configure.ac
46
configure.ac
|
@ -39,7 +39,8 @@ AC_ARG_ENABLE(sun,[ --disable-sun disable sun support (default: enable)],[e
|
|||
AC_ARG_ENABLE(oss,[ --disable-oss disable OSS support (default: enable)],[enable_oss=$enableval],[enable_oss=yes])
|
||||
AC_ARG_ENABLE(alsa,[ --disable-alsa disable ALSA support (default: enable)],[enable_alsa=$enableval],[enable_alsa=yes])
|
||||
AC_ARG_ENABLE(mvp,[ --enable-mvp enable support for Hauppauge Media MVP (default: disable)],[enable_mvp=$enableval],[enable_mvp=no])
|
||||
AC_ARG_ENABLE(ogg,[ --disable-ogg disable ogg support (default: enable)],[enable_ogg=$enableval],[enable_ogg=yes])
|
||||
AC_ARG_ENABLE(oggvorbis,[ --disable-oggvorbis disable Ogg Vorbis support (default: enable)],[enable_oggvorbis=$enableval],enable_oggvorbis=yes)
|
||||
AC_ARG_ENABLE(oggflac,[ --disable-oggflac disable OggFLAC support (default: enable)],[enable_oggflac=$enableval],enable_oggflac=yes)
|
||||
AC_ARG_ENABLE(flac,[ --disable-flac disable flac support (default: enable)],[enable_flac=$enableval],[enable_flac=yes])
|
||||
AC_ARG_ENABLE(mp3,[ --disable-mp3 disable mp3 support (default: enable)],[enable_mp3=$enableval],[enable_mp3=yes])
|
||||
AC_ARG_ENABLE(aac,[ --disable-aac disable AAC support (default: enable)],[enable_aac=$enableval],[enable_aac=yes])
|
||||
|
@ -120,7 +121,7 @@ case $host in
|
|||
enable_osx=yes ;;
|
||||
esac
|
||||
|
||||
if test x$enable_ogg = xno; then
|
||||
if test x$enable_oggvorbis = xno; then
|
||||
AC_MSG_WARN("disabling shout streaming support because vorbis encoder was not found")
|
||||
enable_shout=no
|
||||
elif test x$use_tremor = xyes; then
|
||||
|
@ -439,8 +440,8 @@ int main() {
|
|||
CPPFLAGS=$oldcppflags
|
||||
fi
|
||||
|
||||
if test x$enable_ogg = xyes && test x$use_tremor != xyes; then
|
||||
XIPH_PATH_OGG(MPD_LIBS="$MPD_LIBS $OGG_LIBS" MPD_CFLAGS="$MPD_CFLAGS $OGG_CFLAGS",enable_ogg=no)
|
||||
if test x$enable_oggvorbis = xyes && test x$use_tremor != xyes; then
|
||||
XIPH_PATH_OGG(MPD_LIBS="$MPD_LIBS $OGG_LIBS" MPD_CFLAGS="$MPD_CFLAGS $OGG_CFLAGS",enable_oggvorbis=no)
|
||||
fi
|
||||
|
||||
if test x$use_tremor = xyes; then
|
||||
|
@ -463,20 +464,20 @@ if test x$use_tremor = xyes; then
|
|||
ac_save_LIBS="$LIBS"
|
||||
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
||||
LIBS="$LIBS $TREMOR_LIBS"
|
||||
AC_CHECK_LIB(vorbisidec,ov_read,,enable_ogg=no;AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
||||
AC_CHECK_LIB(vorbisidec,ov_read,,enable_oggvorbis=no;AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
|
||||
if test x$enable_ogg = xyes; then
|
||||
if test x$enable_oggvorbis = xyes; then
|
||||
if test x$use_tremor = xyes; then
|
||||
VORBIS_LIBS="$TREMOR_LIBS"
|
||||
VORBIS_CFLAGS="$TREMOR_CFLAGS"
|
||||
else
|
||||
enable_vorbistest=no
|
||||
XIPH_PATH_VORBIS(,enable_ogg=no)
|
||||
XIPH_PATH_VORBIS(,enable_oggvorbis=no)
|
||||
fi
|
||||
if test x$enable_ogg = xyes; then
|
||||
if test x$enable_oggvorbis = xyes; then
|
||||
|
||||
dnl
|
||||
dnl Vorbis Test
|
||||
|
@ -526,21 +527,21 @@ int main ()
|
|||
return 0;
|
||||
}
|
||||
|
||||
], AC_MSG_RESULT(yes), AC_MSG_RESULT(no); enable_ogg=no,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
|
||||
], AC_MSG_RESULT(yes), AC_MSG_RESULT(no) enable_oggvorbis=no,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
dnl
|
||||
dnl End of Vorbis Test
|
||||
dnl
|
||||
if test x$enable_ogg = xyes; then
|
||||
if test x$enable_oggvorbis = xyes; then
|
||||
MPD_LIBS="$MPD_LIBS $VORBIS_LIBS $VORBISFILE_LIBS $VORBISENC_LIBS"
|
||||
MPD_CFLAGS="$MPD_CFLAGS $VORBIS_CFLAGS $VORBISFILE_CFLAGS $VORBISENC_CFLAGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$enable_ogg = xyes; then
|
||||
AC_DEFINE(HAVE_OGG,1,[Define for ogg vorbis support])
|
||||
if test x$enable_oggvorbis = xyes; then
|
||||
AC_DEFINE(HAVE_OGGVORBIS,1,[Define for Ogg Vorbis support])
|
||||
fi
|
||||
|
||||
if test x$use_tremor = xyes; then
|
||||
|
@ -553,6 +554,7 @@ if test x$enable_flac = xyes; then
|
|||
AM_PATH_LIBFLAC(MPD_LIBS="$MPD_LIBS $LIBFLAC_LIBS" MPD_CFLAGS="$MPD_CFLAGS $LIBFLAC_CFLAGS",enable_flac=no)
|
||||
fi
|
||||
|
||||
|
||||
if test x$enable_flac = xyes; then
|
||||
oldcflags="$CFLAGS"
|
||||
oldlibs="$LIBS"
|
||||
|
@ -573,6 +575,18 @@ if test x$enable_flac = xyes; then
|
|||
AC_DEFINE(HAVE_FLAC,1,[Define for FLAC support])
|
||||
fi
|
||||
|
||||
|
||||
if test x$enable_oggflac = xyes; then
|
||||
oldmpdcflags="$MPD_CFLAGS"
|
||||
oldmpdlibs="$MPD_LIBS"
|
||||
AM_PATH_LIBOGGFLAC(MPD_LIBS="$MPD_LIBS $LIBOGGFLAC_LIBS" MPD_CFLAGS="$MPD_CFLAGS $LIBOGGFLAC_CFLAGS",enable_oggflac=no)
|
||||
fi
|
||||
|
||||
if test x$enable_oggflac = xyes; then
|
||||
AC_DEFINE(HAVE_OGGFLAC,1,[Define for OggFLAC support])
|
||||
fi
|
||||
|
||||
|
||||
if test x$enable_audiofile = xyes; then
|
||||
AM_PATH_AUDIOFILE(0.1.7, MPD_LIBS="$MPD_LIBS $AUDIOFILE_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AUDIOFILE_CFLAGS",
|
||||
[enable_audiofile=no;AC_MSG_WARN(You need audiofile -- disabling audiofile support)])
|
||||
|
@ -675,7 +689,7 @@ else
|
|||
echo " mp3 support ...................disabled"
|
||||
fi
|
||||
|
||||
if test x$enable_ogg = xyes; then
|
||||
if test x$enable_oggvorbis = xyes; then
|
||||
echo " Ogg Vorbis support ............enabled"
|
||||
if test x$use_tremor = xyes; then
|
||||
echo " using tremor.................yes"
|
||||
|
@ -692,6 +706,12 @@ else
|
|||
echo " FLAC support ..................disabled"
|
||||
fi
|
||||
|
||||
if test x$enable_oggflac = xyes; then
|
||||
echo " OggFLAC support ...............enabled"
|
||||
else
|
||||
echo " OggFLAC support ...............disabled"
|
||||
fi
|
||||
|
||||
if test x$enable_audiofile = xyes; then
|
||||
echo " Wave file support .............enabled"
|
||||
else
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# Configure paths for libOggFLAC
|
||||
# "Inspired" by ogg.m4
|
||||
|
||||
dnl AM_PATH_LIBOGGFLAC([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
|
||||
dnl Test for libOggFLAC, and define LIBOGGFLAC_CFLAGS and LIBOGGFLAC_LIBS
|
||||
dnl
|
||||
AC_DEFUN([AM_PATH_LIBOGGFLAC],
|
||||
[dnl
|
||||
dnl Get the cflags and libraries
|
||||
dnl
|
||||
AC_ARG_WITH(libOggFLAC,[ --with-libOggFLAC=PFX Prefix where libOggFLAC is installed (optional)], libOggFLAC_prefix="$withval", libOggFLAC_prefix="")
|
||||
AC_ARG_WITH(libOggFLAC-libraries,[ --with-libOggFLAC-libraries=DIR Directory where libOggFLAC library is installed (optional)], libOggFLAC_libraries="$withval", libOggFLAC_libraries="")
|
||||
AC_ARG_WITH(libOggFLAC-includes,[ --with-libOggFLAC-includes=DIR Directory where libOggFLAC header files are installed (optional)], libOggFLAC_includes="$withval", libOggFLAC_includes="")
|
||||
AC_ARG_ENABLE(libOggFLACtest, [ --disable-libOggFLACtest Do not try to compile and run a test libOggFLAC program],, enable_libOggFLACtest=yes)
|
||||
|
||||
if test "x$libOggFLAC_libraries" != "x" ; then
|
||||
LIBOGGFLAC_LIBS="-L$libOggFLAC_libraries"
|
||||
elif test "x$libOggFLAC_prefix" != "x" ; then
|
||||
LIBOGGFLAC_LIBS="-L$libOggFLAC_prefix/lib"
|
||||
elif test "x$prefix" != "xNONE" ; then
|
||||
LIBOGGFLAC_LIBS="-L$libdir"
|
||||
fi
|
||||
|
||||
LIBOGGFLAC_LIBS="$LIBOGGFLAC_LIBS -lOggFLAC -lFLAC -lm"
|
||||
|
||||
if test "x$libOggFLAC_includes" != "x" ; then
|
||||
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_includes"
|
||||
elif test "x$libOggFLAC_prefix" != "x" ; then
|
||||
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_prefix/include"
|
||||
elif test "$prefix" != "xNONE"; then
|
||||
LIBOGGFLAC_CFLAGS="-I$prefix/include"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for libOggFLAC)
|
||||
no_libOggFLAC=""
|
||||
|
||||
|
||||
if test "x$enable_libOggFLACtest" = "xyes" ; then
|
||||
ac_save_CFLAGS="$CFLAGS"
|
||||
ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
ac_save_LIBS="$LIBS"
|
||||
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $LIBOGGFLAC_CFLAGS"
|
||||
LIBS="$LIBS $LIBOGGFLAC_LIBS"
|
||||
dnl
|
||||
dnl Now check if the installed libOggFLAC is sufficiently new.
|
||||
dnl
|
||||
rm -f conf.libOggFLACtest
|
||||
AC_TRY_RUN([
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <OggFLAC/stream_decoder.h>
|
||||
|
||||
int main ()
|
||||
{
|
||||
system("touch conf.libOggFLACtest");
|
||||
return 0;
|
||||
}
|
||||
|
||||
],, no_libOggFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
|
||||
if test "x$no_libOggFLAC" = "x" ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
ifelse([$1], , :, [$1])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
if test -f conf.libOggFLACtest ; then
|
||||
:
|
||||
else
|
||||
echo "*** Could not run libOggFLAC test program, checking why..."
|
||||
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
|
||||
LIBS="$LIBS $LIBOGGFLAC_LIBS"
|
||||
AC_TRY_LINK([
|
||||
#include <stdio.h>
|
||||
#include <OggFLAC/stream_decoder.h>
|
||||
], [ return 0; ],
|
||||
[ echo "*** The test program compiled, but did not run. This usually means"
|
||||
echo "*** that the run-time linker is not finding libOggFLAC or finding the wrong"
|
||||
echo "*** version of libOggFLAC. If it is not finding libOggFLAC, you'll need to set your"
|
||||
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
|
||||
echo "*** to the installed location Also, make sure you have run ldconfig if that"
|
||||
echo "*** is required on your system"
|
||||
echo "***"
|
||||
echo "*** If you have an old version installed, it is best to remove it, although"
|
||||
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
|
||||
[ echo "*** The test program failed to compile or link. See the file config.log for the"
|
||||
echo "*** exact error that occured. This usually means libOggFLAC was incorrectly installed"
|
||||
echo "*** or that you have moved libOggFLAC since it was installed. In the latter case, you"
|
||||
echo "*** may want to edit the libOggFLAC-config script: $LIBOGGFLAC_CONFIG" ])
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
LIBOGGFLAC_CFLAGS=""
|
||||
LIBOGGFLAC_LIBS=""
|
||||
ifelse([$2], , :, [$2])
|
||||
fi
|
||||
AC_SUBST(LIBOGGFLAC_CFLAGS)
|
||||
AC_SUBST(LIBOGGFLAC_LIBS)
|
||||
rm -f conf.libOggFLACtest
|
||||
])
|
|
@ -10,14 +10,17 @@ mpd_audioOutputs = \
|
|||
audioOutputs/audioOutput_shout.c
|
||||
|
||||
mpd_inputPlugins = \
|
||||
inputPlugins/_flac_common.c \
|
||||
inputPlugins/_ogg_common.c \
|
||||
inputPlugins/oggflac_plugin.c \
|
||||
inputPlugins/oggvorbis_plugin.c \
|
||||
inputPlugins/aac_plugin.c \
|
||||
inputPlugins/audiofile_plugin.c \
|
||||
inputPlugins/flac_plugin.c \
|
||||
inputPlugins/mod_plugin.c \
|
||||
inputPlugins/mp3_plugin.c \
|
||||
inputPlugins/mp4_plugin.c \
|
||||
inputPlugins/mpc_plugin.c \
|
||||
inputPlugins/ogg_plugin.c
|
||||
inputPlugins/mpc_plugin.c
|
||||
|
||||
|
||||
mpd_headers = \
|
||||
|
@ -32,6 +35,8 @@ mpd_headers = \
|
|||
decode.h \
|
||||
directory.h \
|
||||
inputPlugin.h \
|
||||
inputPlugins/_flac_common.h \
|
||||
inputPlugins/_ogg_common.h \
|
||||
inputStream.h \
|
||||
inputStream_file.h \
|
||||
inputStream_http.h \
|
||||
|
|
81
src/decode.c
81
src/decode.c
|
@ -322,37 +322,76 @@ void decodeStart(PlayerControl * pc, OutputBuffer * cb, DecoderControl * dc) {
|
|||
|
||||
ret = DECODE_ERROR_UNKTYPE;
|
||||
if(isRemoteUrl(dc->utf8url)) {
|
||||
unsigned int next = 0;
|
||||
cb->acceptMetadata = 1;
|
||||
plugin = getInputPluginFromMimeType(inStream.mime);
|
||||
|
||||
/* first we try mime types: */
|
||||
while(ret && (plugin = getInputPluginFromMimeType(
|
||||
inStream.mime, next++))) {
|
||||
if (!plugin->streamDecodeFunc)
|
||||
continue;
|
||||
if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL))
|
||||
continue;
|
||||
if(plugin->tryDecodeFunc && !plugin->tryDecodeFunc(
|
||||
&inStream))
|
||||
continue;
|
||||
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
/* if that fails, try suffix matching the URL: */
|
||||
if(plugin == NULL) {
|
||||
plugin = getInputPluginFromSuffix(
|
||||
getSuffix(dc->utf8url));
|
||||
char * s = getSuffix(dc->utf8url);
|
||||
next = 0;
|
||||
while(ret && (plugin = getInputPluginFromSuffix(
|
||||
s, next++))) {
|
||||
if (!plugin->streamDecodeFunc)
|
||||
continue;
|
||||
if(!(plugin->streamTypes &
|
||||
INPUT_PLUGIN_STREAM_URL))
|
||||
continue;
|
||||
if(plugin->tryDecodeFunc &&
|
||||
!plugin->tryDecodeFunc(
|
||||
&inStream))
|
||||
continue;
|
||||
ret = plugin->streamDecodeFunc(
|
||||
cb, dc, &inStream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* fallback to mp3: */
|
||||
/* this is needed for bastard streams that don't have a suffix
|
||||
or set the mimeType */
|
||||
if(plugin == NULL) {
|
||||
plugin = getInputPluginFromName("mp3");
|
||||
}
|
||||
if(plugin && (plugin->streamTypes & INPUT_PLUGIN_STREAM_URL) &&
|
||||
plugin->streamDecodeFunc)
|
||||
{
|
||||
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
||||
/* we already know our mp3Plugin supports streams, no
|
||||
* need to check for stream{Types,DecodeFunc} */
|
||||
if ((plugin = getInputPluginFromName("mp3")))
|
||||
ret = plugin->streamDecodeFunc(cb, dc,
|
||||
&inStream);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int next = 0;
|
||||
char * s = getSuffix(dc->utf8url);
|
||||
cb->acceptMetadata = 0;
|
||||
plugin = getInputPluginFromSuffix(getSuffix(dc->utf8url));
|
||||
if(plugin && (plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE))
|
||||
{
|
||||
if(plugin->streamDecodeFunc) {
|
||||
ret = plugin->streamDecodeFunc(cb, dc,
|
||||
&inStream);
|
||||
}
|
||||
else if(plugin->fileDecodeFunc) {
|
||||
closeInputStream(&inStream);
|
||||
ret = plugin->fileDecodeFunc(cb, dc, path);
|
||||
}
|
||||
}
|
||||
while (ret && (plugin = getInputPluginFromSuffix(s, next++))) {
|
||||
if(!plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE)
|
||||
continue;
|
||||
if(plugin->tryDecodeFunc && !plugin->tryDecodeFunc(
|
||||
&inStream))
|
||||
continue;
|
||||
|
||||
if(plugin->streamDecodeFunc) {
|
||||
ret = plugin->streamDecodeFunc(
|
||||
cb, dc, &inStream);
|
||||
break;
|
||||
}
|
||||
else if(plugin->fileDecodeFunc) {
|
||||
closeInputStream(&inStream);
|
||||
ret = plugin->fileDecodeFunc(
|
||||
cb, dc, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret<0 || ret == DECODE_ERROR_UNKTYPE) {
|
||||
|
|
|
@ -323,7 +323,7 @@ int updateInDirectory(Directory * directory, char * shortname, char * name) {
|
|||
|
||||
if(myStat(name, &st)) return -1;
|
||||
|
||||
if(S_ISREG(st.st_mode) && hasMusicSuffix(name)) {
|
||||
if(S_ISREG(st.st_mode) && hasMusicSuffix(name, 0)) {
|
||||
if(0==findInList(directory->songs,shortname,&song)) {
|
||||
addToDirectory(directory, shortname, name);
|
||||
return DIRECTORY_RETURN_UPDATE;
|
||||
|
@ -414,7 +414,7 @@ int removeDeletedFromDirectory(Directory * directory, DIR * dir) {
|
|||
while(node) {
|
||||
tmpNode = node->nextNode;
|
||||
if(findInList(entList,node->key,(void **)&name)) {
|
||||
if(!isMusic(name,NULL)) {
|
||||
if(!isMusic(name,NULL,0)) {
|
||||
removeSongFromDirectory(directory,node->key);
|
||||
ret = 1;
|
||||
}
|
||||
|
@ -550,7 +550,7 @@ int updatePath(char * utf8path) {
|
|||
parentDirectory->parent,
|
||||
parentDirectory->stat->inode,
|
||||
parentDirectory->stat->device) &&
|
||||
song && isMusic(getSongUrl(song), &mtime))
|
||||
song && isMusic(getSongUrl(song), &mtime, 0))
|
||||
{
|
||||
free(path);
|
||||
if(song->mtime==mtime) return 0;
|
||||
|
@ -573,7 +573,7 @@ int updatePath(char * utf8path) {
|
|||
* Also, if by chance a directory was replaced by a file of the same
|
||||
* name or vice versa, we need to add it to the db
|
||||
*/
|
||||
if(isDir(path) || isMusic(path,NULL)) {
|
||||
if(isDir(path) || isMusic(path,NULL,0)) {
|
||||
parentDirectory = addParentPathToDB(path,&shortname);
|
||||
if(!parentDirectory || (
|
||||
!parentDirectory->stat &&
|
||||
|
@ -765,7 +765,7 @@ int addToDirectory(Directory * directory, char * shortname, char * name) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if(S_ISREG(st.st_mode) && hasMusicSuffix(name)) {
|
||||
if(S_ISREG(st.st_mode) && hasMusicSuffix(name, 0)) {
|
||||
Song * song;
|
||||
song = addSongToList(directory->songs, shortname, name,
|
||||
SONG_TYPE_FILE, directory);
|
||||
|
|
|
@ -49,15 +49,23 @@ static int stringFoundInStringArray(char ** array, char * suffix) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
InputPlugin * getInputPluginFromSuffix(char * suffix) {
|
||||
ListNode * node = inputPlugin_list->firstNode;
|
||||
InputPlugin * plugin = NULL;
|
||||
InputPlugin * getInputPluginFromSuffix(char * suffix, unsigned int next) {
|
||||
static ListNode * pos = NULL;
|
||||
ListNode * node;
|
||||
InputPlugin * plugin;
|
||||
|
||||
if(suffix == NULL) return NULL;
|
||||
|
||||
if (next) {
|
||||
if (pos) node = pos;
|
||||
else return NULL;
|
||||
} else
|
||||
node = inputPlugin_list->firstNode;
|
||||
|
||||
while(node != NULL) {
|
||||
plugin = node->data;
|
||||
if(stringFoundInStringArray(plugin->suffixes, suffix)) {
|
||||
pos = node->nextNode;
|
||||
return plugin;
|
||||
}
|
||||
node = node->nextNode;
|
||||
|
@ -66,15 +74,19 @@ InputPlugin * getInputPluginFromSuffix(char * suffix) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
InputPlugin * getInputPluginFromMimeType(char * mimeType) {
|
||||
ListNode * node = inputPlugin_list->firstNode;
|
||||
InputPlugin * plugin = NULL;
|
||||
InputPlugin * getInputPluginFromMimeType(char * mimeType, unsigned int next) {
|
||||
static ListNode * pos = NULL;
|
||||
ListNode * node;
|
||||
InputPlugin * plugin;
|
||||
|
||||
if(mimeType == NULL) return NULL;
|
||||
|
||||
node = (next && pos) ? pos : inputPlugin_list->firstNode;
|
||||
|
||||
while(node != NULL) {
|
||||
plugin = node->data;
|
||||
if(stringFoundInStringArray(plugin->mimeTypes, mimeType)) {
|
||||
pos = node->nextNode;
|
||||
return plugin;
|
||||
}
|
||||
node = node->nextNode;
|
||||
|
@ -109,8 +121,9 @@ void printAllInputPluginSuffixes(FILE * fp) {
|
|||
}
|
||||
|
||||
extern InputPlugin mp3Plugin;
|
||||
extern InputPlugin oggPlugin;
|
||||
extern InputPlugin oggvorbisPlugin;
|
||||
extern InputPlugin flacPlugin;
|
||||
extern InputPlugin oggflacPlugin;
|
||||
extern InputPlugin audiofilePlugin;
|
||||
extern InputPlugin mp4Plugin;
|
||||
extern InputPlugin mpcPlugin;
|
||||
|
@ -122,7 +135,8 @@ void initInputPlugins() {
|
|||
|
||||
/* load plugins here */
|
||||
loadInputPlugin(&mp3Plugin);
|
||||
loadInputPlugin(&oggPlugin);
|
||||
loadInputPlugin(&oggvorbisPlugin);
|
||||
loadInputPlugin(&oggflacPlugin);
|
||||
loadInputPlugin(&flacPlugin);
|
||||
loadInputPlugin(&audiofilePlugin);
|
||||
loadInputPlugin(&mp4Plugin);
|
||||
|
|
|
@ -25,30 +25,53 @@
|
|||
#include "outputBuffer.h"
|
||||
#include "tag.h"
|
||||
|
||||
/* valid values for streamTypes in the InputPlugin struct: */
|
||||
#define INPUT_PLUGIN_STREAM_FILE 0x01
|
||||
#define INPUT_PLUGIN_STREAM_URL 0x02
|
||||
|
||||
/* optional, set this to NULL if the InputPlugin doesn't have/need one
|
||||
* this must return < 0 if there is an error and >= 0 otherwise */
|
||||
typedef int (* InputPlugin_initFunc) ();
|
||||
|
||||
/* optional, set this to NULL if the InputPlugin doesn't have/need one */
|
||||
typedef void (* InputPlugin_finishFunc) ();
|
||||
|
||||
/* boolean return value, returns 1 if the InputStream is decodable by
|
||||
* the InputPlugin, 0 if not */
|
||||
typedef unsigned int (* InputPlugin_tryDecodeFunc) (InputStream *);
|
||||
|
||||
/* this will be used to decode InputStreams, and is recommended for files
|
||||
* and networked (HTTP) connections.
|
||||
*
|
||||
* returns -1 on error, 0 on success */
|
||||
typedef int (* InputPlugin_streamDecodeFunc) (OutputBuffer *, DecoderControl *,
|
||||
InputStream *);
|
||||
|
||||
/* use this if and only if your InputPlugin can only be passed a filename or
|
||||
* handle as input, and will not allow callbacks to be set (like Ogg-Vorbis
|
||||
* and FLAC libraries allow)
|
||||
*
|
||||
* returns -1 on error, 0 on success */
|
||||
typedef int (* InputPlugin_fileDecodeFunc) (OutputBuffer *, DecoderControl *,
|
||||
char * path);
|
||||
|
||||
/* file should be the full path! */
|
||||
/* file should be the full path! Returns NULL if a tag cannot be found
|
||||
* or read */
|
||||
typedef MpdTag * (* InputPlugin_tagDupFunc) (char * file);
|
||||
|
||||
typedef struct _InputPlugin {
|
||||
char * name;
|
||||
InputPlugin_initFunc initFunc;
|
||||
InputPlugin_finishFunc finishFunc;
|
||||
InputPlugin_tryDecodeFunc tryDecodeFunc;
|
||||
InputPlugin_streamDecodeFunc streamDecodeFunc;
|
||||
InputPlugin_fileDecodeFunc fileDecodeFunc;
|
||||
InputPlugin_tagDupFunc tagDupFunc;
|
||||
|
||||
/* one or more of the INPUT_PLUGIN_STREAM_* values OR'd together */
|
||||
unsigned char streamTypes;
|
||||
|
||||
/* last element in these arrays must always be a NULL: */
|
||||
char ** suffixes;
|
||||
char ** mimeTypes;
|
||||
} InputPlugin;
|
||||
|
@ -59,9 +82,9 @@ void unloadInputPlugin(InputPlugin * inputPlugin);
|
|||
|
||||
/* interface for using plugins */
|
||||
|
||||
InputPlugin * getInputPluginFromSuffix(char * suffix);
|
||||
InputPlugin * getInputPluginFromSuffix(char * suffix, unsigned int next);
|
||||
|
||||
InputPlugin * getInputPluginFromMimeType(char * mimeType);
|
||||
InputPlugin * getInputPluginFromMimeType(char * mimeType, unsigned int next);
|
||||
|
||||
InputPlugin * getInputPluginFromName(char * name);
|
||||
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* Common data structures and functions used by FLAC and OggFLAC
|
||||
* (c) 2005 by Eric Wong <normalperson@yhbt.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#if defined(HAVE_FLAC) || defined(HAVE_OGGFLAC)
|
||||
|
||||
#include "_flac_common.h"
|
||||
|
||||
#include "../log.h"
|
||||
#include "../tag.h"
|
||||
#include "../inputStream.h"
|
||||
#include "../outputBuffer.h"
|
||||
#include "../decode.h"
|
||||
#include "../replayGain.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <FLAC/format.h>
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
void init_FlacData (FlacData * data, OutputBuffer * cb,
|
||||
DecoderControl * dc, InputStream * inStream)
|
||||
{
|
||||
data->chunk_length = 0;
|
||||
data->time = 0;
|
||||
data->position = 0;
|
||||
data->bitRate = 0;
|
||||
data->cb = cb;
|
||||
data->dc = dc;
|
||||
data->inStream = inStream;
|
||||
data->replayGainInfo = NULL;
|
||||
data->tag = NULL;
|
||||
}
|
||||
|
||||
static int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block,
|
||||
char * cmnt, float * fl)
|
||||
{
|
||||
int offset = FLAC__metadata_object_vorbiscomment_find_entry_from(
|
||||
block,0,cmnt);
|
||||
|
||||
if(offset >= 0) {
|
||||
size_t pos = strlen(cmnt)+1; /* 1 is for '=' */
|
||||
int len = block->data.vorbis_comment.comments[offset].length
|
||||
-pos;
|
||||
if(len > 0) {
|
||||
unsigned char tmp;
|
||||
unsigned char * dup = &(block->data.vorbis_comment.
|
||||
comments[offset].entry[pos]);
|
||||
tmp = dup[len];
|
||||
dup[len] = '\0';
|
||||
*fl = atof((char *)dup);
|
||||
dup[len] = tmp;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* replaygain stuff by AliasMrJones */
|
||||
static void flacParseReplayGain(const FLAC__StreamMetadata *block,
|
||||
FlacData * data) {
|
||||
unsigned int found = 0;
|
||||
|
||||
if (data->replayGainInfo)
|
||||
freeReplayGainInfo(data->replayGainInfo);
|
||||
|
||||
data->replayGainInfo = newReplayGainInfo();
|
||||
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_album_gain",
|
||||
&data->replayGainInfo->albumGain);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_album_peak",
|
||||
&data->replayGainInfo->albumPeak);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_track_gain",
|
||||
&data->replayGainInfo->trackGain);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_track_peak",
|
||||
&data->replayGainInfo->trackPeak);
|
||||
|
||||
if (!found) {
|
||||
freeReplayGainInfo(data->replayGainInfo);
|
||||
data->replayGainInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
|
||||
* tag names match */
|
||||
static const char * VORBIS_COMMENT_TRACK_KEY = "tracknumber";
|
||||
|
||||
static unsigned int commentMatchesAddToTag(
|
||||
const FLAC__StreamMetadata_VorbisComment_Entry * entry,
|
||||
unsigned int itemType,
|
||||
MpdTag ** tag)
|
||||
{
|
||||
const char * str = (itemType == TAG_ITEM_TRACK) ?
|
||||
VORBIS_COMMENT_TRACK_KEY : mpdTagItemKeys[itemType];
|
||||
size_t slen = strlen(str);
|
||||
int vlen = entry->length - slen - 1;
|
||||
|
||||
if ((vlen > 0) && (0 == strncasecmp(str,(char *)entry->entry, slen))
|
||||
&& (*(entry->entry + slen) == '=')) {
|
||||
if (!*tag)
|
||||
*tag = newMpdTag();
|
||||
|
||||
addItemToMpdTagWithLen(*tag, itemType,
|
||||
(char *)(entry->entry+slen + 1), vlen);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MpdTag * copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
|
||||
MpdTag * tag)
|
||||
{
|
||||
unsigned int i, j;
|
||||
FLAC__StreamMetadata_VorbisComment_Entry *comments;
|
||||
|
||||
comments = block->data.vorbis_comment.comments;
|
||||
|
||||
for (i = block->data.vorbis_comment.num_comments; i != 0; --i) {
|
||||
for (j = TAG_NUM_OF_ITEM_TYPES; j--; ) {
|
||||
if (commentMatchesAddToTag(comments, j, &tag))
|
||||
break;
|
||||
}
|
||||
comments++;
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
void flac_metadata_common_cb(const FLAC__StreamMetadata *block, FlacData *data)
|
||||
{
|
||||
DecoderControl *dc = data->dc;
|
||||
const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info);
|
||||
|
||||
switch(block->type) {
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
dc->audioFormat.bits = si->bits_per_sample;
|
||||
dc->audioFormat.sampleRate = si->sample_rate;
|
||||
dc->audioFormat.channels = si->channels;
|
||||
dc->totalTime = ((float)si->total_samples) / (si->sample_rate);
|
||||
getOutputAudioFormat(&(dc->audioFormat),
|
||||
&(data->cb->audioFormat));
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
flacParseReplayGain(block,data);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flac_error_common_cb( const char * plugin,
|
||||
const FLAC__StreamDecoderErrorStatus status,
|
||||
FlacData *data)
|
||||
{
|
||||
if(data->dc->stop) return;
|
||||
|
||||
switch(status) {
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
|
||||
ERROR("%s lost sync\n", plugin);
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
|
||||
ERROR("bad %s header\n", plugin);
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
|
||||
ERROR("%s crc mismatch\n", plugin);
|
||||
break;
|
||||
default:
|
||||
ERROR("unknown %s error\n",plugin);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_FLAC || HAVE_OGGFLAC */
|
|
@ -0,0 +1,77 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* Common data structures and functions used by FLAC and OggFLAC
|
||||
* (c) 2005 by Eric Wong <normalperson@yhbt.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _FLAC_COMMON_H
|
||||
#define _FLAC_COMMON_H
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#if defined(HAVE_FLAC) || defined(HAVE_OGGFLAC)
|
||||
|
||||
#include "../tag.h"
|
||||
#include "../inputStream.h"
|
||||
#include "../outputBuffer.h"
|
||||
#include "../decode.h"
|
||||
#include <FLAC/seekable_stream_decoder.h>
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
#define FLAC_CHUNK_SIZE 4080
|
||||
|
||||
typedef struct {
|
||||
unsigned char chunk[FLAC_CHUNK_SIZE];
|
||||
int chunk_length;
|
||||
float time;
|
||||
int bitRate;
|
||||
FLAC__uint64 position;
|
||||
OutputBuffer * cb;
|
||||
DecoderControl * dc;
|
||||
InputStream * inStream;
|
||||
ReplayGainInfo * replayGainInfo;
|
||||
MpdTag * tag;
|
||||
} FlacData;
|
||||
|
||||
/* initializes a given FlacData struct */
|
||||
void init_FlacData (FlacData * data, OutputBuffer * cb,
|
||||
DecoderControl * dc, InputStream * inStream);
|
||||
void flac_metadata_common_cb( const FLAC__StreamMetadata *block,
|
||||
FlacData *data);
|
||||
void flac_error_common_cb( const char * plugin,
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
FlacData *data);
|
||||
|
||||
MpdTag * copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
|
||||
MpdTag * tag);
|
||||
|
||||
static inline int flacSendChunk(FlacData * data)
|
||||
{
|
||||
if (sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk,
|
||||
data->chunk_length, data->time, data->bitRate,
|
||||
data->replayGainInfo) == OUTPUT_BUFFER_DC_STOP)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_FLAC || HAVE_OGGFLAC */
|
||||
|
||||
#endif /* _FLAC_COMMON_H */
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
|
||||
* (c) 2005 by Eric Wong <normalperson@yhbt.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#if defined(HAVE_OGGFLAC) || defined(HAVE_OGGVORBIS)
|
||||
|
||||
#include "../utils.h"
|
||||
#include "_ogg_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
ogg_stream_type ogg_stream_type_detect(InputStream * inStream)
|
||||
{
|
||||
/* oggflac detection based on code in ogg123 and this post
|
||||
* http://lists.xiph.org/pipermail/flac/2004-December/000393.html
|
||||
* ogg123 trunk still doesn't have this patch as of June 2005 */
|
||||
unsigned char buf[41];
|
||||
size_t r, to_read = 41;
|
||||
|
||||
seekInputStream(inStream, 0, SEEK_SET);
|
||||
|
||||
while (to_read) {
|
||||
r = readFromInputStream(inStream, buf, 1, to_read);
|
||||
if (r < 0)
|
||||
break;
|
||||
to_read -= r;
|
||||
if (!r && !inputStreamAtEOF(inStream))
|
||||
my_usleep(10000);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
seekInputStream(inStream, 0, SEEK_SET);
|
||||
|
||||
if (r >= 32 && memcmp(buf, "OggS", 4) == 0 && (
|
||||
(memcmp(buf+29, "FLAC", 4) == 0
|
||||
&& memcmp(buf+37, "fLaC", 4) == 0)
|
||||
|| (memcmp(buf+28, "FLAC", 4) == 0)
|
||||
|| (memcmp(buf+28, "fLaC", 4) == 0))) {
|
||||
return FLAC;
|
||||
}
|
||||
return VORBIS;
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_OGGFLAC || defined(HAVE_OGGVORBIS) */
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
|
||||
* (c) 2005 by Eric Wong <normalperson@yhbt.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _OGG_COMMON_H
|
||||
#define _OGG_COMMON_H
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#if defined(HAVE_OGGFLAC) || defined(HAVE_OGGVORBIS)
|
||||
|
||||
typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;
|
||||
|
||||
ogg_stream_type ogg_stream_type_detect(InputStream * inStream);
|
||||
|
||||
#endif /* defined(HAVE_OGGFLAC || defined(HAVE_OGGVORBIS) */
|
||||
|
||||
#endif /* _OGG_COMMON_H */
|
|
@ -418,6 +418,7 @@ InputPlugin aacPlugin =
|
|||
{
|
||||
"aac",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
aac_decode,
|
||||
|
@ -435,6 +436,7 @@ InputPlugin aacPlugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
|
|
|
@ -163,6 +163,7 @@ InputPlugin audiofilePlugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
audiofile_decode,
|
||||
audiofileTagDup,
|
||||
INPUT_PLUGIN_STREAM_FILE,
|
||||
|
@ -176,6 +177,7 @@ InputPlugin audiofilePlugin =
|
|||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#ifdef HAVE_FLAC
|
||||
|
||||
#include "_flac_common.h"
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../log.h"
|
||||
#include "../pcm_utils.h"
|
||||
|
@ -34,22 +36,8 @@
|
|||
#include <FLAC/seekable_stream_decoder.h>
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
typedef struct {
|
||||
#define FLAC_CHUNK_SIZE 4080
|
||||
unsigned char chunk[FLAC_CHUNK_SIZE];
|
||||
int chunk_length;
|
||||
float time;
|
||||
int bitRate;
|
||||
FLAC__uint64 position;
|
||||
OutputBuffer * cb;
|
||||
DecoderControl * dc;
|
||||
InputStream * inStream;
|
||||
ReplayGainInfo * replayGainInfo;
|
||||
} FlacData;
|
||||
|
||||
/* this code is based on flac123, from flac-tools */
|
||||
|
||||
int flacSendChunk(FlacData * data);
|
||||
void flacError(const FLAC__SeekableStreamDecoder *,
|
||||
FLAC__StreamDecoderErrorStatus, void *);
|
||||
void flacPrintErroredState(FLAC__SeekableStreamDecoderState state);
|
||||
|
@ -75,14 +63,7 @@ int flac_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream)
|
|||
int status = 1;
|
||||
int ret =0;
|
||||
|
||||
data.chunk_length = 0;
|
||||
data.time = 0;
|
||||
data.position = 0;
|
||||
data.bitRate = 0;
|
||||
data.cb = cb;
|
||||
data.dc = dc;
|
||||
data.inStream = inStream;
|
||||
data.replayGainInfo = NULL;
|
||||
init_FlacData(&data, cb, dc, inStream);
|
||||
|
||||
if(!(flacDec = FLAC__seekable_stream_decoder_new())) {
|
||||
ret = -1;
|
||||
|
@ -176,19 +157,16 @@ int flac_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream)
|
|||
dc->seek = 0;
|
||||
} */
|
||||
|
||||
if(dc->stop) {
|
||||
dc->state = DECODE_STATE_STOP;
|
||||
dc->stop = 0;
|
||||
}
|
||||
else dc->state = DECODE_STATE_STOP;
|
||||
dc->state = DECODE_STATE_STOP;
|
||||
dc->stop = 0;
|
||||
|
||||
fail:
|
||||
if(data.replayGainInfo) freeReplayGainInfo(data.replayGainInfo);
|
||||
|
||||
closeInputStream(inStream);
|
||||
|
||||
if(flacDec) FLAC__seekable_stream_decoder_delete(flacDec);
|
||||
|
||||
closeInputStream(inStream);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -250,35 +228,17 @@ FLAC__SeekableStreamDecoderLengthStatus flacLength(
|
|||
}
|
||||
|
||||
FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder * flacDec, void * fdata) {
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
switch(inputStreamAtEOF(data->inStream)) {
|
||||
case 1:
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
if (inputStreamAtEOF(data->inStream) == 1)
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void flacError(const FLAC__SeekableStreamDecoder *dec,
|
||||
FLAC__StreamDecoderErrorStatus status, void *fdata)
|
||||
{
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
if(data->dc->stop) return;
|
||||
|
||||
switch(status) {
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
|
||||
ERROR("flac lost sync\n");
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
|
||||
ERROR("bad header\n");
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
|
||||
ERROR("crc mismatch\n");
|
||||
break;
|
||||
default:
|
||||
ERROR("unknown flac error\n");
|
||||
}
|
||||
flac_error_common_cb("flac",status,(FlacData *) fdata);
|
||||
}
|
||||
|
||||
void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
|
||||
|
@ -312,93 +272,10 @@ void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
|
|||
}
|
||||
}
|
||||
|
||||
int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block, char * cmnt,
|
||||
float * fl)
|
||||
{
|
||||
int offset = FLAC__metadata_object_vorbiscomment_find_entry_from(
|
||||
block,0,cmnt);
|
||||
|
||||
if(offset >= 0) {
|
||||
int pos = strlen(cmnt)+1; /* 1 is for '=' */
|
||||
int len = block->data.vorbis_comment.comments[offset].length
|
||||
-pos;
|
||||
if(len > 0) {
|
||||
char * dup = malloc(len+1);
|
||||
memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
|
||||
dup[len] = '\0';
|
||||
*fl = atof(dup);
|
||||
free(dup);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* replaygain stuff by AliasMrJones */
|
||||
void flacParseReplayGain(const FLAC__StreamMetadata *block, FlacData * data) {
|
||||
int found = 0;
|
||||
|
||||
if(NULL != data->replayGainInfo) {
|
||||
freeReplayGainInfo(data->replayGainInfo);
|
||||
data->replayGainInfo = NULL;
|
||||
}
|
||||
|
||||
data->replayGainInfo = newReplayGainInfo();
|
||||
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_album_gain",
|
||||
&data->replayGainInfo->albumGain);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_album_peak",
|
||||
&data->replayGainInfo->albumPeak);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_track_gain",
|
||||
&data->replayGainInfo->trackGain);
|
||||
found &= flacFindVorbisCommentFloat(block,"replaygain_track_peak",
|
||||
&data->replayGainInfo->trackPeak);
|
||||
|
||||
if(!found) {
|
||||
freeReplayGainInfo(data->replayGainInfo);
|
||||
data->replayGainInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void flacMetadata(const FLAC__SeekableStreamDecoder *dec,
|
||||
const FLAC__StreamMetadata *block, void *vdata)
|
||||
{
|
||||
FlacData * data = (FlacData *)vdata;
|
||||
|
||||
switch(block->type) {
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
data->dc->audioFormat.bits =
|
||||
block->data.stream_info.bits_per_sample;
|
||||
data->dc->audioFormat.sampleRate =
|
||||
block->data.stream_info.sample_rate;
|
||||
data->dc->audioFormat.channels =
|
||||
block->data.stream_info.channels;
|
||||
data->dc->totalTime =
|
||||
((float)block->data.stream_info.total_samples)/
|
||||
data->dc->audioFormat.sampleRate;
|
||||
getOutputAudioFormat(&(data->dc->audioFormat),
|
||||
&(data->cb->audioFormat));
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
flacParseReplayGain(block,data);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int flacSendChunk(FlacData * data) {
|
||||
switch(sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk,
|
||||
data->chunk_length, data->time, data->bitRate,
|
||||
data->replayGainInfo))
|
||||
{
|
||||
case OUTPUT_BUFFER_DC_STOP:
|
||||
return -1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
flac_metadata_common_cb(block, (FlacData *)vdata);
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec,
|
||||
|
@ -447,79 +324,6 @@ FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec,
|
|||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
static int commentMatchesAddToTag(
|
||||
char * str,
|
||||
FLAC__StreamMetadata_VorbisComment_Entry * entry,
|
||||
int itemType,
|
||||
MpdTag ** tag)
|
||||
{
|
||||
int slen = strlen(str);
|
||||
int vlen = entry->length - slen;
|
||||
|
||||
if( vlen <= 0 ) return 0;
|
||||
|
||||
if( 0 == strncasecmp(str, entry->entry, slen) ) {
|
||||
if(*tag == NULL) *tag = newMpdTag();
|
||||
addItemToMpdTagWithLen(*tag, itemType,
|
||||
entry->entry+slen, vlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static MpdTag * copyVorbisCommentBlockToMpdTag(FLAC__StreamMetadata * block,
|
||||
MpdTag * tag)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
|
||||
if(commentMatchesAddToTag(
|
||||
"artist=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_ARTIST,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"title=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_TITLE,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"album=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_ALBUM,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"tracknumber=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_TRACK,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"genre=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_GENRE,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"date=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_DATE,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"composer=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_COMPOSER,
|
||||
&tag));
|
||||
else if(commentMatchesAddToTag(
|
||||
"performer=",
|
||||
block->data.vorbis_comment.comments+i,
|
||||
TAG_ITEM_PERFORMER,
|
||||
&tag));
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
MpdTag * flacMetadataDup(char * file, int * vorbisCommentFound) {
|
||||
MpdTag * ret = NULL;
|
||||
FLAC__Metadata_SimpleIterator * it;
|
||||
|
@ -594,30 +398,32 @@ char * flac_mime_types[] = {"application/x-flac", NULL};
|
|||
|
||||
InputPlugin flacPlugin =
|
||||
{
|
||||
"flac",
|
||||
NULL,
|
||||
"flac",
|
||||
NULL,
|
||||
flac_decode,
|
||||
NULL,
|
||||
flacTagDup,
|
||||
INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
|
||||
flacSuffixes,
|
||||
flac_mime_types
|
||||
NULL,
|
||||
flac_decode,
|
||||
NULL,
|
||||
flacTagDup,
|
||||
INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
|
||||
flacSuffixes,
|
||||
flac_mime_types
|
||||
};
|
||||
|
||||
#else
|
||||
#else /* !HAVE_FLAC */
|
||||
|
||||
InputPlugin flacPlugin =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif /* HAVE_FLAC */
|
||||
|
|
|
@ -259,6 +259,7 @@ InputPlugin modPlugin =
|
|||
NULL,
|
||||
mod_finishMikMod,
|
||||
NULL,
|
||||
NULL,
|
||||
mod_decode,
|
||||
modTagDup,
|
||||
INPUT_PLUGIN_STREAM_FILE,
|
||||
|
@ -273,6 +274,7 @@ InputPlugin modPlugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
|
@ -820,6 +820,7 @@ InputPlugin mp3Plugin =
|
|||
"mp3",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
mp3_decode,
|
||||
NULL,
|
||||
mp3_tagDup,
|
||||
|
@ -837,6 +838,7 @@ InputPlugin mp3Plugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
|
|
|
@ -431,6 +431,7 @@ InputPlugin mp4Plugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
mp4_decode,
|
||||
mp4TagDup,
|
||||
INPUT_PLUGIN_STREAM_FILE,
|
||||
|
@ -447,6 +448,7 @@ InputPlugin mp4Plugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
|
|
|
@ -336,6 +336,7 @@ InputPlugin mpcPlugin =
|
|||
"mpc",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
mpc_decode,
|
||||
NULL,
|
||||
mpcTagDup,
|
||||
|
@ -351,6 +352,7 @@ InputPlugin mpcPlugin =
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
/* the Music Player Daemon (MPD)
|
||||
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
*
|
||||
* OggFLAC support (half-stolen from flac_plugin.c :))
|
||||
* (c) 2005 by Eric Wong <normalperson@yhbt.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#ifdef HAVE_OGGFLAC
|
||||
|
||||
#include "_flac_common.h"
|
||||
#include "_ogg_common.h"
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../log.h"
|
||||
#include "../pcm_utils.h"
|
||||
#include "../inputStream.h"
|
||||
#include "../outputBuffer.h"
|
||||
#include "../replayGain.h"
|
||||
#include "../audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <OggFLAC/seekable_stream_decoder.h>
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
static void oggflac_cleanup(InputStream * inStream,
|
||||
FlacData * data,
|
||||
OggFLAC__SeekableStreamDecoder * decoder)
|
||||
{
|
||||
if (data->replayGainInfo)
|
||||
freeReplayGainInfo(data->replayGainInfo);
|
||||
if (decoder)
|
||||
OggFLAC__seekable_stream_decoder_delete(decoder);
|
||||
closeInputStream(inStream);
|
||||
}
|
||||
|
||||
static OggFLAC__SeekableStreamDecoderReadStatus of_read_cb(
|
||||
const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
FLAC__byte buf[], unsigned * bytes, void * fdata) {
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
size_t r;
|
||||
|
||||
while (1) {
|
||||
r = readFromInputStream(data->inStream,(void *)buf,1,*bytes);
|
||||
if (r == 0 && !inputStreamAtEOF(data->inStream) &&
|
||||
!data->dc->stop)
|
||||
my_usleep(10000);
|
||||
else
|
||||
break;
|
||||
}
|
||||
*bytes = r;
|
||||
|
||||
if (r == 0 && !inputStreamAtEOF(data->inStream) && !data->dc->stop)
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
|
||||
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
|
||||
}
|
||||
|
||||
static OggFLAC__SeekableStreamDecoderSeekStatus of_seek_cb(
|
||||
const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
FLAC__uint64 offset, void * fdata)
|
||||
{
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
if(seekInputStream(data->inStream,offset,SEEK_SET)<0) {
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
|
||||
}
|
||||
|
||||
static OggFLAC__SeekableStreamDecoderTellStatus of_tell_cb(
|
||||
const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
FLAC__uint64 * offset, void * fdata)
|
||||
{
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
*offset = (long)(data->inStream->offset);
|
||||
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
static OggFLAC__SeekableStreamDecoderLengthStatus of_length_cb(
|
||||
const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
FLAC__uint64 * length, void * fdata)
|
||||
{
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
*length = (size_t)(data->inStream->size);
|
||||
|
||||
return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
|
||||
static FLAC__bool of_EOF_cb(const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
void * fdata) {
|
||||
FlacData * data = (FlacData *) fdata;
|
||||
|
||||
if (inputStreamAtEOF(data->inStream) == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void of_error_cb(const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
FLAC__StreamDecoderErrorStatus status, void *fdata)
|
||||
{
|
||||
flac_error_common_cb("oggflac",status,(FlacData *) fdata);
|
||||
}
|
||||
|
||||
static void oggflacPrintErroredState(OggFLAC__SeekableStreamDecoderState state)
|
||||
{
|
||||
switch(state) {
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
|
||||
ERROR("oggflac allocation error\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
|
||||
ERROR("oggflac read error\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
|
||||
ERROR("oggflac seek error\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
|
||||
ERROR("oggflac seekable stream error\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
|
||||
ERROR("oggflac decoder already initialized\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
|
||||
ERROR("invalid oggflac callback\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
|
||||
ERROR("oggflac decoder uninitialized\n");
|
||||
break;
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_OK:
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING:
|
||||
case OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderWriteStatus oggflacWrite(
|
||||
const OggFLAC__SeekableStreamDecoder *decoder,
|
||||
const FLAC__Frame *frame, const FLAC__int32 * const buf[],
|
||||
void * vdata)
|
||||
{
|
||||
FlacData * data = (FlacData *)vdata;
|
||||
FLAC__uint32 samples = frame->header.blocksize;
|
||||
FLAC__uint16 u16;
|
||||
unsigned char * uc;
|
||||
int c_samp, c_chan, d_samp;
|
||||
int i;
|
||||
float timeChange;
|
||||
const int bytesPerSample = data->dc->audioFormat.bits/8;
|
||||
|
||||
timeChange = ((float)samples)/frame->header.sample_rate;
|
||||
data->time+= timeChange;
|
||||
|
||||
/* ogg123 uses a complicated method of calculating bitrate
|
||||
* with averaging which I'm not too fond of.
|
||||
* (waste of memory/CPU cycles, especially given this is _lossless_)
|
||||
* a get_decode_position() is not available in OggFLAC, either
|
||||
*
|
||||
* this does not give an accurate bitrate:
|
||||
* (bytes_last_read was set in the read callback)
|
||||
data->bitRate = ((8.0 * data->bytes_last_read *
|
||||
frame->header.sample_rate)
|
||||
/((float)samples * 1000)) + 0.5;
|
||||
*/
|
||||
|
||||
for(c_samp = d_samp = 0; c_samp < frame->header.blocksize; c_samp++) {
|
||||
for(c_chan = 0; c_chan < frame->header.channels;
|
||||
c_chan++, d_samp++) {
|
||||
u16 = buf[c_chan][c_samp];
|
||||
uc = (unsigned char *)&u16;
|
||||
for(i=0;i<(data->dc->audioFormat.bits/8);i++) {
|
||||
if(data->chunk_length>=FLAC_CHUNK_SIZE) {
|
||||
if(flacSendChunk(data)<0) {
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
}
|
||||
data->chunk_length = 0;
|
||||
if(data->dc->seek) {
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
}
|
||||
data->chunk[data->chunk_length++] = *(uc++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
/* used by TagDup */
|
||||
static void of_metadata_dup_cb(
|
||||
const OggFLAC__SeekableStreamDecoder * decoder,
|
||||
const FLAC__StreamMetadata *block, void *vdata)
|
||||
{
|
||||
FlacData * data = (FlacData *)vdata;
|
||||
|
||||
switch(block->type) {
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
if (!data->tag) data->tag = newMpdTag();
|
||||
data->tag->time = ((float)block->data.stream_info.
|
||||
total_samples) /
|
||||
block->data.stream_info.sample_rate +
|
||||
0.5;
|
||||
return;
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
copyVorbisCommentBlockToMpdTag(block,data->tag);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* used by decode */
|
||||
static void of_metadata_decode_cb(const OggFLAC__SeekableStreamDecoder * dec,
|
||||
const FLAC__StreamMetadata *block, void *vdata)
|
||||
{
|
||||
flac_metadata_common_cb(block, (FlacData *)vdata);
|
||||
}
|
||||
|
||||
static OggFLAC__SeekableStreamDecoder * full_decoder_init_and_read_metadata(
|
||||
FlacData * data,
|
||||
unsigned int metadata_only)
|
||||
{
|
||||
OggFLAC__SeekableStreamDecoder * decoder = NULL;
|
||||
unsigned int s = 1;
|
||||
|
||||
if (!(decoder = OggFLAC__seekable_stream_decoder_new()))
|
||||
return NULL;
|
||||
|
||||
if (metadata_only) {
|
||||
s &= OggFLAC__seekable_stream_decoder_set_metadata_callback(
|
||||
decoder, of_metadata_dup_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_metadata_respond(
|
||||
decoder,
|
||||
FLAC__METADATA_TYPE_STREAMINFO);
|
||||
} else {
|
||||
s &= OggFLAC__seekable_stream_decoder_set_metadata_callback(
|
||||
decoder, of_metadata_decode_cb);
|
||||
}
|
||||
|
||||
s &= OggFLAC__seekable_stream_decoder_set_read_callback(decoder,
|
||||
of_read_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_seek_callback(decoder,
|
||||
of_seek_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_tell_callback(decoder,
|
||||
of_tell_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_length_callback(decoder,
|
||||
of_length_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_eof_callback(decoder,
|
||||
of_EOF_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_write_callback(decoder,
|
||||
oggflacWrite);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_metadata_respond(decoder,
|
||||
FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_error_callback(decoder,
|
||||
of_error_cb);
|
||||
s &= OggFLAC__seekable_stream_decoder_set_client_data(decoder,
|
||||
(void *)data);
|
||||
|
||||
if (!s) {
|
||||
ERROR("oggflac problem before init()\n");
|
||||
goto fail;
|
||||
}
|
||||
if (OggFLAC__seekable_stream_decoder_init(decoder) !=
|
||||
OggFLAC__SEEKABLE_STREAM_DECODER_OK)
|
||||
{
|
||||
ERROR("oggflac problem doing init()\n");
|
||||
goto fail;
|
||||
}
|
||||
if (!OggFLAC__seekable_stream_decoder_process_until_end_of_metadata(
|
||||
decoder)) {
|
||||
ERROR("oggflac problem reading metadata\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return decoder;
|
||||
|
||||
fail:
|
||||
oggflacPrintErroredState(
|
||||
OggFLAC__seekable_stream_decoder_get_state(decoder));
|
||||
OggFLAC__seekable_stream_decoder_delete(decoder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* public functions: */
|
||||
static MpdTag * oggflac_TagDup(char * file)
|
||||
{
|
||||
InputStream inStream;
|
||||
OggFLAC__SeekableStreamDecoder * decoder;
|
||||
FlacData data;
|
||||
|
||||
if (openInputStream(&inStream, file) < 0)
|
||||
return NULL;
|
||||
if (ogg_stream_type_detect(&inStream) != FLAC) {
|
||||
closeInputStream(&inStream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
init_FlacData(&data, NULL, NULL, &inStream);
|
||||
|
||||
/* errors here won't matter,
|
||||
* data.tag will be set or unset, that's all we care about */
|
||||
decoder = full_decoder_init_and_read_metadata(&data,1);
|
||||
|
||||
oggflac_cleanup(&inStream, &data, decoder);
|
||||
|
||||
return data.tag;
|
||||
}
|
||||
|
||||
static unsigned int oggflac_try_decode(InputStream * inStream)
|
||||
{
|
||||
return (ogg_stream_type_detect(inStream) == FLAC) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int oggflac_decode(OutputBuffer * cb, DecoderControl * dc,
|
||||
InputStream * inStream)
|
||||
{
|
||||
OggFLAC__SeekableStreamDecoder * decoder = NULL;
|
||||
FlacData data;
|
||||
int ret = 0;
|
||||
|
||||
init_FlacData(&data, cb, dc, inStream);
|
||||
|
||||
if(!(decoder = full_decoder_init_and_read_metadata(&data,0))){
|
||||
ret = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dc->state = DECODE_STATE_DECODE;
|
||||
|
||||
while(1) {
|
||||
OggFLAC__seekable_stream_decoder_process_single(decoder);
|
||||
if(OggFLAC__seekable_stream_decoder_get_state(decoder)!=
|
||||
OggFLAC__SEEKABLE_STREAM_DECODER_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(dc->seek) {
|
||||
FLAC__uint64 sampleToSeek = dc->seekWhere*
|
||||
dc->audioFormat.sampleRate+0.5;
|
||||
if(OggFLAC__seekable_stream_decoder_seek_absolute(
|
||||
decoder, sampleToSeek))
|
||||
{
|
||||
clearOutputBuffer(cb);
|
||||
data.time = ((float)sampleToSeek)/
|
||||
dc->audioFormat.sampleRate;
|
||||
data.position = 0;
|
||||
}
|
||||
else dc->seekError = 1;
|
||||
dc->seek = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dc->stop) {
|
||||
oggflacPrintErroredState(
|
||||
OggFLAC__seekable_stream_decoder_get_state(decoder));
|
||||
OggFLAC__seekable_stream_decoder_finish(decoder);
|
||||
}
|
||||
/* send last little bit */
|
||||
if(data.chunk_length>0 && !dc->stop) {
|
||||
flacSendChunk(&data);
|
||||
flushOutputBuffer(data.cb);
|
||||
}
|
||||
|
||||
dc->state = DECODE_STATE_STOP;
|
||||
dc->stop = 0;
|
||||
|
||||
fail:
|
||||
oggflac_cleanup(inStream, &data, decoder);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char * oggflac_Suffixes[] = {"ogg", NULL};
|
||||
static char * oggflac_mime_types[] = {"application/ogg", NULL};
|
||||
|
||||
InputPlugin oggflacPlugin =
|
||||
{
|
||||
"oggflac",
|
||||
NULL,
|
||||
NULL,
|
||||
oggflac_try_decode,
|
||||
oggflac_decode,
|
||||
NULL,
|
||||
oggflac_TagDup,
|
||||
INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
|
||||
oggflac_Suffixes,
|
||||
oggflac_mime_types
|
||||
};
|
||||
|
||||
#else /* !HAVE_FLAC */
|
||||
|
||||
InputPlugin oggflacPlugin =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#endif /* HAVE_OGGFLAC */
|
||||
|
|
@ -16,9 +16,13 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* TODO 'ogg' should probably be replaced with 'oggvorbis' in all instances */
|
||||
|
||||
#include "../inputPlugin.h"
|
||||
|
||||
#ifdef HAVE_OGG
|
||||
#ifdef HAVE_OGGVORBIS
|
||||
|
||||
#include "_ogg_common.h"
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../audio.h"
|
||||
|
@ -99,7 +103,7 @@ long ogg_tell_cb(void * vdata) {
|
|||
return (long)(data->inStream->offset);
|
||||
}
|
||||
|
||||
char * ogg_parseComment(char * comment, char * needle) {
|
||||
static inline char * ogg_parseComment(char * comment, char * needle) {
|
||||
int len = strlen(needle);
|
||||
|
||||
if(strncasecmp(comment, needle, len) == 0 && *(comment+len) == '=') {
|
||||
|
@ -150,48 +154,40 @@ void ogg_getReplayGainInfo(char ** comments, ReplayGainInfo ** infoPtr) {
|
|||
}
|
||||
}
|
||||
|
||||
MpdTag * oggCommentsParse(char ** comments) {
|
||||
MpdTag * ret = NULL;
|
||||
char * temp;
|
||||
static const char * VORBIS_COMMENT_TRACK_KEY = "tracknumber";
|
||||
|
||||
static inline unsigned int ogg_parseCommentAddToTag(char * comment,
|
||||
unsigned int itemType, MpdTag ** tag)
|
||||
{
|
||||
const char * needle = (itemType == TAG_ITEM_TRACK) ?
|
||||
VORBIS_COMMENT_TRACK_KEY : mpdTagItemKeys[itemType];
|
||||
unsigned int len = strlen(needle);
|
||||
|
||||
if(strncasecmp(comment, needle, len) == 0 && *(comment+len) == '=') {
|
||||
if (!*tag)
|
||||
*tag = newMpdTag();
|
||||
|
||||
addItemToMpdTag(*tag, itemType, comment+len+1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MpdTag * oggCommentsParse(char ** comments) {
|
||||
MpdTag * tag = NULL;
|
||||
|
||||
while(*comments) {
|
||||
if((temp = ogg_parseComment(*comments,"artist"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_ARTIST, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"title"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_TITLE, temp);
|
||||
unsigned int j;
|
||||
for (j = TAG_NUM_OF_ITEM_TYPES; j--; ) {
|
||||
if (ogg_parseCommentAddToTag(*comments, j, &tag))
|
||||
break;
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"album"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_ALBUM, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"tracknumber"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_TRACK, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"genre"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_GENRE, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"date"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_DATE, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"composer"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_COMPOSER, temp);
|
||||
}
|
||||
else if((temp = ogg_parseComment(*comments,"performer"))) {
|
||||
if(!ret) ret = newMpdTag();
|
||||
addItemToMpdTag(ret, TAG_ITEM_PERFORMER, temp);
|
||||
}
|
||||
|
||||
comments++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
void putOggCommentsIntoOutputBuffer(OutputBuffer * cb, char * streamName,
|
||||
|
@ -220,7 +216,9 @@ void putOggCommentsIntoOutputBuffer(OutputBuffer * cb, char * streamName,
|
|||
freeMpdTag(tag);
|
||||
}
|
||||
|
||||
int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream)
|
||||
/* public */
|
||||
int oggvorbis_decode(OutputBuffer * cb, DecoderControl * dc,
|
||||
InputStream * inStream)
|
||||
{
|
||||
OggVorbis_File vf;
|
||||
ov_callbacks callbacks;
|
||||
|
@ -360,7 +358,7 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
MpdTag * oggTagDup(char * file) {
|
||||
MpdTag * oggvorbis_TagDup(char * file) {
|
||||
MpdTag * ret = NULL;
|
||||
FILE * fp;
|
||||
OggVorbis_File vf;
|
||||
|
@ -386,35 +384,43 @@ MpdTag * oggTagDup(char * file) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
char * oggSuffixes[] = {"ogg", NULL};
|
||||
char * oggMimeTypes[] = {"application/ogg", NULL};
|
||||
|
||||
InputPlugin oggPlugin =
|
||||
static unsigned int oggvorbis_try_decode(InputStream * inStream)
|
||||
{
|
||||
"ogg",
|
||||
return (ogg_stream_type_detect(inStream) == VORBIS) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static char * oggvorbis_Suffixes[] = {"ogg", NULL};
|
||||
static char * oggvorbis_MimeTypes[] = {"application/ogg", NULL};
|
||||
|
||||
InputPlugin oggvorbisPlugin =
|
||||
{
|
||||
"oggvorbis",
|
||||
NULL,
|
||||
NULL,
|
||||
ogg_decode,
|
||||
NULL,
|
||||
oggTagDup,
|
||||
INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
|
||||
oggSuffixes,
|
||||
oggMimeTypes
|
||||
oggvorbis_try_decode,
|
||||
oggvorbis_decode,
|
||||
NULL,
|
||||
oggvorbis_TagDup,
|
||||
INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
|
||||
oggvorbis_Suffixes,
|
||||
oggvorbis_MimeTypes
|
||||
};
|
||||
|
||||
#else
|
||||
#else /* !HAVE_OGGVORBIS */
|
||||
|
||||
InputPlugin oggPlugin =
|
||||
InputPlugin oggvorbisPlugin =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif /* HAVE_OGGVORBIS */
|
|
@ -708,6 +708,20 @@ int inputStream_httpOpen(InputStream * inStream, char * url) {
|
|||
}
|
||||
|
||||
int inputStream_httpSeek(InputStream * inStream, long offset, int whence) {
|
||||
/* hack to reopen an HTTP stream if we're trying to seek to
|
||||
* the beginning */
|
||||
if ((whence == SEEK_SET) && (offset == 0)) {
|
||||
InputStreamHTTPData * data;
|
||||
|
||||
data = (InputStreamHTTPData*)inStream->data;
|
||||
close(data->sock);
|
||||
data->connState = HTTP_CONN_STATE_REOPEN;
|
||||
data->buflen = 0;
|
||||
inStream->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* otherwise, we don't know how to seek in HTTP yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
10
src/ls.c
10
src/ls.c
|
@ -262,12 +262,12 @@ int isDir(char * utf8name) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
InputPlugin * hasMusicSuffix(char * utf8file) {
|
||||
InputPlugin * hasMusicSuffix(char * utf8file, unsigned int next) {
|
||||
InputPlugin * ret = NULL;
|
||||
|
||||
char * s = getSuffix(utf8file);
|
||||
if(s) {
|
||||
ret = getInputPluginFromSuffix(s);
|
||||
if(s) {
|
||||
ret = getInputPluginFromSuffix(s, next);
|
||||
}
|
||||
else {
|
||||
DEBUG("hasMusicSuffix: The file: %s has no valid suffix\n",utf8file);
|
||||
|
@ -276,9 +276,9 @@ InputPlugin * hasMusicSuffix(char * utf8file) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
InputPlugin * isMusic(char * utf8file, time_t * mtime) {
|
||||
InputPlugin * isMusic(char * utf8file, time_t * mtime, unsigned int next) {
|
||||
if(isFile(utf8file,mtime)) {
|
||||
InputPlugin * plugin = hasMusicSuffix(utf8file);
|
||||
InputPlugin * plugin = hasMusicSuffix(utf8file, next);
|
||||
if (plugin != NULL)
|
||||
return plugin;
|
||||
}
|
||||
|
|
4
src/ls.h
4
src/ls.h
|
@ -45,9 +45,9 @@ int isDir(char * utf8name);
|
|||
|
||||
int isPlaylist(char * utf8file);
|
||||
|
||||
InputPlugin * hasMusicSuffix(char * utf8file);
|
||||
InputPlugin * hasMusicSuffix(char * utf8file, unsigned int next);
|
||||
|
||||
InputPlugin * isMusic(char * utf8file, time_t * mtime);
|
||||
InputPlugin * isMusic(char * utf8file, time_t * mtime, unsigned int next);
|
||||
|
||||
char * dupAndStripPlaylistSuffix(char * file);
|
||||
|
||||
|
|
20
src/song.c
20
src/song.c
|
@ -64,9 +64,12 @@ Song * newSong(char * url, int type, Directory * parentDir) {
|
|||
|
||||
if(song->type == SONG_TYPE_FILE) {
|
||||
InputPlugin * plugin;
|
||||
if((plugin = isMusic(getSongUrl(song), &(song->mtime)))) {
|
||||
song->tag = plugin->tagDupFunc(
|
||||
rmp2amp(utf8ToFsCharset(getSongUrl(song))));
|
||||
unsigned int next = 0;
|
||||
char * song_url = getSongUrl(song);
|
||||
char * abs_path = rmp2amp(utf8ToFsCharset(song_url));
|
||||
while(!song->tag && (plugin = isMusic(song_url,
|
||||
&(song->mtime), next++))) {
|
||||
song->tag = plugin->tagDupFunc(abs_path);
|
||||
}
|
||||
if(!song->tag || song->tag->time<0) {
|
||||
freeSong(song);
|
||||
|
@ -100,7 +103,7 @@ Song * addSongToList(SongList * list, char * url, char * utf8path,
|
|||
|
||||
switch(songType) {
|
||||
case SONG_TYPE_FILE:
|
||||
if(isMusic(utf8path, NULL)) {
|
||||
if(isMusic(utf8path, NULL, 0)) {
|
||||
song = newSong(url, songType, parentDirectory);
|
||||
}
|
||||
break;
|
||||
|
@ -278,14 +281,17 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir) {
|
|||
int updateSongInfo(Song * song) {
|
||||
if(song->type == SONG_TYPE_FILE) {
|
||||
InputPlugin * plugin;
|
||||
unsigned int next = 0;
|
||||
char * song_url = getSongUrl(song);
|
||||
char * abs_path = rmp2amp(song_url);
|
||||
|
||||
if(song->tag) freeMpdTag(song->tag);
|
||||
|
||||
song->tag = NULL;
|
||||
|
||||
if((plugin = isMusic(getSongUrl(song),&(song->mtime)))) {
|
||||
song->tag = plugin->tagDupFunc(
|
||||
rmp2amp(getSongUrl(song)));
|
||||
while(!song->tag && (plugin = isMusic(song_url,
|
||||
&(song->mtime), next++))) {
|
||||
song->tag = plugin->tagDupFunc(abs_path);
|
||||
}
|
||||
if(!song->tag || song->tag->time<0) return -1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue