summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-08-31 15:20:01 +0000
committerlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-08-31 15:20:01 +0000
commitdbb6752bbd1f5b47c65eb45c418a53f23cae866f (patch)
tree2ae5bfa4e22abffdf47b93968a5f95b7e6f60ed8
parentfc058329734920aeec424d191458ee6b3b472e5b (diff)
downloadvdr-plugin-muggle-dbb6752bbd1f5b47c65eb45c418a53f23cae866f.tar.gz
vdr-plugin-muggle-dbb6752bbd1f5b47c65eb45c418a53f23cae866f.tar.bz2
Added ogg decoder files (still to be worked upon).
Added documentation stuff to some files. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/branches/ogg_player@131 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--HISTORY6
-rw-r--r--TODO12
-rw-r--r--gd_content_interface.c5
-rw-r--r--gd_content_interface.h54
-rw-r--r--mg_tools.h28
-rw-r--r--muggle.h26
-rw-r--r--vdr_decoder_ogg.c337
-rw-r--r--vdr_decoder_ogg.h54
8 files changed, 479 insertions, 43 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/TODO b/TODO
index d11098e..8b7ab3c 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,13 @@
TODO File for Muggle
====================
+Ogg/Vorbis decoder integration
+==============================
+- cOggFile needed? equivalence in Mp3?
+- cOggInfo/cSongInfo to be replaced by DB information
+
+Deployment
+==========
- Script to publish a version
- Checkout
- make dist
@@ -8,6 +15,7 @@ TODO File for Muggle
- generate documentation
- copy into web directory
- sync with web
+- How to track bugs and feature requests?
Testing/bugs
============
@@ -24,10 +32,10 @@ Import
- Modified
- Tracks
+ - !Audio properties( bitrate, channels, length, sampleRate )
- Language (?)
- Genre1, 2
- Rating?
- - Bitrate
- Modified, created
- Lyrics
@@ -38,10 +46,10 @@ Code polishing
- 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
diff --git a/gd_content_interface.c b/gd_content_interface.c
index 8df7c48..3622f37 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
*
diff --git a/gd_content_interface.h b/gd_content_interface.h
index 5d2626b..9bb9213 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
@@ -220,11 +222,9 @@ class mgGdTrack : public mgContentItem
*/
virtual std::vector<mgFilter*> *getTrackInfo();
- /*\@}*/
-
- /*! \addtogroup Data write access */
- /*\@{*/
+ //\@}
+ //\@{
/*!
* \brief set the title of the track
*/
@@ -271,8 +271,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;
@@ -348,6 +347,7 @@ private:
/*
* \brief a list of tracks stored in the databas
+ * \ingroup giantdisc
*/
class GdTracklist : public mgTracklist
{
@@ -365,9 +365,7 @@ class GdPlaylist : public mgPlaylist
{
public:
- /*! \addtogroup Object creation and destruction */
- /*\@{*/
-
+ //@{
/*!
* \brief opens an existing or creates empty playlist by name
*
@@ -386,7 +384,7 @@ class GdPlaylist : public mgPlaylist
*/
virtual ~GdPlaylist();
- /* \@} */
+ //@}
/*!
* changes the listname of the playlist (and unset the sql id)
@@ -430,6 +428,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 +443,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 +459,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 +482,7 @@ public:
*/
virtual mgContentItem* getSingleTrack();
- /* \@} */
+ //@}
};
/* -------------------- begin CVS log ---------------------------------
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/vdr_decoder_ogg.c b/vdr_decoder_ogg.c
new file mode 100644
index 0000000..dbe5a89
--- /dev/null
+++ b/vdr_decoder_ogg.c
@@ -0,0 +1,337 @@
+/*! \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 <stdlib.h>
+#include <stdio.h>
+
+#include "vdr_decoder_ogg.h"
+
+class cOggFile : public cFileInfo
+{
+friend class cOggInfo;
+private:
+ bool opened, canSeek;
+ OggVorbis_File vf;
+ //
+ void Error(const char *action, const int err);
+public:
+ cOggFile(const char *Filename);
+ ~cOggFile();
+ bool Open(bool log=true);
+ void Close(void);
+ long long cOggFile::Seek(long long posMs=0, bool relativ=false);
+ int Stream(short *buffer, int samples);
+ bool CanSeek(void) { return canSeek; }
+ long long IndexMs(void);
+ };
+
+// --- cOggFile ----------------------------------------------------------------
+
+cOggFile::cOggFile(const char *Filename)
+:cFileInfo(Filename)
+{
+ canSeek=opened=false;
+}
+
+cOggFile::~cOggFile()
+{
+ Close();
+}
+
+bool cOggFile::Open(bool log)
+{
+ if( opened )
+ {
+ if( canSeek )
+ {
+ return (Seek()>=0);
+ }
+ return true;
+ }
+
+ if( FileInfo(log) )
+ {
+ FILE *f=fopen(Filename,"r");
+ if(f)
+ {
+ int r=ov_open(f,&vf,0,0);
+ if(!r)
+ {
+ canSeek=(ov_seekable(&vf)!=0);
+ opened=true;
+ }
+ else
+ {
+ fclose(f);
+ if(log)
+ {
+ Error("open",r);
+ }
+ }
+ }
+ else
+ {
+ if(log)
+ {
+ esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno));
+ }
+ }
+ return opened;
+}
+
+void cOggFile::Close(void)
+{
+ if(opened)
+ {
+ ov_clear(&vf);
+ opened = false;
+ }
+}
+
+void cOggFile::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,Filename,errstr);
+}
+
+long long cOggFile::IndexMs(void)
+{
+ double p = ov_time_tell(&vf);
+ if( p < 0.0 )
+ {
+ p=0.0;
+ }
+ return (long long)( p*1000.0 );
+}
+
+long long cOggFile::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 cOggFile::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);
+}
+
+// --- cOggDecoder -------------------------------------------------------------
+
+cOggDecoder::cOggDecoder( string filename ) : cDecoder(Filename)
+ ,file(filename)
+{
+ pcm = 0;
+ Init();
+}
+
+cOggDecoder::~cOggDecoder()
+{
+ Clean();
+}
+
+bool cOggDecoder::Valid(void)
+{
+ bool res = false;
+ if(TryLock())
+ {
+ if( file.Open(false) )
+ {
+ res = true;
+ }
+ Unlock();
+ }
+ return res;
+}
+
+cPlayInfo *cOggDecoder::PlayInfo(void)
+{
+ if(playing)
+ {
+ pi.Index = index/1000;
+ pi.Total = info.Total;
+ return &pi;
+ }
+ return 0;
+}
+
+void cOggDecoder::Init(void)
+{
+ Clean();
+ pcm = new struct mad_pcm;
+ index = 0;
+}
+
+bool cOggDecoder::Clean(void)
+{
+ playing = false;
+
+ delete pcm;
+ pcm = 0;
+
+ file.Close();
+ return false;
+}
+
+#define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
+
+bool cOggDecoder::Start(void)
+{
+ Lock(true);
+ Init();
+ playing = true;
+
+ if( 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( info.Channels <= 2 )
+ {
+ Unlock();
+ return true;
+ }
+ else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
+ }
+
+ Clean();
+ Unlock();
+ return false;
+}
+
+bool cOggDecoder::Stop(void)
+{
+ Lock();
+ if( playing )
+ {
+ Clean();
+ }
+ Unlock();
+ return true;
+}
+
+struct Decode *cOggDecoder::Done(eDecodeStatus status)
+{
+ ds.status=status;
+ ds.index=index;
+ ds.pcm=pcm;
+ Unlock(); // release the lock from Decode()
+ return &ds;
+}
+
+struct Decode *cOggDecoder::Decode(void)
+{
+ Lock(); // this is released in Done()
+ if(playing)
+ {
+ short framebuff[2*SF_SAMPLES];
+ int n = file.Stream(framebuff,SF_SAMPLES);
+
+ if( n < 0 )
+ {
+ return Done(dsError);
+ }
+
+ if( n == 0 )
+ {
+ return Done(dsEof);
+ }
+
+ pcm->samplerate = info.SampleFreq; // from database
+ pcm->channels = info.Channels; // from database
+ n /= pcm->channels; // from database
+ pcm->length = n;
+ index = file.IndexMs();
+
+ short *data = framebuff;
+ mad_fixed_t *sam0 = pcm->samples[0], *sam1 = pcm->samples[1];
+
+ const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
+
+ if(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 cOggDecoder::Skip(int Seconds, int Avail, int Rate)
+{
+ Lock();
+ bool res=false;
+ if(playing && file.CanSeek()) {
+ float fsecs=(float)Seconds - ((float)Avail / (float)(Rate * (16/8 * 2))); // Byte/s = samplerate * 16 bit * 2 chan
+ long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
+ if(newpos<0) newpos=0;
+ d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
+
+ newpos=file.Seek(newpos,false);
+ if(newpos>=0) {
+ index=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..6bee406
--- /dev/null
+++ b/vdr_decoder_ogg.h
@@ -0,0 +1,54 @@
+/*! \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 <mad.h>
+#include <vorbis/vorbisfile.h>
+
+#include "vdr_decoder.h"
+
+// ----------------------------------------------------------------
+
+class cOggDecoder : public cDecoder {
+private:
+ cOggFile file;
+ cOggInfo info;
+ struct Decode ds;
+ struct mad_pcm *pcm;
+ unsigned long long index;
+ //
+ void Init(void);
+ bool Clean(void);
+ bool GetInfo(bool keepOpen);
+ struct Decode *Done(eDecodeStatus status);
+public:
+ cOggDecoder(const char *Filename);
+ ~cOggDecoder();
+ virtual bool Valid(void);
+ virtual cFileInfo *FileInfo(void);
+ virtual cSongInfo *SongInfo(bool get);
+ virtual cPlayInfo *PlayInfo(void);
+ virtual bool Start(void);
+ virtual bool Stop(void);
+ virtual bool Skip(int Seconds, int Avail, int Rate);
+ virtual struct Decode *Decode(void);
+ };
+
+// ----------------------------------------------------------------
+
+#endif //HAVE_VORBISFILE
+#endif //___DECODER_OGG_H