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:
Eric Wong 2006-03-16 06:52:46 +00:00
parent d7e846bdc2
commit 6963502213
25 changed files with 1201 additions and 354 deletions

View File

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

104
m4/libOggFLAC.m4 Normal file
View File

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

View File

@ -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 \

View File

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

View File

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

View File

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

View File

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

View File

@ -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 */

View File

@ -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 */

View File

@ -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) */

View File

@ -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 */

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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,

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#ifdef HAVE_OGG
#ifdef HAVE_OGGVORBIS
#include <vorbis/vorbisfile.h>
#endif
#ifdef HAVE_FLAC