diff options
author | lvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-08-31 22:03:39 +0000 |
---|---|---|
committer | lvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-08-31 22:03:39 +0000 |
commit | 16856546e54a78e1326d5492d446c79e31194a41 (patch) | |
tree | 06644c5f9f88d0b0a5d5be7e6abd2028d6884f15 | |
parent | dbb6752bbd1f5b47c65eb45c418a53f23cae866f (diff) | |
download | vdr-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-- | TODO | 13 | ||||
-rwxr-xr-x | mugglei.c | 686 | ||||
-rwxr-xr-x | scripts/createtables.mysql | 4 | ||||
-rw-r--r-- | vdr_decoder_mp3.h | 8 | ||||
-rw-r--r-- | vdr_decoder_ogg.c | 449 | ||||
-rw-r--r-- | vdr_decoder_ogg.h | 58 |
6 files changed, 647 insertions, 571 deletions
@@ -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 @@ -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 π } 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); +}; // ---------------------------------------------------------------- |