aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/decode.c81
-rw-r--r--src/directory.c10
-rw-r--r--src/inputPlugin.c30
-rw-r--r--src/inputPlugin.h29
-rw-r--r--src/inputPlugins/_flac_common.c195
-rw-r--r--src/inputPlugins/_flac_common.h77
-rw-r--r--src/inputPlugins/_ogg_common.c65
-rw-r--r--src/inputPlugins/_ogg_common.h35
-rw-r--r--src/inputPlugins/aac_plugin.c2
-rw-r--r--src/inputPlugins/audiofile_plugin.c2
-rw-r--r--src/inputPlugins/flac_plugin.c258
-rw-r--r--src/inputPlugins/mod_plugin.c2
-rw-r--r--src/inputPlugins/mp3_plugin.c2
-rw-r--r--src/inputPlugins/mp4_plugin.c2
-rw-r--r--src/inputPlugins/mpc_plugin.c2
-rw-r--r--src/inputPlugins/oggflac_plugin.c426
-rw-r--r--src/inputPlugins/oggvorbis_plugin.c (renamed from src/inputPlugins/ogg_plugin.c)126
-rw-r--r--src/inputStream_http.c14
-rw-r--r--src/ls.c10
-rw-r--r--src/ls.h4
-rw-r--r--src/song.c20
-rw-r--r--src/tag.c2
23 files changed, 1063 insertions, 340 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b861f1b65..9197c3d69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/decode.c b/src/decode.c
index 25b34ded8..146f5a974 100644
--- a/src/decode.c
+++ b/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) {
diff --git a/src/directory.c b/src/directory.c
index d6c5ca659..891e7c351 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -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);
diff --git a/src/inputPlugin.c b/src/inputPlugin.c
index 2fe20f5fc..5c08b265f 100644
--- a/src/inputPlugin.c
+++ b/src/inputPlugin.c
@@ -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);
diff --git a/src/inputPlugin.h b/src/inputPlugin.h
index dc2e2219e..78268a7f3 100644
--- a/src/inputPlugin.h
+++ b/src/inputPlugin.h
@@ -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);
diff --git a/src/inputPlugins/_flac_common.c b/src/inputPlugins/_flac_common.c
new file mode 100644
index 000000000..f6a470ed2
--- /dev/null
+++ b/src/inputPlugins/_flac_common.c
@@ -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 */
diff --git a/src/inputPlugins/_flac_common.h b/src/inputPlugins/_flac_common.h
new file mode 100644
index 000000000..830d3bfc0
--- /dev/null
+++ b/src/inputPlugins/_flac_common.h
@@ -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 */
+
diff --git a/src/inputPlugins/_ogg_common.c b/src/inputPlugins/_ogg_common.c
new file mode 100644
index 000000000..fb0bb19e0
--- /dev/null
+++ b/src/inputPlugins/_ogg_common.c
@@ -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) */
+
diff --git a/src/inputPlugins/_ogg_common.h b/src/inputPlugins/_ogg_common.h
new file mode 100644
index 000000000..4cd7fda41
--- /dev/null
+++ b/src/inputPlugins/_ogg_common.h
@@ -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 */
diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c
index e368f3089..3fb5f4742 100644
--- a/src/inputPlugins/aac_plugin.c
+++ b/src/inputPlugins/aac_plugin.c
@@ -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,
diff --git a/src/inputPlugins/audiofile_plugin.c b/src/inputPlugins/audiofile_plugin.c
index adc279b60..d212eaf47 100644
--- a/src/inputPlugins/audiofile_plugin.c
+++ b/src/inputPlugins/audiofile_plugin.c
@@ -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,
diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c
index 455394888..242dc6f22 100644
--- a/src/inputPlugins/flac_plugin.c
+++ b/src/inputPlugins/flac_plugin.c
@@ -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,
NULL,
- flac_decode,
NULL,
- flacTagDup,
- INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
- flacSuffixes,
- flac_mime_types
+ 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 */
diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c
index 547f4af4a..8b5a245a0 100644
--- a/src/inputPlugins/mod_plugin.c
+++ b/src/inputPlugins/mod_plugin.c
@@ -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,
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index 0657f25b1..8a73ed85d 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -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
diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c
index a1af848c5..e41c054c0 100644
--- a/src/inputPlugins/mp4_plugin.c
+++ b/src/inputPlugins/mp4_plugin.c
@@ -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,
diff --git a/src/inputPlugins/mpc_plugin.c b/src/inputPlugins/mpc_plugin.c
index 335c5c368..01a165c44 100644
--- a/src/inputPlugins/mpc_plugin.c
+++ b/src/inputPlugins/mpc_plugin.c
@@ -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,
diff --git a/src/inputPlugins/oggflac_plugin.c b/src/inputPlugins/oggflac_plugin.c
new file mode 100644
index 000000000..25f9a3cdb
--- /dev/null
+++ b/src/inputPlugins/oggflac_plugin.c
@@ -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 */
+
diff --git a/src/inputPlugins/ogg_plugin.c b/src/inputPlugins/oggvorbis_plugin.c
index abadca388..63bac411e 100644
--- a/src/inputPlugins/ogg_plugin.c
+++ b/src/inputPlugins/oggvorbis_plugin.c
@@ -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);
- }
- 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);
+ unsigned int j;
+ for (j = TAG_NUM_OF_ITEM_TYPES; j--; ) {
+ if (ogg_parseCommentAddToTag(*comments, j, &tag))
+ break;
}
-
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};
+static unsigned int oggvorbis_try_decode(InputStream * inStream)
+{
+ return (ogg_stream_type_detect(inStream) == VORBIS) ? 1 : 0;
+}
+
-InputPlugin oggPlugin =
+static char * oggvorbis_Suffixes[] = {"ogg", NULL};
+static char * oggvorbis_MimeTypes[] = {"application/ogg", NULL};
+
+InputPlugin oggvorbisPlugin =
{
- "ogg",
+ "oggvorbis",
+ NULL,
NULL,
+ oggvorbis_try_decode,
+ oggvorbis_decode,
NULL,
- ogg_decode,
- NULL,
- oggTagDup,
- INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
- oggSuffixes,
- oggMimeTypes
+ 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 */
diff --git a/src/inputStream_http.c b/src/inputStream_http.c
index 34303dfab..9781b1b7a 100644
--- a/src/inputStream_http.c
+++ b/src/inputStream_http.c
@@ -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;
}
diff --git a/src/ls.c b/src/ls.c
index 762ddd672..f913ff2d3 100644
--- a/src/ls.c
+++ b/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;
}
diff --git a/src/ls.h b/src/ls.h
index dd8dddd9c..97406c062 100644
--- a/src/ls.h
+++ b/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);
diff --git a/src/song.c b/src/song.c
index 9a2c8d67d..07a7e04fb 100644
--- a/src/song.c
+++ b/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;
}
diff --git a/src/tag.c b/src/tag.c
index b1239f56d..0fa232345 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -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