summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-09-07 17:40:47 +0000
committerlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-09-07 17:40:47 +0000
commit653ca204d10b3481670a80769baa3d768bb5f64a (patch)
tree97845708a0991d68ac10ec36618fe5fbd6eb584b
parent22cedb0c0bb5345d92b67216f792810e75c75241 (diff)
downloadvdr-plugin-muggle-653ca204d10b3481670a80769baa3d768bb5f64a.tar.gz
vdr-plugin-muggle-653ca204d10b3481670a80769baa3d768bb5f64a.tar.bz2
Merged ogg vorbis player to trunk
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@148 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--HISTORY6
-rw-r--r--Makefile10
-rw-r--r--TODO46
-rw-r--r--gd_content_interface.c69
-rw-r--r--gd_content_interface.h84
-rwxr-xr-xmg_content_interface.c25
-rwxr-xr-xmg_content_interface.h75
-rw-r--r--mg_tools.h28
-rw-r--r--muggle.h26
-rwxr-xr-xmugglei.c690
-rwxr-xr-xscripts/createtables.mysql4
-rw-r--r--vdr_decoder.c73
-rw-r--r--vdr_decoder.h25
-rw-r--r--vdr_decoder_mp3.c22
-rw-r--r--vdr_decoder_mp3.h13
-rw-r--r--vdr_decoder_ogg.c393
-rw-r--r--vdr_decoder_ogg.h60
-rw-r--r--vdr_player.c43
18 files changed, 1150 insertions, 542 deletions
diff --git a/HISTORY b/HISTORY
index 0f92024..7350748 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,8 @@
VDR Plugin 'muggle' Revision History
------------------------------------
-2004-01-15: Version 0.0.1
+2004-08-31: Version 0.0.1-ALPHA
+
+- An initial revision given to a few people.
+
-- Initial revision.
diff --git a/Makefile b/Makefile
index 870bd6e..d9bf48c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for a Video Disk Recorder plugin
#
-# $Id: Makefile,v 1.10 2004/08/29 14:39:33 lvw Exp $
+# $Id$
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -21,7 +21,7 @@ CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g
### The directory environment:
DVBDIR = ../../../../DVB
-VDRDIR = ../../..
+VDRDIR = /usr/local/src/VDR
LIBDIR = ../../lib
TMPDIR = /tmp
@@ -42,14 +42,14 @@ PACKAGE = vdr-$(ARCHIVE)
INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/
-DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE
MIFLAGS += -I/usr/include/taglib -ltag -lmysqlclient
### The object files (add further files here):
-OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o
+OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o vdr_decoder_ogg.o
-LIBS = -lmad -lmysqlclient
+LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis
### Targets:
diff --git a/TODO b/TODO
index d11098e..cac1f2c 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,12 @@
TODO File for Muggle
====================
+Ogg/Vorbis decoder integration
+------------------------------
+- Think, whether type (mp3, ogg, flac) should be stored in database
+
+Deployment
+----------
- Script to publish a version
- Checkout
- make dist
@@ -9,14 +15,16 @@ TODO File for Muggle
- copy into web directory
- sync with web
+- How to track bugs and feature requests?
+
Testing/bugs
-============
+------------
- Test execution of playlist commands
-- Test mgPCMPlayer::getSourceFile() for GD case (find)
+- Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find)
- Test saving/loading playlists to database
Import
-======
+------
- Album
- Cover text
- Cover images (based on filename or tag)
@@ -27,35 +35,32 @@ Import
- Language (?)
- Genre1, 2
- Rating?
- - Bitrate
- Modified, created
- Lyrics
- Playlist from m3u
Code polishing
-==============
+--------------
+- Check for unnecessary log commands
+- Generate HTML documentation using doxygen,
+ - use dotty/gv for state machines of player
+ - make available online
+- Clean up mugglei
- Check for memory leaks
- Why do filters use pointers?
- Check for (reasonably) consistent usage of char*/string
+
- mgDatabase is not used?
- should handle a static object with a MySQL connection
- execute queries?
-
- mgPlayer used what for?
- Could save IP/host name and associate last playlist loaded
-- Check for unnecessary log commands
-- Generate HTML documentation using doxygen,
- - use dotty/gv for state machines of player
- - make available online
-
- Check compatibility for 1.3.12
Short term items
================
-- Import existing m3u playlists (in import)
-- Import genres
OSD in general
--------------
@@ -148,6 +153,21 @@ Already Done
- i18n (english and german)
- Album import
- Various artists
+- Ogg/Vorbis decoder integration
+ - cOggFile kept
+ - cOggInfo dismissed in favor of obtaining info from DB
+ - coding conventions adapted
+ - Schema extended to keep audio properties
+ - Import (mugglei) extended to store audio properties in DB
+ (most notably samplerate, no. channels)
+ - Extended mgContentItem with audio properties
+ - Extended mgGdTrack with audio properties (bitrate, samplerate, channels)
+ - in mgPCMPlayer/vdr_player.c:
+ - pass m_playing to mp3/ogg decoder (instead of filename)
+ - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem)
+ - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem)
+ and made concrete in subclasses (mgGdTrack)
+ - mgDecoders::findDecoder: extend decoder detection
************************************************************
*
diff --git a/gd_content_interface.c b/gd_content_interface.c
index 8df7c48..6c2f496 100644
--- a/gd_content_interface.c
+++ b/gd_content_interface.c
@@ -1,10 +1,11 @@
/*! \file gd_content_interface.c
* \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin
+ * \ingroup giantdisc
*
* \version $Revision: 1.27 $
- * \date $Date: 2004/08/30 14:31:43 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: LarsAC $
+ * \author Responsible author: $Author$
*
* Implements main classes of for content items and interfaces to SQL databases
*
@@ -293,35 +294,35 @@ bool mgGdTrack::readData()
// note: this does not work with empty album or genre fields
result = mgSqlReadQuery(&m_db,
"SELECT tracks.artist, album.title, tracks.title, "
- " tracks.mp3file, genre.genre, tracks.year, "
- " tracks.rating, tracks.length "
- "FROM tracks, album, genre "
+ "tracks.mp3file, genre.genre, tracks.year, "
+ "tracks.rating, tracks.length, tracks.samplerate, tracks.channels, tracks.bitrate "
+ "FROM tracks, album, genre "
"WHERE tracks.id=%d "
"AND album.cddbid=tracks.sourceid AND "
- " genre.id=tracks.genre1",
+ "genre.id=tracks.genre1",
m_uniqID);
nrows = mysql_num_rows(result);
nfields = mysql_num_fields(result);
- if(nrows == 0)
+ if( nrows == 0 )
{
- mgWarning("No entries found \n");
+ mgWarning( "No entries found \n" );
return false;
}
else
{
- if( nrows > 1 )
- {
- mgWarning("mgGdTrack::readData: More than one entry found. Using first entry.");
- }
+ if( nrows > 1 )
+ {
+ mgWarning("mgGdTrack::readData: More than one entry found. Using first entry.");
+ }
MYSQL_ROW row = mysql_fetch_row(result);
m_artist = row[0];
m_album = row[1];
m_title = row[2];
- m_mp3file = row[3];
- m_genre = row[4];
+ m_mp3file = string( the_setup.ToplevelDir ) + row[3];
+ m_genre = row[4];
if( sscanf( row[5], "%d", &m_year) != 1 )
{
@@ -338,6 +339,18 @@ bool mgGdTrack::readData()
mgError( "Invalid duration '%s' in database", row [7]);
}
+ if( row[8] && sscanf( row[8], "%d", &m_samplerate ) != 1 )
+ {
+ mgError( "Invalid samplerate '%s' in database", row [7]);
+ }
+
+ if( row[9] && sscanf( row[9], "%d", &m_channels ) != 1 )
+ {
+ mgError( "Invalid channels '%s' in database", row [7]);
+ }
+
+ m_bitrate = row[10];
+
}
m_retrieved = true;
return true;
@@ -445,6 +458,34 @@ int mgGdTrack::getDuration()
}
return m_rating;
}
+
+int mgGdTrack::getSampleRate()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_samplerate;
+}
+
+int mgGdTrack::getChannels()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_channels;
+}
+
+string mgGdTrack::getBitrate()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_bitrate;
+}
+
string mgGdTrack::getImageFile()
{
return "dummyImg.jpg";
diff --git a/gd_content_interface.h b/gd_content_interface.h
index 5d2626b..d810fa5 100644
--- a/gd_content_interface.h
+++ b/gd_content_interface.h
@@ -2,11 +2,12 @@
* \file gd_content_interface.h
* \brief Data objects for content (e.g. mp3 files, movies)
* for the vdr muggle plugin database
+ * \ingroup giantdisc
*
* \version $Revision: 1.11 $
- * \date $Date: 2004/08/30 14:31:43 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: LarsAC $
+ * \author Responsible author: $Author$
*
* Declares main classes for content items and interfaces to SQL databases
*
@@ -35,26 +36,28 @@
/*!
* \brief Initialize a database for GD use
+ * \ingroup giantdisc
* \todo Should be a static member of some GD class
*/
int GdInitDatabase(MYSQL *db);
/*!
* \brief Obtain the playlists stored within the GD schema
+ * \ingroup giantdisc
* \todo Should be a static member of some GD class
*/
std::vector<std::string> *GdGetStoredPlaylists(MYSQL db);
/*!
* \brief A set of filters to search for content
+ * \ingroup giantdisc
*/
class gdFilterSets : public mgFilterSets
{
public:
- /*! \addtogroup Object creation and destruction */
- /*\@{*/
+ //\@{
/*!
* \brief the default constructor
@@ -69,7 +72,7 @@ class gdFilterSets : public mgFilterSets
*/
virtual ~gdFilterSets();
- /*\@}*/
+ //\@
/*!
* \brief compute restriction w.r.t active filters
@@ -86,7 +89,8 @@ class gdFilterSets : public mgFilterSets
/*!
- * \brief represents a a single track
+ * \brief represents a a single track
+ * \ingroup giantdisc
*
* This may be any content item. e.g. a mp3 fileselection
* The object is initially created with a database identifier.
@@ -100,8 +104,7 @@ class mgGdTrack : public mgContentItem
{
public:
- /*! \addtogroup Object creation and destruction */
- /*\@{*/
+ //@{
/*!
* \brief a constructor
@@ -137,7 +140,7 @@ class mgGdTrack : public mgContentItem
*/
virtual ~mgGdTrack();
- /*\@}*/
+ //@}
/*!
* \brief obtain the content type of the item
@@ -157,8 +160,7 @@ class mgGdTrack : public mgContentItem
return mgMediaPlayer();
}
- /*! \addtogroup Data read access */
- /*\@{*/
+ //\@{
/*!
* \brief returns a certain field of the item as a string
@@ -215,16 +217,26 @@ class mgGdTrack : public mgContentItem
*/
virtual int getRating();
+ /*! \brief obtain the samplerate of the track
+ */
+ virtual int getSampleRate();
+
+ /*! \brief obtain the number of audio channels of the track
+ */
+ virtual int getChannels();
+
+ /*! \brief obtain the bitrate of the track
+ */
+ virtual std::string getBitrate();
+
/*!
* \brief obtain the complete track information
*/
virtual std::vector<mgFilter*> *getTrackInfo();
- /*\@}*/
-
- /*! \addtogroup Data write access */
- /*\@{*/
+ //\@}
+ //\@{
/*!
* \brief set the title of the track
*/
@@ -271,8 +283,7 @@ class mgGdTrack : public mgContentItem
* Genre and album field information is lost.
*/
bool writeData();
-
- /*\@}*/
+ //\@}
//! \brief a special instance denoting an undefined track
static mgGdTrack UNDEFINED;
@@ -319,6 +330,11 @@ private:
std::string m_genre;
/*!
+ * \brief The bitrate of the music
+ */
+ std::string m_bitrate;
+
+ /*!
* \brief The year in which the track appeared
*/
int m_year;
@@ -329,12 +345,21 @@ private:
int m_rating;
/*!
- * \brief The length of the file in bytes.
- * \todo TODO: is this true? Or what length are we talking about?
+ * \brief The length of the track in seconds
*/
int m_length;
/*!
+ * \brief The sampling rate of the track in Hz
+ */
+ int m_samplerate;
+
+ /*!
+ * \brief The number of channels of the track
+ */
+ int m_channels;
+
+ /*!
* \brief Access the data of the item from the database and fill actual data fields
*
* In order to avoid abundant queries to the database, the content fields
@@ -348,6 +373,7 @@ private:
/*
* \brief a list of tracks stored in the databas
+ * \ingroup giantdisc
*/
class GdTracklist : public mgTracklist
{
@@ -365,9 +391,7 @@ class GdPlaylist : public mgPlaylist
{
public:
- /*! \addtogroup Object creation and destruction */
- /*\@{*/
-
+ //@{
/*!
* \brief opens an existing or creates empty playlist by name
*
@@ -386,7 +410,7 @@ class GdPlaylist : public mgPlaylist
*/
virtual ~GdPlaylist();
- /* \@} */
+ //@}
/*!
* changes the listname of the playlist (and unset the sql id)
@@ -430,6 +454,7 @@ class GdPlaylist : public mgPlaylist
/*!
* \brief hierarchical representation of a set of tracks
+ * \ingroup giantdisc
*
* The selection can be based on the whole database or a subset of it.
* Within this selection, the data is organized in a tree hierarchy
@@ -444,9 +469,7 @@ class GdTreeNode : public mgSelectionTreeNode
{
public:
- /*! \addtogroup Object creation and destruction */
- /*\@{*/
-
+ //@{
/*!
* \brief Construct a top level tree node in a certain view with certain filters
*/
@@ -462,12 +485,9 @@ public:
* \brief destructor to clean up the object
*/
virtual ~GdTreeNode();
+ //@}
- /* \@} */
-
- /*! \addtogroup Access node data */
- /*\@{*/
-
+ //@{
/*!
* \brief check, whether the node is a leaf node (i.e. has no more children)
*/
@@ -488,7 +508,7 @@ public:
*/
virtual mgContentItem* getSingleTrack();
- /* \@} */
+ //@}
};
/* -------------------- begin CVS log ---------------------------------
diff --git a/mg_content_interface.c b/mg_content_interface.c
index c62bd2d..5c4890b 100755
--- a/mg_content_interface.c
+++ b/mg_content_interface.c
@@ -2,9 +2,9 @@
* \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin
*
* \version $Revision: 1.6 $
- * \date $Date: 2004/07/25 21:33:35 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
* Implements main classes of for content items and interfaces to SQL databases
*
@@ -22,7 +22,7 @@
#define DUMMY
/* constructor */
-mgContentItem mgContentItem::UNDEFINED = mgContentItem();
+mgContentItem mgContentItem::UNDEFINED = mgContentItem();
using namespace std;
@@ -37,7 +37,7 @@ mgTracklist::mgTracklist()
}
/*!
- * \brief destrucor
+ * \brief destructor
*
* Deletes all items in the tracklist and removes the list itself
*/
@@ -46,7 +46,7 @@ mgTracklist::~mgTracklist()
mgContentItem* ptr;
vector<mgContentItem*>::iterator iter;
- for(iter = m_list.begin(); iter != m_list.end();iter++)
+ for( iter = m_list.begin(); iter != m_list.end(); iter++ )
{
ptr = *iter;
delete ptr;
@@ -191,10 +191,8 @@ bool mgTracklist::remove(unsigned int position)
}
/*!
- *****************************************************************************
* \brief remove all occurences of item
- *
- ****************************************************************************/
+ */
int mgTracklist::remove(mgContentItem* item)
{
int retval = 0;
@@ -211,13 +209,6 @@ int mgTracklist::remove(mgContentItem* item)
return retval;
}
-
-
-/*=================================================================*/
-/* */
-/* class mgSelectionTreeNode */
-/* */
-/*=================================================================*/
mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view)
{
m_db = db;
@@ -239,7 +230,7 @@ mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id,
m_expanded = false;
}
- /*==== destructor ====*/
+/*==== destructor ====*/
mgSelectionTreeNode::~mgSelectionTreeNode()
{
collapse();
@@ -275,7 +266,7 @@ vector<mgSelectionTreeNode*> &mgSelectionTreeNode::getChildren()
return m_children;
}
-// access data in current node
+// access data in current node
string mgSelectionTreeNode::getID()
{
return m_id;
diff --git a/mg_content_interface.h b/mg_content_interface.h
index bb99999..44658e8 100755
--- a/mg_content_interface.h
+++ b/mg_content_interface.h
@@ -1,13 +1,10 @@
-/*******************************************************************/
/*! \file mg_content_interface.h
* \brief data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin
*
- ********************************************************************
- *
* \version $Revision: 1.4 $
- * \date $Date: 2004/05/28 15:29:18 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: lvw $
+ * \author file owner: $Author$
*
* Declares generic classes of for content items and interfaces to SQL databases
*
@@ -16,9 +13,7 @@
* - mgContentItem
* - mgTracklist
* - mgSelectionTreeNode
- *
*/
-/*******************************************************************/
/* makes sure we dont parse the same declarations twice */
#ifndef _CONTENT_INTERFACE_H
@@ -115,14 +110,15 @@ class mgContentItem
return mgMediaPlayer();
}
- /*! \brief get the "file" (or URL) that is passed to the player
+ //@{
+
+ /*! \brief return a specific label
*/
- virtual std::string getSourceFile()
+ virtual std::string getLabel(int col = 0)
{
return "";
}
- // ============ data access =================
/*! \brief return the title
*/
virtual std::string getTitle()
@@ -130,9 +126,9 @@ class mgContentItem
return "";
}
- /*! \brief return a specific label
+ /*! \brief get the "file" (or URL) that is passed to the player
*/
- virtual std::string getLabel(int col = 0)
+ virtual std::string getSourceFile()
{
return "";
}
@@ -144,26 +140,62 @@ class mgContentItem
return "";
}
- virtual std::vector<mgFilter*> *getTrackInfo()
+ /*! \brief obtain the genre to which the track belongs
+ */
+ virtual std::string getGenre()
{
- return NULL;
+ return "";
}
- virtual bool updateTrackInfo(std::vector<mgFilter*>*)
+ /*! \brief obtain the rating of the title
+ */
+ virtual int getRating()
{
- return false;
+ return 0;
}
- virtual std::string getGenre()
+ /*! \brief obtain the samplerate of the track
+ */
+ virtual int getSampleRate()
{
- return "";
+ return 0;
}
- virtual int getRating()
+ /*! \brief obtain the length of the track (in seconds)
+ */
+ virtual int getLength()
{
return 0;
}
+ /*! \brief obtain the number of audio channels of the track
+ */
+ virtual int getChannels()
+ {
+ return 0;
+ }
+
+ /*! \brief obtain the bitrate of the track
+ */
+ virtual std::string getBitrate()
+ {
+ return "";
+ }
+
+ /*! \brief obtain track information in aggregated form
+ */
+ virtual std::vector<mgFilter*> *getTrackInfo()
+ {
+ return NULL;
+ }
+
+ //@}
+
+ virtual bool updateTrackInfo(std::vector<mgFilter*>*)
+ {
+ return false;
+ }
+
virtual bool operator == (mgContentItem o)
{
return m_uniqID == o.m_uniqID;
@@ -206,7 +238,10 @@ class mgTracklist
virtual bool remove(unsigned int position); // remove item at position
};
-
+/*!
+ * \brief represent a node in a tree of selections
+ * \ingroup muggle
+ */
class mgSelectionTreeNode
{
diff --git a/mg_tools.h b/mg_tools.h
index 3b7bf12..3307e3e 100644
--- a/mg_tools.h
+++ b/mg_tools.h
@@ -1,10 +1,11 @@
/*! \file muggle_tools.h
+ * \ingroup muggle
* \brief A few utility functions for standalone and plugin messaging for the vdr muggle plugindatabase
*
* \version $Revision: 1.4 $
- * \date $Date: 2004/08/30 14:31:43 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author file owner: $Author$
*
*/
@@ -18,18 +19,30 @@
#define STANDALONE 1 // what's this?
-// mySql helper functions
+/*! \brief mySql helper function to execute read queries
+ * \ingroup muggle
+ *
+ * \todo Could be a member of mgDatabase?
+ */
MYSQL_RES* mgSqlReadQuery( MYSQL *db, const char *fmt, ... );
+
+/*! \brief mySql helper function to execute write queries
+ * \ingroup muggle
+ *
+ * \todo Could be a member of mgDatabase?
+ */
void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... );
-// Messaging functions
+/*! \brief Logging utilities */
+//@{
void mgSetDebugLevel(int new_level);
void mgDebug(int level, const char *fmt, ...);
void mgDebug( const char *fmt, ... );
void mgWarning(const char *fmt, ...);
void mgError(const char *fmt, ...);
+//@}
#ifdef DEBUG
#define MGLOG(x) mgLog __thelog(x)
@@ -43,6 +56,13 @@ void mgError(const char *fmt, ...);
#define MGLOGSTREAM __thelog.getStream()
#endif
+/*! \brief simplified logging class
+ * \ingroup muggle
+ *
+ * Create a local instance at the beginning of the method
+ * and entering/leaving the function will be logged
+ * as constructors/destructors are called.
+ */
class mgLog
{
public:
diff --git a/muggle.h b/muggle.h
index 835957f..be074c8 100644
--- a/muggle.h
+++ b/muggle.h
@@ -1,13 +1,33 @@
/*!
* \file muggle.h
+ * \ingroup vdr
* \brief Implements a plugin for browsing media libraries within VDR
*
* \version $Revision: 1.6 $
- * \date $Date: 2004/07/09 12:22:00 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author file owner: $Author$
*
- * $Id: muggle.h,v 1.6 2004/07/09 12:22:00 LarsAC Exp $
+ * $Id$
+ */
+
+
+// Some notes about the general structure of the plugin
+
+/* \defgroup giantdisc GiantDisc integration layer
+ * The GiantDisc integration layer contains functions and classes
+ * which enable interoperability with a database schema that conforms
+ * to the GiantDisc layout.
+ *
+ * \defgroup vdr VDR integration layer
+ * The VDR integration layer contains components which allow the
+ * plugin functionality to be accessed by VDR. These are mainly
+ * related to the OSD and a player/control combination.
+ *
+ * \defgroup muggle Main muggle business
+ * The core of the plugin is an abstract representation of information
+ * organized in trees (thus suitable for OSD navigation) as well as
+ * means to organize
*/
#ifndef _MUGGLE_H
diff --git a/mugglei.c b/mugglei.c
index 66fb766..3db2f04 100755
--- a/mugglei.c
+++ b/mugglei.c
@@ -1,340 +1,350 @@
-
-#include <string>
-using namespace std;
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <mysql/mysql.h>
-
-#include <taglib/tag.h>
-#include <taglib/fileref.h>
-
-#include "mg_tools.h"
-
-MYSQL *db;
-
-string host, user, pass, dbname;
-bool import_assorted;
-
-int init_database()
-{
- db = mysql_init(NULL);
- if( db == NULL )
- {
- return -1;
- }
-
- if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(),
- 0, NULL, 0 ) == NULL )
- {
- return -2;
- }
-
- return 0;
-}
-
-time_t get_fs_modification_time( string filename )
-{
- struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) );
-
- // yes: obtain modification date for file and db entry
- int statres = stat( filename.c_str(), buf );
-
- time_t mod = buf->st_mtime;
- free( buf );
-
- return mod;
-}
-
-time_t get_db_modification_time( long uid )
-{
- time_t mt;
-
- MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid );
- MYSQL_ROW row = mysql_fetch_row( result );
-
- string mod_time = row[0];
- mt = (time_t) atol( mod_time.c_str() );
-
- return mt;
-}
-
-TagLib::String escape_string( MYSQL *db, TagLib::String s )
-{
- char *buf = strdup( s.toCString() );
- char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 );
-
- int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) );
-
- return TagLib::String( escbuf );
-}
-
-long find_file_in_database( MYSQL *db, string filename )
-{
- TagLib::String file = TagLib::String( filename.c_str() );
- file = escape_string( db, file );
-
- MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() );
- MYSQL_ROW row = mysql_fetch_row( result );
-
- // obtain ID and return
- return atol( row[0] );
-}
-
-// read tags from the mp3 file and store them into the corresponding database entry
-void update_db( long uid, string filename )
-{
- TagLib::String title, album, artist, genre, cddbid;
- uint trackno, year;
-
- // ID3_Tag filetag( filename.c_str() );
- TagLib::FileRef f( filename.c_str() );
-
- if( !f.isNull() && f.tag() )
- {
- // cout << "Evaluating " << filename << endl;
- TagLib::Tag *tag = f.tag();
-
- // obtain tag information
- title = tag->title();
- album = tag->album();
- year = tag->year();
- artist = tag->artist();
- trackno = tag->track();
- genre = tag->genre();
-
- title = escape_string( db, title );
- album = escape_string( db, album );
- artist = escape_string( db, artist );
-
- // TODO: CD identifier (if it exists), playcounter, popularimeter (rating?), volume adjustment, lyrics, cover
-
- // finally update the database
-
- // obtain associated album or create
- if( album == "" )
- { // no album found, create default album for artist
- MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() );
- MYSQL_ROW row = mysql_fetch_row( result );
-
- // Default album does not yet exist (num rows == 0)
- int nrows = mysql_num_rows(result);
- if( nrows == 0 )
- {
- // create new album entry "Unassigned" for this artist
- long id = random();
- char *buf;
- asprintf( &buf, "%d-%s", id, tag->artist().toCString() );
- cddbid = TagLib::String( buf ).substr( 0, 20 );
- cddbid = escape_string( db, cddbid );
- free( buf );
-
- mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() );
- }
- else
- { // use first album found as source id for the track
- cddbid = row[0];
- }
- }
- else
- { // album tag found, associate or create
- MYSQL_RES *result;
- if( import_assorted )
- { // lookup an existing album by title only (artist should be "Various Artists"
- result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"Various Artists\"",
- album.toCString(), artist.toCString() );
- }
- else
- {
- result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"",
- album.toCString(), artist.toCString() );
- }
- MYSQL_ROW row = mysql_fetch_row( result );
-
- // num rows == 0 ?
- int nrows = mysql_num_rows(result);
- if( nrows == 0 )
- {
- // create new album entry
- long id = random();
- char *buf;
- asprintf( &buf, "%d-%s", id, tag->album().toCString() );
- cddbid = TagLib::String( buf ).substr( 0, 20 );
- cddbid = escape_string( db, cddbid );
- free( buf );
-
- if( import_assorted )
- { // in this case, the album author is "Various Artists"
- mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); }
- else
- {
- mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); }
- }
- else
- { // use first album found as source id for the track
- cddbid = row[0];
- }
- }
-
- // update tracks table
- if( uid > 0 )
- { // the entry is known to exist already, hence update it
- mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\", sourceid=\"%s\", mp3file=\"%s\""
- "WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), uid );
- }
- else
- { // the entry does not exist, create it
- // int t = title.find( "'" );
- // int a = artist.find( "'" );
- mgSqlWriteQuery( db,
- "INSERT INTO tracks (artist,title,genre1,genre2,year,sourceid,tracknb,mp3file)"
- " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\")",
- artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str() );
- /*
- cout << "-- TAG --" << endl;
- cout << "title - \"" << tag->title() << "\"" << endl;
- cout << "artist - \"" << tag->artist() << "\"" << endl;
- cout << "album - \"" << tag->album() << "\"" << endl;
- cout << "year - \"" << tag->year() << "\"" << endl;
- cout << "comment - \"" << tag->comment() << "\"" << endl;
- cout << "track - \"" << tag->track() << "\"" << endl;
- cout << "genre - \"" << tag->genre() << "\"" << endl;
- */
- }
- }
-}
-
-void update_tags( long uid, string filename )
-{
-
-}
-
-void evaluate_file( MYSQL *db, string filename )
-{
- if( 0 == init_database() )
- {
- // is filename stored in database?
- long uid = find_file_in_database( db, filename );
- if( uid >= 0 )
- {
- // currently only update database, do not consider writing changes from the db back
- /*
- // determine modification times in database and on filesystem
- time_t db_time = get_db_modification_time( uid );
- time_t fs_time = get_fs_modification_time( filename );
-
- if( db_time > fs_time )
- {
- // db is newer: update id3 tags from db
- update_tags( uid, filename );
- }
- else
- {
- // file is newer: update db from id3 tags
- update_db( uid, filename );
- }
- */
-
- update_db( uid, filename );
- }
- else
- {
- // not in db yet: import file
- update_db( -1, filename );
- }
- }
-}
-
-int main( int argc, char *argv[] )
-{
- int option_index;
- string filename;
-
- if( argc < 2 )
- { // we need at least a filename!
- cout << "mugglei -- import helper for Muggle VDR plugin" << endl;
- cout << "(C) Lars von Wedel" << endl;
- cout << "This is free software; see the source for copying conditions." << endl;
- cout << "" << endl;
- cout << "Options:" << endl;
- cout << " -h <hostname> - specify host of mySql database server (default is 'localhost')" << endl;
- cout << " -n <database> - specify database name (default is 'GiantDisc')" << endl;
- cout << " -u <username> - specify user of mySql database (default is empty)" << endl;
- cout << " -p <password> - specify password of user (default is empty password)" << endl;
- cout << " -f <filename> - name of music file to import" << endl;
- cout << " -a - import track as if it was on an assorted album" << endl;
-
- exit( 1 );
- }
-
- // option defaults
- host = "localhost";
- dbname = "GiantDisc";
- user = "";
- pass = "";
- import_assorted = false;
-
- // parse command line options
- while( 1 )
- {
- int c = getopt(argc, argv, "h:u:p:n:af:");
-
- if (c == -1)
- break;
-
- switch (c)
- {
- case 0:
- { // long option
-
- } break;
- case 'h':
- {
- host = optarg;
- } break;
- case 'u':
- {
- user = optarg;
- } break;
- case 'p':
- {
- pass = optarg;
- } break;
- case 'd':
- {
- dbname = optarg;
- } break;
- case 'a':
- {
- import_assorted = true;
- } break;
- case 'f':
- {
- filename = optarg;
- } break;
- }
- }
-
- // init random number generator
- struct timeval tv;
- struct timezone tz;
- gettimeofday( &tv, &tz );
- srandom( tv.tv_usec );
-
- int res = init_database();
-
- if( !res )
- {
- update_db( 0, filename );
- }
- else
- {
- cout << "Database initialization failed. Exiting.\n" << endl;
- }
-
- return res;
-}
-
+
+#include <string>
+using namespace std;
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <mysql/mysql.h>
+
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include "mg_tools.h"
+
+MYSQL *db;
+
+string host, user, pass, dbname;
+bool import_assorted;
+
+int init_database()
+{
+ db = mysql_init(NULL);
+ if( db == NULL )
+ {
+ return -1;
+ }
+
+ if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(),
+ 0, NULL, 0 ) == NULL )
+ {
+ return -2;
+ }
+
+ return 0;
+}
+
+time_t get_fs_modification_time( string filename )
+{
+ struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) );
+
+ // yes: obtain modification date for file and db entry
+ int statres = stat( filename.c_str(), buf );
+
+ time_t mod = buf->st_mtime;
+ free( buf );
+
+ return mod;
+}
+
+time_t get_db_modification_time( long uid )
+{
+ time_t mt;
+
+ MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid );
+ MYSQL_ROW row = mysql_fetch_row( result );
+
+ string mod_time = row[0];
+ mt = (time_t) atol( mod_time.c_str() );
+
+ return mt;
+}
+
+TagLib::String escape_string( MYSQL *db, TagLib::String s )
+{
+ char *buf = strdup( s.toCString() );
+ char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 );
+
+ int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) );
+
+ return TagLib::String( escbuf );
+}
+
+long find_file_in_database( MYSQL *db, string filename )
+{
+ TagLib::String file = TagLib::String( filename.c_str() );
+ file = escape_string( db, file );
+
+ MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() );
+ MYSQL_ROW row = mysql_fetch_row( result );
+
+ // obtain ID and return
+ return atol( row[0] );
+}
+
+// read tags from the mp3 file and store them into the corresponding database entry
+void update_db( long uid, string filename )
+{
+ TagLib::String title, album, artist, genre, cddbid;
+ uint trackno, year;
+
+ // ID3_Tag filetag( filename.c_str() );
+ TagLib::FileRef f( filename.c_str() );
+
+ if( !f.isNull() && f.tag() )
+ {
+ // cout << "Evaluating " << filename << endl;
+ TagLib::Tag *tag = f.tag();
+
+ // obtain tag information
+ title = tag->title();
+ album = tag->album();
+ year = tag->year();
+ artist = tag->artist();
+ trackno = tag->track();
+ genre = tag->genre();
+
+ AudioProperties *ap = f.audioProperties();
+ int len = ap->length(); // tracks.length
+ int bitrate = ap->bitrate(); // tracks.bitrate
+ int sample = ap->sampleRate(); //tracks.samplerate
+ int channels = ap->channels(); //tracks.channels
+
+ title = escape_string( db, title );
+ album = escape_string( db, album );
+ artist = escape_string( db, artist );
+
+ // TODO: CD identifier (if it exists), playcounter, popularimeter (rating?), volume adjustment, lyrics, cover
+
+ // finally update the database
+
+ // obtain associated album or create
+ if( album == "" )
+ { // no album found, create default album for artist
+ MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() );
+ MYSQL_ROW row = mysql_fetch_row( result );
+
+ // Default album does not yet exist (num rows == 0)
+ int nrows = mysql_num_rows(result);
+ if( nrows == 0 )
+ {
+ // create new album entry "Unassigned" for this artist
+ long id = random();
+ char *buf;
+ asprintf( &buf, "%d-%s", id, tag->artist().toCString() );
+ cddbid = TagLib::String( buf ).substr( 0, 20 );
+ cddbid = escape_string( db, cddbid );
+ free( buf );
+
+ mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() );
+ }
+ else
+ { // use first album found as source id for the track
+ cddbid = row[0];
+ }
+ }
+ else
+ { // album tag found, associate or create
+ MYSQL_RES *result;
+ if( import_assorted )
+ { // lookup an existing album by title only (artist should be "Various Artists"
+ result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"Various Artists\"",
+ album.toCString(), artist.toCString() );
+ }
+ else
+ {
+ result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"",
+ album.toCString(), artist.toCString() );
+ }
+ MYSQL_ROW row = mysql_fetch_row( result );
+
+ // num rows == 0 ?
+ int nrows = mysql_num_rows(result);
+ if( nrows == 0 )
+ {
+ // create new album entry
+ long id = random();
+ char *buf;
+ asprintf( &buf, "%d-%s", id, tag->album().toCString() );
+ cddbid = TagLib::String( buf ).substr( 0, 20 );
+ cddbid = escape_string( db, cddbid );
+ free( buf );
+
+ if( import_assorted )
+ { // in this case, the album author is "Various Artists"
+ mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); }
+ else
+ {
+ mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); }
+ }
+ else
+ { // use first album found as source id for the track
+ cddbid = row[0];
+ }
+ }
+
+ // update tracks table
+ if( uid > 0 )
+ { // the entry is known to exist already, hence update it
+
+ mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\","
+ "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\","
+ "samplerate=%d, channels=%d WHERE id=%d",
+ artist.toCString(), title.toCString(), year,
+ cddbid.toCString(), filename.c_str(), len, bitrate,
+ sample, channels, uid );
+ }
+ else
+ { // the entry does not exist, create it
+ mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year,"
+ "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels)"
+ " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d)",
+ artist.toCString(), title.toCString(), year, cddbid.toCString(),
+ trackno, filename.c_str(), len, bitrate, sample, channels );
+ /*
+ cout << "-- TAG --" << endl;
+ cout << "title - \"" << tag->title() << "\"" << endl;
+ cout << "artist - \"" << tag->artist() << "\"" << endl;
+ cout << "album - \"" << tag->album() << "\"" << endl;
+ cout << "year - \"" << tag->year() << "\"" << endl;
+ cout << "comment - \"" << tag->comment() << "\"" << endl;
+ cout << "track - \"" << tag->track() << "\"" << endl;
+ cout << "genre - \"" << tag->genre() << "\"" << endl;
+ */
+ }
+ }
+}
+
+void update_tags( long uid, string filename )
+{
+
+}
+
+void evaluate_file( MYSQL *db, string filename )
+{
+ if( 0 == init_database() )
+ {
+ // is filename stored in database?
+ long uid = find_file_in_database( db, filename );
+ if( uid >= 0 )
+ {
+ // currently only update database, do not consider writing changes from the db back
+ /*
+ // determine modification times in database and on filesystem
+ time_t db_time = get_db_modification_time( uid );
+ time_t fs_time = get_fs_modification_time( filename );
+
+ if( db_time > fs_time )
+ {
+ // db is newer: update id3 tags from db
+ update_tags( uid, filename );
+ }
+ else
+ {
+ // file is newer: update db from id3 tags
+ update_db( uid, filename );
+ }
+ */
+
+ update_db( uid, filename );
+ }
+ else
+ {
+ // not in db yet: import file
+ update_db( -1, filename );
+ }
+ }
+}
+
+int main( int argc, char *argv[] )
+{
+ int option_index;
+ string filename;
+
+ if( argc < 2 )
+ { // we need at least a filename!
+ cout << "mugglei -- import helper for Muggle VDR plugin" << endl;
+ cout << "(C) Lars von Wedel" << endl;
+ cout << "This is free software; see the source for copying conditions." << endl;
+ cout << "" << endl;
+ cout << "Options:" << endl;
+ cout << " -h <hostname> - specify host of mySql database server (default is 'localhost')" << endl;
+ cout << " -n <database> - specify database name (default is 'GiantDisc')" << endl;
+ cout << " -u <username> - specify user of mySql database (default is empty)" << endl;
+ cout << " -p <password> - specify password of user (default is empty password)" << endl;
+ cout << " -f <filename> - name of music file to import" << endl;
+ cout << " -a - import track as if it was on an assorted album" << endl;
+
+ exit( 1 );
+ }
+
+ // option defaults
+ host = "localhost";
+ dbname = "GiantDisc";
+ user = "";
+ pass = "";
+ import_assorted = false;
+
+ // parse command line options
+ while( 1 )
+ {
+ int c = getopt(argc, argv, "h:u:p:n:af:");
+
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ { // long option
+
+ } break;
+ case 'h':
+ {
+ host = optarg;
+ } break;
+ case 'u':
+ {
+ user = optarg;
+ } break;
+ case 'p':
+ {
+ pass = optarg;
+ } break;
+ case 'd':
+ {
+ dbname = optarg;
+ } break;
+ case 'a':
+ {
+ import_assorted = true;
+ } break;
+ case 'f':
+ {
+ filename = optarg;
+ } break;
+ }
+ }
+
+ // init random number generator
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday( &tv, &tz );
+ srandom( tv.tv_usec );
+
+ int res = init_database();
+
+ if( !res )
+ {
+ update_db( 0, filename );
+ }
+ else
+ {
+ cout << "Database initialization failed. Exiting.\n" << endl;
+ }
+
+ return res;
+}
+
diff --git a/scripts/createtables.mysql b/scripts/createtables.mysql
index 5969ce2..079b3bd 100755
--- a/scripts/createtables.mysql
+++ b/scripts/createtables.mysql
@@ -219,6 +219,10 @@ CREATE TABLE tracks (
created date default NULL,
modified date default NULL,
backup tinyint(3) unsigned default NULL,
+
+ samplerate int(7) unsigned default NULL,
+ channels tinyint(3) unsigned default NULL,
+
id int(11) NOT NULL auto_increment,
PRIMARY KEY (id),
KEY title (title(10)),
diff --git a/vdr_decoder.c b/vdr_decoder.c
index 8b4cf70..1c0b154 100644
--- a/vdr_decoder.c
+++ b/vdr_decoder.c
@@ -1,13 +1,14 @@
/*!
* \file vdr_decoder.h
* \brief A generic decoder for a VDR media plugin (muggle)
+ * \ingroup vdr
*
* \version $Revision: 1.2 $
- * \date $Date: 2004/05/28 15:29:18 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
- * $Id: vdr_decoder.c,v 1.2 2004/05/28 15:29:18 lvw Exp $
+ * $Id$
*
* Adapted from:
* MP3/MPlayer plugin to VDR (C++)
@@ -23,15 +24,11 @@
#include <videodir.h>
#include <interface.h>
-// #include "common.h"
-// #include "data-mp3.h"
-// #include "decoder-core.h"
-// #include "decoder-mp3-stream.h"
-// #include "decoder-snd.h"
-// #include "decoder-ogg.h"
-
#include "vdr_decoder.h"
#include "vdr_decoder_mp3.h"
+#include "vdr_decoder_ogg.h"
+
+#include "mg_content_interface.h"
using namespace std;
@@ -39,38 +36,58 @@ using namespace std;
mgMediaType mgDecoders::getMediaType( string s )
{
+ mgMediaType mt = MT_UNKNOWN;
+
// TODO: currently handles only mp3. LVW
- return MT_MP3;
+ char *f = (char *)s.c_str();
+ char *p = f + strlen( f ) - 1; // point to the end
+
+ while( p >= f && *p != '.') --p;
+
+ if( !strcmp( p, ".mp3" ) )
+ {
+ mt = MT_MP3;
+ }
+ else
+ {
+ if( !strcmp( p, ".ogg" ) )
+ {
+ mt = MT_OGG;
+ }
+ }
+ return mt;
}
-mgDecoder *mgDecoders::findDecoder( string filename )
+mgDecoder *mgDecoders::findDecoder( mgContentItem *item )
{
mgDecoder *decoder = 0;
+ string filename = item->getSourceFile();
+
switch( getMediaType( filename ) )
{
case MT_MP3:
{
- // prepend filename with path to music library?? TODO
- printf( "mp3 file detected, launching mgMP3Decoder\n" );
- decoder = new mgMP3Decoder(filename);
+ decoder = new mgMP3Decoder( item );
} break;
-
- /*
- case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break;
- #ifdef HAVE_SNDFILE
- case MT_SND: decoder = new cSndDecoder(full); break;
- #endif
- #ifdef HAVE_VORBISFILE
- case MT_OGG: decoder = new mgOggDecoder(full); break;
- #endif
- */
+#ifdef HAVE_VORBISFILE
+ case MT_OGG:
+ {
+ decoder = new mgOggDecoder( item );
+ } break;
+#endif
+ /*
+ case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break;
+ #ifdef HAVE_SNDFILE
+ case MT_SND: decoder = new cSndDecoder(full); break;
+ #endif
+ */
default:
{
esyslog("ERROR: unknown media type" );
} break;
}
-
+
if( decoder && !decoder->valid() )
{
// no decoder found or decoder doesn't match
@@ -85,9 +102,9 @@ mgDecoder *mgDecoders::findDecoder( string filename )
// --- mgDecoder ----------------------------------------------------------------
-mgDecoder::mgDecoder(string filename)
+mgDecoder::mgDecoder( mgContentItem *item )
{
- m_filename = filename;
+ m_item = item;
m_locked = 0;
m_urgentLock = false;
m_playing = false;
diff --git a/vdr_decoder.h b/vdr_decoder.h
index 74b200e..a673cbc 100644
--- a/vdr_decoder.h
+++ b/vdr_decoder.h
@@ -1,13 +1,14 @@
/*!
* \file vdr_decoder.h
* \brief A generic decoder for a VDR media plugin (muggle)
+ * \ingroup vdr
*
* \version $Revision: 1.2 $
- * \date $Date: 2004/05/28 15:29:18 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
- * $Id: vdr_decoder.h,v 1.2 2004/05/28 15:29:18 lvw Exp $
+ * $Id$
*
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
@@ -24,10 +25,13 @@
#define DEC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+class mgContentItem;
+
// --------From decoder_core.h ------------------------------------
/*!
* \brief The current status of the decoder
+ * \ingroup vdr
*/
enum eDecodeStatus
{
@@ -38,6 +42,7 @@ enum eDecodeStatus
/*!
* \brief A data structure to put decoded PCM data
+ * \ingroup vdr
*/
struct mgDecode
{
@@ -50,6 +55,7 @@ struct mgDecode
/*!
* \brief Information about ???
+ * \ingroup vdr
*/
class mgPlayInfo
{
@@ -61,19 +67,24 @@ public:
/*!
* \brief Media types
+ * \ingroup vdr
*/
enum mgMediaType
{
- MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC
+ MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_UNKNOWN
};
/*!
* \brief A generic decoder class to handle conversion into PCM format
+ * \ingroup vdr
*/
class mgDecoder
{
protected:
+ /*! \brief database handle to the track being decoded */
+ mgContentItem *m_item;
+
/*! \brief The currently playing file */
std::string m_filename;
@@ -99,11 +110,13 @@ protected:
public:
+ //@{
/*! \brief The constructor */
- mgDecoder( std::string filename );
+ mgDecoder( mgContentItem *item );
/*! \brief The destructor */
virtual ~mgDecoder();
+ //@}
/*! \brief Whether a decoder instance is able to play the given file */
virtual bool valid() = 0;
@@ -147,7 +160,7 @@ public:
/*! \brief Try to find a valid decoder for a file
*/
- static mgDecoder *findDecoder( std::string filename );
+ static mgDecoder *findDecoder( mgContentItem *item );
/*! \brief determine the media type for a given source
*/
diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c
index 436d7b6..20fca5e 100644
--- a/vdr_decoder_mp3.c
+++ b/vdr_decoder_mp3.c
@@ -3,11 +3,11 @@
* \brief An mp3 decoder for a VDR media plugin (muggle)
*
* \version $Revision: 1.2 $
- * \date $Date: 2004/05/28 15:29:19 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
- * $Id: vdr_decoder_mp3.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ * $Id$
*
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
@@ -27,6 +27,7 @@
#include "vdr_decoder_mp3.h"
#include "vdr_stream.h"
+#include "mg_content_interface.h"
#include "mg_tools.h"
#define d(x) x
@@ -52,17 +53,16 @@ int mgMadStream(struct mad_stream *stream, mgStream *str)
// --- mgMP3Decoder -------------------------------------------------------------
-mgMP3Decoder::mgMP3Decoder(string filename, bool preinit)
- : mgDecoder(filename)
+mgMP3Decoder::mgMP3Decoder( mgContentItem *item, bool preinit)
+ : mgDecoder(item)
{
- MGLOG( "mgMP3Decoder::mgMP3Decoder" );
-
m_stream = 0;
m_isStream = false;
+ m_filename = item->getSourceFile();
if( preinit )
{
- m_stream = new mgStream( filename );
+ m_stream = new mgStream( m_filename );
}
m_madframe = 0;
m_madsynth = 0;
@@ -181,8 +181,6 @@ mgPlayInfo *mgMP3Decoder::playInfo()
bool mgMP3Decoder::start()
{
- MGLOG( "mgMP3Decoder::start" );
-
lock(true);
init();
m_playing = true;
@@ -219,8 +217,6 @@ bool mgMP3Decoder::start()
bool mgMP3Decoder::stop(void)
{
- MGLOG( "mgMP3Decoder::stop" );
-
lock();
if( m_playing )
@@ -234,8 +230,6 @@ bool mgMP3Decoder::stop(void)
struct mgDecode *mgMP3Decoder::done(eDecodeStatus status)
{
- // MGLOG( "mgMP3Decoder::done" );
-
m_ds.status = status;
m_ds.index = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS );
m_ds.pcm = &m_madsynth->pcm;
diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h
index abc8eed..f9f3bde 100644
--- a/vdr_decoder_mp3.h
+++ b/vdr_decoder_mp3.h
@@ -3,11 +3,11 @@
* \brief An mp3 decoder for a VDR media plugin (muggle)
*
* \version $Revision: 1.2 $
- * \date $Date: 2004/05/28 15:29:19 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
- * $Id: vdr_decoder_mp3.h,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ * $Id$
*
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
@@ -30,6 +30,7 @@
#endif
class mgStream;
+class mgContentItem;
// ----------------------------------------------------------------
@@ -55,7 +56,7 @@ private:
} *m_frameinfo;
int m_framenum, m_framemax, m_errcount, m_mute;
- //
+
void init();
void clean();
@@ -65,7 +66,7 @@ private:
virtual mgPlayInfo *playInfo();
eDecodeStatus decodeError(bool hdr);
-
+
void makeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime,
int secs, int avail, int dvbrate);
@@ -78,7 +79,7 @@ public:
/*!
* \brief construct a decoder from a filename
*/
- mgMP3Decoder( std::string filename, bool preinit = true );
+ mgMP3Decoder( mgContentItem *item, bool preinit = true );
/*!
* \brief the destructor
diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c
new file mode 100644
index 0000000..9525979
--- /dev/null
+++ b/vdr_decoder_ogg.c
@@ -0,0 +1,393 @@
+/*! \file vdr_decoder_ogg.c
+ * \ingroup vdr
+ *
+ * The file implements a decoder which is used by the player to decode ogg vorbis audio files.
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001-2003 Stefan Huelswitt <huels@iname.com>
+ */
+
+
+#ifdef HAVE_VORBISFILE
+
+#include "vdr_decoder_ogg.h"
+
+#include <mad.h>
+#include <vorbis/vorbisfile.h>
+
+#include <string>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mg_content_interface.h"
+
+using namespace std;
+
+// --- mgOggFile ----------------------------------------------------------------
+
+class mgOggFile // : public mgFileInfo
+{
+ private:
+
+ bool m_opened, m_canSeek;
+
+ OggVorbis_File vf;
+
+ void error( const char *action, const int err );
+
+ string m_filename;
+
+ public:
+
+ mgOggFile( string filename );
+ ~mgOggFile();
+
+ bool open(bool log=true);
+
+ void close(void);
+
+ long long seek(long long posMs=0, bool relativ=false);
+
+ int stream(short *buffer, int samples);
+
+ bool canSeek() { return m_canSeek; }
+
+ long long indexMs(void);
+};
+
+mgOggFile::mgOggFile( string filename ) :
+ m_filename( filename )
+{
+ m_canSeek = false;
+ m_opened = false;
+}
+
+mgOggFile::~mgOggFile()
+{
+ close();
+}
+
+bool mgOggFile::open(bool log)
+{
+ if( m_opened )
+ {
+ if( m_canSeek )
+ {
+ return ( seek() >= 0 );
+ }
+ return true;
+ }
+
+ FILE *f = fopen( m_filename.c_str(), "r" );
+ if( f )
+ {
+ int r = ov_open( f, &vf, 0, 0 );
+ if( !r )
+ {
+ m_canSeek = ( ov_seekable( &vf ) !=0 );
+ m_opened = true;
+ }
+ else
+ {
+ fclose( f );
+ if( log )
+ {
+ error( "open", r );
+ }
+ }
+ }
+ else
+ {
+ if(log)
+ {
+ // esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+ return m_opened;
+}
+
+void mgOggFile::close()
+{
+ if( m_opened )
+ {
+ ov_clear( &vf );
+ m_opened = false;
+ }
+}
+
+void mgOggFile::error( const char *action, const int err )
+{
+ char *errstr;
+ switch(err)
+ {
+ case OV_FALSE: errstr = "false/no data available"; break;
+ case OV_EOF: errstr = "EOF"; break;
+ case OV_HOLE: errstr = "missing or corrupted data"; break;
+ case OV_EREAD: errstr = "read error"; break;
+ case OV_EFAULT: errstr = "internal error"; break;
+ case OV_EIMPL: errstr = "unimplemented feature"; break;
+ case OV_EINVAL: errstr = "invalid argument"; break;
+ case OV_ENOTVORBIS: errstr = "no Ogg Vorbis stream"; break;
+ case OV_EBADHEADER: errstr = "corrupted Ogg Vorbis stream"; break;
+ case OV_EVERSION: errstr = "unsupported bitstream version"; break;
+ case OV_ENOTAUDIO: errstr = "ENOTAUDIO"; break;
+ case OV_EBADPACKET: errstr = "EBADPACKET"; break;
+ case OV_EBADLINK: errstr = "corrupted link"; break;
+ case OV_ENOSEEK: errstr = "stream not seekable"; break;
+ default: errstr = "unspecified error"; break;
+ }
+ // esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr );
+}
+
+long long mgOggFile::indexMs(void)
+{
+ double p = ov_time_tell(&vf);
+ if( p < 0.0 )
+ {
+ p = 0.0;
+ }
+
+ return (long long)( p*1000.0 );
+}
+
+long long mgOggFile::seek( long long posMs, bool relativ )
+{
+ if( relativ )
+ {
+ posMs += indexMs();
+ }
+
+ int r = ov_time_seek( &vf, (double) posMs/1000.0 );
+
+ if(r)
+ {
+ error( "seek", r );
+ return -1;
+ }
+
+ posMs = indexMs();
+ return posMs;
+}
+
+int mgOggFile::stream( short *buffer, int samples )
+{
+ int n;
+ do
+ {
+ int stream;
+ n = ov_read( &vf, (char *)buffer, samples*2, 0, 2, 1, &stream );
+ }
+ while( n == OV_HOLE );
+
+ if(n < 0)
+ {
+ error( "read", n );
+ }
+
+ return (n/2);
+}
+
+// --- mgOggDecoder -------------------------------------------------------------
+
+mgOggDecoder::mgOggDecoder( mgContentItem *item )
+ : mgDecoder( item )
+{
+ m_filename = item->getSourceFile();
+ m_file = new mgOggFile( m_filename );
+ m_pcm = 0;
+ init();
+}
+
+mgOggDecoder::~mgOggDecoder()
+{
+ delete m_file;
+ clean();
+}
+
+bool mgOggDecoder::valid()
+{
+ bool res = false;
+ if( tryLock() )
+ {
+ if( m_file->open( false ) )
+ {
+ res = true;
+ }
+ unlock();
+ }
+ return res;
+}
+
+mgPlayInfo *mgOggDecoder::playInfo(void)
+{
+ if( m_playing )
+ {
+ // m_playinfo.m_index = index/1000;
+ // m_playinfo.m_total = info.Total;
+
+ return &m_playinfo;
+ }
+
+ return 0;
+}
+
+void mgOggDecoder::init()
+{
+ clean();
+ m_pcm = new struct mad_pcm;
+ m_index = 0;
+}
+
+bool mgOggDecoder::clean()
+{
+ m_playing = false;
+
+ delete m_pcm;
+ m_pcm = 0;
+
+ m_file->close();
+ return false;
+}
+
+#define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t))
+
+bool mgOggDecoder::start()
+{
+ lock(true);
+ init();
+ m_playing = true;
+
+ if( m_file->open() /*&& info.DoScan(true)*/ )
+ {
+ // obtain from database: rate, channels
+ /* d(printf("ogg: open rate=%d channels=%d seek=%d\n",
+ info.SampleFreq,info.Channels,file.CanSeek()))
+ */
+ if( m_item->getChannels() <= 2 )
+ {
+ unlock();
+ return true;
+ }
+ else
+ {
+ // esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() );
+ }
+ }
+
+ clean();
+ unlock();
+
+ return false;
+}
+
+bool mgOggDecoder::stop(void)
+{
+ lock();
+
+ if( m_playing )
+ {
+ clean();
+ }
+ unlock();
+
+ return true;
+}
+
+struct mgDecode *mgOggDecoder::done(eDecodeStatus status)
+{
+ m_ds.status = status;
+ m_ds.index = m_index;
+ m_ds.pcm = m_pcm;
+
+ unlock(); // release the lock from decode()
+
+ return &m_ds;
+}
+
+struct mgDecode *mgOggDecoder::decode(void)
+{
+ lock(); // this is released in Done()
+
+ if( m_playing )
+ {
+ short framebuff[2*SF_SAMPLES];
+ int n = m_file->stream( framebuff, SF_SAMPLES );
+
+ if( n < 0 )
+ {
+ return done(dsError);
+ }
+
+ if( n == 0 )
+ {
+ return done(dsEof);
+ }
+
+ // should be done during initialization
+ m_pcm->samplerate = m_item->getSampleRate(); // from database
+ m_pcm->channels = m_item->getChannels(); // from database
+
+ n /= m_pcm->channels;
+ m_pcm->length = n;
+ m_index = m_file->indexMs();
+
+ short *data = framebuff;
+ mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1];
+
+ const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 ); // shift value for mad_fixed conversion
+
+ if( m_pcm->channels>1 )
+ {
+ for(; n > 0 ; n-- )
+ {
+ *sam0++=(*data++) << s;
+ *sam1++=(*data++) << s;
+ }
+ }
+ else
+ {
+ for(; n>0 ; n--)
+ {
+ *sam0++=(*data++) << s;
+ }
+ }
+ return done(dsPlay);
+ }
+ return done(dsError);
+}
+
+bool mgOggDecoder::skip(int Seconds, int Avail, int Rate)
+{
+ lock();
+ bool res = false;
+
+ if( m_playing && m_file->canSeek() )
+ {
+ float fsecs = (float)Seconds - ( (float)Avail / (float)(Rate * (16/8 * 2) ) );
+ // Byte/s = samplerate * 16 bit * 2 chan
+
+ long long newpos = m_file->indexMs() + (long long)(fsecs*1000.0);
+
+ if( newpos < 0 )
+ {
+ newpos=0;
+ }
+
+ newpos = m_file->seek( newpos, false );
+
+ if( newpos >= 0 )
+ {
+ m_index = m_file->indexMs();
+#ifdef DEBUG
+ int i = index/1000;
+ printf( "ogg: skipping to %02d:%02d\n", i/60, i%60 );
+#endif
+ res = true;
+ }
+ }
+ unlock();
+ return res;
+}
+
+#endif //HAVE_VORBISFILE
diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h
new file mode 100644
index 0000000..b6bfc17
--- /dev/null
+++ b/vdr_decoder_ogg.h
@@ -0,0 +1,60 @@
+/*! \file vdr_decoder_ogg.h
+ * \ingroup vdr
+ *
+ * The file contains a decoder which is used by the player to decode ogg vorbis audio files.
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001-2003 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___DECODER_OGG_H
+#define ___DECODER_OGG_H
+
+#define DEC_OGG DEC_ID('O','G','G',' ')
+#define DEC_OGG_STR "OGG"
+
+#ifdef HAVE_VORBISFILE
+
+#include "vdr_decoder.h"
+
+// ----------------------------------------------------------------
+
+class mgOggFile;
+
+class mgOggDecoder : public mgDecoder
+{
+ private:
+
+ mgOggFile *m_file;
+ struct mgDecode m_ds;
+ struct mad_pcm *m_pcm;
+ unsigned long long m_index;
+
+ //
+ void init(void);
+ bool clean(void);
+ struct mgDecode *done( eDecodeStatus status );
+
+ public:
+
+ mgOggDecoder( mgContentItem *item );
+ ~mgOggDecoder();
+
+ virtual mgPlayInfo *playInfo();
+
+ virtual bool valid();
+
+ virtual bool start();
+
+ virtual bool stop();
+
+ virtual bool skip(int Seconds, int Avail, int Rate);
+
+ virtual struct mgDecode *decode(void);
+};
+
+// ----------------------------------------------------------------
+
+#endif //HAVE_VORBISFILE
+#endif //___DECODER_OGG_H
diff --git a/vdr_player.c b/vdr_player.c
index 105072b..5cde3fe 100644
--- a/vdr_player.c
+++ b/vdr_player.c
@@ -3,11 +3,11 @@
* \brief A generic PCM player for a VDR media plugin (muggle)
*
* \version $Revision: 1.7 $
- * \date $Date: 2004/07/27 20:50:54 $
+ * \date $Date$
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author Responsible author: $Author: lvw $
+ * \author Responsible author: $Author$
*
- * $Id: vdr_player.c,v 1.7 2004/07/27 20:50:54 lvw Exp $
+ * $Id$
*
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
@@ -176,7 +176,6 @@ private:
void SetPlayMode(ePlayMode mode);
void WaitPlayMode(ePlayMode mode, bool inv);
- string getSourceFile();
protected:
virtual void Activate(bool On);
virtual void Action(void);
@@ -363,10 +362,10 @@ void mgPCMPlayer::Action(void)
m_index = 0;
m_playing = m_current;
- string filename = getSourceFile();
+ string filename = m_playing->getSourceFile();
mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() );
- if( ( m_decoder = mgDecoders::findDecoder( filename ) ) && m_decoder->start() )
+ if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() )
{
levelgood = true;
haslevel = false;
@@ -671,38 +670,6 @@ void mgPCMPlayer::StopPlay()
}
}
-string mgPCMPlayer::getSourceFile()
-{
- string filename;
-
- if( !the_setup.GdCompatibility )
- { // use filename itself
- filename = string( the_setup.ToplevelDir ) + m_playing->getSourceFile();
- }
- else
- { // find the unique name within any directory, but what is top? video?
- char *cmd = NULL;
- asprintf( &cmd, "find %s -follow -type f -name '%s'|sort ",
- the_setup.ToplevelDir, m_playing->getSourceFile().c_str() );
-
- FILE *p = popen(cmd, "r");
- if (p)
- {
- char *s;
- if( (s = readline(p) ) != NULL )
- {
- filename = string( s );
- }
- }
- pclose( p );
- delete cmd;
- }
-
- cout << "mgPCMPlayer::getSourceFile: found filename " << filename << endl << flush;
- return filename;
- // return "/test.mp3";
-}
-
bool mgPCMPlayer::NextFile()
{
bool res = false;