diff options
author | lvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-08-31 15:20:01 +0000 |
---|---|---|
committer | lvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-08-31 15:20:01 +0000 |
commit | dbb6752bbd1f5b47c65eb45c418a53f23cae866f (patch) | |
tree | 2ae5bfa4e22abffdf47b93968a5f95b7e6f60ed8 | |
parent | fc058329734920aeec424d191458ee6b3b472e5b (diff) | |
download | vdr-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-- | HISTORY | 6 | ||||
-rw-r--r-- | TODO | 12 | ||||
-rw-r--r-- | gd_content_interface.c | 5 | ||||
-rw-r--r-- | gd_content_interface.h | 54 | ||||
-rw-r--r-- | mg_tools.h | 28 | ||||
-rw-r--r-- | muggle.h | 26 | ||||
-rw-r--r-- | vdr_decoder_ogg.c | 337 | ||||
-rw-r--r-- | vdr_decoder_ogg.h | 54 |
8 files changed, 479 insertions, 43 deletions
@@ -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. @@ -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 --------------------------------- @@ -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: @@ -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 π + } + 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 |