summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-08-31 22:03:39 +0000
committerlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-08-31 22:03:39 +0000
commit16856546e54a78e1326d5492d446c79e31194a41 (patch)
tree06644c5f9f88d0b0a5d5be7e6abd2028d6884f15
parentdbb6752bbd1f5b47c65eb45c418a53f23cae866f (diff)
downloadvdr-plugin-muggle-16856546e54a78e1326d5492d446c79e31194a41.tar.gz
vdr-plugin-muggle-16856546e54a78e1326d5492d446c79e31194a41.tar.bz2
Coding style and naming of ogg ddecoder adapted. DB schema extended to hold audio properties sampling rate and channel count.
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/branches/ogg_player@132 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--TODO13
-rwxr-xr-xmugglei.c686
-rwxr-xr-xscripts/createtables.mysql4
-rw-r--r--vdr_decoder_mp3.h8
-rw-r--r--vdr_decoder_ogg.c449
-rw-r--r--vdr_decoder_ogg.h58
6 files changed, 647 insertions, 571 deletions
diff --git a/TODO b/TODO
index 8b7ab3c..f70744b 100644
--- a/TODO
+++ b/TODO
@@ -3,8 +3,16 @@ TODO File for Muggle
Ogg/Vorbis decoder integration
==============================
-- cOggFile needed? equivalence in Mp3?
-- cOggInfo/cSongInfo to be replaced by DB information
+- cOggFile kept
+- cOggInfo dismissed in favor of obtaining info from DB
+- coding conventions adapted
+
+- mugglei.c: Store audio properties in DB (most notably samplerate, no. channels
+- mgOggDecoder: obtain audio properties from DB
+- in mgPCMPlayer/vdr_player.c:
+ - pass m_playing to decoder (instead of filename)
+ - getSourceFile to be moved to mgTrack class and subclasses
+
Deployment
==========
@@ -15,6 +23,7 @@ Deployment
- generate documentation
- copy into web directory
- sync with web
+
- How to track bugs and feature requests?
Testing/bugs
diff --git a/mugglei.c b/mugglei.c
index 66fb766..37925ff 100755
--- a/mugglei.c
+++ b/mugglei.c
@@ -1,340 +1,346 @@
-
-#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(); //
+ int sample = ap->sampleRate();
+ int channels = ap->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\""
+ "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;
+}
+
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_mp3.h b/vdr_decoder_mp3.h
index abc8eed..a90a2fe 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++)
@@ -65,7 +65,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);
diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c
index dbe5a89..c5ae3a8 100644
--- a/vdr_decoder_ogg.c
+++ b/vdr_decoder_ogg.c
@@ -11,326 +11,377 @@
#ifdef HAVE_VORBISFILE
+#include "vdr_decoder_ogg.h"
+
+#include <mad.h>
+#include <vorbis/vorbisfile.h>
+
+#include <string>
#include <stdlib.h>
#include <stdio.h>
-#include "vdr_decoder_ogg.h"
+using namespace std;
+
+// --- mgOggFile ----------------------------------------------------------------
-class cOggFile : public cFileInfo
+class mgOggFile // : public mgFileInfo
{
-friend class cOggInfo;
-private:
- bool opened, canSeek;
+ private:
+
+ bool m_opened, m_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)
+
+ 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(void) { return canSeek; }
+
+ long long indexMs(void);
+};
+
+mgOggFile::mgOggFile( string filename ) :
+ m_filename( filename )
{
- canSeek=opened=false;
+ m_canSeek = false;
+ m_opened = false;
}
-cOggFile::~cOggFile()
+mgOggFile::~mgOggFile()
{
- Close();
+ m_close();
}
-bool cOggFile::Open(bool log)
+bool mgOggFile::Open(bool log)
{
- if( opened )
+ if( m_opened )
{
- if( canSeek )
- {
- return (Seek()>=0);
- }
+ if( m_canSeek )
+ {
+ return ( seek() >= 0 );
+ }
return true;
}
-
- if( FileInfo(log) )
- {
- FILE *f=fopen(Filename,"r");
- if(f)
+
+ FILE *f = fopen( filename.c_str(), "r" );
+ if( f )
+ {
+ int r = ov_open( f, &vf, 0, 0 );
+ if( !r )
{
- int r=ov_open(f,&vf,0,0);
- if(!r)
- {
- canSeek=(ov_seekable(&vf)!=0);
- opened=true;
- }
+ 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));
- }
+ fclose( f );
+ if( log )
+ {
+ error( "open", r );
+ }
+ }
}
- return opened;
-}
-
-void cOggFile::Close(void)
-{
- if(opened)
+ else
+ {
+ if(log)
{
- ov_clear(&vf);
- opened = false;
+ esyslog("ERROR: failed to open file %s: %s", filename.c_str(), strerror(errno) );
}
+ }
+ return m_opened;
+}
+
+void mgOggFile::close()
+{
+ if( m_opened )
+ {
+ ov_clear( &vf );
+ m_opened = false;
+ }
}
-void cOggFile::Error(const char *action, const int err)
+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;
+ 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);
+ esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr );
}
-long long cOggFile::IndexMs(void)
+long long mgOggFile::indexMs(void)
{
- double p = ov_time_tell(&vf);
- if( p < 0.0 )
- {
- p=0.0;
- }
- return (long long)( p*1000.0 );
+ 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)
+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;
+ if( relativ )
+ {
+ posMs += indexMs();
}
- posMs = IndexMs();
- return posMs;
+ 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 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);
+ 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 -------------------------------------------------------------
+// --- mgOggDecoder -------------------------------------------------------------
-cOggDecoder::cOggDecoder( string filename ) : cDecoder(Filename)
- ,file(filename)
+mgOggDecoder::mgOggDecoder( string filename )
+ : mgDecoder( filename )
{
- pcm = 0;
- Init();
+ m_file = new mgOggFile( filename );
+ m_pcm = 0;
+ init();
}
-cOggDecoder::~cOggDecoder()
+mgOggDecoder::~mgOggDecoder()
{
- Clean();
+ delete m_file;
+ clean();
}
-bool cOggDecoder::Valid(void)
+bool mgOggDecoder::valid()
{
bool res = false;
- if(TryLock())
+ if( tryLock() )
{
- if( file.Open(false) )
- {
- res = true;
- }
- Unlock();
+ if( m_file->open( false ) )
+ {
+ res = true;
+ }
+ unlock();
}
return res;
}
-cPlayInfo *cOggDecoder::PlayInfo(void)
+cPlayInfo *mgOggDecoder::playInfo(void)
{
- if(playing)
+ if( playing )
{
pi.Index = index/1000;
pi.Total = info.Total;
+
return &pi;
}
return 0;
}
-void cOggDecoder::Init(void)
+void mgOggDecoder::init()
{
- Clean();
- pcm = new struct mad_pcm;
- index = 0;
+ clean();
+ m_pcm = new struct mad_pcm;
+ m_index = 0;
}
-bool cOggDecoder::Clean(void)
+bool mgOggDecoder::clean()
{
- playing = false;
+ m_playing = false;
- delete pcm;
- pcm = 0;
+ delete m_pcm;
+ m_pcm = 0;
- file.Close();
+ m_file->close();
return false;
}
#define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
-bool cOggDecoder::Start(void)
+bool mgOggDecoder::start()
{
- Lock(true);
- Init();
- playing = true;
-
- if( file.Open() /*&& info.DoScan(true)*/ )
+ lock(true);
+ init();
+ m_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 )
{
- // 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);
+ unlock();
+ return true;
}
-
- Clean();
- Unlock();
- return false;
+ else
+ {
+ esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() );
+ }
+ }
+
+ clean();
+ unlock();
+
+ return false;
}
-bool cOggDecoder::Stop(void)
+bool mgOggDecoder::stop(void)
{
- Lock();
- if( playing )
+ lock();
+
+ if( m_playing )
{
- Clean();
+ clean();
}
- Unlock();
+ unlock();
+
return true;
}
-struct Decode *cOggDecoder::Done(eDecodeStatus status)
+struct Decode *mgOggDecoder::done(eDecodeStatus status)
{
- ds.status=status;
- ds.index=index;
- ds.pcm=pcm;
- Unlock(); // release the lock from Decode()
+ ds.status = status;
+ ds.index = m_index;
+ ds.pcm = m_pcm;
+
+ unlock(); // release the lock from Decode()
+
return &ds;
}
-struct Decode *cOggDecoder::Decode(void)
+struct Decode *mgOggDecoder::Decode(void)
{
Lock(); // this is released in Done()
if(playing)
{
short framebuff[2*SF_SAMPLES];
- int n = file.Stream(framebuff,SF_SAMPLES);
+ int n = m_file->stream( framebuff, SF_SAMPLES );
if( n < 0 )
- {
- return Done(dsError);
- }
+ {
+ return done(dsError);
+ }
if( n == 0 )
- {
- return Done(dsEof);
- }
+ {
+ return done(dsEof);
+ }
+ // TODO
pcm->samplerate = info.SampleFreq; // from database
pcm->channels = info.Channels; // from database
- n /= pcm->channels; // from database
+
+ n /= pcm->channels;
pcm->length = n;
- index = file.IndexMs();
+ index = m_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
+ const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 ); // shift value for mad_fixed conversion
- if(pcm->channels>1)
- {
- for(; n > 0 ; n-- )
+ if( pcm->channels>1 )
+ {
+ for(; n > 0 ; n-- )
{
- *sam0++=(*data++) << s;
- *sam1++=(*data++) << s;
+ *sam0++=(*data++) << s;
+ *sam1++=(*data++) << s;
+ }
}
- }
else
- {
- for(; n>0 ; n--)
- *sam0++=(*data++) << s;
- }
- return Done(dsPlay);
+ {
+ for(; n>0 ; n--)
+ {
+ *sam0++=(*data++) << s;
+ }
+ }
+ return done(dsPlay);
}
- return Done(dsError);
+ return done(dsError);
}
-bool cOggDecoder::Skip(int Seconds, int Avail, int Rate)
+bool mgOggDecoder::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();
+ lock();
+ bool res = false;
+
+ if( 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 )
+ {
+ index = m_file->indexMs();
#ifdef DEBUG
- int i=index/1000;
- printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
+ int i = index/1000;
+ printf( "ogg: skipping to %02d:%02d\n", i/60, i%60 );
#endif
- res=true;
- }
+ res = true;
+ }
}
- Unlock();
+ unlock();
return res;
}
diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h
index 6bee406..ce8fbee 100644
--- a/vdr_decoder_ogg.h
+++ b/vdr_decoder_ogg.h
@@ -16,37 +16,43 @@
#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;
+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);
- 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);
- };
+ void init(void);
+ bool clean(void);
+ struct mgDecode *done( eDecodeStatus status );
+
+ public:
+
+ mgOggDecoder( std::string filename );
+ ~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);
+};
// ----------------------------------------------------------------