diff options
41 files changed, 3154 insertions, 532 deletions
@@ -1,6 +1,10 @@ VDR Plugin 'upnp' Revision History ---------------------------------- +2009-11-19: Version 0.0.1-alpha4 + +- New: Added options for verbosity level and autodetect settings. + 2009-10-23: Version 0.0.1-alpha1 - Fixed #185: Database rejected statements with single quotes inside strings @@ -53,6 +53,29 @@ The command line settings are: Supported ports: 0 (auto detect) 49152-65535 (user defined) + -d --autodetect Force auto detection + Use this option to + overwrite the setup menu + options. + -v --verbose Increase verbosity level + The more v options the + higher the output level + +The verbose option can be more than once. With each option the verbosity level +will be increased. The maximum level is 5. Further options are silently ignored. + + -v Print most important messages + -vv Print messages from the components + i.e. the webserver, CDS, CMS, etc. + -vvv Print messages from receivers and players + and other additional components + -vvvv You can't get enough, hum? + This will print SQL messages and DIDL + messages + -vvvvv Can you read the matrix? You can't? + Then try first reading SQL statements, + fetches, buffer and parser outputs or + any other outputs. If not options are set, menu options will be used. @@ -68,7 +91,18 @@ Dependencies: This plugin is tested with and requires the following libraries to work: -libupnp-1.6.6 -libsqlite-3.6 +libupnp-1.6.6 The UPnP SDK +libsqlite-3.6 SQLite 3 Database +libavcodec-svn20090303 FFMPEG Library for analyzing audio video codecs +libavformat-svn20090303 FFMPEG Library for analyzing audio video formats +boost::spirit Grammar parser +boost::function Boost function library +boost::bind Boost bind library -libupnp-1.8.0 is known not to work with this plugin! +The boost libraries are usually available in a packed version, so that only a +single installation is required. The FFMPEG libraries comes with FFMPEG itself. + +ATTENTION: DO NOT USE ANOTHER VERSION OF LIBUPNP. + +libupnp-1.8.0 is known not to work with this plugin! Versions below 1.6.6 may +work. However, there may exist some unknown issues. @@ -7,9 +7,12 @@ #include <stdarg.h> #include "common.h" +#include "misc/config.h" DLNAProfile DLNA_PROFILE_MPEG_TS_SD_EU = { "MPEG_TS_SD_EU", "video/vnd.dlna.mpeg-tts" }; DLNAProfile DLNA_PROFILE_AVC_TS_HD_EU = { "AVC_TS_HD_EU", "video/vnd.dlna.mpeg-tts" }; +DLNAProfile DLNA_PROFILE_MPEG_TS_SD_EU_ISO = { "MPEG_TS_SD_EU_ISO", "video/mpeg" }; +DLNAProfile DLNA_PROFILE_AVC_TS_HD_EU_ISO = { "AVC_TS_HD_EU_ISO", "video/mpeg" }; DLNAProfile DLNA_PROFILE_MPEG1_L3 = { "MP3", "audio/mpeg" }; DLNAIconProfile DLNA_ICON_JPEG_SM_24 = { "image/jpeg", 48, 48, 24 }; @@ -19,11 +22,14 @@ DLNAIconProfile DLNA_ICON_PNG_LRG_24A = { "image/png", 120, 120, 24 }; #define MESSAGE_SIZE 256 -void message(const char* File, int Line, const char* Format, ...){ - va_list ap; - char Message[MESSAGE_SIZE]; - snprintf(Message, MESSAGE_SIZE, "(%s:%d) %s\n", File, Line, Format); - va_start(ap, Format); - vprintf(Message, ap); - va_end(ap); +void message(int Level, const char* , int , const char* Format, ...){ + if(Level && cUPnPConfig::verbosity >= Level){ + va_list ap; + char Message[MESSAGE_SIZE]; + + snprintf(Message, sizeof(Message), "[%d] %s", cThread::ThreadId(), Format); + va_start(ap, Format); + vsyslog(LOG_NOTICE, Message, ap); + va_end(ap); + } }
\ No newline at end of file @@ -91,7 +91,7 @@ #define att(s) strchr(s,'@')!=NULL?strchr(s,'@')+1:NULL #define prop(s) substr(s, 0, strchr(s,'@')-s) -void message(const char* File, int Line, const char* Format, ...) __attribute__ ((format (printf, 3, 4))); +void message(int level, const char* File, int Line, const char* Format, ...) __attribute__ ((format (printf, 4, 5))); /**************************************************** * @@ -154,12 +154,48 @@ void message(const char* File, int Line, const char* Format, ...) __attribute__ * Messages are additional information about the servers behavior. This will * be useful for debugging. */ -#ifdef DEBUG -#define MESSAGE(s...) message(__FILE__, __LINE__, "UPnP server message: " s) +#ifndef DEBUG +#define MESSAGE(l,s...) message(l,__FILE__, __LINE__, "UPnP server message: " s) #else -#define MESSAGE(s...) dsyslog("UPnP server message: " s) +#define MESSAGE(l,s...) dsyslog("UPnP server message: " s) #endif + +/** + * Define at which level the different messages will be printed + * + * The log levels reach from 1 to 5, where 1 is the highest log + * priority and 5 the lowest. 0 will deactivate the logging of + * the message. If you want to specifiy a certain level, add + * 'v' options as many as required for the log level. + * + * @example + * - \-v Show warnings + * - \-vv Log level 1 + * - \-vvv Log level 2 + * - \-vvvv Log level 3 + * - \-vvvvv Log level 4 + * - \-vvvvvv Log level 5 + */ +#define VERBOSE_SQL 4 +#define VERBOSE_SQL_FETCHES 5 +#define VERBOSE_SQL_STATEMENTS 5 +#define VERBOSE_DIDL 4 +#define VERBOSE_LIVE_TV 3 +#define VERBOSE_RECORDS 3 +#define VERBOSE_CUSTOMFILES 3 +#define VERBOSE_SDK 1 +#define VERBOSE_EPG_UPDATES 3 +#define VERBOSE_WEBSERVER 2 +#define VERBOSE_MODIFICATIONS 2 +#define VERBOSE_METADATA 3 +#define VERBOSE_CUSTOM_OUTPUT 5 +#define VERBOSE_PARSERS 5 +#define VERBOSE_BUFFERS 5 +#define VERBOSE_CDS 2 +#define VERBOSE_CMS 2 +#define VERBOSE_OBJECTS 3 + /**************************************************** * * 1.3 Plugin constants @@ -648,17 +684,17 @@ enum UPnPWriteStatus { * The following operation field assumes that s0 is NOT changing. Only changes to sN are permitted. * If s0 and/or sN changes these fields must be set to false. Use DLNA_FLAG_*_BASED_SEEK flags instead. */ -#define DLNA_OPERATION_NONE 00 // No seek operations supported -#define DLNA_OPERATION_TIME_SEEK_RANGE 10 // is the server supporting time based seeks? -#define DLNA_OPERATION_RANGE 01 // or byte based seeks? +#define DLNA_OPERATION_NONE 00 ///< No seek operations supported +#define DLNA_OPERATION_TIME_SEEK_RANGE 10 ///< is the server supporting time based seeks? +#define DLNA_OPERATION_RANGE 01 ///< or byte based seeks? -#define DLNA_CONVERSION_TRANSCODED 1 // the content was converted from one media format to another -#define DLNA_CONVERSION_NONE 0 // the content is available without conversion +#define DLNA_CONVERSION_TRANSCODED 1 ///< the content was converted from one media format to another +#define DLNA_CONVERSION_NONE 0 ///< the content is available without conversion -#define DLNA_SUPPORTED_PLAYSPEEDS "2,4,8,-2,-4,-8"; // 1 is required, but omited in the PS parameter +#define DLNA_SUPPORTED_PLAYSPEEDS "2,4,8,-2,-4,-8"; ///< 1 is required, but omited in the PS parameter -#define DLNA_TRANSFER_PROTOCOL_HTTP 1 // use http tranfer -#define DLNA_TRANSFER_PROTOCOL_RTP 2 // use rtp tranfer +#define DLNA_TRANSFER_PROTOCOL_HTTP 1 ///< use http tranfer +#define DLNA_TRANSFER_PROTOCOL_RTP 2 ///< use rtp tranfer /**************************************************** * @@ -666,21 +702,21 @@ enum UPnPWriteStatus { * ****************************************************/ -#define DLNA_FLAG_SENDER_PACED 1 << 31 // is the server setting the pace (i.e. RTP)? -#define DLNA_FLAG_TIME_BASED_SEEK 1 << 30 // is the server supporting time based seeks? -#define DLNA_FLAG_BYTE_BASED_SEEK 1 << 29 // or byte based seeking? -#define DLNA_FLAG_PLAY_CONTAINER 1 << 28 // is it possible to play all contents of a container? -#define DLNA_FLAG_S0_INCREASE 1 << 27 // is the beginning changing (time shift)? -#define DLNA_FLAG_SN_INCREASE 1 << 26 // is the end changing (live-TV)? -#define DLNA_FLAG_RTSP_PAUSE 1 << 25 // is pausing rtp streams permitted? -#define DLNA_FLAG_STREAMING_TRANSFER 1 << 24 // is the transfer a stream (Audio/AV)? -#define DLNA_FLAG_INTERACTIVE_TRANSFER 1 << 23 // is the transfer interactiv (printings)? -#define DLNA_FLAG_BACKGROUND_TRANSFER 1 << 22 // is the tranfer done in background (downloaded)? -#define DLNA_FLAG_CONNECTION_STALLING 1 << 21 // can the connection be paused on HTTP streams? -#define DLNA_FLAG_VERSION_1_5 1 << 20 // does the server complies with DLNA V1.5 -#define DLNA_FLAG_CLEARTEXT_CONTENT 1 << 16 // (Link Protection) currently not used -#define DLNA_FLAG_CLEARTEXT_BYTE_FULL_SEEK 1 << 15 // (Link Protection) currently not used -#define DLNA_FLAG_CLEARTEXT_LIMITED_SEEK 1 << 14 // (Link Protection) currently not used +#define DLNA_FLAG_SENDER_PACED 1 << 31 ///< is the server setting the pace (i.e. RTP)? +#define DLNA_FLAG_TIME_BASED_SEEK 1 << 30 ///< is the server supporting time based seeks? +#define DLNA_FLAG_BYTE_BASED_SEEK 1 << 29 ///< or byte based seeking? +#define DLNA_FLAG_PLAY_CONTAINER 1 << 28 ///< is it possible to play all contents of a container? +#define DLNA_FLAG_S0_INCREASE 1 << 27 ///< is the beginning changing (time shift)? +#define DLNA_FLAG_SN_INCREASE 1 << 26 ///< is the end changing (live-TV)? +#define DLNA_FLAG_RTSP_PAUSE 1 << 25 ///< is pausing rtp streams permitted? +#define DLNA_FLAG_STREAMING_TRANSFER 1 << 24 ///< is the transfer a stream (Audio/AV)? +#define DLNA_FLAG_INTERACTIVE_TRANSFER 1 << 23 ///< is the transfer interactiv (printings)? +#define DLNA_FLAG_BACKGROUND_TRANSFER 1 << 22 ///< is the tranfer done in background (downloaded)? +#define DLNA_FLAG_CONNECTION_STALLING 1 << 21 ///< can the connection be paused on HTTP streams? +#define DLNA_FLAG_VERSION_1_5 1 << 20 ///< does the server complies with DLNA V1.5 +#define DLNA_FLAG_CLEARTEXT_CONTENT 1 << 16 ///< (Link Protection) currently not used +#define DLNA_FLAG_CLEARTEXT_BYTE_FULL_SEEK 1 << 15 ///< (Link Protection) currently not used +#define DLNA_FLAG_CLEARTEXT_LIMITED_SEEK 1 << 14 ///< (Link Protection) currently not used #define DLNA_FLAGS_PLUGIN_SUPPORT DLNA_FLAG_BYTE_BASED_SEEK | \ DLNA_FLAG_SN_INCREASE | \ @@ -703,29 +739,37 @@ enum UPnPWriteStatus { * label field as it seams to be not needed. */ struct DLNAProfile { - const char* ID; - const char* mime; + const char* ID; ///< the DLNA profile ID + const char* mime; ///< the mime type of the resource }; +/** + * The DLNA profile for a icon image + * + * This complies with the DLNA media format guidelines. It contains a valid + * mime type, the resolution of the image and the corresponding bit depth + */ struct DLNAIconProfile { - const char* mime; - unsigned short width; - unsigned short height; - unsigned char bitDepth; + const char* mime; ///< the mime type of the image + unsigned short width; ///< image width in pixel + unsigned short height; ///< image height in pixel + unsigned char bitDepth; ///< bit depth in bits per pixel }; /* Images */ /* Audio */ -extern DLNAProfile DLNA_PROFILE_MPEG1_L3; // MP3 +extern DLNAProfile DLNA_PROFILE_MPEG1_L3; ///< DLNA MP3 Profile /* Video */ -extern DLNAProfile DLNA_PROFILE_MPEG_TS_SD_EU; // This is the profile for DVB-TV -extern DLNAProfile DLNA_PROFILE_AVC_TS_HD_EU; // This is the profile for DVB-TV +extern DLNAProfile DLNA_PROFILE_MPEG_TS_SD_EU; ///< DLNA Profile for DVB Television broadcasts +extern DLNAProfile DLNA_PROFILE_AVC_TS_HD_EU; ///< DLNA Profile for HD DVB Television broadcasts +extern DLNAProfile DLNA_PROFILE_MPEG_TS_SD_EU_ISO; ///< DLNA Profile for DVB Television broadcasts without timestamp +extern DLNAProfile DLNA_PROFILE_AVC_TS_HD_EU_ISO; ///< DLNA Profile for HD DVB Television broadcasts without timestamp /* Icons */ -extern DLNAIconProfile DLNA_ICON_JPEG_SM_24; -extern DLNAIconProfile DLNA_ICON_JPEG_LRG_24; -extern DLNAIconProfile DLNA_ICON_PNG_SM_24A; -extern DLNAIconProfile DLNA_ICON_PNG_LRG_24A; +extern DLNAIconProfile DLNA_ICON_JPEG_SM_24; ///< DLNA icon profile of small jpeg images +extern DLNAIconProfile DLNA_ICON_JPEG_LRG_24; ///< DLNA icon profile of large jpeg images +extern DLNAIconProfile DLNA_ICON_PNG_SM_24A; ///< DLNA icon profile of small png images +extern DLNAIconProfile DLNA_ICON_PNG_LRG_24A; ///< DLNA icon profile of large png images /**************************************************** * diff --git a/database/database.cpp b/database/database.cpp index b3d11b2..e47dcbf 100644 --- a/database/database.cpp +++ b/database/database.cpp @@ -46,9 +46,7 @@ int cSQLiteDatabase::exec(const char* Statement){ return -1; } this->mRows = new cRows; -#ifdef SQLITE_PRINT_STATEMENTS - MESSAGE("SQLite: %s", Statement); -#endif + MESSAGE(VERBOSE_SQL_STATEMENTS,"SQLite: %s", Statement); if(sqlite3_exec(this->mDatabase, Statement, cSQLiteDatabase::getResultRow, (cSQLiteDatabase*)this, &Error)!=SQLITE_OK){ ERROR("Database error: %s", Error); ERROR("Statement was: %s", Statement); @@ -143,13 +141,12 @@ bool cRow::fetchColumn(cString* Column, cString* Value){ return ret; } + bool cRow::fetchColumn(char** Column, char** Value){ if(currentCol>=this->ColCount){ return false; } - #ifdef SQLITE_PRINT_FETCHES - MESSAGE("Fetching column %s='%s' (%d/%d)", this->Columns[currentCol], this->Values[currentCol], currentCol+1, this->ColCount); - #endif + MESSAGE(VERBOSE_SQL_FETCHES,"Fetching column %s='%s' (%d/%d)", this->Columns[currentCol], this->Values[currentCol], currentCol+1, this->ColCount); *Column = strdup0(this->Columns[currentCol]); if(this->Values[currentCol]){ *Value = strcasecmp(this->Values[currentCol],"NULL")?strdup(this->Values[currentCol]):NULL; @@ -169,7 +166,7 @@ int cSQLiteDatabase::initialize(){ sqlite3_close(this->mDatabase); return -1; } - MESSAGE("Database file %s opened.", *File); + MESSAGE(VERBOSE_SDK,"Database file %s opened.", *File); if(this->initializeTables()){ ERROR("Error while creating tables"); return -1; @@ -191,17 +188,19 @@ void cSQLiteDatabase::startTransaction(){ } } this->execStatement("BEGIN TRANSACTION"); - MESSAGE("Start new transaction"); + MESSAGE(VERBOSE_SQL,"Start new transaction"); this->mActiveTransaction = true; } void cSQLiteDatabase::commitTransaction(){ this->execStatement("COMMIT TRANSACTION"); + MESSAGE(VERBOSE_SQL,"Commited transaction"); this->mActiveTransaction = false; } void cSQLiteDatabase::rollbackTransaction(){ this->execStatement("ROLLBACK TRANSACTION"); + MESSAGE(VERBOSE_SQL,"Rolled back transaction"); this->mActiveTransaction = false; } diff --git a/database/database.h b/database/database.h index 041772e..5bb595f 100644 --- a/database/database.h +++ b/database/database.h @@ -12,8 +12,6 @@ #include <vdr/tools.h> #include "../common.h" -#define SQLITE_PRINT_STATEMENTS -#define SQLITE_PRINT_FETCHES #define SQLITE_CASCADE_DELETES #define PK_OBJECTS TOSTRING(1) @@ -144,7 +142,7 @@ "FOR EACH ROW BEGIN "\ "SELECT CASE "\ "WHEN ("\ - "((SELECT " SQLITE_COL_OBJECTID " FROM " TableA " "\ + "((SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_OBJECTS " "\ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID " "\ "AND " SQLITE_COL_CLASS " LIKE '" Class "%%') IS NULL)"\ ") THEN "\ @@ -238,11 +236,11 @@ #define SQLITE_DELETE_PARENT_TRIGGER SQLITE_DELETE_REFERENCE_TRIGGER(SQLITE_TABLE_OBJECTS, SQLITE_COL_PARENTID) #endif - /**********************************************\ - * * - * Primary keys * - * * - \**********************************************/ +/**********************************************\ +* * +* Primary keys * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_PRIMARY_KEYS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_PRIMARY_KEYS \ @@ -274,11 +272,11 @@ "UPDATE " SQLITE_TABLE_PRIMARY_KEYS " SET Key=Key+1 WHERE KeyID=" PK_OBJECTS "; "\ "END;" - /**********************************************\ - * * - * System settings * - * * - \**********************************************/ +/**********************************************\ +* * +* System settings * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_SYSTEM "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_SYSTEM " "\ @@ -294,11 +292,11 @@ "WHEN ((SELECT Key FROM " SQLITE_TABLE_SYSTEM " WHERE Key=NEW.Key) IS NULL) "\ "BEGIN INSERT INTO " SQLITE_TABLE_SYSTEM " (Key) VALUES (NEW.Key); END;" - /**********************************************\ - * * - * Fast item finder * - * * - \**********************************************/ +/**********************************************\ +* * +* Fast item finder * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_ITEMFINDER "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_ITEMFINDER " "\ @@ -310,11 +308,11 @@ #define SQLITE_TRIGGER_D_OBJECTS_ITEMFINDER SQLITE_DELETE_TRIGGER(SQLITE_TABLE_OBJECTS,\ SQLITE_TABLE_ITEMFINDER) - /**********************************************\ - * * - * Objects * - * * - \**********************************************/ +/**********************************************\ +* * +* Objects * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_OBJECTS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_OBJECTS \ @@ -369,11 +367,11 @@ SQLITE_COL_PARENTID " must uniquely be -1') "\ "END; END;" - /**********************************************\ - * * - * Items * - * * - \**********************************************/ +/**********************************************\ +* * +* Items * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_ITEMS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_ITEMS \ @@ -403,11 +401,11 @@ #define SQLITE_TRIGGER_D_ITEMS_ITEMS SQLITE_DELETE_REFERENCE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_COL_REFERENCEID) - /**********************************************\ - * * - * Containers * - * * - \**********************************************/ +/**********************************************\ +* * +* Containers * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_CONTAINER "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_CONTAINERS \ @@ -429,11 +427,11 @@ SQLITE_TABLE_CONTAINERS,\ UPNP_CLASS_CONTAINER) - /**********************************************\ - * * - * Video items * - * * - \**********************************************/ +/**********************************************\ +* * +* Video items * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_VIDEOITEMS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_VIDEOITEMS \ @@ -461,11 +459,11 @@ SQLITE_TABLE_VIDEOITEMS, \ UPNP_CLASS_VIDEO) - /**********************************************\ - * * - * Audio items * - * * - \**********************************************/ +/**********************************************\ +* * +* Audio items * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_AUDIOITEMS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_AUDIOITEMS \ @@ -488,11 +486,11 @@ SQLITE_TABLE_AUDIOITEMS, \ UPNP_CLASS_AUDIO) - /**********************************************\ - * * - * Image items * - * * - \**********************************************/ +/**********************************************\ +* * +* Image items * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_IMAGEITEMS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_IMAGEITEMS \ @@ -517,11 +515,11 @@ SQLITE_TABLE_IMAGEITEMS, \ UPNP_CLASS_IMAGE) - /**********************************************\ - * * - * Video broadcasts * - * * - \**********************************************/ +/**********************************************\ +* * +* Video broadcasts * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_VIDEOBROADCASTS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_VIDEOBROADCASTS \ @@ -543,11 +541,11 @@ SQLITE_TABLE_VIDEOBROADCASTS,\ UPNP_CLASS_VIDEOBC) - /**********************************************\ - * * - * Audio broadcasts * - * * - \**********************************************/ +/**********************************************\ +* * +* Audio broadcasts * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_AUDIOBROADCASTS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_AUDIOBROADCASTS \ @@ -570,11 +568,11 @@ SQLITE_TABLE_AUDIOBROADCASTS,\ UPNP_CLASS_AUDIOBC) - /**********************************************\ - * * - * Movies * - * * - \**********************************************/ +/**********************************************\ +* * +* Movies * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_MOVIES "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_MOVIES \ @@ -598,11 +596,11 @@ SQLITE_TABLE_MOVIES,\ UPNP_CLASS_MOVIE) - /**********************************************\ - * * - * Photos * - * * - \**********************************************/ +/**********************************************\ +* * +* Photos * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_PHOTOS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_PHOTOS \ @@ -621,11 +619,11 @@ SQLITE_TABLE_PHOTOS,\ UPNP_CLASS_PHOTO) - /**********************************************\ - * * - * Albums * - * * - \**********************************************/ +/**********************************************\ +* * +* Albums * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_ALBUMS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_ALBUMS \ @@ -651,11 +649,11 @@ SQLITE_TABLE_ALBUMS,\ UPNP_CLASS_ALBUM) - /**********************************************\ - * * - * Playlists * - * * - \**********************************************/ +/**********************************************\ +* * +* Playlists * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_PLAYLISTS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_PLAYLISTS \ @@ -683,11 +681,11 @@ SQLITE_TABLE_PLAYLISTS,\ UPNP_CLASS_PLAYLISTCONT) - /**********************************************\ - * * - * Search classes * - * * - \**********************************************/ +/**********************************************\ +* * +* Search classes * +* * +\**********************************************/ #define SQLITE_CREATE_TABLE_SEARCHCLASS "CREATE TABLE IF NOT EXISTS "\ SQLITE_TABLE_SEARCHCLASS \ @@ -798,6 +796,13 @@ class cSQLiteDatabase; +/** + * Result row of a SQL SELECT request + * + * This is a single row of a {\c SQL SELECT} request. + * + * @see cRows + */ class cRow : public cListObject { friend class cSQLiteDatabase; private: @@ -808,11 +813,50 @@ private: cRow(); public: virtual ~cRow(); + /** + * Number of columns in this row + * + * @return the number of rows + */ int Count(){ return this->ColCount; } - bool fetchColumn(cString* Column, cString* Value); - bool fetchColumn(char** Column, char** Value); + /** + * Fetches a Column + * + * This will fetch a column of this row and stores the name of the column + * in the first parameter and the value in the second parameter. + * + * @return returns + * - \bc true, if more columns to come + * - \bc false, if the column is its last in this row. + */ + bool fetchColumn( + cString* Column, /**< The name of the current column */ + cString* Value /**< The value of the current value */ + ); + + /** + * Fetches a Column + * + * This will fetch a column of this row and stores the name of the column + * in the first parameter and the value in the second parameter. + * + * @return returns + * - \bc true, if more columns to come + * - \bc false, if the column is its last in this row. + */ + bool fetchColumn( + char** Column, /**< The name of the current column */ + char** Value /**< The value of the current column */ + ); }; +/** + * Result rows of a SQL SELECT request + * + * Contains the rows of a SQL SELECT request + * + * @see cRow + */ class cRows : public cList<cRow> { friend class cSQLiteDatabase; private: @@ -820,9 +864,29 @@ private: cRows(); public: virtual ~cRows(); - bool fetchRow(cRow** Row); + /** + * Fetches a row from the result + * + * This fetches the next row in the resultset by storing the contents of + * that row in the first parameter. + * + * @return returns + * - \bc true, if more rows to come + * - \bc false, if the row is its last in this resultset. + */ + bool fetchRow( + cRow** Row /**< The Pointer of the row */ + ); }; +/** + * SQLite Database + * + * This is a wrapper class for a SQLite3 database connection + * It supports simple execution functions. + * + * On requests with returns any results a instance of \c cRows* will be created. + */ class cSQLiteDatabase { friend class cStatement; private: @@ -839,17 +903,122 @@ private: static int getResultRow(void* DB, int NumCols, char** Values, char** ColNames); int exec(const char* Statement); public: - static const char* sprintf(const char* Format, ...); + /** + * Prints a SQLite escaped text + * + * Returns a formated text with special characters to escape SQLite special + * characters like "'". Additionally to the well known characters of \a printf + * the following are allowed: + * + * - \bc q, like s, escapes single quotes in strings + * - \bc Q, like q, surrounds the escaped string with additional + * single quotes + * - \bc z, frees the string after reading and coping it + * + * @see sprintf() + * @return the formated string + */ + static const char* sprintf( + const char* Format, /**< The format string */ + ... /**< optional properties which will be passed to sprintf */ + ); virtual ~cSQLiteDatabase(); + /** + * Returns the instance of the database + * + * Returns the instance of the SQLite database. This will create a single + * instance of none is existing on the very first call. A subsequent call + * will return the same instance. + * + * @return the database instance + */ static cSQLiteDatabase* getInstance(); + /** + * Row count of the last result + * + * Returns the row count of the last {\c SQL SELECT} request. + * + * @see cRows + * @return the result row count + */ int getResultCount() const { return this->mRows->Count(); } + /** + * The last \c INSERT RowID + * + * Returns the primary key of the last inserted row. + * This will only work if there are no successive calls to the database. + * + * @return the last insert RowID + */ long getLastInsertRowID() const; + /** + * Result set of the last request + * + * Returns the result rows of the SQL SELECT request. + * This might be NULL, if the last statement was not a SELECT. + * + * @see cRows + * @return the result rows of the last \c SELECT statement. + */ cRows* getResultRows() const { return this->mRows; } - int execStatement(const char* Statement, ...); + /** + * Executes a SQL statement + * + * This will execute the statement in the first parameter. If it is followed + * by any optional parameters it will be formated using the same function as + * in \c cSQLiteDatabase::sprintf(). + * + * \sa cSQLiteDatabase::sprintf(). + * + * @return returns an integer representing + * - \bc -1, in case of an error + * - \bc 0, when the statement was executed successfuly + */ + int execStatement( + const char* Statement , /**< Statement to be executed */ + ... /**< optional parameters passed to the format string */ + ); + /** + * Starts a transaction + * + * This starts a new transaction and commits or rolls back a previous. + * + * @see cSQLiteDatabase::setAutoCommit + * @see cSQLiteDatabase::commitTransaction + */ void startTransaction(); + /** + * Commits a transaction + * + * This function commits the transaction and writes all changes to the + * database + * + * @see cSQLiteDatabase::startTransaction + */ void commitTransaction(); + /** + * Performs a rollback on a transaction + * + * This function performs a rollback. No changes will be made to the + * database + * + * @see cSQLiteDatabase::rollbackTransaction + */ void rollbackTransaction(); - void setAutoCommit(bool Commit=true){ this->mAutoCommit = Commit; } + /** + * Set the commit behavior + * + * This function sets the auto commit behavior on new transactions with + * \sa cSQLiteDatabase::startTransaction. + * + * - \bc true, commits the last transaction before starting a + * new one + * - \bc false, performs a rollback on the old transaction + * + */ + void setAutoCommit( + bool Commit=true /**< Switches the behavior of auto commit */ + ){ this->mAutoCommit = Commit; } }; #endif /* _DATABASE_H */
\ No newline at end of file diff --git a/database/metadata.cpp b/database/metadata.cpp index 3316522..91c512f 100644 --- a/database/metadata.cpp +++ b/database/metadata.cpp @@ -44,7 +44,7 @@ cMediaDatabase::~cMediaDatabase(){ } bool cMediaDatabase::init(){ - MESSAGE("Initializing..."); + MESSAGE(VERBOSE_SDK,"Initializing..."); if(this->prepareDatabase()){ ERROR("Initializing of database failed."); return false; @@ -122,7 +122,7 @@ cUPnPObjectID cMediaDatabase::getNextObjectID(){ int cMediaDatabase::addFastFind(cUPnPClassObject* Object, const char* FastFind){ if(!Object || !FastFind){ - MESSAGE("Invalid fast find parameters"); + MESSAGE(VERBOSE_OBJECTS,"Invalid fast find parameters"); return -1; } @@ -140,7 +140,7 @@ int cMediaDatabase::addFastFind(cUPnPClassObject* Object, const char* FastFind){ cUPnPClassObject* cMediaDatabase::getObjectByFastFind(const char* FastFind){ if(!FastFind) return NULL; - MESSAGE("Try to find Object with identifier %s", FastFind); + MESSAGE(VERBOSE_OBJECTS,"Try to find Object with identifier %s", FastFind); cString Column, Value; if(this->mDatabase->execStatement("SELECT %s FROM %s WHERE %s=%Q", SQLITE_COL_OBJECTID, @@ -166,14 +166,14 @@ cUPnPClassObject* cMediaDatabase::getObjectByFastFind(const char* FastFind){ } cUPnPClassObject* cMediaDatabase::getObjectByID(cUPnPObjectID ID){ - MESSAGE("Try to find Object with ID '%s'", *ID); + MESSAGE(VERBOSE_OBJECTS, "Try to find Object with ID '%s'", *ID); cUPnPClassObject* Object; if((Object = this->mObjects->Get((unsigned int)ID))){ - MESSAGE("Found cached object with ID '%s'", *ID); + MESSAGE(VERBOSE_OBJECTS, "Found cached object with ID '%s'", *ID); } else if((Object = this->mFactory->getObject(ID))){ //this->cacheObject(Object); - MESSAGE("Found object with ID '%s' in database", *ID); + MESSAGE(VERBOSE_OBJECTS, "Found object with ID '%s' in database", *ID); } else { ERROR("No object with such ID '%s'", *ID); @@ -184,14 +184,14 @@ cUPnPClassObject* cMediaDatabase::getObjectByID(cUPnPObjectID ID){ void cMediaDatabase::cacheObject(cUPnPClassObject* Object){ if(this->mObjects->Get((unsigned int)Object->getID())==NULL){ - MESSAGE("Added %s to cache.", *Object->getID()); + MESSAGE(VERBOSE_OBJECTS ,"Added %s to cache.", *Object->getID()); this->mObjects->Add(Object, (unsigned int)Object->getID()); } } int cMediaDatabase::prepareDatabase(){ if(this->getObjectByID(0)==NULL){ - MESSAGE("Creating database structure"); + MESSAGE(VERBOSE_SDK, "Creating database structure"); cUPnPClassContainer* Root = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _(PLUGIN_SHORT_NAME)); Root->setID(0); if(this->mFactory->saveObject(Root)) return -1; @@ -248,7 +248,7 @@ int cMediaDatabase::prepareDatabase(){ } int cMediaDatabase::loadChannels(){ - MESSAGE("Loading channels"); + MESSAGE(VERBOSE_LIVE_TV ,"Loading channels"); cUPnPClassContainer* TV = (cUPnPClassContainer*)this->getObjectByID(3); if(TV){ bool noResource = false; @@ -262,7 +262,7 @@ int cMediaDatabase::loadChannels(){ bool inList = false; tChannelID ChannelID = Channel->GetChannelID(); - MESSAGE("Determine if the channel %s is already listed", *ChannelID.ToString()); + MESSAGE(VERBOSE_LIVE_TV, "Determine if the channel %s is already listed", *ChannelID.ToString()); cUPnPClassVideoBroadcast* ChannelItem = NULL; ChannelItem = (cUPnPClassVideoBroadcast*)this->getObjectByFastFind(ChannelID.ToString()); @@ -271,18 +271,18 @@ int cMediaDatabase::loadChannels(){ if(!inList){ if(Channel->GroupSep()){ - MESSAGE("Skipping group '%s'", Channel->Name()); + MESSAGE(VERBOSE_LIVE_TV, "Skipping group '%s'", Channel->Name()); // Skip channel groups // Channel groups may be supported theoretically. However, DLNA states that a tuner needs // a consecutive list of channels. A simple work-around may be a virtual tuner for each group. } else if(Channel->Vpid()==0){ // TODO: add radio support - MESSAGE("Skipping radio '%s'", Channel->Name()); + MESSAGE(VERBOSE_LIVE_TV, "Skipping radio '%s'", Channel->Name()); } else { noResource = false; - MESSAGE("Adding channel '%s' ID:%s", Channel->Name(), *ChannelID.ToString()); + MESSAGE(VERBOSE_LIVE_TV, "Adding channel '%s' ID:%s", Channel->Name(), *ChannelID.ToString()); ChannelItem = (cUPnPClassVideoBroadcast*)this->mFactory->createObject(UPNP_CLASS_VIDEOBC, Channel->Name()); ChannelItem->setChannelName(Channel->Name()); ChannelItem->setChannelNr(Channel->Number()); @@ -301,7 +301,7 @@ int cMediaDatabase::loadChannels(){ this->mFactory->deleteObject(ChannelItem); return -1; } - MESSAGE("Successfuly added channel"); + MESSAGE(VERBOSE_LIVE_TV, "Successfuly added channel"); } else { // Delete temporarily created object with no resource @@ -310,7 +310,7 @@ int cMediaDatabase::loadChannels(){ } } else { - MESSAGE("Skipping %s, already in database", Channel->Name()); + MESSAGE(VERBOSE_LIVE_TV, "Skipping %s, already in database", Channel->Name()); } } } @@ -318,7 +318,7 @@ int cMediaDatabase::loadChannels(){ } int cMediaDatabase::loadRecordings(){ - MESSAGE("Loading recordings"); + MESSAGE(VERBOSE_RECORDS, "Loading recordings"); cUPnPClassContainer* Records = (cUPnPClassContainer*)this->getObjectByID(4); if(Records){ bool noResource = false; @@ -330,7 +330,7 @@ int cMediaDatabase::loadRecordings(){ // Iterating the records bool inList = false; - MESSAGE("Determine if the channel %s is already listed", Recording->FileName()); + MESSAGE(VERBOSE_RECORDS, "Determine if the channel %s is already listed", Recording->FileName()); cUPnPClassMovie *MovieItem = NULL; @@ -342,7 +342,7 @@ int cMediaDatabase::loadRecordings(){ noResource = false; const cRecordingInfo* RecInfo = Recording->Info(); - MESSAGE("Adding movie '%s' File name:%s", RecInfo->Title(), Recording->FileName()); + MESSAGE(VERBOSE_RECORDS, "Adding movie '%s' File name:%s", RecInfo->Title(), Recording->FileName()); MovieItem = (cUPnPClassMovie*)this->mFactory->createObject(UPNP_CLASS_MOVIE, RecInfo->Title()); MovieItem->setDescription(RecInfo->ShortText()); @@ -366,7 +366,7 @@ int cMediaDatabase::loadRecordings(){ this->mFactory->deleteObject(MovieItem); return -1; } - MESSAGE("Successfuly added movie"); + MESSAGE(VERBOSE_RECORDS, "Successfuly added movie"); } else { // Delete temporarily created object with no resource @@ -374,7 +374,7 @@ int cMediaDatabase::loadRecordings(){ } } else { - MESSAGE("Skipping %s, already in Database", Recording->FileName()); + MESSAGE(VERBOSE_RECORDS, "Skipping %s, already in Database", Recording->FileName()); } } } @@ -386,7 +386,7 @@ void cMediaDatabase::Action(){ while(this->Running()){ if(cSchedules::Modified() >= LastEPGUpdate){ - MESSAGE("Schedule changed. Updating..."); + MESSAGE(VERBOSE_EPG_UPDATES, "Schedule changed. Updating..."); updateChannelEPG(); LastEPGUpdate = cSchedules::Modified(); } @@ -399,19 +399,19 @@ void cMediaDatabase::updateChannelEPG(){ cUPnPClassContainer* TV = (cUPnPClassContainer*)this->getObjectByID(3); if(TV){ // Iterating channels - MESSAGE("Getting schedule..."); + MESSAGE(VERBOSE_EPG_UPDATES, "Getting schedule..."); cSchedulesLock SchedulesLock; const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); cList<cUPnPClassObject>* List = TV->getObjectList(); - MESSAGE("TV folder has %d items", List->Count()); + MESSAGE(VERBOSE_EPG_UPDATES, "TV folder has %d items", List->Count()); for(cUPnPClassVideoBroadcast* ChannelItem = (cUPnPClassVideoBroadcast*)List->First(); ChannelItem; ChannelItem = (cUPnPClassVideoBroadcast*)List->Next(ChannelItem) ){ - MESSAGE("Find channel by number %d", ChannelItem->getChannelNr()); + MESSAGE(VERBOSE_EPG_UPDATES, "Find channel by number %d", ChannelItem->getChannelNr()); cChannel* Channel = Channels.GetByNumber(ChannelItem->getChannelNr()); - MESSAGE("Found channel with ID %s", *Channel->GetChannelID().ToString()); + MESSAGE(VERBOSE_EPG_UPDATES, "Found channel with ID %s", *Channel->GetChannelID().ToString()); const cSchedule* Schedule = Schedules->GetSchedule(Channel); const cEvent* Event = Schedule?Schedule->GetPresentEvent():NULL; @@ -420,10 +420,10 @@ void cMediaDatabase::updateChannelEPG(){ time_t LastEPGChange = Event->StartTime(); time_t LastObjectChange = ChannelItem->modified(); - MESSAGE("Last event start: %s", ctime(&LastEPGChange)); - MESSAGE("Last object modification: %s", ctime(&LastObjectChange)); + MESSAGE(VERBOSE_EPG_UPDATES, "Last event start: %s", ctime(&LastEPGChange)); + MESSAGE(VERBOSE_EPG_UPDATES, "Last object modification: %s", ctime(&LastObjectChange)); if(LastEPGChange >= LastObjectChange){ - MESSAGE("Updating details"); + MESSAGE(VERBOSE_EPG_UPDATES, "Updating details"); if(Event){ ChannelItem->setTitle(Event->Title()?Event->Title():Channel->Name()); @@ -439,11 +439,11 @@ void cMediaDatabase::updateChannelEPG(){ this->mFactory->saveObject(ChannelItem); } else { - MESSAGE("Channel did not change"); + MESSAGE(VERBOSE_EPG_UPDATES, "Channel did not change"); } } else { - MESSAGE("No EPG data"); + MESSAGE(VERBOSE_EPG_UPDATES, "No EPG data"); ChannelItem->setTitle(Channel->Name()); ChannelItem->setLongDescription(NULL); ChannelItem->setDescription(NULL); @@ -452,19 +452,27 @@ void cMediaDatabase::updateChannelEPG(){ } } -int cMediaDatabase::browse(cUPnPResultSet** Results, const char* ID, bool BrowseMetadata, const char* Filter, unsigned int Offset, unsigned int Count, const char* SortCriteria){ +int cMediaDatabase::browse( + OUT cUPnPResultSet** Results, + IN const char* ID, + IN bool BrowseMetadata, + IN const char* Filter, + IN unsigned int Offset, + IN unsigned int Count, + IN const char* SortCriteria +){ *Results = new cUPnPResultSet; (*Results)->mNumberReturned = 0; (*Results)->mTotalMatches = 0; (*Results)->mResult = NULL; - MESSAGE("===== Browsing ====="); - MESSAGE("ID: %s", ID); - MESSAGE("Browse %s", BrowseMetadata?"metadata":"children"); - MESSAGE("Filter: %s", Filter); - MESSAGE("Offset: %d", Offset); - MESSAGE("Count: %d", Count); - MESSAGE("Sort: %s", SortCriteria); + MESSAGE(VERBOSE_DIDL, "===== Browsing ====="); + MESSAGE(VERBOSE_DIDL, "ID: %s", ID); + MESSAGE(VERBOSE_DIDL, "Browse %s", BrowseMetadata?"metadata":"children"); + MESSAGE(VERBOSE_DIDL, "Filter: %s", Filter); + MESSAGE(VERBOSE_DIDL, "Offset: %d", Offset); + MESSAGE(VERBOSE_DIDL, "Count: %d", Count); + MESSAGE(VERBOSE_DIDL, "Sort: %s", SortCriteria); cUPnPObjectID ObjectID = atoi(ID); @@ -498,7 +506,7 @@ int cMediaDatabase::browse(cUPnPResultSet** Results, const char* ID, bool Browse if(SortCriterias){ for(cSortCrit* SortBy = SortCriterias->First(); SortBy ; SortBy = SortCriterias->Next(SortBy)){ - MESSAGE("Sorting by %s %s", SortBy->Property, SortBy->SortDescending?"ascending":"descending"); + MESSAGE(VERBOSE_DIDL, "Sorting by %s %s", SortBy->Property, SortBy->SortDescending?"ascending":"descending"); Children->SortBy(SortBy->Property, SortBy->SortDescending); } } @@ -507,7 +515,7 @@ int cMediaDatabase::browse(cUPnPResultSet** Results, const char* ID, bool Browse if(Count==0) Count = Container->getChildCount(); while(Offset-- && (Child = Children->Next(Child))){} for(; Count && Child ; Child = Children->Next(Child), Count--){ - MESSAGE("Appending %s to didl", Child->getTitle()); + MESSAGE(VERBOSE_DIDL, "Appending %s to didl", Child->getTitle()); ixmlNode_appendChild(Root, Child->createDIDLFragment(DIDLDoc, FilterList)); (*Results)->mNumberReturned++; } diff --git a/database/metadata.h b/database/metadata.h index 6e3732c..4868231 100644 --- a/database/metadata.h +++ b/database/metadata.h @@ -16,17 +16,25 @@ #include "object.h" #include "resources.h" +/** + * The result set of a request + * + * This contains the results of a previous \e Browse or \e Search request. + */ struct cUPnPResultSet { - int mNumberReturned; - int mTotalMatches; - const char* mResult; -}; - -struct cSearchCriteria { - const char* Property; - bool Descending; + int mNumberReturned; ///< The number of returned matches + int mTotalMatches; ///< The total amount of matches + const char* mResult; ///< The DIDL-Lite fragment }; +/** + * The media database + * + * This class is the global object manager. It holds every object in a local cache. + * Only this class is allowed to create new objects. + * + * @see cUPnPClassObject + */ class cMediaDatabase : public cThread { friend class cUPnPServer; friend class cUPnPObjectMediator; @@ -47,15 +55,130 @@ private: void updateSystemID(); virtual void Action(); public: + /** + * Returns the SystemUpdateID + * + * This returns the \e SystemUpdateID. This changes whenever anything changed + * within the content directory. This value will be sent through the UPnP + * network every 2 seconds. + * + * @return the SystemUpdateID + */ unsigned int getSystemUpdateID(); + /** + * Returns a CSV list with ContainerUpdateIDs + * + * This list contains an unordered list of ordered pairs of ContainerID and + * its ContainerUpdateID. It contains only recent changes which are not yet + * beeing evented. This means that evented updates will be removed from list. + * + * @return CSV list of ContainerUpdateIDs + */ const char* getContainerUpdateIDs(); + /** + * Constructor + * + * This creates an instance of the media database. + */ cMediaDatabase(); virtual ~cMediaDatabase(); - int addFastFind(cUPnPClassObject* Object, const char* FastFind); - cUPnPClassObject* getObjectByFastFind(const char* FastFind); - cUPnPClassObject* getObjectByID(cUPnPObjectID ID); - int browse(OUT cUPnPResultSet** Results, IN const char* ID, IN bool BrowseMetadata, IN const char* Filter = "*", IN unsigned int Offset = 0, IN unsigned int Count = 0, IN const char* SortCriteria = ""); - int search(OUT cUPnPResultSet** Results, IN const char* ID, IN const char* Search, IN const char* Filter = "*", IN unsigned int Offset = 0, IN unsigned int Count = 0, IN const char* SortCriteria = ""); + /** + * Add a Fastfind + * + * This creates a \e Fastfind entry. It is a string which can be used to + * relocate a objectID. Usually this is a file name or another ID with which + * the related object can be found. + * + * @return returns + * - \bc -1, if the creation was successful + * - \bc 0, otherwise + */ + int addFastFind( + cUPnPClassObject* Object, ///< the object, which should be registered + const char* FastFind ///< the string with which the object shall be + ///< relocated + ); + /** + * Finds a object by Fastfind + * + * This returns the object via the \e Fastfind string. The object must be + * previosly registered via \c cMediaDatabase::addFastFind(). + * + * It tries to find the object in the internal object cache. If this fails, + * the object will be loaded from the database. + * + * @see cMediaDatabase::addFastFind + * @return The object associated with FastFind + */ + cUPnPClassObject* getObjectByFastFind( + const char* FastFind ///< the string with which the object shall be + ///< relocated + ); + /** + * Finds a object by its ObjectID + * + * This returns the object via its \e ObjectID. + * + * It tries to find the object in the internal object cache. If this fails, + * the object will be loaded from the database. + * + * @return The object associated with FastFind + */ + cUPnPClassObject* getObjectByID( + cUPnPObjectID ID ///< The ObjectID of the requested object + ); + /** + * Performs a browse on the database + * + * This performs a browse request on the database and returns a structure + * containing the matching count and DIDL-Lite fragement which is sent to + * the control point. + * + * @return returns an integer representing one of the following: + * - \bc UPNP_CDS_E_INVALID_SORT_CRITERIA, when the sort criteria is malformed + * - \bc UPNP_CDS_E_CANT_PROCESS_REQUEST, when there is an internal error while + * processing the request + * - \bc UPNP_CDS_E_NO_SUCH_OBJECT, when the requested ObjectID does not exist + * - \bc UPNP_SOAP_E_ACTION_FAILED, when the action failed due any reasons + * - \bc UPNP_E_SUCCESS, if the request was successful + */ + int browse( + OUT cUPnPResultSet** Results, ///< the result of the request + IN const char* ID, ///< the objectID of the request + IN bool BrowseMetadata, ///< \b true to browse metadata, \b false otherwise + IN const char* Filter = "*", ///< the filter applied to the returned metadata + IN unsigned int Offset = 0, ///< the starting offset + IN unsigned int Count = 0, ///< maximum count returned + IN const char* SortCriteria = "" ///< sorts the results before returning them + ); + /** + * Performs a search on the database + * + * This performs a search request on the database and returns a structure + * containing the matching count and DIDL-Lite fragement which is sent to + * the control point. + * + * @note + * The submitted ID must be a ContainerID. Searches are performed only + * in this container. + * + * @return returns an integer representing one of the following: + * - \bc UPNP_CDS_E_INVALID_SORT_CRITERIA, when the sort criteria is malformed + * - \bc UPNP_CDS_E_CANT_PROCESS_REQUEST, when there is an internal error while + * processing the request + * - \bc UPNP_CDS_E_NO_SUCH_OBJECT, when the requested ObjectID does not exist + * - \bc UPNP_SOAP_E_ACTION_FAILED, when the action failed due any reasons + * - \bc UPNP_E_SUCCESS, if the request was successful + */ + int search( + OUT cUPnPResultSet** Results, ///< the result of the request + IN const char* ID, ///< the ContainerID + IN const char* Search, ///< the search string + IN const char* Filter = "*", ///< the filter applied to the returned metadata + IN unsigned int Offset = 0, ///< the starting offset + IN unsigned int Count = 0, ///< maximum count returned + IN const char* SortCriteria = "" ///< sorts the results before returning them + ); }; #endif /* _METADATA_H */ diff --git a/database/object.cpp b/database/object.cpp index 94d9415..f6875f9 100644 --- a/database/object.cpp +++ b/database/object.cpp @@ -150,7 +150,7 @@ void cUPnPClassObject::clearSortCriteria(){ } int cUPnPClassObject::setID(cUPnPObjectID ID){ - MESSAGE("Set ID from %s to %s", *this->getID(),*ID); + MESSAGE(VERBOSE_MODIFICATIONS, "Set ID from %s to %s", *this->getID(),*ID); if((int)ID < 0){ ERROR("Invalid object ID '%s'",*ID); return -1; @@ -162,7 +162,7 @@ int cUPnPClassObject::setID(cUPnPObjectID ID){ int cUPnPClassObject::setParent(cUPnPClassContainer* Parent){ if(Parent==NULL){ - MESSAGE("Object '%s' elected as root object", *this->getID()); + MESSAGE(VERBOSE_MODIFICATIONS, "Object '%s' elected as root object", *this->getID()); } // unregister from old parent if(this->mParent && Parent != this->mParent){ @@ -316,7 +316,7 @@ bool cUPnPClassObject::setProperty(const char* Property, const char* Value){ } int cUPnPClassObject::addResource(cUPnPResource* Resource){ - MESSAGE("Adding resource #%d", Resource->getID()); + MESSAGE(VERBOSE_MODIFICATIONS, "Adding resource #%d", Resource->getID()); if(!Resource){ ERROR("No resource"); return -1; @@ -374,10 +374,10 @@ bool cUPnPClassItem::setProperty(const char* Property, const char* Value){ IXML_Node* cUPnPClassItem::createDIDLFragment(IXML_Document* Document, cStringList* Filter){ this->mDIDLFragment = Document; - MESSAGE("==(%s)= %s =====", *this->getID(), this->getTitle()); - MESSAGE("ParentID: %s", *this->getParentID()); - MESSAGE("Restricted: %s", this->isRestricted()?"1":"0"); - MESSAGE("Class: %s", this->getClass()); + MESSAGE(VERBOSE_DIDL, "==(%s)= %s =====", *this->getID(), this->getTitle()); + MESSAGE(VERBOSE_DIDL, "ParentID: %s", *this->getParentID()); + MESSAGE(VERBOSE_DIDL, "Restricted: %s", this->isRestricted()?"1":"0"); + MESSAGE(VERBOSE_DIDL, "Class: %s", this->getClass()); IXML_Node* Didl = ixmlNode_getFirstChild((IXML_Node*) this->mDIDLFragment); @@ -404,13 +404,13 @@ IXML_Node* cUPnPClassItem::createDIDLFragment(IXML_Document* Document, cStringLi // if(Filter==NULL || Filter->Find(UPNP_PROP_REFERENCEID)) ixmlAddProperty(this->mDIDLFragment, eItem, UPNP_PROP_REFERENCEID, *this->getReferenceID()); for(cUPnPResource* Resource = this->getResources()->First(); Resource; Resource = this->getResources()->Next(Resource)){ - MESSAGE("Resource: %s", Resource->getResource()); - MESSAGE("Protocolinfo: %s", Resource->getProtocolInfo()); + MESSAGE(VERBOSE_DIDL, "Resource: %s", Resource->getResource()); + MESSAGE(VERBOSE_DIDL, "Protocolinfo: %s", Resource->getProtocolInfo()); cString URLBase = cString::sprintf("http://%s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort()); cString ResourceURL = cString::sprintf("%s%s/get?resId=%d", *URLBase, UPNP_DIR_SHARES, Resource->getID()); - MESSAGE("Resource-URI: %s", *ResourceURL); + MESSAGE(VERBOSE_DIDL, "Resource-URI: %s", *ResourceURL); IXML_Element* eRes = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_RESOURCE); IXML_Node* Res = ixmlDocument_createTextNode(this->mDIDLFragment, *ResourceURL); @@ -451,10 +451,10 @@ cUPnPClassContainer::~cUPnPClassContainer(){ IXML_Node* cUPnPClassContainer::createDIDLFragment(IXML_Document* Document, cStringList* Filter){ this->mDIDLFragment = Document; - MESSAGE("===(%s)= %s =====", *this->getID(), this->getTitle()); - MESSAGE("ParentID: %s", *this->getParentID()); - MESSAGE("Restricted: %s", this->isRestricted()?"1":"0"); - MESSAGE("Class: %s", this->getClass()); + MESSAGE(VERBOSE_DIDL, "===(%s)= %s =====", *this->getID(), this->getTitle()); + MESSAGE(VERBOSE_DIDL, "ParentID: %s", *this->getParentID()); + MESSAGE(VERBOSE_DIDL, "Restricted: %s", this->isRestricted()?"1":"0"); + MESSAGE(VERBOSE_DIDL, "Class: %s", this->getClass()); IXML_Node* Didl = ixmlNode_getFirstChild((IXML_Node*) this->mDIDLFragment); IXML_Element* eItem = ixmlDocument_createElement(this->mDIDLFragment, "container"); @@ -521,7 +521,7 @@ bool cUPnPClassContainer::getProperty(const char* Property, char** Value) const } void cUPnPClassContainer::addObject(cUPnPClassObject* Object){ - MESSAGE("Adding object (ID:%s) to container (ID:%s)", *Object->getID(), *this->getID()); + MESSAGE(VERBOSE_MODIFICATIONS, "Adding object (ID:%s) to container (ID:%s)", *Object->getID(), *this->getID()); Object->setParent(this); this->mChildren->Add(Object); this->mChildrenID->Add(Object, (unsigned int)Object->getID()); @@ -531,11 +531,11 @@ void cUPnPClassContainer::removeObject(cUPnPClassObject* Object){ this->mChildrenID->Del(Object, (unsigned int)Object->getID()); this->mChildren->Del(Object, false); Object->mParent = NULL; - MESSAGE("Removed object (ID:%s) from container (ID:%s)", *Object->getID(), *this->getID()); + MESSAGE(VERBOSE_MODIFICATIONS, "Removed object (ID:%s) from container (ID:%s)", *Object->getID(), *this->getID()); } cUPnPClassObject* cUPnPClassContainer::getObject(cUPnPObjectID ID) const { - MESSAGE("Getting object (ID:%s)", *ID); + MESSAGE(VERBOSE_METADATA, "Getting object (ID:%s)", *ID); if((int)ID < 0){ ERROR("Invalid object ID"); return NULL; @@ -1011,9 +1011,9 @@ void cUPnPObjectFactory::registerMediator(const char* UPnPClass, cMediatorInterf ERROR("Mediator is undefined"); return; } - MESSAGE("Registering mediator for class '%s'", UPnPClass); + MESSAGE(VERBOSE_SDK, "Registering mediator for class '%s'", UPnPClass); this->mMediators[UPnPClass] = Mediator; - MESSAGE("Now %d mediators registered", this->mMediators.size()); + MESSAGE(VERBOSE_SDK, "Now %d mediators registered", this->mMediators.size()); return; } @@ -1027,10 +1027,10 @@ void cUPnPObjectFactory::unregisterMediator(const char* UPnPClass, bool freeMedi ERROR("No such mediator found for class '%s'", UPnPClass); return; } - MESSAGE("Unregistering mediator for class '%s'", UPnPClass); + MESSAGE(VERBOSE_SDK, "Unregistering mediator for class '%s'", UPnPClass); this->mMediators.erase(MediatorIterator); if(freeMediator) delete MediatorIterator->second; - MESSAGE("Now %d mediators registered", this->mMediators.size()); + MESSAGE(VERBOSE_SDK, "Now %d mediators registered", this->mMediators.size()); return; } @@ -1057,7 +1057,7 @@ cMediatorInterface* cUPnPObjectFactory::findMediatorByID(cUPnPObjectID ID){ cMediatorInterface* cUPnPObjectFactory::findMediatorByClass(const char* Class){ if(!Class){ ERROR("No class specified"); return NULL; } - MESSAGE("Searching for mediator '%s' in %d mediators", Class, this->mMediators.size()); + MESSAGE(VERBOSE_SQL, "Searching for mediator '%s' in %d mediators", Class, this->mMediators.size()); tMediatorMap::iterator MediatorIterator = this->mMediators.find(Class); if(MediatorIterator==this->mMediators.end()){ ERROR("No matching mediator for class '%s'",Class); @@ -1212,7 +1212,7 @@ cUPnPClassObject* cUPnPObjectMediator::getObject(cUPnPObjectID){ WARNING("Gettin cUPnPClassObject* cUPnPObjectMediator::createObject(const char*, bool){ WARNING("Getting instance of class 'Object' forbidden"); return NULL; } int cUPnPObjectMediator::objectToDatabase(cUPnPClassObject* Object){ - MESSAGE("Updating object #%s", *Object->getID()); + MESSAGE(VERBOSE_MODIFICATIONS, "Updating object #%s", *Object->getID()); cString Format = "UPDATE %s SET %s WHERE %s='%s'"; //cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);"; cString Set=NULL; @@ -1261,41 +1261,47 @@ int cUPnPObjectMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectI } while(Row->fetchColumn(&Column, &Value)){ if(!strcasecmp(Column, SQLITE_COL_OBJECTID)){ - if(Object->setID(atoi(Value))){ + if(!*Value || Object->setID(atoi(Value))){ ERROR("Error while setting object ID"); return -1; } this->mMediaDatabase->cacheObject(Object); } else if(!strcasecmp(Column, SQLITE_COL_PARENTID)){ - cUPnPObjectID RefID = atoi(Value); - cUPnPClassContainer* ParentObject; - if(RefID == -1){ - ParentObject = NULL; + if(*Value){ + cUPnPObjectID RefID = atoi(Value); + cUPnPClassContainer* ParentObject; + if(RefID == -1){ + ParentObject = NULL; + } + else { + ParentObject = (cUPnPClassContainer*)this->mMediaDatabase->getObjectByID(RefID); + if(!ParentObject){ + ERROR("No such parent with ID '%s' found.",*RefID); + return -1; + } + } + Object->setParent(ParentObject); } else { - ParentObject = (cUPnPClassContainer*)this->mMediaDatabase->getObjectByID(RefID); - if(!ParentObject){ - ERROR("No such parent with ID '%s' found.",*RefID); - return -1; - } + ERROR("Invalid parent ID"); + return -1; } - Object->setParent(ParentObject); } else if(!strcasecmp(Column, SQLITE_COL_CLASS)){ - if(Object->setClass(Value)){ + if(!*Value || Object->setClass(Value)){ ERROR("Error while setting class"); return -1; } } else if(!strcasecmp(Column, SQLITE_COL_TITLE)){ - if(Object->setTitle(Value)){ + if(!*Value || Object->setTitle(Value)){ ERROR("Error while setting title"); return -1; } } else if(!strcasecmp(Column, SQLITE_COL_RESTRICTED)){ - if(Object->setRestricted(atoi(Value)==1?true:false)){ + if(!*Value || Object->setRestricted(atoi(Value)==1?true:false)){ ERROR("Error while setting restriction"); return -1; } @@ -1307,7 +1313,7 @@ int cUPnPObjectMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectI } } else if(!strcasecmp(Column, SQLITE_COL_WRITESTATUS)){ - if(Object->setWriteStatus(atoi(Value))){ + if(*Value && Object->setWriteStatus(atoi(Value))){ ERROR("Error while setting write status"); return -1; } @@ -1369,7 +1375,7 @@ int cUPnPItemMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID } Rows = this->mDatabase->getResultRows(); if(!Rows->fetchRow(&Row)){ - MESSAGE("No item properties found"); + MESSAGE(VERBOSE_SQL, "No item properties found"); return 0; } while(Row->fetchColumn(&Column, &Value)){ @@ -1393,14 +1399,14 @@ int cUPnPItemMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID } cUPnPClassItem* cUPnPItemMediator::getObject(cUPnPObjectID ID){ - MESSAGE("Getting Item with ID '%s'",*ID); + MESSAGE(VERBOSE_METADATA, "Getting Item with ID '%s'",*ID); cUPnPClassItem* Object = new cUPnPClassItem; if(this->databaseToObject(Object, ID)) return NULL; return Object; } cUPnPClassItem* cUPnPItemMediator::createObject(const char* Title, bool Restricted){ - MESSAGE("Creating Item '%s'",Title); + MESSAGE(VERBOSE_MODIFICATIONS, "Creating Item '%s'",Title); cUPnPClassItem* Object = new cUPnPClassItem; if(this->initializeObject(Object, UPNP_CLASS_ITEM, Title, Restricted)) return NULL; return Object; @@ -1468,7 +1474,7 @@ int cUPnPContainerMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObje } Rows = this->mDatabase->getResultRows(); if(!Rows->fetchRow(&Row)){ - MESSAGE("No item properties found"); + MESSAGE(VERBOSE_SQL, "No item properties found"); return 0; } while(Row->fetchColumn(&Column, &Value)){ @@ -1532,14 +1538,14 @@ int cUPnPContainerMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObje } cUPnPClassContainer* cUPnPContainerMediator::createObject(const char* Title, bool Restricted){ - MESSAGE("Creating Container '%s'",Title); + MESSAGE(VERBOSE_MODIFICATIONS, "Creating Container '%s'",Title); cUPnPClassContainer* Object = new cUPnPClassContainer; if(this->initializeObject(Object, UPNP_CLASS_CONTAINER, Title, Restricted)) return NULL; return Object; } cUPnPClassContainer* cUPnPContainerMediator::getObject(cUPnPObjectID ID){ - MESSAGE("Getting Container with ID '%s'",*ID); + MESSAGE(VERBOSE_METADATA, "Getting Container with ID '%s'",*ID); cUPnPClassContainer* Object = new cUPnPClassContainer; if(this->databaseToObject(Object, ID)) return NULL; return Object; @@ -1555,14 +1561,14 @@ cUPnPVideoItemMediator::cUPnPVideoItemMediator(cMediaDatabase* MediaDatabase) : cUPnPItemMediator(MediaDatabase){} cUPnPClassVideoItem* cUPnPVideoItemMediator::createObject(const char* Title, bool Restricted){ - MESSAGE("Creating Video item '%s'",Title); + MESSAGE(VERBOSE_MODIFICATIONS, "Creating Video item '%s'",Title); cUPnPClassVideoItem* Object = new cUPnPClassVideoItem; if(this->initializeObject(Object, UPNP_CLASS_VIDEO, Title, Restricted)) return NULL; return Object; } cUPnPClassVideoItem* cUPnPVideoItemMediator::getObject(cUPnPObjectID ID){ - MESSAGE("Getting Video item with ID '%s'",*ID); + MESSAGE(VERBOSE_METADATA, "Getting Video item with ID '%s'",*ID); cUPnPClassVideoItem* Object = new cUPnPClassVideoItem; if(this->databaseToObject(Object, ID)) return NULL; return Object; @@ -1618,7 +1624,7 @@ int cUPnPVideoItemMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObje } Rows = this->mDatabase->getResultRows(); if(!Rows->fetchRow(&Row)){ - MESSAGE("No item properties found"); + MESSAGE(VERBOSE_SQL, "No item properties found"); return 0; } while(Row->fetchColumn(&Column, &Value)){ @@ -1696,14 +1702,14 @@ cUPnPVideoBroadcastMediator::cUPnPVideoBroadcastMediator(cMediaDatabase* MediaDa cUPnPVideoItemMediator(MediaDatabase){} cUPnPClassVideoBroadcast* cUPnPVideoBroadcastMediator::createObject(const char* Title, bool Restricted){ - MESSAGE("Creating Video broadcast '%s'",Title); + MESSAGE(VERBOSE_MODIFICATIONS, "Creating Video broadcast '%s'",Title); cUPnPClassVideoBroadcast* Object = new cUPnPClassVideoBroadcast; if(this->initializeObject(Object, UPNP_CLASS_VIDEOBC, Title, Restricted)) return NULL; return Object; } cUPnPClassVideoBroadcast* cUPnPVideoBroadcastMediator::getObject(cUPnPObjectID ID){ - MESSAGE("Getting Video broadcast with ID '%s'",*ID); + MESSAGE(VERBOSE_METADATA, "Getting Video broadcast with ID '%s'",*ID); cUPnPClassVideoBroadcast* Object = new cUPnPClassVideoBroadcast; if(this->databaseToObject(Object, ID)) return NULL; return Object; @@ -1753,7 +1759,7 @@ int cUPnPVideoBroadcastMediator::databaseToObject(cUPnPClassObject* Object, cUPn } Rows = this->mDatabase->getResultRows(); if(!Rows->fetchRow(&Row)){ - MESSAGE("No item properties found"); + MESSAGE(VERBOSE_SQL, "No item properties found"); return 0; } while(Row->fetchColumn(&Column, &Value)){ @@ -1795,14 +1801,14 @@ cUPnPMovieMediator::cUPnPMovieMediator(cMediaDatabase* MediaDatabase) : cUPnPVideoItemMediator(MediaDatabase){} cUPnPClassMovie* cUPnPMovieMediator::createObject(const char* Title, bool Restricted){ - MESSAGE("Creating movie '%s'",Title); + MESSAGE(VERBOSE_MODIFICATIONS, "Creating movie '%s'",Title); cUPnPClassMovie* Object = new cUPnPClassMovie; if(this->initializeObject(Object, UPNP_CLASS_MOVIE, Title, Restricted)) return NULL; return Object; } cUPnPClassMovie* cUPnPMovieMediator::getObject(cUPnPObjectID ID){ - MESSAGE("Getting movie with ID '%s'",*ID); + MESSAGE(VERBOSE_METADATA, "Getting movie with ID '%s'",*ID); cUPnPClassMovie* Object = new cUPnPClassMovie; if(this->databaseToObject(Object, ID)) return NULL; return Object; @@ -1850,7 +1856,7 @@ int cUPnPMovieMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID } Rows = this->mDatabase->getResultRows(); if(!Rows->fetchRow(&Row)){ - MESSAGE("No item properties found"); + MESSAGE(VERBOSE_SQL, "No item properties found"); return 0; } while(Row->fetchColumn(&Column, &Value)){ diff --git a/database/object.h b/database/object.h index a38098d..245a617 100644 --- a/database/object.h +++ b/database/object.h @@ -17,37 +17,112 @@ #include <vector> #include <upnp/ixml.h> +/** + * UPnP Object ID + * + * This is a UPnP Object ID representation. + */ struct cUPnPObjectID { - int _ID; + int _ID; ///< The UPnP Object ID + /** + * Constructor + * + * Creates invalid ID + */ cUPnPObjectID():_ID(-1){} - cUPnPObjectID(long ID){ _ID = (int)ID; } - cUPnPObjectID(int ID){ _ID = ID; } - cUPnPObjectID &operator=(long ID){ _ID = ID; return *this; } - cUPnPObjectID &operator=(int ID){ _ID = ID; return *this; } - cUPnPObjectID &operator=(const cUPnPObjectID& ID){ if(this != &ID){ _ID = ID._ID; } return *this; } + /** + * Constructor + * + * Creates from long integer + */ + cUPnPObjectID( + long ID ///< new ID + ){ _ID = (int)ID; } + /** + * Constructor + * + * Creates from integer + */ + cUPnPObjectID( + int ID ///< new ID + ){ _ID = ID; } + /** Set the object ID */ + cUPnPObjectID &operator=( + long ID ///< new ID + ){ _ID = ID; return *this; } + /** @overload cUPnPObjectID &operator=(long ID) */ + cUPnPObjectID &operator=( + int ID ///< new ID + ){ _ID = ID; return *this; } + /** @overload cUPnPObjectID &operator=(long ID) */ + cUPnPObjectID &operator=( + const cUPnPObjectID& ID ///< new ID + ){ if(this != &ID){ _ID = ID._ID; } return *this; } + /** Pre increment the ID */ cUPnPObjectID &operator++(){ _ID++; return *this; } + /** Post increment the ID */ cUPnPObjectID operator++(int){ cUPnPObjectID old = *this; _ID++; return old; } + /** Post decrement the ID */ cUPnPObjectID operator--(int){ cUPnPObjectID old = *this; _ID--; return old; } + /** Pre decrement the ID */ cUPnPObjectID &operator--(){ _ID--; return *this; } - bool operator!=(long ID){ return _ID != ID; } - bool operator==(long ID){ return _ID == ID; } - bool operator!=(int ID){ return _ID != ID; } - bool operator==(int ID){ return _ID == ID; } - bool operator!=(const cUPnPObjectID& ID){ return *this == ID; } - bool operator==(const cUPnPObjectID& ID){ return *this == ID; } + /** Not equal */ + bool operator!=( + long ID ///< compare with this ID + ){ return _ID != ID; } + /** Equal */ + bool operator==( + long ID ///< compare with this ID + ){ return _ID == ID; } + /** @overload bool operator!=(long ID) */ + bool operator!=( + int ID ///< compare with this ID + ){ return _ID != ID; } + /** @overload bool operator==(long ID) */ + bool operator==( + int ID ///< compare with this ID + ){ return _ID == ID; } + /** @overload bool operator!=(long ID) */ + bool operator!=( + const cUPnPObjectID& ID ///< compare with this ID + ){ return *this == ID; } + /** @overload bool operator==(long ID) */ + bool operator==( + const cUPnPObjectID& ID ///< compare with this ID + ){ return *this == ID; } + /** Casts to unsigned int */ operator unsigned int(){ return (unsigned int)_ID; } + /** Casts to int */ operator int(){ return _ID; } + /** Casts to long */ operator long(){ return (long)_ID; } + /** Casts to string */ const char* operator*(){ char* buf; return asprintf(&buf,"%d",_ID)?buf:NULL; } }; +/** + * Structure of a UPnP Class + * + * This represents a UPnP Class + */ struct cClass { - cString ID; - bool includeDerived; + cString ID; ///< The upnp class ID + bool includeDerived; ///< flag, to indicate if derived classes are allowed + /** + * Compares two classes + * + * @param cmp the other class to compare with + */ bool operator==(const cClass &cmp){ return (!strcasecmp(cmp.ID,ID) && includeDerived==cmp.includeDerived); } + /*! @copydoc operator==(const cClass &cmp) */ bool operator!=(const cClass &cmp){ return !(*this==cmp); } }; +/** + * UPnP Resource + * + * This contains all details about a resource + */ class cUPnPResource : public cListObject { friend class cUPnPResourceMediator; friend class cUPnPResources; @@ -70,21 +145,137 @@ private: unsigned int mColorDepth; cUPnPResource(); public: + /** + * Get resource ID + * + * Gets the resource ID + * + * @return the resource ID + */ unsigned int getID() const { return this->mResourceID; } + /** + * Get the resources + * + * Returns the resource. This is in most cases the file name or resource locator + * where to find the resource + * + * @return the resource string + */ const char* getResource() const { return this->mResource; } + /** + * Get the duration + * + * Returns a date time string with the duration of the resource + * + * @return the duration of the resource + */ const char* getDuration() const { return this->mDuration; } + /** + * Get the resolution + * + * Returns the resolution string with the pattern width x height in pixels + * + * @return the resolution of the resource + */ const char* getResolution() const { return this->mResolution; } + /** + * Get the protocol info + * + * This returns the protocol info field of a resource + * + * @return the protocol info string + */ const char* getProtocolInfo() const { return this->mProtocolInfo; } + /** + * Get the content type + * + * Returns the mime type of the content of the resource + * + * @return the content type of the resource + */ const char* getContentType() const { return this->mContentType; } + /** + * Get the import URI + * + * This returns the import URI where the resource was located before importing + * it + * + * @return the import URI + */ const char* getImportURI() const { return this->mImportURI; } + /** + * Get the resource type + * + * This returns the resource type of the resource. + * + * @return the resource type + */ int getResourceType() const { return this->mResourceType; } + /** + * Get the size + * + * Returns the resource size or -1 if its unknown + * + * @return the resource size or -1 if unknown + */ unsigned long getSize() const { return this->mSize; } + /** + * Get the file size + * + * Returns the file size in bytes of the resource or 0 if its unknown or a + * stream + * + * @return the file size + */ off64_t getFileSize() const; + /** + * Get the last modification + * + * This returns the timestamp of the last modification to the file. If it + * is a stream, then its the current time. + * + * @return the timestamp with the last modification of the resource + */ time_t getLastModification() const; + /** + * Get the bitrate + * + * This returns the bitrate of the resource in bits per second. + * + * @return the bitrate of the resource + */ unsigned int getBitrate() const { return this->mBitrate; } + /** + * Get the sample frequency + * + * Returns the sample frequency in samples per second. + * + * @return the sample frequency of the resource + */ unsigned int getSampleFrequency() const { return this->mSampleFrequency; } + /** + * Get the bits per sample + * + * Returns the number of bits per sample. + * + * @return the bits per sample of the resource + */ unsigned int getBitsPerSample() const { return this->mBitsPerSample; } + /** + * Get number of audio channels + * + * Returns the number of audio channels of the audio stream in a video + * + * @return the number of audio channels + */ unsigned int getNrAudioChannels() const { return this->mNrAudioChannels; } + /** + * Get the color depth + * + * Returns the color depth of the resource in pits per pixel + * + * @return the color depth of the resource + */ unsigned int getColorDepth() const { return this->mColorDepth; } }; @@ -93,79 +284,398 @@ class cUPnPObjectMediator; class cUPnPContainerMediator; class cUPnPClassContainer; +/** + * List of UPnP Objects + * + * This is a cList of UPnP Objects + * The list can be sorted by using a specific property + */ class cUPnPObjects : public cList<cUPnPClassObject> { public: cUPnPObjects(); virtual ~cUPnPObjects(); - void SortBy(const char* Property, bool Descending = false); + /** + * Sorts the list + * + * This sorts the list by a specific property and a certain direction + */ + void SortBy( + const char* Property, ///< the property used for sorting + bool Descending = false ///< the direction of the sort + ); }; +/** + * The UPnP class Object + * + * This is a UPnP class Object representation with all its properties. + */ class cUPnPClassObject : public cListObject { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPClassContainer; private: cUPnPObjectID mLastID; - bool mDeleted; // is this Objected marked as deleted + bool mDeleted; // is this Objected marked as deleted, NOT used yet. protected: - time_t mLastModified; - cUPnPObjectID mID; // The object ID - cUPnPClassObject* mParent; - cString mClass; // Class (Who am I?) - cString mTitle; // Object title - cString mCreator; // Creator of this object - bool mRestricted; // Ability of changing metadata? - int mWriteStatus; // Ability of writing resources? - cList<cUPnPResource>* mResources; // The resources of this object - cHash<cUPnPResource>* mResourcesID; - IXML_Document* mDIDLFragment; - cString mSortCriteria; - bool mSortDescending; + time_t mLastModified; ///< The last modification of this property + cUPnPObjectID mID; ///< The object ID + cUPnPClassObject* mParent; ///< The parent object + cString mClass; ///< Class (Who am I?) + cString mTitle; ///< Object title + cString mCreator; ///< Creator of this object + bool mRestricted; ///< Ability of changing metadata? + int mWriteStatus; ///< Ability of writing resources? + cList<cUPnPResource>* mResources; ///< The resources of this object + cHash<cUPnPResource>* mResourcesID; ///< The resources of this object as hashmap + IXML_Document* mDIDLFragment; ///< The DIDL fragment of the object + cString mSortCriteria; ///< The sort criteria to sort with + bool mSortDescending; ///< The direction of the sort cUPnPClassObject(); + /** + * Set the Object ID + * + * This is only allowed by mediators and the media database. Manually editing + * the object ID may result in unpredictable behavior. + * + * @param ID the ObjectID of this object + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + */ int setID(cUPnPObjectID ID); + /** + * Set the Parent Object + * + * This is only allowed by mediators and the media database. Manually editing + * the parent may result in unpredictable behavior. + * + * @param Parent the parent of this object + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + */ int setParent(cUPnPClassContainer* Parent); + /** + * Set the object class + * + * This is only allowed by mediators and the media database. Manually editing + * the object class may result in unpredictable behavior. + * + * @param Class the class of this object + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + */ int setClass(const char* Class); + /** + * Set the modification time + * + * This sets the last modification time to the current timestamp. This is + * used to indicate when the object was updated the last time. + */ void setModified(void){ this->mLastModified = time(NULL); } public: + /** + * Last modified + * + * Returns when the object was modified the last time. + * + * @return last modification timestamp + */ time_t modified() const { return this->mLastModified; } virtual ~cUPnPClassObject(); + /** + * Compares a object + * + * This compares a given object with this object + * It uses the SortCriteria to compare them. + * + * @return returns + * - \bc >0, if the object comes after this one + * - \bc 0, if the objects have the same property + * - \bc <0, if the object comes before this one + * @param ListObject the object to compare with + */ virtual int Compare(const cListObject& ListObject) const; + /** + * Get the properties of the object + * + * This returns a property list with all the properties which can be obtained + * or set with \c getProperty or \c setProperty. + * + * @return a stringlist with the properties + */ virtual cStringList* getPropertyList(); + /** + * Gets a property + * + * Returns the value of a specified property. The value is converted into a + * string. + * + * @return returns + * - \bc true, if the property exists + * - \bc false, otherwise + * @param Property the property which should be returned + * @param Value the value of that property + */ virtual bool getProperty(const char* Property, char** Value) const ; + /** + * Sets a property + * + * Sets the value of a specified property. The value is converted from string + * into the propper data type + * + * @return returns + * - \bc true, if the property exists + * - \bc false, otherwise + * @param Property the property which should be set + * @param Value the value of that property + */ virtual bool setProperty(const char* Property, const char* Value); + /** + * Converts to container + * + * This will convert the object into a container if it is one. If not, it + * returns \bc NULL. + * + * @return returns + * - \bc NULL, if it is not a container + * - a container representation of this object + */ virtual cUPnPClassContainer* getContainer(){ return NULL; } + /** + * Create the DIDL fragment + * + * This creates the DIDL-Lite fragment of the object. The DIDL is written to the + * specified \em IXML document. The details of the output can be controlled via + * the filter stringlist + * + * @return the DIDL fragment of the object + * @param Document the IXML document where to write the contents + * @param Filter the string list with the filter criteria + */ virtual IXML_Node* createDIDLFragment(IXML_Document* Document, cStringList* Filter) = 0; + /** + * Is this a container? + * + * Returns if this object is a container or not + * + * @return returns + * - \bc true, if it is a container + * - \bc false, otherwise + */ bool isContainer(){ return this->getContainer()==NULL?false:true; } + /** + * Set the sort criteria + * + * This sets a certain criteria which the object can be compared with. + * + * @param Property the property to sort after + * @param Descending sort the objects in descending order + */ void setSortCriteria(const char* Property, bool Descending = false); + /** + * Clears the sort criteria + * + * Clears the property of the sort criteria and sets the descending flag to + * false. + */ void clearSortCriteria(); /******* Setter *******/ + /** + * Set the title + * + * This sets the title of the object. It is a required metadata information. + * It must not be \bc NULL or an empty string. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Title the title of the object + */ int setTitle(const char* Title); + /** + * Set the creator + * + * The creator of an object is primarily the creator or owner of the object + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Creator the creator of the object + */ int setCreator(const char* Creator); + /** + * Set the restriction + * + * This sets the restriction flag. If the object is restricted, no modifications + * to its metadata by the user are allowed. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Restricted \bc true, to disallow modification, \bc false to allow it + */ int setRestricted(bool Restricted); + /** + * Set the write status + * + * This sets the write status of a resource. With this indicator, you can set + * the modifiabilty of resources by a control point. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Status the write status + */ int setWriteStatus(int Status); + /** + * Set the resources + * + * This sets the list of resources of an object. The list usally contain a + * single resource. However, multiple resources a also very common. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Resources the resource list of this object + */ int setResources(cList<cUPnPResource>* Resources); + /** + * Add resource to list + * + * This adds the specified resource to the resource list of the object + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Resource the resource to be added + */ int addResource(cUPnPResource* Resource); + /** + * Remove resource from list + * + * This removes the specified resource from the resource list of the object + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Resource the resource to be removed + */ int removeResource(cUPnPResource* Resource); /******* Getter *******/ + /** + * Get the object ID + * + * This returns the object ID of the object. + * + * @return the object ID + */ cUPnPObjectID getID() const { return this->mID; } + /** + * Get the parent ID + * + * This returns the ID of the parent container object, associated with this object. + * It is \bc -1, if the object is the root object. + * + * @return the parent ID + */ cUPnPObjectID getParentID() const { return this->mParent?this->mParent->getID():cUPnPObjectID(-1); } + /** + * Get the parent object + * + * This returns the parent container object, associated with this object. It is + * \bc NULL, if the object is the root object. + * + * @return the parent object + */ cUPnPClassContainer* getParent() const { return (cUPnPClassContainer*)this->mParent; } + /** + * Get the title + * + * This returns the title of the object. This may be the title of an item or + * the folder name in case of a container. + * + * @return the title of the object + */ const char* getTitle() const { return this->mTitle; } + /** + * Get the object class + * + * This returns the object class of the object. The classes are defined by + * the UPnP Working Committee. However, custom classes which are derived from + * a standardized class are also possible. + * + * @return the class of the object + */ const char* getClass() const { return this->mClass; } + /** + * Get the creator + * + * This returns the creator of the object. Usually, this is the primary + * content creator or the owner of the object + * + * @return the creator of the object + */ const char* getCreator() const { return this->mCreator; } + /** + * Is the resource restricted? + * + * Returns \bc true, if the object is restricted or \bc false, otherwise. + * When the object is restricted, then modifications to the metadata of the + * object are disallowed. + * + * @return returns + * - \bc true, if the object is restricted + * - \bc false, otherwise + */ bool isRestricted() const { return this->mRestricted; } + /** + * Get write status + * + * This returns the write status of the object. It gives information, if the + * resource is modifiable. + * + * @return the write status + */ int getWriteStatus() const { return this->mWriteStatus; } + /** + * Get a resource by its ID + * + * Returns the resource with the specified resource ID. + * + * @return the resource by ID + * @param ResourceID the resource ID of the demanded resource + */ cUPnPResource* getResource(unsigned int ResourceID) const { return this->mResourcesID->Get(ResourceID); } + /** + * Get the resources + * + * This returns a list with resources associated with this object. + * + * @return the resources of this object + */ cList<cUPnPResource>* getResources() const { return this->mResources; } }; +/** + * The UPnP class Item + * + * This is a UPnP class Item representation with all its properties. + */ class cUPnPClassItem : public cUPnPClassObject { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPItemMediator; protected: // cUPnPObjectID mReferenceID; - cUPnPClassItem* mReference; + cUPnPClassItem* mReference; ///< The reference item + /** + * Constructor of an item + * + * This creates a new instance of an item + */ cUPnPClassItem(); public: virtual ~cUPnPClassItem(){}; @@ -174,28 +684,78 @@ public: virtual bool setProperty(const char* Property, const char* Value); virtual bool getProperty(const char* Property, char** Value) const; /******** Setter ********/ + /** + * Set a reference item + * + * This sets a reference item. Its comparable with symlinks in *nix systems + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Reference the reference item + */ int setReference(cUPnPClassItem* Reference); /******** Getter ********/ + /** + * Get the referenced item + * + * This returns the referenced item of this item + * + * @return the referenced item + */ cUPnPClassItem* getReference() const { return this->mReference; } + /** + * Get the reference ID + * + * This returns the object ID of the referenced item or \b -1, if + * no reference exists. + * + * @return the reference ID + */ cUPnPObjectID getReferenceID() const { return this->mReference?this->mReference->getID():cUPnPObjectID(-1); } }; typedef std::vector<cClass> tClassVector; +/** + * The UPnP class Container + * + * This is a UPnP class Container representation with all its properties. + */ class cUPnPClassContainer : public cUPnPClassObject { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPContainerMediator; protected: - cString mContainerType; - tClassVector mSearchClasses; - tClassVector mCreateClasses; - bool mSearchable; - unsigned int mUpdateID; - cUPnPObjects* mChildren; - cHash<cUPnPClassObject>* mChildrenID; + cString mContainerType; ///< DLNA container type + tClassVector mSearchClasses; ///< Classes which are searchable + tClassVector mCreateClasses; ///< Classes which are creatable + bool mSearchable; ///< Is the Container searchable? + unsigned int mUpdateID; ///< The containerUpdateID + cUPnPObjects* mChildren; ///< List of children + cHash<cUPnPClassObject>* mChildrenID; ///< List of children as hash map + /** + * Update the container + * + * This performs an update, which acutally increases the containerUpdateID. + */ void update(); + /** + * Sets the containerUpdateID + * + * This method should only be used when the containerUpdateID is loaded from + * the database. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param UID the containerUpdateID + */ int setUpdateID(unsigned int UID); + /** + * Constructor of a container + * + * This creates a new instance of a container + */ cUPnPClassContainer(); public: virtual ~cUPnPClassContainer(); @@ -204,44 +764,231 @@ public: virtual bool setProperty(const char* Property, const char* Value); virtual bool getProperty(const char* Property, char** Value) const; virtual cUPnPClassContainer* getContainer(){ return this; } + /** + * Add a child + * + * This adds the specified child to this container. The parent container of the + * child will be set to this container. + * + * @param Object the child to be added + */ void addObject(cUPnPClassObject* Object); + /** + * Remove a child + * + * This removes the specified child from the list of children. The child will + * also loose its parent container, so that there is no link between left. + * + * @param Object the child to be removed + */ void removeObject(cUPnPClassObject* Object); + /** + * Get a child by ID + * + * Returns the child, which is specified by the \c ObjectID. + * + * @return the child with the specified ID + * @param ID the \c ObjectID of the child + */ cUPnPClassObject* getObject(cUPnPObjectID ID) const; + /** + * Get the list of children + * + * This returns a list of the children of the container. + * + * @return the list of children + */ cUPnPObjects* getObjectList() const { return this->mChildren; } + /** + * Add a search class + * + * This adds a search class to the search classes vector + * + * @return returns + * - \bc 0, if adding was successful + * - \bc <0, otherwise + * @param SearchClass the new class to be added + */ int addSearchClass(cClass SearchClass); + /** + * Remove a search class + * + * This removes a search class from the search classes vector + * + * @return returns + * - \bc 0, if deleting was successful + * - \bc <0, otherwise + * @param SearchClass the class to be deleted + */ int delSearchClass(cClass SearchClass); + /** + * Add a create class + * + * This adds a create class to the create classes vector + * + * @return returns + * - \bc 0, if adding was successful + * - \bc <0, otherwise + * @param CreateClass the new class to be added + */ int addCreateClass(cClass CreateClass); + /** + * Remove a create class + * + * This removes a create class from the create classes vector + * + * @return returns + * - \bc 0, if deleting was successful + * - \bc <0, otherwise + * @param CreateClass the class to be deleted + */ int delCreateClass(cClass CreateClass); /******** Setter ********/ + /** + * Set the DLNA container type + * + * This sets the DLNA container type. It must be a valid container type value. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Type the DLNA container type + */ int setContainerType(const char* Type); + /** + * Sets the search classes + * + * This sets the search classes, which allows the user to search only for + * these classes in the current container and its children. If the vector + * is empty the search can return any match. If the additional flag \bc + * derived is set, then also any derived classes are matched. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param SearchClasses a vector container the allowed search classes + */ int setSearchClasses(std::vector<cClass> SearchClasses); + /** + * Sets the create classes + * + * This sets the create classes, which allows the user to create new objects + * in this container, if \em restricted is \bc false. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param CreateClasses a vector containing the create classes + */ int setCreateClasses(std::vector<cClass> CreateClasses); + /** + * Sets the searchable flag + * + * This sets the searchable flag, which allows or disallows search on this + * container. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Searchable \bc true, to enable or \bc false, to disable searching + */ int setSearchable(bool Searchable); /******** Getter ********/ + /** + * Get the DLNA container type + * + * This returns the DLNA container type. Currently there are only these possible + * values beside \bc NULL: + * - \bc TUNER_1_0 + * + * @return the DLNA container type + */ const char* getContainerType() const { return this->mContainerType; } + /** + * Get the search classes + * + * This returns a vector container all possible search classes. This are classes, + * which can be used for searching in this container. + * + * @return a vector with all search classes + */ const std::vector<cClass>* getSearchClasses() const { return &(this->mSearchClasses); } + /** + * Get the create classes + * + * This returns a vector containing all possible create classes. This are classes, + * which can be created in this container. For instance a TV container can only create + * items of the class VideoBroadcast. The vector is empty when creation of new items + * by the user is not allowed. + * + * @return a vector with create classes + */ const std::vector<cClass>* getCreateClasses() const { return &(this->mCreateClasses); } + /** + * Is this container searchable + * + * This returns \bc true, if the container can be search via \em Search or + * \bc false, otherwise. + * + * @return returns + * - \bc true, if the container is searchable + * - \bc false, otherwise + */ bool isSearchable() const { return this->mSearchable; } + /** + * Get the number of children + * + * This returns the total number of children of this container + * + * @return the number of childen + */ unsigned int getChildCount() const { return this->mChildren->Count(); } + /** + * Get the containerUpdateID + * + * This returns the containerUpdateID + * + * @return the containerUpdateID of this container + */ unsigned int getUpdateID() const { return this->mUpdateID; } + /** + * Has the container been updated? + * + * This returns \bc true, if the container was recently updated or + * \bc false, otherwise + * + * @return returns + * - \bc true, if the container was updated + * - \bc false, otherwise + */ bool isUpdated(); }; +/** + * The UPnP class VideoItem + * + * This is a UPnP class VideoItem representation with all its properties. + */ class cUPnPClassVideoItem : public cUPnPClassItem { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPVideoItemMediator; protected: - cString mGenre; // Genre - cString mDescription; // Description - cString mLongDescription; // a longer description - cString mPublishers; // CSV of Publishers - cString mLanguage; // RFC 1766 Language code - cString mRelations; // Relation to other contents - cString mProducers; // CSV of Producers - cString mRating; // Rating (for parential control) - cString mActors; // CSV of Actors - cString mDirectors; // CSV of Directors + cString mGenre; ///< Genre of the video + cString mDescription; ///< Description + cString mLongDescription; ///< a longer description + cString mPublishers; ///< CSV of Publishers + cString mLanguage; ///< RFC 1766 Language code + cString mRelations; ///< Relation to other contents + cString mProducers; ///< CSV of Producers + cString mRating; ///< Rating (for parential control) + cString mActors; ///< CSV of Actors + cString mDirectors; ///< CSV of Directors + /** + * Constructor of a video item + * + * This creates a new instance of a video item + */ cUPnPClassVideoItem(); public: virtual ~cUPnPClassVideoItem(); @@ -250,36 +997,230 @@ public: virtual bool setProperty(const char* Property, const char* Value); virtual bool getProperty(const char* Property, char** Value) const; /******** Setter ********/ + /** + * Set a long description + * + * A long description may hold information about the content or the story + * of a video + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param LongDescription the content or story of a video + */ int setLongDescription(const char* LongDescription); + /** + * Set a description + * + * A description may hold short information about the content or the story + * of a video. Unlike a long description, this contains just a very short + * brief like a subtitle or the episode title. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Description the description of a video + */ int setDescription(const char* Description); + /** + * Set the publishers + * + * This is a CSV list of publishers, who distributes the video. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Publishers a CSV list of publishers + */ int setPublishers(const char* Publishers); + /** + * Set a genre + * + * This is a CSV list of genre of a video. This may be something like + * "Western" or "SciFi". Actually, there is no standardized rule for + * a genre name, which results in an ambiguous definition of certain + * genre, like Thriller and Horror. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Genre a CSV list of genre + */ int setGenre(const char* Genre); + /** + * Set the language + * + * This sets the language of a video. It is defined by RFC 1766. + * A valid language definition is \em "de-DE" or \em "en-US". + * + * @see http://www.ietf.org/rfc/rfc1766.txt + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Language the language (RFC 1766) + */ int setLanguage(const char* Language); + /** + * Sets a relation URL + * + * This sets a CSV list of relation URLs, where to find additional + * information about the movie. The URLs may not contain commas and they + * must be properly escaped as in RFC 2396 + * + * @see http://www.ietf.org/rfc/rfc2396.txt + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Relations a CSV list with relations + */ int setRelations(const char* Relations); + /** + * Sets the directors + * + * This sets a CSV list of directors. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Directors a CSV list of directors + */ int setDirectors(const char* Directors); + /** + * Sets the actors + * + * This sets a CSV list of actors in a video. This usually contain the main actors. + * However, also other actors appearing in the video can be mentioned here. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Actors a CSV list of actors + */ int setActors(const char* Actors); + /** + * Sets the producers + * + * This sets a CSV list of producers of a video. These are the people who are + * involed in the production of a video + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Producers a CSV list of producers + */ int setProducers(const char* Producers); + /** + * Sets the rating + * + * This is a rating, which can be used for parential control issues. + * + * @see http://en.wikipedia.org/wiki/Motion_picture_rating_system + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Rating the rating of a video + */ int setRating(const char* Rating); /******** Getter ********/ + /** + * Get the genres + * + * This returns a CSV list of genre + * + * @return the genre of a video + */ const char* getGenre() const { return this->mGenre; } + /** + * Get the long description + * + * This returns the long description of a video + * + * @return the long description of a video + */ const char* getLongDescription() const { return this->mLongDescription; } + /** + * Get the description + * + * This returns the description of a video + * + * @return the description of a video + */ const char* getDescription() const { return this->mDescription; } + /** + * Get the publishers + * + * This returns a CSV list of publishers of the video + * + * @return a CSV list of publishers + */ const char* getPublishers() const { return this->mPublishers; } + /** + * Get the language + * + * This returns the language of the video + * + * @return the language + */ const char* getLanguage() const { return this->mLanguage; } + /** + * Get the relations + * + * This returns a CSV list of relation URLs. + * + * @return a CSV list of relation URLs + */ const char* getRelations() const { return this->mRelations; } + /** + * Get the actors + * + * This returns a CSV list of actors in the video + * + * @return a CSV list of actors + */ const char* getActors() const { return this->mActors; } + /** + * Get the producers + * + * This returns a CSV list of producers of a video + * + * @return a CSV list of producers + */ const char* getProducers() const { return this->mProducers; } + /** + * Get the directors + * + * This returns a CSV list of directors + * + * @return a CSV list of directors + */ const char* getDirectors() const { return this->mDirectors; } + /** + * Get the rating + * + * This returns the rating used for parental control. + * + * @return the rating of a video + */ const char* getRating() const { return this->mRating; } }; +/** + * The UPnP class Movie + * + * This is a UPnP class Movie representation with all its properties. + */ class cUPnPClassMovie : public cUPnPClassVideoItem { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPMovieMediator; protected: - int mDVDRegionCode; - cString mStorageMedium; + int mDVDRegionCode; ///< The Region code of the movie (0 - 8) + cString mStorageMedium; ///< The storage medium where the movie is stored + /** + * Constructor of a movie + * + * This creates a new instance of a movie + */ cUPnPClassMovie(); public: virtual ~cUPnPClassMovie(); @@ -288,22 +1229,75 @@ public: virtual bool setProperty(const char* Property, const char* Value); virtual bool getProperty(const char* Property, char** Value) const; /******** Setter ********/ + /** + * Sets the DVD region code + * + * For more information on this, see http://en.wikipedia.org/wiki/DVD_region_code + * + * The integer representation for \em ALL is 9. + * + * @see http://en.wikipedia.org/wiki/DVD_region_code + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param RegionCode the region code of this movie + */ int setDVDRegionCode(int RegionCode); + /** + * Sets the storage medium + * + * This will set the storage medium, where the movie resides. Valid media + * are defined in \link common.h \endlink + * + * @see common.h + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param StorageMedium the medium where the movie is located + */ int setStorageMedium(const char* StorageMedium); /******** Getter ********/ + /** + * Get the DVD region code + * + * This returns the DVD region code. For more information, + * see http://en.wikipedia.org/wiki/DVD_region_code + * + * The integer representation for \em ALL is 9. + * + * @see http://en.wikipedia.org/wiki/DVD_region_code + * @return the DVD region code + */ int getDVDRegionCode() const { return this->mDVDRegionCode; } + /** + * Get the storage medium + * + * This returns the storage medium, where the movie resides. + * + * @return the storage medium + */ const char* getStorageMedium() const { return this->mStorageMedium; } }; +/** + * The UPnP class VideoBroadcast + * + * This is a UPnP class VideoBroadcast representation with all its properties. + */ class cUPnPClassVideoBroadcast : public cUPnPClassVideoItem { friend class cMediaDatabase; friend class cUPnPObjectMediator; friend class cUPnPVideoBroadcastMediator; protected: - cString mIcon; - cString mRegion; - int mChannelNr; - cString mChannelName; + cString mIcon; ///< The channel icon of the channel + cString mRegion; ///< The region where the channel can be received + int mChannelNr; ///< The channel number + cString mChannelName; ///< The channel name or provider name + /** + * Constructor of a video broadcast + * + * This creates a new instance of a video broadcast + */ cUPnPClassVideoBroadcast(); public: virtual ~cUPnPClassVideoBroadcast(); @@ -312,29 +1306,166 @@ public: virtual bool setProperty(const char* Property, const char* Value); virtual bool getProperty(const char* Property, char** Value) const; /******** Setter ********/ + /** + * Set the channel icon + * + * This sets the channel icon of this channel. The resource must be a valid + * URI which can be obtained via the internal webserver + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param IconURI the URI to the icon file + */ int setIcon(const char* IconURI); + /** + * Set the channel region + * + * This sets the region of a channel, where it can be received + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param Region the location where the channel can be received + */ int setRegion(const char* Region); + /** + * Set channel number + * + * This sets the channel number, so that it can be used for directly navigation + * or channel up and down navigation respectively. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param ChannelNr the channel number + */ int setChannelNr(int ChannelNr); + /** + * Set the channel name + * + * This sets the channel name or the provider of the channel. + * + * @return returns + * - \bc 0, if setting was successful + * - \bc <0, otherwise + * @param ChannelName the channel name + */ int setChannelName(const char* ChannelName); /******** Getter ********/ + /** + * Get the channel icon + * + * This returns the channel icon of the channel. + * + * @return the channel icon + */ const char* getIcon() const { return this->mIcon; } + /** + * Get the region + * + * This returns the region, where the channel can be received + * + * @return the channel region + */ const char* getRegion() const { return this->mRegion; } + /** + * Get the channel number + * + * This returns the channel number + * + * @return the channel number + */ int getChannelNr() const { return this->mChannelNr; } + /** + * Get the channel name + * + * This returns the channel name or provider name respectively + * + * @return the channel name + */ const char* getChannelName() const { return this->mChannelName; } }; +/** + * Mediator interface + * + * This is an interface for mediators used to communicate with the database. + * A mediator is applied to get, create, save or delete an UPnP object. + */ class cMediatorInterface { public: virtual ~cMediatorInterface(){}; + /** + * Creates an object + * + * This creates a new UPnP object with the specific title and the restriction. + * + * @return the newly created object + * @param Title the title of that object + * @param Restricted the restriction of the object + */ virtual cUPnPClassObject* createObject(const char* Title, bool Restricted) = 0; + /** + * Get an object + * + * Retrieves a UPnP object from the database and stores its information in the + * object. The object is obtained via its object ID. + * + * @return the object, found in the database + * @param ID the object ID + */ virtual cUPnPClassObject* getObject(cUPnPObjectID ID) = 0; + /** + * Saves the object + * + * This saves the object in the database by updating the values in the database + * with those in the object. + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param Object the object to be saved + */ virtual int saveObject(cUPnPClassObject* Object) = 0; + /** + * Deletes the object + * + * This deletes the object in the database by removing all its children and then + * deleting the contents from the database + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param Object the object to be deleted + */ virtual int deleteObject(cUPnPClassObject* Object) = 0; + /** + * Clears the object + * + * This clears the object, i.e. all its children will be removed and deleted + * from the database + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param Object the object to be cleared + */ virtual int clearObject(cUPnPClassObject* Object) = 0; }; typedef std::map<const char*, cMediatorInterface*, strCmp> tMediatorMap; +/** + * The object factory + * + * This factory can create, delete, clear or save UPnP objects. It uses mediators + * to communicate with the persistance database to load or persist the objects. + * + * If a new type of object shall be stored in the database an according mediator + * is needed, which knows the internal database structure. It must implement the + * cMediatorInterface class to work with this factory. + */ class cUPnPObjectFactory { private: static cUPnPObjectFactory* mInstance; @@ -344,84 +1475,257 @@ private: cMediatorInterface* findMediatorByClass(const char* Class); cUPnPObjectFactory(); public: + /** + * Return the instance of the factory + * + * This returns the instance of the factory. When the media database is + * initialized successfully, it usally has all known mediators already + * registered. + * + * @return the instance of the factory + */ static cUPnPObjectFactory* getInstance(); + /** + * Register a mediator + * + * This registers a new mediator by the associated class. The mediator + * must implement the cMediatorInterface class to be used with this + * factory. + * + * @param UPnPClass the class of which the mediator is associated to + * @param Mediator the mediator itself + */ void registerMediator(const char* UPnPClass, cMediatorInterface* Mediator); + /** + * Unregisters a mediator + * + * This unregisters a mediator if it is not needed anylonger. If the optional + * parameter \c freeMediator is set, the object instance will be free'd after + * removing it from the list. + * + * @param UPnPClass the class of the associated mediator + * @param freeMediator flag to indicate if the mediator shall be free'd after removing + */ void unregisterMediator(const char* UPnPClass, bool freeMediator=true); + /** + * @copydoc cMediatorInterface::createObject(const char* Title, bool Restricted) + * + * @param UPnPClass the class of the new object + */ cUPnPClassObject* createObject(const char* UPnPClass, const char* Title, bool Restricted=true); + /*! @copydoc cMediatorInterface::getObject(cUPnPObjectID ID) */ cUPnPClassObject* getObject(cUPnPObjectID ID); + /*! @copydoc cMediatorInterface::saveObject(cUPnPClassObject* Object) */ int saveObject(cUPnPClassObject* Object); + /*! @copydoc cMediatorInterface::deleteObject(cUPnPClassObject* Object) */ int deleteObject(cUPnPClassObject* Object); + /*! @copydoc cMediatorInterface::clearObject(cUPnPClassObject* Object) */ int clearObject(cUPnPClassObject* Object); }; class cMediaDatabase; +/** + * Object Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPObjectMediator : public cMediatorInterface { protected: - cSQLiteDatabase* mDatabase; - cMediaDatabase* mMediaDatabase; - cUPnPObjectMediator(cMediaDatabase* MediaDatabase); - virtual int initializeObject(cUPnPClassObject* Object, const char* Class, const char* Title, bool Restricted); + cSQLiteDatabase* mDatabase; ///< the SQLite 3 database wrapper + cMediaDatabase* mMediaDatabase; ///< the media database + /** + * Constructor of object mediator + * + * This constructs a new object mediator. This is actually not allowed because + * it is prohibited to create instances of the UPnP class Object + */ + cUPnPObjectMediator( + cMediaDatabase* MediaDatabase ///< the media database + ); + /** + * Initializes an object + * + * This initializes an object, which means, that it will be created in the database with + * the required details. + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + */ + virtual int initializeObject( + cUPnPClassObject* Object, ///< the object to be initialized + const char* Class, ///< the class of the object + const char* Title, ///< the title of the object + bool Restricted ///< restriction of the object + ); + /** + * Store the object in the database + * + * This stores the information of an object in the database + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param Object the object to be saved + */ virtual int objectToDatabase(cUPnPClassObject* Object); + /** + * Loads an object from database + * + * This loads an object from the database + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param Object the object to be loaded + * @param ID the object ID of that object + */ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: virtual ~cUPnPObjectMediator(); + /*! @copydoc cMediatorInterface::createObject(const char* Title, bool Restricted) */ virtual cUPnPClassObject* createObject(const char* Title, bool Restricted); - virtual cUPnPClassObject* getObject(cUPnPObjectID); + /*! @copydoc cMediatorInterface::getObject(cUPnPObjectID ID) */ + virtual cUPnPClassObject* getObject(cUPnPObjectID ID); + /*! @copydoc cMediatorInterface::saveObject(cUPnPClassObject* Object) */ virtual int saveObject(cUPnPClassObject* Object); + /*! @copydoc cMediatorInterface::deleteObject(cUPnPClassObject* Object) */ virtual int deleteObject(cUPnPClassObject* Object); + /*! @copydoc cMediatorInterface::clearObject(cUPnPClassObject* Object) */ virtual int clearObject(cUPnPClassObject* Object); }; +/** + * Item Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPItemMediator : public cUPnPObjectMediator { protected: + /*! @copydoc cUPnPObjectMediator::objectToDatabase(cUPnPClassObject* Object) */ virtual int objectToDatabase(cUPnPClassObject* Object); + /*! @copydoc cUPnPObjectMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID) */ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: + /** + * Constructor of item mediator + * + * This creates a new item mediator with which it is possible to create new + * instances of Item objects. + * + * @param MediaDatabase the media database + */ cUPnPItemMediator(cMediaDatabase* MediaDatabase); virtual ~cUPnPItemMediator(){}; + /*! @copydoc cUPnPObjectMediator::createObject(const char* Title, bool Restricted) */ virtual cUPnPClassItem* createObject(const char* Title, bool Restricted); + /*! @copydoc cUPnPObjectMediator::getObject(cUPnPObjectID ID) */ virtual cUPnPClassItem* getObject(cUPnPObjectID ID); }; +/** + * VideoItem Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPVideoItemMediator : public cUPnPItemMediator { protected: virtual int objectToDatabase(cUPnPClassObject* Object); virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: + /** + * Constructor of videoitem mediator + * + * This creates a new videoitem mediator with which it is possible to create new + * instances of VideoItem objects. + * + * @param MediaDatabase the media database + */ cUPnPVideoItemMediator(cMediaDatabase* MediaDatabase); virtual ~cUPnPVideoItemMediator(){}; virtual cUPnPClassVideoItem* createObject(const char* Title, bool Restricted); virtual cUPnPClassVideoItem* getObject(cUPnPObjectID ID); }; +/** + * VideoBroadcast Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPVideoBroadcastMediator : public cUPnPVideoItemMediator { protected: virtual int objectToDatabase(cUPnPClassObject* Object); virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: + /** + * Constructor of video broadcast mediator + * + * This creates a new video broadcast mediator with which it is possible to create new + * instances of VideoBroadcast objects. + * + * @param MediaDatabase the media database + */ cUPnPVideoBroadcastMediator(cMediaDatabase* MediaDatabase); virtual ~cUPnPVideoBroadcastMediator(){}; virtual cUPnPClassVideoBroadcast* createObject(const char* Title, bool Restricted); virtual cUPnPClassVideoBroadcast* getObject(cUPnPObjectID ID); }; +/** + * Movie Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPMovieMediator : public cUPnPVideoItemMediator { protected: virtual int objectToDatabase(cUPnPClassObject* Object); virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: + /** + * Constructor of movie mediator + * + * This creates a new movie mediator with which it is possible to create new + * instances of Movie objects. + * + * @param MediaDatabase the media database + */ cUPnPMovieMediator(cMediaDatabase* MediaDatabase); virtual ~cUPnPMovieMediator(){}; virtual cUPnPClassMovie* createObject(const char* Title, bool Restricted); virtual cUPnPClassMovie* getObject(cUPnPObjectID ID); }; +/** + * Container Mediator + * + * This is the interface between the objects and the database. It is possible to + * create new objects, stores objects in the database as well as removing them from + * it. + */ class cUPnPContainerMediator : public cUPnPObjectMediator { protected: virtual int objectToDatabase(cUPnPClassObject* Object); virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID); public: + /** + * Constructor of container mediator + * + * This creates a new container mediator with which it is possible to create new + * instances of Container objects. + * + * @param MediaDatabase the media database + */ cUPnPContainerMediator(cMediaDatabase* MediaDatabase); virtual ~cUPnPContainerMediator(){}; virtual cUPnPClassContainer* createObject(const char* Title, bool Restricted); diff --git a/database/resources.cpp b/database/resources.cpp index 7d01ceb..8f0dec4 100644 --- a/database/resources.cpp +++ b/database/resources.cpp @@ -75,11 +75,11 @@ int cUPnPResources::getResourcesOfObject(cUPnPClassObject* Object){ cUPnPResource* cUPnPResources::getResource(unsigned int ResourceID){ cUPnPResource* Resource; if((Resource = this->mResources->Get(ResourceID))){ - MESSAGE("Found cached resource"); + MESSAGE(VERBOSE_METADATA, "Found cached resource"); return Resource; } else if((Resource = this->mMediator->getResource(ResourceID))){ - MESSAGE("Found resource in database"); + MESSAGE(VERBOSE_METADATA, "Found resource in database"); this->mResources->Add(Resource, ResourceID); return Resource; } @@ -107,12 +107,12 @@ int cUPnPResources::createFromRecording(cUPnPClassVideoItem* Object, cRecording* } delete Detector; - MESSAGE("To be continued, may it work with DLNA?! Guess, not!"); + MESSAGE(VERBOSE_SDK, "To be continued, may it work with DLNA?! Guess, not!"); return 0; } int cUPnPResources::createFromFile(cUPnPClassItem* , cString ){ - MESSAGE("To be done"); + MESSAGE(VERBOSE_SDK, "To be done"); return -1; } @@ -131,12 +131,12 @@ int cUPnPResources::createFromChannel(cUPnPClassVideoBroadcast* Object, cChannel const char* ProtocolInfo = cDlna::getInstance()->getProtocolInfo(Profile); - MESSAGE("Protocol info: %s", ProtocolInfo); + MESSAGE(VERBOSE_METADATA, "Protocol info: %s", ProtocolInfo); // Adapted from streamdev int index = 0; for(int i=0; Channel->Apid(i)!=0; i++, index++){ - MESSAGE("Analog channel %d", i); + MESSAGE(VERBOSE_METADATA, "Analog channel %d", i); cString ResourceFile = cString::sprintf("%s:%d", *Channel->GetChannelID().ToString(), index); cUPnPResource* Resource = this->mMediator->newResource(Object, UPNP_RESOURCE_CHANNEL,ResourceFile, Profile->mime, ProtocolInfo); Resource->mBitrate = 0; @@ -153,7 +153,7 @@ int cUPnPResources::createFromChannel(cUPnPClassVideoBroadcast* Object, cChannel this->mResources->Add(Resource, Resource->getID()); } for(int i=0; Channel->Dpid(i)!=0; i++, index++){ - MESSAGE("Digital channel %d", i); + MESSAGE(VERBOSE_METADATA, "Digital channel %d", i); cString ResourceFile = cString::sprintf("%s:%d", *Channel->GetChannelID().ToString(), index); cUPnPResource* Resource = this->mMediator->newResource(Object, UPNP_RESOURCE_CHANNEL,ResourceFile, Profile->mime, ProtocolInfo); Resource->mBitrate = 0; diff --git a/database/resources.h b/database/resources.h index 46ec0d5..8f7fa86 100644 --- a/database/resources.h +++ b/database/resources.h @@ -16,6 +16,12 @@ class cUPnPResourceMediator; class cMediaDatabase; +/** + * The resource manager + * + * This manages the resources in an internal cache. It may create a new resource + * from a channel, a recording or a custom file. + */ class cUPnPResources { private: cHash<cUPnPResource>* mResources; @@ -24,16 +30,89 @@ private: cSQLiteDatabase* mDatabase; cUPnPResources(); public: + /** + * Fill object with its resources + * + * This will load all the resources from the database, which are associated + * to the given object + * + * @param Object the object, which shall be filled + * @return returns + * - \bc 0, if loading was successful + * - \bc <0, otherwise + */ int getResourcesOfObject(cUPnPClassObject* Object); + /** + * Loads all resources from database + * + * This loads all resources from the database into the internal cache. + * + * @return returns + * - \bc 0, if loading was successful + * - \bc <0, otherwise + */ int loadResources(); + /*! @copydoc cUPnPResourceMediator::getResource */ cUPnPResource* getResource(unsigned int ResourceID); virtual ~cUPnPResources(); + /** + * Get the instance of the resource manager + * + * This returns the instance of the resource manager. + * + * @return the instance of the manager + */ static cUPnPResources* getInstance(); + /** + * Create resource from channel + * + * This creates a new resource from the given channel. It determines what + * kind of video stream it is and further details if available. It stores + * the resource in the database after creating it. + * + * @param Object the videoBroadcast item which holds the resource + * @param Channel the VDR TV channel + * @return returns + * - \bc 0, if loading was successful + * - \bc <0, otherwise + */ int createFromChannel(cUPnPClassVideoBroadcast* Object, cChannel* Channel); + /** + * Create resource from recording + * + * This creates a new resource from the given recording. It determines what + * kind of video stream it is and further details if available. It stores + * the resource in the database after creating it. + * + * @param Object the videoItem item which holds the resource + * @param Recording the VDR TV recording + * @return returns + * - \bc 0, if loading was successful + * - \bc <0, otherwise + */ int createFromRecording(cUPnPClassVideoItem* Object, cRecording* Recording); + /** + * Create resource from file + * + * This creates a new resource from the given file. It determines all available + * information about the resource by analizing the content. It stores + * the resource in the database after creating it. + * + * @param Object the item which holds the resource + * @param File the file name + * @return returns + * - \bc 0, if loading was successful + * - \bc <0, otherwise + */ int createFromFile(cUPnPClassItem* Object, cString File); }; +/** + * The resource mediator + * + * This is another mediator which communicates with the database. It manages the + * resources in the database + */ class cUPnPResourceMediator { friend class cUPnPResources; private: @@ -42,8 +121,40 @@ private: unsigned int getNextResourceID(); public: virtual ~cUPnPResourceMediator(); + /** + * Get a resource by ID + * + * This returns a resource by its resource ID + * + * @param ResourceID the resource ID of the demanded resource + * @return the requested resource + */ cUPnPResource* getResource(unsigned int ResourceID); + /** + * Saves the resource + * + * This updates the information in the database with those in the resource + * object + * + * @param Resource the resource which shall be saved + * @return returns + * - \bc 0, if saving was successful + * - \bc <0, if an error occured + */ int saveResource(cUPnPResource* Resource); + /** + * Create new resource + * + * This creates a new resource and stores the skeleton in the database. The + * newly created resource will only contain all required information. + * + * @param Object the Object which will hold the resource + * @param ResourceType the type of the resource + * @param ResourceFile the file or URL, where the resource can be located + * @param ContentType the mime type of the content + * @param ProtocolInfo the protocol information of the resource + * @return the newly created resource + */ cUPnPResource* newResource(cUPnPClassObject* Object, int ResourceType, cString ResourceFile, cString ContentType, cString ProtocolInfo); }; diff --git a/misc/avdetector.cpp b/misc/avdetector.cpp index d66988e..8f3bc8f 100644 --- a/misc/avdetector.cpp +++ b/misc/avdetector.cpp @@ -55,7 +55,7 @@ int cAudioVideoDetector::detectVideoProperties(cUPnPResource* Resource, const ch unsigned int bitrate = CodecCtx->bit_rate; const char* codecName = (Codec)?Codec->name:"unknown"; - MESSAGE("AVDetector: %s-stream %dx%d at %d bit/s", codecName, width, height, bitrate); + MESSAGE(VERBOSE_METADATA, "AVDetector: %s-stream %dx%d at %d bit/s", codecName, width, height, bitrate); Resource->mBitrate = bitrate; Resource->mSampleFrequency = CodecCtx->sample_rate; diff --git a/misc/avdetector.h b/misc/avdetector.h index 0b8bb81..b043c59 100644 --- a/misc/avdetector.h +++ b/misc/avdetector.h @@ -10,10 +10,29 @@ #include "../database/object.h" +/** + * The audio/video detector + * + * This is the audio video detector, which analizes the audio and video stream + * of a file to gather more information about the resource. This is also + * required for determination of a suitable DLNA profile. + */ class cAudioVideoDetector { public: cAudioVideoDetector(){}; virtual ~cAudioVideoDetector(){}; + /** + * Detect video properties + * + * This detects video properties of a video stream and stores them in the + * Resource object. + * + * @param Resource the resource, where to save the data + * @param Filename the file, which shall be read + * @return returns + * - \bc 0, if the detection was successful + * - \bc <0, otherwise + */ int detectVideoProperties(cUPnPResource* Resource, const char* Filename); private: }; diff --git a/misc/config.cpp b/misc/config.cpp index 59bf4a6..1fa0ccb 100644 --- a/misc/config.cpp +++ b/misc/config.cpp @@ -32,6 +32,7 @@ cUPnPConfig* cUPnPConfig::get(){ } cUPnPConfig* cUPnPConfig::mInstance = NULL; +int cUPnPConfig::verbosity = 0; bool cUPnPConfig::processArgs(int argc, char* argv[]){ // Implement command line argument processing here if applicable. @@ -39,7 +40,9 @@ bool cUPnPConfig::processArgs(int argc, char* argv[]){ {"int", required_argument, NULL, 'i'}, {"address", required_argument, NULL, 'a'}, {"port", required_argument, NULL, 'p'}, - {"autodetect", no_argument, NULL, 'd'} + {"autodetect", no_argument, NULL, 'd'}, + {"verbose", no_argument, NULL, 'v'}, + {0, 0, 0, 0} }; int c = 0; @@ -55,8 +58,9 @@ bool cUPnPConfig::processArgs(int argc, char* argv[]){ bool success = true; bool ifaceExcistent = false; bool addExcistent = false; + static int verbose = 0; - while((c = getopt_long(argc, argv, "i:a:p:v",long_options, NULL)) != -1){ + while((c = getopt_long(argc, argv, "i:a:p:dv",long_options, NULL)) != -1){ switch(c){ case 'i': if(addExcistent) { ERROR("Address given but must be absent!"); return false; } @@ -78,6 +82,12 @@ bool cUPnPConfig::processArgs(int argc, char* argv[]){ break; case 'd': success = this->parseSetup(SETUP_SERVER_AUTO, optarg) && success; + break; + case 'v': + cUPnPConfig::verbosity++; + verbose++; + WARNING("Verbosity level: %i ", verbose); + break; default: return false; } @@ -90,11 +100,11 @@ bool cUPnPConfig::parseSetup(const char *Name, const char *Value) { const char* ptr; if(*this->mParsedArgs && (ptr = strstr(this->mParsedArgs,Name))!=NULL){ - MESSAGE("Skipping %s=%s, was overridden in command line.",Name, Value); + MESSAGE(VERBOSE_SDK, "Skipping %s=%s, was overridden in command line.",Name, Value); return true; } - MESSAGE("VARIABLE %s has value %s", Name, Value); + MESSAGE(VERBOSE_SDK, "VARIABLE %s has value %s", Name, Value); // Parse your own setup parameters and store their values. if (!strcasecmp(Name, SETUP_SERVER_ENABLED)) this->mEnable = atoi(Value); else if (!strcasecmp(Name, SETUP_SERVER_AUTO)) this->mAutoSetup = atoi(Value); diff --git a/misc/config.h b/misc/config.h index 018213e..40ae6f7 100644 --- a/misc/config.h +++ b/misc/config.h @@ -11,21 +11,60 @@ #include <vdr/tools.h> #include "../common.h" +/** + * The configuration settings + * + * This holds the configurations for the server. It holds information about the + * network settings as well as some status flags. + */ class cUPnPConfig { private: static cUPnPConfig* mInstance; cString mParsedArgs; cUPnPConfig(); public: - char* mInterface; - char* mAddress; - int mPort; - int mEnable; - int mAutoSetup; + static int verbosity; ///< the verbosity of the plugin, the higher the more messages + ///< are printed. + char* mInterface; ///< the network interface, which the server is bound to + char* mAddress; ///< the IP address which is used by the server + int mPort; ///< the port which the server is listening on + int mEnable; ///< indicates, if the server is enabled or not + int mAutoSetup; ///< indicates, if the settings are automatically detected public: virtual ~cUPnPConfig(); + /** + * Get the configuration + * + * This returns the instance of the current configuration settings. + * + * @return the configuration object + */ static cUPnPConfig* get(); + /** + * Parse setup variable + * + * This parses the setup variable with the according value. The value is a + * string representation and must be converted into the according data type. + * + * @return returns + * - \bc true, if parsing was successful + * - \bc false, otherwise + * @param Name the name of the variable + * @param Value the according value of the variable + */ bool parseSetup(const char* Name, const char* Value); + /** + * Processes the commandline arguments + * + * This processes the commandline arguments which the user specified at the + * start of the plugin. + * + * @return returns + * - \bc true, if processing was successful + * - \bc false, otherwise + * @param argc the number of arguments in the list + * @param argv the arguments as a char array + */ bool processArgs(int argc, char* argv[]); }; diff --git a/misc/menusetup.cpp b/misc/menusetup.cpp index 9e8386f..c385b2d 100644 --- a/misc/menusetup.cpp +++ b/misc/menusetup.cpp @@ -18,7 +18,7 @@ cMenuSetupUPnP::cMenuSetupUPnP(){ // Get server acitve state - MESSAGE("Creating menu"); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Creating menu"); this->mCtrlBind = NULL; this->mCtrlAutoMode = NULL; this->mCtrlEnabled = NULL; @@ -63,7 +63,7 @@ const char* const* cMenuSetupUPnP::getInterfaceList(int* count){ } int cMenuSetupUPnP::getInterfaceIndex(const char* Interface){ - MESSAGE("Getting Index of %s", Interface); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Getting Index of %s", Interface); if(!Interface) return 0; int count; int Index = 0; diff --git a/misc/menusetup.h b/misc/menusetup.h index ae40e97..4e32efc 100644 --- a/misc/menusetup.h +++ b/misc/menusetup.h @@ -17,17 +17,42 @@ * * This class shows and manages the settings within the VDR setup OSD * - * @author Denis Loh - * @version 0.0.1 */ class cMenuSetupUPnP : public cMenuSetupPage { public: cMenuSetupUPnP(); // virtual ~cMenuSetupUPnP(); - virtual eOSState ProcessKey(eKeys Key); + /** + * Processes a keystroke + * + * This processes a keystroke which is done by the user and updates the + * menu accordingly + * + * It returns the current state of the VDR after pressing a key + * + * @return The current state of the VDR + */ + virtual eOSState ProcessKey( + eKeys Key ///< Key, pressed by the user + ); protected: + /** + * Stores the setup information + * + * This stores the setup information in the configuration file + */ virtual void Store(void); + /** + * Update the menu + * + * This updates the menu osd and refreshes the screen. + */ void Update(void); + /** + * Loads the setup information + * + * This loads the setup information from the configuration file + */ void Load(void); private: const char* const* getInterfaceList(int *count); diff --git a/misc/search.cpp b/misc/search.cpp index deee8b0..b3534f1 100644 --- a/misc/search.cpp +++ b/misc/search.cpp @@ -28,6 +28,7 @@ typedef function1<void, const char> charCallback; typedef function1<void, int> intCallback; // The defined ColumnNames +/** @private */ struct cProperties : symbols<const char*> { //struct cProperties : symbols<> { cProperties(){ @@ -107,6 +108,7 @@ struct cProperties : symbols<const char*> { } } Properties; +/** @private */ struct cOperators : symbols<const char*> { cOperators(){ add @@ -123,6 +125,7 @@ struct cOperators : symbols<const char*> { } } Operators; +/** @private */ struct cConcatOperators : symbols<const char*> { cConcatOperators(){ add @@ -132,6 +135,7 @@ struct cConcatOperators : symbols<const char*> { } } ConcatOperators; +/** @private */ struct cExistanceOperator : symbols<const char*> { cExistanceOperator(){ add @@ -145,6 +149,7 @@ struct cExistanceOperator : symbols<const char*> { // This is the grammar including the functors which calls the member functions // of search. The callback definitions at the end of the constructor are // essential. DO NOT MODIFY if you don't know how! +/** @private */ struct cSearchGrammar : public boost::spirit::grammar<cSearchGrammar> { // The callbacks members charCallback &endBrackedExp; @@ -176,6 +181,7 @@ struct cSearchGrammar : public boost::spirit::grammar<cSearchGrammar> { startBrackedExp(startBrackedExp){} template <typename scanner> + /** @private */ struct definition { boost::spirit::rule<scanner> searchCrit, searchExp, logOp, \ relExp, binOp, relOp, stringOp, \ @@ -278,6 +284,7 @@ struct cSearchGrammar : public boost::spirit::grammar<cSearchGrammar> { }; }; +/** @private */ struct cSortGrammar : public boost::spirit::grammar<cSortGrammar> { // The callback members propCallback &pushProperty; @@ -290,6 +297,7 @@ struct cSortGrammar : public boost::spirit::grammar<cSortGrammar> { pushDirection(pushDirection){} template <typename scanner> + /** @private */ struct definition { boost::spirit::rule<scanner> sortCrit, sortExp, property, direction; @@ -312,6 +320,7 @@ struct cSortGrammar : public boost::spirit::grammar<cSortGrammar> { }; }; +/** @private */ struct cFilterGrammar : public boost::spirit::grammar<cFilterGrammar> { // The callback members propCallback &pushProperty; @@ -324,6 +333,7 @@ struct cFilterGrammar : public boost::spirit::grammar<cFilterGrammar> { pushAsterisk(pushAsterisk){} template <typename scanner> + /** @private */ struct definition { boost::spirit::rule<scanner> filterCrit, filterExp, property; @@ -351,7 +361,7 @@ struct cFilterGrammar : public boost::spirit::grammar<cFilterGrammar> { \**********************************************/ void cSearch::pushEndBrackedExp(const char){ - MESSAGE("Pushing closing bracket"); + MESSAGE(VERBOSE_PARSERS, "Pushing closing bracket"); if(asprintf(&this->SQLWhereStmt, "%s)", this->SQLWhereStmt)==-1){ ERROR("Unable to allocate SQL Statement"); return; @@ -369,21 +379,21 @@ void cSearch::pushExpression(const char*, const char*){ long int IntegerValue; if(sscanf(Value, "%ld", &IntegerValue)!=EOF && sscanf(Value, "%*4d-%*2d-%*2d")==EOF){ - MESSAGE("Popping '%s %s %ld'",Property, Operator, IntegerValue); + MESSAGE(VERBOSE_PARSERS, "Popping '%s %s %ld'",Property, Operator, IntegerValue); if(asprintf(&Statement, "%s %s %ld", Property, Operator, IntegerValue)==-1){ ERROR("Failed to allocated memory for statement."); return; } } else if(!strcasecmp(Operator, "IS")){ - MESSAGE("Popping '%s %s %s'", Property, Operator, Value); + MESSAGE(VERBOSE_PARSERS, "Popping '%s %s %s'", Property, Operator, Value); if(asprintf(&Statement, "%s %s %s", Property, Operator, Value)==-1){ ERROR("Failed to allocated memory for statement."); return; } } else { - MESSAGE("Popping '%s %s \"%s\"'",Property, Operator, Value); + MESSAGE(VERBOSE_PARSERS, "Popping '%s %s \"%s\"'",Property, Operator, Value); if(asprintf(&Statement, "%s %s '%s'", Property, Operator, Value)==-1){ ERROR("Failed to allocated memory for statement."); return; @@ -401,12 +411,12 @@ void cSearch::pushExpression(const char*, const char*){ void cSearch::pushProperty(const char* Property){ this->CurrentProperty = strdup(Property); - MESSAGE("Property %s added",Property); + MESSAGE(VERBOSE_PARSERS, "Property %s added",Property); } void cSearch::pushOperator(const char* Operator){ this->CurrentOperator = strdup(Operator); - MESSAGE("Operator %s added",Operator); + MESSAGE(VERBOSE_PARSERS, "Operator %s added",Operator); } void cSearch::pushValue(const char* Start, const char* End){ @@ -414,13 +424,13 @@ void cSearch::pushValue(const char* Start, const char* End){ if(!Value || !strcmp(Value,"")) return; this->CurrentValue = strdup(Value); - MESSAGE("Value %s added", Value); + MESSAGE(VERBOSE_PARSERS, "Value %s added", Value); } void cSearch::pushExistance(const char* Exists){ this->CurrentValue = strdup(Exists); this->CurrentOperator = strdup("IS"); - MESSAGE("Existance expression called. '%s'", Exists); + MESSAGE(VERBOSE_PARSERS, "Existance expression called. '%s'", Exists); } void cSearch::pushConcatOp(const char* Operator){ @@ -429,11 +439,11 @@ void cSearch::pushConcatOp(const char* Operator){ return; } - MESSAGE("Concatenation expression called. '%s'", Operator); + MESSAGE(VERBOSE_PARSERS, "Concatenation expression called. '%s'", Operator); } void cSearch::pushStartBrackedExp(const char){ - MESSAGE("Pushing opening bracket"); + MESSAGE(VERBOSE_PARSERS, "Pushing opening bracket"); if(asprintf(&this->SQLWhereStmt, "%s(", this->SQLWhereStmt)==-1){ ERROR("Unable to allocate SQL Statement"); return; @@ -478,13 +488,13 @@ bool cSearch::parseCriteria(const char* Search){ pushConcatOpCB, startBrackedExpCB); - MESSAGE("Starting search parsing"); + MESSAGE(VERBOSE_PARSERS, "Starting search parsing"); if(boost::spirit::parse(Search, Grammar).full){ - MESSAGE("Parsing successful"); + MESSAGE(VERBOSE_DIDL, "Parsed search expression successfuly"); } else { - ERROR("Parsing failed"); + ERROR("Parsing search expression failed"); return false; } return true; @@ -532,7 +542,7 @@ bool cFilterCriteria::parseFilter(const char* Filter){ this->mFilterList = new cStringList; if(Filter && !strcasecmp(Filter, "")){ - MESSAGE("Empty filter"); + MESSAGE(VERBOSE_PARSERS, "Empty filter"); return true; } @@ -542,7 +552,7 @@ bool cFilterCriteria::parseFilter(const char* Filter){ cFilterGrammar Grammar(pushPropertyCB, pushAsteriskCB); if(boost::spirit::parse(Filter, Grammar).full){ - MESSAGE("Parse filter successful"); + MESSAGE(VERBOSE_PARSERS, "Parse filter successful"); } else { ERROR("Parsing filter failed"); @@ -558,12 +568,12 @@ bool cFilterCriteria::parseFilter(const char* Filter){ \**********************************************/ void cFilterCriteria::pushProperty(const char* Property){ - MESSAGE("Pushing property"); + MESSAGE(VERBOSE_PARSERS, "Pushing property"); this->mFilterList->Append(strdup(Property)); } void cFilterCriteria::pushAsterisk(const char){ - MESSAGE("Pushing asterisk (*)"); + MESSAGE(VERBOSE_PARSERS, "Pushing asterisk (*)"); if(this->mFilterList) delete this->mFilterList; this->mFilterList = NULL; return; @@ -596,7 +606,7 @@ cList<cSortCrit>* cSortCriteria::parse(const char* Sort){ bool cSortCriteria::parseSort(const char* Sort){ if(!Sort || !strcasecmp(Sort, "")){ - MESSAGE("Empty Sort"); + MESSAGE(VERBOSE_PARSERS, "Empty Sort"); return true; } @@ -606,7 +616,7 @@ bool cSortCriteria::parseSort(const char* Sort){ cSortGrammar Grammar(pushPropertyCB, pushDirectionCB); if(boost::spirit::parse(Sort, Grammar).full){ - MESSAGE("Parse Sort successful"); + MESSAGE(VERBOSE_PARSERS, "Parse Sort successful"); } else { ERROR("Parsing Sort failed"); @@ -622,14 +632,14 @@ bool cSortCriteria::parseSort(const char* Sort){ \**********************************************/ void cSortCriteria::pushProperty(const char* Property){ - MESSAGE("Pushing property '%s'", Property); + MESSAGE(VERBOSE_PARSERS, "Pushing property '%s'", Property); this->mCurrentCrit->Property = strdup(Property); this->mCriteriaList->Add(this->mCurrentCrit); return; } void cSortCriteria::pushDirection(const char Direction){ - MESSAGE("Pushing direction '%c'", Direction); + MESSAGE(VERBOSE_PARSERS, "Pushing direction '%c'", Direction); this->mCurrentCrit = new cSortCrit; this->mCurrentCrit->SortDescending = (Direction=='-')?true:false; return; @@ -641,6 +651,7 @@ void cSortCriteria::pushDirection(const char Direction){ * * \**********************************************/ +/** @private */ struct cWebserverSections : symbols<> { cWebserverSections(){ add @@ -649,6 +660,7 @@ struct cWebserverSections : symbols<> { } } WebserverSections; +/** @private */ struct cWebserverMethods : symbols<int> { cWebserverMethods(){ add @@ -661,6 +673,7 @@ struct cWebserverMethods : symbols<int> { } } WebserverMethods; +/** @private */ struct cPathParserGrammar : public boost::spirit::grammar<cPathParserGrammar> { intCallback &pushSection; @@ -678,6 +691,7 @@ struct cPathParserGrammar : public boost::spirit::grammar<cPathParserGrammar> { pushPropertyValue(pushPropertyValue){} template <typename scanner> + /** @private */ struct definition { boost::spirit::rule<scanner> pathExp, section, method, methodProperties, property, key, value, uncriticalChar; @@ -742,7 +756,7 @@ bool cPathParser::parsePath(const char* Path, int* Section, int* Method, propert cPathParserGrammar Grammar(pushSectionCB, pushMethodCB, pushPropertyKeyCB, pushPropertyValueCB); if(boost::spirit::parse(Path, Grammar).full){ - MESSAGE("Parse path successful"); + MESSAGE(VERBOSE_PARSERS, "Parse path successful"); *Section = this->mSection; *Method = this->mMethod; *Properties = this->mProperties; @@ -765,7 +779,7 @@ bool cPathParser::parsePath(const char* Path, int* Section, int* Method, propert void cPathParser::pushPropertyKey(const char* Start, const char* End){ char* Key = strndup(Start, End-Start); - MESSAGE("Pushing key '%s'", Key); + MESSAGE(VERBOSE_PARSERS, "Pushing key '%s'", Key); this->mKey = Key; @@ -775,7 +789,7 @@ void cPathParser::pushPropertyKey(const char* Start, const char* End){ void cPathParser::pushPropertyValue(const char* Start, const char* End){ char* Value = strndup(Start, End-Start); - MESSAGE("Pushing value '%s'", Value); + MESSAGE(VERBOSE_PARSERS, "Pushing value '%s'", Value); // TODO: urlDecode Value if(*this->mKey){ @@ -785,11 +799,11 @@ void cPathParser::pushPropertyValue(const char* Start, const char* End){ } void cPathParser::pushMethod(int Method){ - MESSAGE("Pushing method '%d'", Method); + MESSAGE(VERBOSE_PARSERS, "Pushing method '%d'", Method); this->mMethod = Method; } void cPathParser::pushSection(int Section){ - MESSAGE("Pushing section '%d'", Section); + MESSAGE(VERBOSE_PARSERS, "Pushing section '%d'", Section); this->mSection = Section; }
\ No newline at end of file diff --git a/misc/search.h b/misc/search.h index df2442e..ef162b1 100644 --- a/misc/search.h +++ b/misc/search.h @@ -12,13 +12,27 @@ #include <vdr/tools.h> #include "util.h" +/** + * Sort criteria + * + * This is a structure for sorting objects. It has a certain property and + * a direction flag. + */ struct cSortCrit : public cListObject { - const char* Property; - bool SortDescending; + const char* Property; ///< the Property, which shall be sorted + bool SortDescending; ///< sort the objects in descending order }; typedef std::map<const char*, const char*, strCmp> propertyMap; +/** + * Web path parser + * + * Parses paths which came from the webserver. It splits the path into + * a section, a certain method and its properties. + * + * This can be used to easily determine which file was requested by a client + */ class cPathParser { private: cString mKey; @@ -33,9 +47,30 @@ private: cPathParser(); public: virtual ~cPathParser(); - static bool parse(const char* Path, int* Section, int* Method, propertyMap* Properties); + /** + * Parses the path + * + * This will parse the path and stores the result in the pointers given. + * + * @return returns + * - \bc true, if the parsing was successful + * - \bc false, otherwise + */ + static bool parse( + const char* Path, ///< the path which is parsed + int* Section, ///< the number of the registered section + int* Method, ///< the number of the registered method + propertyMap* Properties ///< the properties found in the path + ); }; +/** + * Creates a list with sort criteria + * + * This parser creates a list of sort criteria. It parses the sort criteria string + * from a \em Browse or \em Search request and stores the information in a \c cSortCrit + * structure. + */ class cSortCriteria { private: cSortCrit* mCurrentCrit; @@ -47,9 +82,26 @@ private: cSortCriteria(); public: virtual ~cSortCriteria(); - static cList<cSortCrit>* parse(const char* Sort); + /** + * Parses the sort criteria + * + * This parses the sort criteria and returns a list with valid criterias + * + * @return returns + * - a list with valid sort criterias + * - \bc null, otherwise + */ + static cList<cSortCrit>* parse( + const char* Sort ///< the string container the sort criteria + ); }; +/** + * Parses the filter criteria + * + * This parses the filter criteria which comes from a \em Browse or \em Search + * request. + */ class cFilterCriteria { private: cStringList* mFilterList; @@ -60,9 +112,23 @@ private: cStringList* getFilterList() const { return this->mFilterList; } public: virtual ~cFilterCriteria(); - static cStringList* parse(const char* Filter); + /** + * Parses the filter criteria + * + * This parses the filter criteria. It may be a empty string list, a \bc NULL + * pointer or a list with properties which shall be shown in the \em DIDL-Lite fragment. + * + * @return the stringlist containing the filter + */ + static cStringList* parse( + const char* Filter ///< the filter string + ); }; +/** + * @private + * @todo This is implemented very soon + */ class cSearch { private: char* SQLWhereStmt; diff --git a/misc/util.cpp b/misc/util.cpp index dc75706..8cfc524 100644 --- a/misc/util.cpp +++ b/misc/util.cpp @@ -240,25 +240,6 @@ eOSState cMenuEditIpItem::ProcessKey(eKeys Key) { return osContinue; } -const char* escapeSQLite(const char* Data, char** Buf){ - if(!Data){ - *Buf = NULL; - } - else { - std::string NewData = ""; - int Char = 0; - for(unsigned int i = 0; i < strlen(Data); i++){ - Char = Data[i]; - switch(Char){ - case L'\'': NewData += "''"; break; - default: NewData += Data[i]; break; - } - } - *Buf = strdup(NewData.c_str()); - } - return (*Buf); -} - const char* escapeXMLCharacters(const char* Data, char** Buf){ if(Data==NULL){ ERROR("Escape XML: No data to escape"); diff --git a/misc/util.h b/misc/util.h index ffedd67..6ff2a54 100644 --- a/misc/util.h +++ b/misc/util.h @@ -14,19 +14,116 @@ #ifdef __cplusplus extern "C" { -struct strCmp { bool operator()(const char* s1, const char* s2) const { return (strcmp(s1,s2) < 0); }}; +#if 0 +} +#endif +/** + * Compares two strings + * + * This struct compares two strings and returns true on equality or false otherwise + * It is used in conjuction with hashmaps + */ +struct strCmp { + /** + * Compares two strings + * @return returns + * - \bc true, in case of equality + * - \bc false, otherwise + * @param s1 the first string + * @param s2 the second string + */ + bool operator()(const char* s1, const char* s2) const { return (strcmp(s1,s2) < 0); } +}; +/** + * Gets the IP address + * + * Returns the IP address of a given interface. The interface must be a valid interface + * identifier like eth0 or wlan1. + * + * @return a structure containing the IP address + * @param Interface to obtain the IP from + */ const sockaddr_in* getIPFromInterface(const char* Interface); +/** + * Gets the MAC address + * + * Returns a string representation of the MAC address of a given interface. The interface + * must be a valid interface identifier like eth0 or wlan1. + * + * The pattern of the address is sixth byte hex number separated with ":" + * + * @return a string containing the MAC + * @param Interface to obtain the MAC from + */ const char* getMACFromInterface(const char* Interface); +/** + * List with interfaces + * + * Returns an array with interfaces found on the system. The number of items + * in the array is stored in the parameter \c count. + * + * @return array list of interfaces + * @param count number of interfaces in the array + */ char** getNetworkInterfaces(int *count); +/** + * First occurance of item + * + * Finds the first occurance of a specified item in a given \bc IXML document and returns its value. + * If an error occures, its code is stored in the last parameter \c 'error'. + * + * @return the value of the item + * @param doc the \c IXML document to be parsed + * @param item the item which shall be found + * @param error the error code in case of an error + */ char* ixmlGetFirstDocumentItem( IN IXML_Document * doc, IN const char *item, int* error ); +/** + * Adds a property + * + * This adds a UPnP property to an \bc IXML document. + * The property must have the pattern "namespace:property@attribute". + * + * @return returns + * - \bc <0, in case of an error + * - \bc 0, otherwise + * @param document the \c IXML document to put the parameter in + * @param node the specific node where to put the parameter + * @param upnpproperty the upnp property + * @param value the value of the upnp property + */ int ixmlAddProperty(IN IXML_Document* document, IN IXML_Element* node, const char* upnpproperty, const char* value ); +/** + * creates a part of a string + * + * This creates a substring of a string which begins at the given offset and has the + * specified lenght. + * + * @return the new string + * @param str the full string + * @param offset the starting index + * @param length the length of the new string + */ char* substr(const char* str, unsigned int offset, unsigned int length); +#if 0 +{ +#endif } #endif -const char* escapeSQLite(const char* Data, char** Buf); +/** + * Escapes XML special characters + * + * This function escapes XML special characters, which must be transformed before + * inserting it into another XML document. + * + * @return the escaped document + * @param Data the data to escape + * @param Buf the pointer where the escaped document shall be stored + */ const char* escapeXMLCharacters(const char* Data, char** Buf); +/** @private */ class cMenuEditIpItem: public cMenuEditItem { private: char *value; diff --git a/receiver/filehandle.h b/receiver/filehandle.h index 37f06e8..1dc57bf 100644 --- a/receiver/filehandle.h +++ b/receiver/filehandle.h @@ -11,12 +11,94 @@ #include <upnp/upnp.h> #include "../common.h" +/** + * Interface for File Handles + * + * This class is a pure virtual class to act as an interface for file handles + * used by the webserver. + */ class cFileHandle { public: - virtual void open(UpnpOpenFileMode mode) = 0; - virtual int read(char* buf, size_t buflen) = 0; - virtual int write(char* buf, size_t buflen) = 0; - virtual int seek(off_t offset, int whence) = 0; + /** + * Opens the file + * + * Opens the file at the given mode. These can be: + * - \b UPNP_READ, to read from the file + * - \b UPNP_WRITE, to write to the file + * + * @param mode The file mode, i.e. one of the following + * - \b UPNP_READ + * - \b UPNP_WRITE + */ + virtual void open( + UpnpOpenFileMode mode ///< The file mode, i.e. one of the following + ///< - \b UPNP_READ + ///< - \b UPNP_WRITE + ) = 0; + /** + * Reads from the file + * + * Reads from the file a certain amount of bytes and stores them in a buffer + * + * @return returns + * - \b <0, in case of an error + * - \b 0, when reading was successful + * + * @param buf The char buffer + * @param buflen The size of the buffer + */ + virtual int read( + char* buf, ///< The char buffer + size_t buflen ///< The size of the buffer + ) = 0; + /** + * Writes to the file + * + * Writes to the file a certain amount of bytes which are stored in a buffer + * + * @return returns + * - \b <0, in case of an error + * - \b 0, when reading was successful + * + * @param buf The char buffer + * @param buflen The size of the buffer + */ + virtual int write( + char* buf, ///< The char buffer + size_t buflen ///< The size of the buffer + ) = 0; + /** + * Seeks in the file + * + * Seeks in the file where the offset is the relativ position depending on + * the second parameter. This means, in case of + * + * - \b SEEK_SET, the offset is relative to the beginning of the file + * - \b SEEK_CUR, it is relative to the current position or + * - \b SEEK_END, relative to the end of the file. + * + * @return returns + * - \b <0, in case of an error + * - \b 0, when reading was successful + * + * @param offset The byte offset in the file + * @param whence one of the following + * - \b SEEK_SET, + * - \b SEEK_CUR, + * - \b SEEK_END + */ + virtual int seek( + off_t offset, ///< The byte offset in the file + int whence ///< one of the following + ///< - \b SEEK_SET, + ///< - \b SEEK_CUR, + ///< - \b SEEK_END + ) = 0; + /** + * Closes the open file + * + * This will close open file handles and frees the memory obtained by it. + */ virtual void close() = 0; virtual ~cFileHandle(){}; private: diff --git a/receiver/livereceiver.cpp b/receiver/livereceiver.cpp index 189f4a4..8ca560f 100644 --- a/receiver/livereceiver.cpp +++ b/receiver/livereceiver.cpp @@ -22,7 +22,7 @@ cLiveReceiver* cLiveReceiver::newInstance(cChannel* Channel, int Priority){ cLiveReceiver *Receiver = new cLiveReceiver(Channel, Device); if(Receiver){ - MESSAGE("Receiver for channel \"%s\" created successfully.", Channel->Name()); + MESSAGE(VERBOSE_SDK, "Receiver for channel \"%s\" created successfully.", Channel->Name()); return Receiver; } else { @@ -62,13 +62,13 @@ void cLiveReceiver::open(UpnpOpenFileMode){ void cLiveReceiver::Activate(bool On){ if(On){ this->Start(); - MESSAGE("Live receiver started."); + MESSAGE(VERBOSE_LIVE_TV, "Live receiver started."); } else { if(this->Running()){ this->Cancel(2); } - MESSAGE("Live receiver stopped"); + MESSAGE(VERBOSE_LIVE_TV, "Live receiver stopped"); } } @@ -82,20 +82,20 @@ void cLiveReceiver::Receive(uchar* Data, int Length){ } void cLiveReceiver::Action(void){ - MESSAGE("Started buffering..."); + MESSAGE(VERBOSE_LIVE_TV, "Started buffering..."); while(this->Running()){ int bytesRead; - //MESSAGE("Buffer is filled with %d bytes", this->mLiveBuffer->Available()); + MESSAGE(VERBOSE_BUFFERS, "Buffer is filled with %d bytes", this->mLiveBuffer->Available()); uchar* bytes = this->mLiveBuffer->Get(bytesRead); if(bytes){ int count = this->mFrameDetector->Analyze(bytes, bytesRead); if(count){ - //MESSAGE("%d bytes analyzed", count); - //MESSAGE("%2.2f FPS", this->mFrameDetector->FramesPerSecond()); + MESSAGE(VERBOSE_BUFFERS, "%d bytes analyzed", count); + MESSAGE(VERBOSE_BUFFERS, "%2.2f FPS", this->mFrameDetector->FramesPerSecond()); if(!this->Running() && this->mFrameDetector->IndependentFrame()) break; if(this->mFrameDetector->Synced()){ - //MESSAGE("Frame detector synced to data stream"); + MESSAGE(VERBOSE_BUFFERS, "Frame detector synced to data stream"); if(this->mFrameDetector->IndependentFrame()){ this->mOutputBuffer->Put(this->mPatPmtGenerator.GetPat(), TS_SIZE); int i = 0; @@ -107,7 +107,7 @@ void cLiveReceiver::Action(void){ if(bytesWrote != count){ this->mLiveBuffer->ReportOverflow(count - bytesWrote); } - //MESSAGE("Wrote %d to output buffer", bytesWrote); + MESSAGE(VERBOSE_BUFFERS, "Wrote %d to output buffer", bytesWrote); if(bytesWrote){ this->mLiveBuffer->Del(bytesWrote); } @@ -121,7 +121,7 @@ void cLiveReceiver::Action(void){ } } } - MESSAGE("Receiver was detached from device"); + MESSAGE(VERBOSE_LIVE_TV, "Receiver was detached from device"); } int cLiveReceiver::read(char* buf, size_t buflen){ @@ -135,7 +135,7 @@ int cLiveReceiver::read(char* buf, size_t buflen){ WARNING("Too few data, waiting..."); cCondWait::SleepMs(RECEIVER_WAIT_ON_NODATA); if(!this->IsAttached()){ - MESSAGE("Lost device..."); + MESSAGE(VERBOSE_LIVE_TV, "Lost device..."); return 0; } WaitTimeout-=RECEIVER_WAIT_ON_NODATA; @@ -160,7 +160,7 @@ int cLiveReceiver::read(char* buf, size_t buflen){ } } - MESSAGE("Read %d bytes from live feed", bytesRead); + MESSAGE(VERBOSE_BUFFERS, "Read %d bytes from live feed", bytesRead); return bytesRead; } @@ -175,10 +175,10 @@ int cLiveReceiver::write(char*, size_t){ } void cLiveReceiver::close(){ - MESSAGE("Closing live receiver"); + MESSAGE(VERBOSE_SDK, "Closing live receiver"); this->Detach(); delete this->mOutputBuffer; this->mOutputBuffer = NULL; delete this->mLiveBuffer; this->mLiveBuffer = NULL; this->mFrameDetector = NULL; - MESSAGE("Live receiver closed."); + MESSAGE(VERBOSE_LIVE_TV, "Live receiver closed."); }
\ No newline at end of file diff --git a/receiver/livereceiver.h b/receiver/livereceiver.h index 6b04619..92fe140 100644 --- a/receiver/livereceiver.h +++ b/receiver/livereceiver.h @@ -17,18 +17,78 @@ #define RECEIVER_WAIT_ON_NODATA 50 // 50 ms #define RECEIVER_WAIT_ON_NODATA_TIMEOUT 1000 * 2 // 2s +/** + * A receiver for live TV + * + * This is a receiver object which is attached to a VDR tv card device. + * It is receiving transport stream packages and generates a single MPEG2 + * transport stream which can be distributed through the network. + * + */ class cLiveReceiver : public cReceiver, public cThread, public cFileHandle { public: - static cLiveReceiver* newInstance(cChannel *Channel, int Priority); + /** + * Creates a new receiver instance + * + * This will create a new instance of a live receiver for the specified + * channel at the specified priority level. + * + * A negativ priority means that the receiver may being detached from a + * device. + * + * The receiver must be free'd with delete after it is not used anylonger. + * + * @return returns a new liveReceiver instance + */ + static cLiveReceiver* newInstance( + cChannel *Channel, ///< the channel which shall be tuned + int Priority ///< the priority level + ); virtual ~cLiveReceiver(void); + /*! @copydoc cFileHandle::open(UpnpOpenFileMode) */ virtual void open(UpnpOpenFileMode mode); + /*! @copydoc cFileHandle::read(char*,size_t) */ virtual int read(char* buf, size_t buflen); + /*! @copydoc cFileHandle::write(char*,size_t) */ virtual int write(char* buf, size_t buflen); + /*! @copydoc cFileHandle::seek(off_t,int) */ virtual int seek(off_t offset, int whence); + /*! @copydoc cFileHandle::close() */ virtual void close(); protected: - virtual void Receive(uchar *Data, int Length); - virtual void Activate(bool On); + /** + * Receives data from VDR + * + * This is the interface for receiving packet data from the VDR. It buffers + * the incoming transport stream packets in a linear ringbuffer and returns + * immediatelly + */ + virtual void Receive( + uchar *Data, ///< The data received from VDR + int Length ///< The length of the data packet, usually 188 bytes + ); + /** + * Activates the receiver + * + * This activates the receiver which initializes internal data structures to + * be prepared for receiving data from the VDR + * + * If the parameter is \bc true, the receiver will be activated. If it is + * \bc false, the receiver will be deactivated and stops its threads. + */ + virtual void Activate( + bool On ///< Activates the receiver thread + ); + /** + * The receiver thread action + * + * This actually is the receiver thread, which runs consequitivelly and + * buffers any received video data from the interal incoming buffer to the + * internal outgoing buffer. + * + * While doing so, it tries to syncronize with the stream and creates new + * MPEG2-TS PATs and PMTs for a single MPEG2-TS stream + */ virtual void Action(void); private: cLiveReceiver(cChannel *Channel, cDevice *Device); diff --git a/receiver/recplayer.cpp b/receiver/recplayer.cpp index d968ae7..a9ea85b 100644 --- a/receiver/recplayer.cpp +++ b/receiver/recplayer.cpp @@ -25,7 +25,7 @@ cRecordingPlayer::~cRecordingPlayer() { } cRecordingPlayer::cRecordingPlayer(cRecording *Recording) { - MESSAGE("Created Recplayer"); + MESSAGE(VERBOSE_SDK, "Created Recplayer"); this->mFile = NULL; this->mTotalLenght = 0; this->mRecording = Recording; @@ -118,11 +118,11 @@ int cRecordingPlayer::seek(off_t offset, int origin){ } void cRecordingPlayer::Scan(){ - MESSAGE("Scanning video files..."); + MESSAGE(VERBOSE_RECORDS, "Scanning video files..."); // Reset the offsets int i = 1; while(this->mOffsets[i++]) this->mOffsets[i] = 0; - MESSAGE("Offsets reseted."); + MESSAGE(VERBOSE_RECORDS, "Offsets reseted."); i = 0; FILE *File; @@ -133,7 +133,7 @@ void cRecordingPlayer::Scan(){ } fseek(File, 0, SEEK_END); off_t offset = ftell(File); - MESSAGE("File %d has its last offset at %ld", i, offset); + MESSAGE(VERBOSE_RECORDS, "File %d has its last offset at %ld", i, offset); this->mOffsets[i+1] = this->mOffsets[i] + offset; this->mTotalLenght = this->mOffsets[i+1]; i++; @@ -150,7 +150,7 @@ FILE *cRecordingPlayer::GetFile(int Index){ if(this->mFile) fclose(this->mFile); char *filename = new char[VDR_FILENAME_BUFSIZE]; snprintf(filename, VDR_FILENAME_BUFSIZE, VDR_RECORDFILE_PATTERN_TS, this->mRecording->FileName(), Index ); - MESSAGE("Filename: %s", filename); + MESSAGE(VERBOSE_BUFFERS, "Filename: %s", filename); this->mFile = NULL; if(this->mFile = fopen(filename, "r")){ this->mIndex = Index; diff --git a/receiver/recplayer.h b/receiver/recplayer.h index 8295539..a2e69ac 100644 --- a/receiver/recplayer.h +++ b/receiver/recplayer.h @@ -12,8 +12,26 @@ #include "filehandle.h" #include <vdr/recording.h> +/** + * The recording player + * + * This class provides the ability to play VDR records. The difference between + * usual files and VDR recording files is, that recordings are possibly splitted + * into multiple files. The class will scan those files and tries to dynamically + * navigate in them like it would do, if it is a single file. + * + */ class cRecordingPlayer : cFileHandle { public: + /** + * Get a new instance of a recording player + * + * This returns a new instance of a recording player which plays the + * specified VDR recording. + * + * @param Recording the recording to play + * @return the new instance of the recording player + */ static cRecordingPlayer *newInstance(cRecording *Recording); virtual ~cRecordingPlayer(); virtual void open(UpnpOpenFileMode mode); diff --git a/server/server.cpp b/server/server.cpp index 03750bd..d98c78b 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -50,7 +50,7 @@ cUPnPServer::~cUPnPServer() { bool cUPnPServer::init(void){ - MESSAGE("Loading configuration..."); + MESSAGE(VERBOSE_SDK, "Loading configuration..."); cUPnPConfig* config = cUPnPConfig::get(); this->enable(config->mEnable == 1 ? true : false); if(!config->mAutoSetup){ @@ -80,7 +80,7 @@ bool cUPnPServer::init(void){ } } - MESSAGE("Initializing Intel UPnP SDK on %s:%d",inet_ntoa(this->mServerAddr->sin_addr), ntohs(this->mServerAddr->sin_port)); + MESSAGE(VERBOSE_SDK, "Initializing Intel UPnP SDK on %s:%d",inet_ntoa(this->mServerAddr->sin_addr), ntohs(this->mServerAddr->sin_port)); int ret = 0; ret = UpnpInit(inet_ntoa(this->mServerAddr->sin_addr), ntohs(this->mServerAddr->sin_port)); @@ -98,18 +98,18 @@ bool cUPnPServer::init(void){ ERROR("Unable to set IP address"); } this->mServerAddr->sin_port = htons(UpnpGetServerPort()); - MESSAGE("Initializing succesfully at %s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort()); + MESSAGE(VERBOSE_SDK, "Initializing succesfully at %s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort()); } - MESSAGE("Setting maximum packet size for SOAP requests"); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Setting maximum packet size for SOAP requests"); UpnpSetMaxContentLength(UPNP_SOAP_MAX_LEN); //set the root directory of the webserver cString WebserverRootDir = cString::sprintf("%s%s", cPluginUpnp::getConfigDirectory(), UPNP_WEB_SERVER_ROOT_DIR); - MESSAGE("Set web server root dir: %s", *WebserverRootDir ); + MESSAGE(VERBOSE_SDK, "Set web server root dir: %s", *WebserverRootDir ); this->mWebServer = cUPnPWebServer::getInstance(WebserverRootDir); - MESSAGE("Initializing web server."); + MESSAGE(VERBOSE_SDK, "Initializing web server."); if (!this->mWebServer->init()) { ERROR("Error while setting web server root dir - Errorcode: %d", ret); return false; @@ -120,7 +120,7 @@ bool cUPnPServer::init(void){ this->mDeviceDescription = cString(cDlna::getInstance()->getDeviceDescription(URLBase),true); - MESSAGE("Register Media Server Device"); + MESSAGE(VERBOSE_SDK, "Register Media Server Device"); ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, this->mDeviceDescription, sizeof(this->mDeviceDescription), 1, &cUPnPServer::upnpActionCallback, @@ -131,14 +131,14 @@ bool cUPnPServer::init(void){ return false; } - MESSAGE("Unregister server to cleanup previously started servers"); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Unregister server to cleanup previously started servers"); ret = UpnpUnRegisterRootDevice(this->mDeviceHandle); if (ret != UPNP_E_SUCCESS) { WARNING("Unregistering old devices failed"); return false; } - MESSAGE("Register Media Server Device"); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Register Media Server Device"); ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, this->mDeviceDescription, sizeof(this->mDeviceDescription), 1, &cUPnPServer::upnpActionCallback, @@ -149,16 +149,16 @@ bool cUPnPServer::init(void){ return false; } - MESSAGE("Initializing media database"); + MESSAGE(VERBOSE_SDK, "Initializing media database"); this->mMediaDatabase = new cMediaDatabase; if(!this->mMediaDatabase->init()){ ERROR("Error while initializing database"); return false; } - MESSAGE("Initializing connection manager"); + MESSAGE(VERBOSE_SDK, "Initializing connection manager"); cUPnPServer::mConnectionManager = new cConnectionManager(this->mDeviceHandle); - MESSAGE("Initializing content directory"); + MESSAGE(VERBOSE_SDK, "Initializing content directory"); cUPnPServer::mContentDirectory = new cContentDirectory(this->mDeviceHandle, this->mMediaDatabase); if(!cUPnPServer::mContentDirectory->Start()){ ERROR("Unable to start content directory thread"); @@ -166,7 +166,7 @@ bool cUPnPServer::init(void){ } //send first advertisments - MESSAGE("Send first advertisements to publish start in network"); + MESSAGE(VERBOSE_SDK, "Send first advertisements to publish start in network"); ret = UpnpSendAdvertisement(this->mDeviceHandle, UPNP_ANNOUNCE_MAX_AGE); if (ret != UPNP_E_SUCCESS) { ERROR("Error while sending first advertisments - Errorcode: %d", ret); @@ -177,20 +177,20 @@ bool cUPnPServer::init(void){ } bool cUPnPServer::uninit(void) { - MESSAGE("Shuting down content directory"); + MESSAGE(VERBOSE_SDK, "Shuting down content directory"); delete cUPnPServer::mContentDirectory; cUPnPServer::mContentDirectory = NULL; - MESSAGE("Shuting down connection manager"); + MESSAGE(VERBOSE_SDK, "Shuting down connection manager"); delete cUPnPServer::mConnectionManager; cUPnPServer::mConnectionManager = NULL; - MESSAGE("Closing metadata database"); + MESSAGE(VERBOSE_SDK, "Closing metadata database"); delete this->mMediaDatabase; this->mMediaDatabase = NULL; - MESSAGE("Closing the web server"); + MESSAGE(VERBOSE_SDK, "Closing the web server"); this->mWebServer->uninit(); delete this->mWebServer; - MESSAGE("Close Intel SDK"); + MESSAGE(VERBOSE_SDK, "Close Intel SDK"); // unregiser media server device from UPnP SDK int ret = UpnpUnRegisterRootDevice(this->mDeviceHandle); if (ret != UPNP_E_SUCCESS) { @@ -200,7 +200,7 @@ bool cUPnPServer::uninit(void) { ret = UpnpFinish(); if (ret == UPNP_E_SUCCESS) { - MESSAGE("Close Intel SDK Successfull"); + MESSAGE(VERBOSE_SDK, "Close Intel SDK Successfull"); return true; } else { ERROR("Intel SDK unintialized or already closed - Errorcode: %d", ret); @@ -277,7 +277,7 @@ bool cUPnPServer::autoDetectSettings(void){ char** Ifaces = getNetworkInterfaces(&count); int i=0; bool ret = false; - MESSAGE("AUTODETECT: Found %d possible interfaces.", sizeof(Ifaces)); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "AUTODETECT: Found %d possible interfaces.", sizeof(Ifaces)); while(Ifaces[i]){ if(strcmp(Ifaces[i],"lo")!=0){ // true || false == true @@ -288,7 +288,7 @@ bool cUPnPServer::autoDetectSettings(void){ } delete [] Ifaces; if(!ret){ - MESSAGE("AUTODETECT: No suitable interface. Giving up."); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "AUTODETECT: No suitable interface. Giving up."); return false; } this->setServerPort(0); @@ -299,8 +299,8 @@ bool cUPnPServer::start(void){ if(!this->isRunning()){ // Put all the stuff which shall be started with the server in here // if the startup failed due any reason return false! - MESSAGE("Starting UPnP Server on %s:%d",inet_ntoa(this->getServerAddress()->sin_addr), ntohs(this->getServerAddress()->sin_port)); - MESSAGE("Using DLNA version: %s", DLNA_PROTOCOL_VERSION_STR); + MESSAGE(VERBOSE_SDK, "Starting UPnP Server on %s:%d",inet_ntoa(this->getServerAddress()->sin_addr), ntohs(this->getServerAddress()->sin_port)); + MESSAGE(VERBOSE_SDK, "Using DLNA version: %s", DLNA_PROTOCOL_VERSION_STR); this->mIsRunning = true; // Start Media database thread this->mMediaDatabase->Start(); @@ -310,7 +310,7 @@ bool cUPnPServer::start(void){ void cUPnPServer::stop(void){ if(this->isRunning()){ - MESSAGE("Call upnpServer STOP"); + MESSAGE(VERBOSE_SDK, "Call upnpServer STOP"); this->uninit(); this->mIsRunning = false; } @@ -318,7 +318,7 @@ void cUPnPServer::stop(void){ } bool cUPnPServer::restart(void){ - MESSAGE("Call upnpServer RESTART"); + MESSAGE(VERBOSE_SDK, "Call upnpServer RESTART"); this->stop(); return this->start(); } @@ -331,11 +331,11 @@ bool cUPnPServer::setInterface(const char* Interface){ if(Interface != NULL) this->mInterface = Interface; if(*this->mInterface!=NULL){ - MESSAGE("Try to retrieve address for NIC %s",Interface); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "Try to retrieve address for NIC %s",Interface); const sockaddr_in* ipAddress = getIPFromInterface(Interface); if(ipAddress!=NULL){ memcpy(&this->mServerAddr->sin_addr,&ipAddress->sin_addr,sizeof(ipAddress->sin_addr)); - MESSAGE("NIC %s has the following IP: %s", *this->mInterface, inet_ntoa(this->mServerAddr->sin_addr)); + MESSAGE(VERBOSE_CUSTOM_OUTPUT, "NIC %s has the following IP: %s", *this->mInterface, inet_ntoa(this->mServerAddr->sin_addr)); this->stop(); return true; } diff --git a/server/server.h b/server/server.h index 72ed2c8..c2a0e9a 100644 --- a/server/server.h +++ b/server/server.h @@ -20,6 +20,13 @@ #include "../upnpcomponents/contentdirectory.h" #include "../upnp.h" +/** + * The UPnP Server + * + * This is the core of the UPnP server. This handles all the components which + * are needed for a UPnP media server. Incoming messages are passed through it + * and it determines what to do. + */ class cUPnPServer { friend class cPluginUpnp; public: @@ -42,7 +49,10 @@ public: * * This switch indicates if the server is startable or not * - * If it is set to FALSE, any invocation of start() will do nothing. + * If it is set to false, any invocation of start() will do nothing. + * + * @param enabled if \bc true, the server will be enabled. If \bc false it is + * disabled. */ void enable(bool enabled); /** @@ -50,7 +60,9 @@ public: * * This will start the UPnP server activities as a background task. * - * @return 1 when the server started successfully, 0 otherwise + * @return returns + * - \bc true, when the server started successfully + * - \bc false, otherwise */ bool start(void); /** @@ -60,7 +72,9 @@ public: * It will stop the server functionalities, clear everything and * start it again. * - * @return 1 when the server restarted successfully, 0 otherwise + * @return returns + * - \bc true, when the server restarted successfully + * - \bc false, otherwise */ bool restart(void); /** @@ -70,6 +84,16 @@ public: * any clients and open ports will be closed. */ void stop(void); + /** + * Automatically detect settings + * + * This will automatically detect the network settings if the autodetection + * is turned on. + * + * @return returns + * - \bc true, if autoDetection was successful + * - \bc false, otherwise + */ bool autoDetectSettings(void); /** * Get the server address @@ -82,7 +106,7 @@ public: /** * Get the interface the server listens to * - * Returns the network interface + * @return the network interface */ const char* getInterface(void) const { return this->mInterface; } /** @@ -98,13 +122,20 @@ public: * Returns 1 when the port is valid, 0 otherwise * * @param port The port of the server - * @return 1 if the new server address is set, 0 otherwise + * @return returns + * - \bc true, if the new server port is set + * - \bc false, otherwise */ bool setServerPort(unsigned short port); /** * The Interface to listen on * * Sets the listener interface, for instance 'eth1' or 'wlan0' + * + * @param Interface The interface of the server + * @return returns + * - \bc true, if the new server address is set + * - \bc false, otherwise */ bool setInterface(const char* Interface); /** @@ -115,7 +146,12 @@ public: * changes through the system. * * This method should only be used in cases of fixed IP addresses - * for example when no DHCP server is available. + * for example when no DHCP server is available. + * + * @param Address The address of the server + * @return returns + * - \bc true, if the new server address is set + * - \bc false, otherwise */ bool setAddress(const char* Address); /** @@ -123,6 +159,11 @@ public: * * If this is set to true, the setup will get it's information via * auto detection + * + * @param enable \bc true enables and \bc false disables the auto detection + * @return returns + * - \bc true, if the new server address is set + * - \bc false, otherwise */ bool setAutoDetection(bool enable); /** @@ -130,7 +171,9 @@ public: * * This indicates if the server is currently enabled. * - * @return 1 if the server is enabled, 0 otherwise + * @return returns + * - \bc true, if the server is enabled + * - \bc false, otherwise */ bool isEnabled(void) const { return this->mIsEnabled; } /** @@ -138,13 +181,19 @@ public: * * If the server is enabled, this indicates if it is running. * - * @return 1 if the server is running, 0 otherwise + * @return returns + * - \bc true if the server is running + * - \bc false, otherwise */ bool isRunning(void) const { return this->mIsRunning; } /** * Is auto detection enabled or not * * Returns true or false if auto detection is enabled or not + * + * @return returns + * - \bc true, if autodetection is enabled + * - \bc false, otherwise */ bool isAutoDetectionEnabled() { return this->mIsAutoDetectionEnabled; } protected: @@ -65,7 +65,14 @@ const char *cPluginUpnp::CommandLineHelp(void) " -p <port> --port=<port> The server port\n" " Supported ports:\n" " %5d (auto detect)\n" - " %5d-%5d (user defined)\n", + " %5d-%5d (user defined)\n" + " -d --autodetect Force auto detection\n" + " Use this option to\n" + " overwrite the setup menu\n" + " options.\n" + " -v --verbose Increase verbosity level\n" + " The more v options the\n" + " higher the output level\n", 0, SERVER_MIN_PORT, SERVER_MAX_PORT @@ -81,21 +88,21 @@ bool cPluginUpnp::ProcessArgs(int argc, char *argv[]) bool cPluginUpnp::Initialize(void) { // Initialize any background activities the plugin shall perform. - MESSAGE("######### LETS GET READY TO RUMBLE #########"); + MESSAGE(VERBOSE_SDK, "######### LETS GET READY TO RUMBLE #########"); cPluginUpnp::mConfigDirectory = strdup(cPlugin::ConfigDirectory(this->Name())); if(!cPluginUpnp::getConfigDirectory()){ ERROR("Cannot set configuration directory"); return false; } - MESSAGE("Configuration directory: %s", cPluginUpnp::getConfigDirectory()); + MESSAGE(VERBOSE_SDK, "Configuration directory: %s", cPluginUpnp::getConfigDirectory()); DatabaseLocker.Signal(); return this->mUpnpServer->init(); } bool cPluginUpnp::Start(void) { - MESSAGE("Call plugin START"); + MESSAGE(VERBOSE_SDK, "Call plugin START"); // Start any background activities the plugin shall perform. return this->mUpnpServer->start(); //return true; @@ -103,7 +110,7 @@ bool cPluginUpnp::Start(void) void cPluginUpnp::Stop(void) { - MESSAGE("Call plugin STOP"); + MESSAGE(VERBOSE_SDK, "Call plugin STOP"); // Stop any background activities the plugin is performing. this->mUpnpServer->stop(); } @@ -15,6 +15,12 @@ class cUPnPServer; +/** + * The UPnP/DLNA plugin + * + * This is a UPnP/DLNA media server plugin. It supports live-TV and recordings + * of the VDR as well as custom video files. + */ class cPluginUpnp : public cPlugin { private: // Add any member variables or functions you may need here. @@ -23,20 +29,92 @@ private: public: cPluginUpnp(void); virtual ~cPluginUpnp(); + /** + * Get the version of the plugin + * + * Returns the version string of the plugin + * + * @return a string representation of the plugin version + */ virtual const char *Version(void); + /** + * Get the description + * + * This returns a brief description of the plugin and what it does. + * + * @return the description of the plugin + */ virtual const char *Description(void); + /** + * Get the command line help + * + * This returns the command line help output, which comes, when the user + * types \c --help into the command line. + * + * @return the command line help + */ virtual const char *CommandLineHelp(void); + /*! @copydoc cUPnPConfig::processArgs */ virtual bool ProcessArgs(int argc, char *argv[]); + /** + * Initializes the plugin + * + * This initializes any background activities of the plugin. + * + * @return returns + * - \bc true, if initializing was successful + * - \bc false, otherwise + */ virtual bool Initialize(void); + /** + * Starts the plugin + * + * This starts the plugin. It starts additional threads, which are required + * by the plugin. + * + * @return returns + * - \bc true, if starting was successful + * - \bc false, otherwise + */ virtual bool Start(void); + /** + * Stops the plugin + * + * This stops the plugin and all its components + */ virtual void Stop(void); + /** + * Message if still active + * + * This returns a message if the plugin is still active when a user attempts + * to shut down the VDR. + * + * @return the message shown on the screen. + */ virtual cString Active(void); + /** + * Setup menu + * + * This creates a new instance of the setup menu, which is shown to the user + * when he enters the VDR plugin setup menu + * + * @return the menu of the plugin + */ virtual cMenuSetupPage *SetupMenu(void); + /*! @copydoc cUPnPConfig::parseSetup */ virtual bool SetupParse(const char *Name, const char *Value); + /** + * Get the configuration directory + * + * This returns the directory, where configuration files are stored. + * + * @return the directory of the configuration files. + */ static const char* getConfigDirectory(); }; -extern cCondWait DatabaseLocker; +extern cCondWait DatabaseLocker; ///< Locks the database to be loaded only if + ///< the configuration file directory is set #endif /* _UPNP_H */ diff --git a/upnpcomponents/connectionmanager.cpp b/upnpcomponents/connectionmanager.cpp index b2c7149..e0d9203 100644 --- a/upnpcomponents/connectionmanager.cpp +++ b/upnpcomponents/connectionmanager.cpp @@ -70,7 +70,7 @@ int cConnectionManager::execute(Upnp_Action_Request* Request){ } int cConnectionManager::getProtocolInfo(Upnp_Action_Request* Request){ - MESSAGE("Protocol info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CMS, "Protocol info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); cString Result = cString::sprintf( "<u:%sResponse xmlns:u=\"%s\"> \ <Source>%s</Source> \ @@ -87,7 +87,7 @@ int cConnectionManager::getProtocolInfo(Upnp_Action_Request* Request){ } int cConnectionManager::getCurrentConnectionIDs(Upnp_Action_Request* Request){ - MESSAGE("Current connection IDs requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CMS, "Current connection IDs requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); cString Result; const char* IDs = this->getConnectionIDsCVS(); if(!IDs){ @@ -109,7 +109,7 @@ int cConnectionManager::getCurrentConnectionIDs(Upnp_Action_Request* Request){ } int cConnectionManager::getCurrentConnectionInfo(Upnp_Action_Request* Request){ - MESSAGE("Current connection info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CMS, "Current connection info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); int ConnectionID; if(this->parseIntegerValue(Request->ActionRequest, "ConnectionID", &ConnectionID) != 0){ @@ -156,7 +156,7 @@ int cConnectionManager::getCurrentConnectionInfo(Upnp_Action_Request* Request){ } int cConnectionManager::prepareForConnection(Upnp_Action_Request* Request){ - MESSAGE("Request for a new connection by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CMS, "Request for a new connection by %s.", inet_ntoa(Request->CtrlPtIPAddr)); //char* Result = NULL; char* RemoteProtocolInfo = NULL; char* PeerConnectionManager = NULL; @@ -198,7 +198,7 @@ int cConnectionManager::prepareForConnection(Upnp_Action_Request* Request){ } int cConnectionManager::connectionComplete(Upnp_Action_Request* Request){ - MESSAGE("Request for closing an open connection by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CMS, "Request for closing an open connection by %s.", inet_ntoa(Request->CtrlPtIPAddr)); //char* Result = NULL; int ConnectionID; @@ -216,27 +216,27 @@ int cConnectionManager::connectionComplete(Upnp_Action_Request* Request){ return Request->ErrCode; } -bool cConnectionManager::setProtocolInfo(const char* ProtocolInfo){ - if(strcmp(this->mSupportedProtocols, ProtocolInfo)){ - // ProtocolInfo changed, save and invoke a event notification - this->mSupportedProtocols = ProtocolInfo; - - IXML_Document* PropertySet = NULL; - UpnpAddToPropertySet(&PropertySet, "SourceProtocolInfo", this->mSupportedProtocols); - int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet); - ixmlDocument_free(PropertySet); - - if(ret != UPNP_E_SUCCESS){ - ERROR("State change notification failed (Error code: %d)",ret); - return false; - } - } - return true; -} +//bool cConnectionManager::setProtocolInfo(const char* ProtocolInfo){ +// if(strcmp(this->mSupportedProtocols, ProtocolInfo)){ +// // ProtocolInfo changed, save and invoke a event notification +// this->mSupportedProtocols = ProtocolInfo; +// +// IXML_Document* PropertySet = NULL; +// UpnpAddToPropertySet(&PropertySet, "SourceProtocolInfo", this->mSupportedProtocols); +// int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet); +// ixmlDocument_free(PropertySet); +// +// if(ret != UPNP_E_SUCCESS){ +// ERROR("State change notification failed (Error code: %d)",ret); +// return false; +// } +// } +// return true; +//} cVirtualConnection* cConnectionManager::createVirtualConnection(const char* RemoteProtocolInfo, const char* RemoteConnectionManager, int RemoteConnectionID, eDirection Direction){ static int lastConnectionID = 0; - MESSAGE("Create virtual connection"); + MESSAGE(VERBOSE_CMS, "Create virtual connection"); if(lastConnectionID == 2147483647) lastConnectionID = 1; cVirtualConnection* Connection = new cVirtualConnection; // AVT is available @@ -268,7 +268,7 @@ cVirtualConnection* cConnectionManager::createVirtualConnection(const char* Remo ERROR("State change notification failed (Error code: %d)",ret); return NULL; } - MESSAGE("Notification of connection creation sent"); + MESSAGE(VERBOSE_CMS, "Notification of connection creation sent"); this->mVirtualConnections->Add(Connection); return Connection; } diff --git a/upnpcomponents/connectionmanager.h b/upnpcomponents/connectionmanager.h index 202df59..4aa25f3 100644 --- a/upnpcomponents/connectionmanager.h +++ b/upnpcomponents/connectionmanager.h @@ -10,6 +10,11 @@ #include "upnpservice.h" +/** + * Connection status + * + * The connection status of a certain virtual connection + */ enum eConnectionStatus { OK, CONTENT_FORMAT_MISMATCH, @@ -18,11 +23,22 @@ enum eConnectionStatus { UNKNOWN }; +/** + * Direction + * + * The direction of a virtual connection. Input means client to server, Output + * server to client + */ enum eDirection { OUTPUT, INPUT }; +/** + * Virtual connection + * + * A virtual connection managed by the connection manager service + */ class cVirtualConnection : public cListObject { friend class cConnectionManager; private: @@ -41,15 +57,31 @@ private: static int getConnectionStatus(const char* ConnectionStatus); }; +/** + * The connection manager service + * + * This is the connection manager service which handles all incoming connection, + * creates and destroys connections to clients. + */ class cConnectionManager : public cUpnpService { public: - cConnectionManager(UpnpDevice_Handle DeviceHandle); + /** + * Constructor of a Connection manager + * + * This creates an instance of a <em>Connection Manager Service</em> and provides + * interfaces for executing actions and subscribing on events. + */ + cConnectionManager( + UpnpDevice_Handle DeviceHandle ///< the UPnP device handle of this root device + ); virtual ~cConnectionManager(); - virtual int execute(Upnp_Action_Request* Request); + /*! @copydoc cUpnpService::subscribe(Upnp_Subscription_Request* Request) */ virtual int subscribe(Upnp_Subscription_Request* Request); - bool setProtocolInfo(const char* ProtocolInfo); -private: + /*! @copydoc cUpnpService::execute(Upnp_Action_Request* Request) */ + virtual int execute(Upnp_Action_Request* Request); + /*! @copydoc cUpnpService::setError(Upnp_Action_Request* Request, int Error) */ virtual void setError(Upnp_Action_Request* Request, int Error); +private: int getProtocolInfo(Upnp_Action_Request* Request); int getCurrentConnectionIDs(Upnp_Action_Request* Request); int getCurrentConnectionInfo(Upnp_Action_Request* Request); diff --git a/upnpcomponents/contentdirectory.cpp b/upnpcomponents/contentdirectory.cpp index a9afdfa..494b638 100644 --- a/upnpcomponents/contentdirectory.cpp +++ b/upnpcomponents/contentdirectory.cpp @@ -40,7 +40,7 @@ int cContentDirectory::subscribe(Upnp_Subscription_Request* Request){ void cContentDirectory::Action(){ static int Retry = 5; - MESSAGE("Start Content directory thread"); + MESSAGE(VERBOSE_CDS, "Start Content directory thread"); while(this->Running()){ IXML_Document* PropertySet = NULL; UpnpAddToPropertySet(&PropertySet, "SystemUpdateID", itoa(this->mMediaDatabase->getSystemUpdateID())); @@ -84,7 +84,7 @@ int cContentDirectory::execute(Upnp_Action_Request* Request){ int cContentDirectory::browse(Upnp_Action_Request* Request){ - MESSAGE("Browse requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); + MESSAGE(VERBOSE_CDS, "Browse requested by %s.", inet_ntoa(Request->CtrlPtIPAddr)); char* ObjectID = NULL; if(this->parseStringValue(Request->ActionRequest, "ObjectID", &ObjectID)){ @@ -201,7 +201,7 @@ int cContentDirectory::getSystemUpdateID(Upnp_Action_Request* Request){ } int cContentDirectory::getSearchCapabilities(Upnp_Action_Request* Request){ - MESSAGE("Sorry, no search capabilities yet"); + MESSAGE(VERBOSE_CDS, "Sorry, no search capabilities yet"); cString Result = cString::sprintf( "<u:%sResponse xmlns:u=\"%s\"> \ @@ -220,7 +220,7 @@ int cContentDirectory::getSearchCapabilities(Upnp_Action_Request* Request){ } int cContentDirectory::getSortCapabilities(Upnp_Action_Request* Request){ - MESSAGE("Sorry, no sort capabilities yet"); + MESSAGE(VERBOSE_CDS, "Sorry, no sort capabilities yet"); cString Result = cString::sprintf( "<u:%sResponse xmlns:u=\"%s\"> \ diff --git a/upnpcomponents/contentdirectory.h b/upnpcomponents/contentdirectory.h index a504fdc..7fb4bb9 100644 --- a/upnpcomponents/contentdirectory.h +++ b/upnpcomponents/contentdirectory.h @@ -12,12 +12,30 @@ #include "upnpservice.h" #include "../database/metadata.h" +/** + * The content directory service + * + * This is the content directory service which handles all incoming requests + * for contents managed by the media server. + */ class cContentDirectory : public cUpnpService, public cThread { public: - cContentDirectory(UpnpDevice_Handle DeviceHandle, cMediaDatabase* MediaDatabase); + /** + * Constructor of a Content Directory + * + * This creates an instance of a <em>Content Directory Service</em> and provides + * interfaces for executing actions and subscribing on events. + */ + cContentDirectory( + UpnpDevice_Handle DeviceHandle, ///< The UPnP device handle of the root device + cMediaDatabase* MediaDatabase ///< the media database where requests are processed + ); virtual ~cContentDirectory(); + /*! @copydoc cUpnpService::subscribe(Upnp_Subscription_Request* Request) */ virtual int subscribe(Upnp_Subscription_Request* Request); + /*! @copydoc cUpnpService::execute(Upnp_Action_Request* Request) */ virtual int execute(Upnp_Action_Request* Request); + /*! @copydoc cUpnpService::setError(Upnp_Action_Request* Request, int Error) */ virtual void setError(Upnp_Action_Request* Request, int Error); private: cMediaDatabase* mMediaDatabase; diff --git a/upnpcomponents/dlna.cpp b/upnpcomponents/dlna.cpp index eaa23c1..27f84f2 100644 --- a/upnpcomponents/dlna.cpp +++ b/upnpcomponents/dlna.cpp @@ -21,7 +21,7 @@ cDlna* cDlna::getInstance(void){ } cDlna::cDlna() { - this->mRegisteredProfiles = new cRegisteredProfiles; + this->mRegisteredProfiles = new cList<cRegisteredProfile>; this->init(); } @@ -47,6 +47,8 @@ void cDlna::registerProfile(DLNAProfile* Profile, int Op, const char* Ps, int Ci void cDlna::registerMainProfiles(){ this->registerProfile(&DLNA_PROFILE_MPEG_TS_SD_EU, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT); this->registerProfile(&DLNA_PROFILE_AVC_TS_HD_EU, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT); + this->registerProfile(&DLNA_PROFILE_MPEG_TS_SD_EU_ISO, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT); + this->registerProfile(&DLNA_PROFILE_AVC_TS_HD_EU_ISO, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT); } const char* cDlna::getSupportedProtocols(){ @@ -73,9 +75,9 @@ DLNAProfile* cDlna::getProfileOfChannel(cChannel* Channel){ switch(Channel->Vtype()){ case 0x02: // MPEG2 Video - return &DLNA_PROFILE_MPEG_TS_SD_EU; + return &DLNA_PROFILE_MPEG_TS_SD_EU_ISO; case 0x1B: - return &DLNA_PROFILE_AVC_TS_HD_EU; + return &DLNA_PROFILE_AVC_TS_HD_EU_ISO; default: ERROR("Unknown video type %d for channel %s!", Channel->Vtype(), Channel->Name()); return NULL; diff --git a/upnpcomponents/dlna.h b/upnpcomponents/dlna.h index c05d69a..80ac328 100644 --- a/upnpcomponents/dlna.h +++ b/upnpcomponents/dlna.h @@ -14,6 +14,12 @@ class cDlna; +/** + * Registered DLNA profile + * + * This class contains information about a certain registered profile + * like play speeds or flags + */ class cRegisteredProfile : public cListObject { friend class cDlna; private: @@ -27,10 +33,6 @@ public: virtual ~cRegisteredProfile(){}; }; -class cRegisteredProfiles : public cList<cRegisteredProfile> { - friend class cDlna; -}; - /** * Enable DLNA compliant media transfer * @@ -41,23 +43,107 @@ class cRegisteredProfiles : public cList<cRegisteredProfile> { class cDlna { friend class cUPnPServer; public: + /** + * Returns the instance of DLNA object + * + * This will create a DLNA object instance. It will return the same instance + * on subsequent calls. + * + * @return the DLNA object instance + */ static cDlna* getInstance(void); virtual ~cDlna(); //const char* getProtocolInfo(UPnPObjectID OID); - const char* getDeviceDescription(const char* URLBase); - void registerProfile(DLNAProfile* Profile, int Op = -1, const char* Ps = NULL, int Ci = -1, unsigned int Flags = 0); + /** + * Device description document + * + * This will return the device description document with service type + * definitions as well as some DLNA specific information + * + * @return The description document + */ + const char* getDeviceDescription( + const char* URLBase ///< the URLBase to be set in the document + ); + /** + * Registeres a DLNA profile + * + * Registeres a DLNA profile with specific optional options + * + * @see common.h + */ + void registerProfile( + DLNAProfile* Profile, ///< the DLNA profile + int Op = -1, ///< operation mode + const char* Ps = NULL, ///< play speed (CSV list) + int Ci = -1, ///< conversion indication flag + unsigned int Flags = 0 ///< DLNA flags + ); + /** + * Registeres all known DLNA profiles + * + * Registeres all well known DLNA profiles with its known options + */ void registerMainProfiles(); + /** + * CSV list of supported protocols + * + * Returns a comma separated list with all supported protocols. This + * means, it returns the list of protocols of the registered profiles. + * + * @return CSV list of registered protocols + */ const char* getSupportedProtocols(); - const char* getProtocolInfo(DLNAProfile *Prof); - DLNAProfile* getProfileOfChannel(cChannel* Channel); - DLNAProfile* getProfileOfRecording(cRecording* Recording); - DLNAProfile* getProfileOfFile(cString File); + /** + * Protocol info of a specific DLNA profile + * + * Returns the protocol info string of a specific DLNA profile with its + * options and flags. + * + * @return the protocol info string of the profile + */ + const char* getProtocolInfo( + DLNAProfile *Prof ///< the Profile of which the protocol info shall be returned + ); + /** + * Profile of a channel + * + * Returns the DLNA profile of a VDR channel. It checks the video type to determine + * which profile will match. + * + * @return the matching DLNA profile + */ + DLNAProfile* getProfileOfChannel( + cChannel* Channel ///< the channel of which the profile should created from + ); + /** + * Profile of a recording + * + * Returns the DLNA profile of a VDR recording. It checks the video file to determine + * which profile will match. + * + * @return the matching DLNA profile + */ + DLNAProfile* getProfileOfRecording( + cRecording* Recording ///< the recording of which the profile should be created from + ); + /** + * Profile of a file + * + * Returns the DLNA profile of a file. It checks the content of the file with \em ffmpeg to + * determine which profile will match. + * + * @return the matching DLNA profile + */ + DLNAProfile* getProfileOfFile( + cString File ///< the file of which the profile should be created from + ); private: const char* getRegisteredProtocolInfoString(cRegisteredProfile *Profile); cDlna(); void init(void); static cDlna* mInstance; - cRegisteredProfiles* mRegisteredProfiles; + cList<cRegisteredProfile>* mRegisteredProfiles; }; #endif /* _DLNA_H */ diff --git a/upnpcomponents/upnpservice.cpp b/upnpcomponents/upnpservice.cpp index a1d6a47..fc24cca 100644 --- a/upnpcomponents/upnpservice.cpp +++ b/upnpcomponents/upnpservice.cpp @@ -13,7 +13,7 @@ cUpnpService::cUpnpService(UpnpDevice_Handle DeviceHandle) { this->mDeviceHandle = DeviceHandle; } -int cUpnpService::parseIntegerValue(IXML_Document* Document, const char* Item, int* Value){ +int cUpnpService::parseIntegerValue(IN IXML_Document* Document, IN const char* Item, OUT int* Value){ char* Val = NULL; int Error = 0; @@ -34,7 +34,7 @@ int cUpnpService::parseIntegerValue(IXML_Document* Document, const char* Item, i return Error; } -int cUpnpService::parseStringValue(IXML_Document* Document, const char* Item, char** Value){ +int cUpnpService::parseStringValue(IN IXML_Document* Document, IN const char* Item, OUT char** Value){ char* Val = NULL; int Error = 0; diff --git a/upnpcomponents/upnpservice.h b/upnpcomponents/upnpservice.h index c8630b5..df74d9b 100644 --- a/upnpcomponents/upnpservice.h +++ b/upnpcomponents/upnpservice.h @@ -10,17 +10,109 @@ #include <upnp/upnp.h> +/** + * UPnP Service interface + * + * This is a service interface implemented by a UPnP service like CDS oder CMS + * + * It comes with some tool functions which are commonly useful for processing + * an event or action. + */ class cUpnpService { public: - cUpnpService(UpnpDevice_Handle DeviceHandle); + /** + * Constructor of a service + * + * @private + * @param DeviceHandle the UPnP device handle of this root device + */ + cUpnpService( + UpnpDevice_Handle DeviceHandle ///< the UPnP device handle of this root device + ); virtual ~cUpnpService(){}; - virtual int subscribe(Upnp_Subscription_Request* Request) = 0; - virtual int execute(Upnp_Action_Request* Request) = 0; + /** + * Subscribes to an event + * + * This is a callback function to register a new subscriber for an event. + * + * @return An integer representing one of the following: + * - \bc UPNP_E_SUCCESS, if subscription was okay + * - or any other non null value in case of an error + * + * @param Request Information about the subscription + */ + virtual int subscribe( + Upnp_Subscription_Request* Request ///< Information about the subscription + ) = 0; + /** + * Executes an action + * + * This executes an action initialized by a control point. The result is + * stored in the first parameter. + * + * @return An integer representing one of the following: + * - \bc UPNP_E_SUCCESS, if subscription was okay + * - or any other non null value in case of an error + * + * @param Request Input and output parameters of an action + */ + virtual int execute( + Upnp_Action_Request* Request ///< Input and output parameters of an action + ) = 0; protected: - virtual void setError(Upnp_Action_Request* Request, int Error); - int parseIntegerValue(IN IXML_Document* Document, IN const char* Item, OUT int* Value); - int parseStringValue(IN IXML_Document* Document, IN const char* Item, OUT char** Value); - UpnpDevice_Handle mDeviceHandle; + /** + * Sets an error on an action request + * + * This function puts a error message into the action request structure + * according to its error code + * + * @param Request the action request, to set the error for + * @param Error the error code of which the message should be obtained + */ + virtual void setError( + Upnp_Action_Request* Request, ///< the action request, to set the error for + int Error ///< the error code of which the message should be obtained + ); + /** + * Parses an integer value + * + * This tool function parses an integer value from a given \em IXML document. It is searching + * for the very first occurance of the demanded item. + * + * @return Returns + * - \bc 0, if parsing was successful + * - \bc <0, if an error occured + * + * @param Document the document, which is parsed + * @param Item the demanded item + * @param Value the value of the item + */ + int parseIntegerValue( + IN IXML_Document* Document, ///< the document, which is parsed + IN const char* Item, ///< the demanded item + OUT int* Value ///< the value of the item + ); + /** + * Parses a string value + * + * This tool function parses a string value from a given \em IXML document. It is searching + * for the very first occurance of the demanded item. + * + * @return Returns + * - \bc 0, if parsing was successful + * - \bc <0, if an error occured + * + * @param Document the document, which is parsed + * @param Item the demanded item + * @param Value the value of the item + */ + int parseStringValue( + IN IXML_Document* Document, ///< the document, which is parsed + IN const char* Item, ///< the demanded item + OUT char** Value ///< the value of the item + ); + + UpnpDevice_Handle mDeviceHandle; ///< the UPnP device handle of the root device }; #endif /* _UPNPSERVICE_H */ diff --git a/upnpcomponents/upnpwebserver.cpp b/upnpcomponents/upnpwebserver.cpp index 383b201..892f5b1 100644 --- a/upnpcomponents/upnpwebserver.cpp +++ b/upnpcomponents/upnpwebserver.cpp @@ -46,6 +46,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ +/** @private */ struct File_Info_ { /** The length of the file. A length less than 0 indicates the size @@ -73,6 +74,7 @@ struct File_Info_ }; +/** @private */ struct cWebFileHandle { cString Filename; off64_t Size; @@ -108,13 +110,13 @@ UpnpVirtualDirCallbacks cUPnPWebServer::mVirtualDirCallbacks = { }; bool cUPnPWebServer::init(){ - MESSAGE("Initialize callbacks for virtual directories."); + MESSAGE(VERBOSE_WEBSERVER, "Initialize callbacks for virtual directories."); if(UpnpSetWebServerRootDir(this->mRootdir) == UPNP_E_INVALID_ARGUMENT){ ERROR("The root directory of the webserver is invalid."); return false; } - MESSAGE("Setting up callbacks"); + MESSAGE(VERBOSE_WEBSERVER, "Setting up callbacks"); if(UpnpSetVirtualDirCallbacks(&cUPnPWebServer::mVirtualDirCallbacks) == UPNP_E_INVALID_ARGUMENT){ ERROR("The virtual directory callbacks are invalid."); @@ -126,7 +128,7 @@ bool cUPnPWebServer::init(){ return false; } - MESSAGE("Add virtual directories."); + MESSAGE(VERBOSE_WEBSERVER, "Add virtual directories."); if(UpnpAddVirtualDir(UPNP_DIR_SHARES) == UPNP_E_INVALID_ARGUMENT){ ERROR("The virtual directory %s is invalid.",UPNP_DIR_SHARES); return false; @@ -135,7 +137,7 @@ bool cUPnPWebServer::init(){ } bool cUPnPWebServer::uninit(){ - MESSAGE("Disabling the internal webserver"); + MESSAGE(VERBOSE_WEBSERVER, "Disabling the internal webserver"); UpnpEnableWebserver(FALSE); return true; @@ -152,7 +154,7 @@ cUPnPWebServer* cUPnPWebServer::getInstance(const char* rootdir){ } int cUPnPWebServer::getInfo(const char* filename, File_Info* info){ - MESSAGE("Getting information of file '%s'", filename); + MESSAGE(VERBOSE_WEBSERVER, "Getting information of file '%s'", filename); propertyMap Properties; int Method; @@ -164,7 +166,7 @@ int cUPnPWebServer::getInfo(const char* filename, File_Info* info){ switch(Method){ case UPNP_WEB_METHOD_STREAM: { - MESSAGE("Stream request"); + MESSAGE(VERBOSE_WEBSERVER, "Stream request"); propertyMap::iterator It = Properties.find("resId"); unsigned int ResourceID = 0; if(It == Properties.end()){ @@ -188,12 +190,12 @@ int cUPnPWebServer::getInfo(const char* filename, File_Info* info){ finfo.last_modified = Resource->getLastModification(); memcpy(info, &finfo, sizeof(File_Info_)); - MESSAGE("==== File info of Resource #%d ====", Resource->getID()); - MESSAGE("Size: %lld", finfo.file_length); - MESSAGE("Dir: %s", finfo.is_directory?"yes":"no"); - MESSAGE("Read: %s", finfo.is_readable?"allowed":"not allowed"); - MESSAGE("Last modified: %s", ctime(&(finfo.last_modified))); - MESSAGE("Content-type: %s", finfo.content_type); + MESSAGE(VERBOSE_METADATA, "==== File info of Resource #%d ====", Resource->getID()); + MESSAGE(VERBOSE_METADATA, "Size: %lld", finfo.file_length); + MESSAGE(VERBOSE_METADATA, "Dir: %s", finfo.is_directory?"yes":"no"); + MESSAGE(VERBOSE_METADATA, "Read: %s", finfo.is_readable?"allowed":"not allowed"); + MESSAGE(VERBOSE_METADATA, "Last modified: %s", ctime(&(finfo.last_modified))); + MESSAGE(VERBOSE_METADATA, "Content-type: %s", finfo.content_type); } } } @@ -222,7 +224,7 @@ int cUPnPWebServer::getInfo(const char* filename, File_Info* info){ } UpnpWebFileHandle cUPnPWebServer::open(const char* filename, UpnpOpenFileMode mode){ - MESSAGE("File %s was opened for %s.",filename,mode==UPNP_READ ? "reading" : "writing"); + MESSAGE(VERBOSE_WEBSERVER, "File %s was opened for %s.",filename,mode==UPNP_READ ? "reading" : "writing"); propertyMap Properties; int Method; @@ -235,7 +237,7 @@ UpnpWebFileHandle cUPnPWebServer::open(const char* filename, UpnpOpenFileMode mo switch(Method){ case UPNP_WEB_METHOD_STREAM: { - MESSAGE("Stream request"); + MESSAGE(VERBOSE_WEBSERVER, "Stream request"); propertyMap::iterator It = Properties.find("resId"); unsigned int ResourceID = 0; if(It == Properties.end()){ @@ -258,7 +260,7 @@ UpnpWebFileHandle cUPnPWebServer::open(const char* filename, UpnpOpenFileMode mo { char* ChannelID = strtok(strdup(Resource->getResource()),":"); int AudioID = atoi(strtok(NULL,":")); - MESSAGE("Try to create Receiver for Channel %s with Audio ID %d", ChannelID, AudioID); + MESSAGE(VERBOSE_LIVE_TV, "Try to create Receiver for Channel %s with Audio ID %d", ChannelID, AudioID); cChannel* Channel = Channels.GetByChannelID(tChannelID::FromString(ChannelID)); if(!Channel){ ERROR("No such channel with ID %s", ChannelID); @@ -303,32 +305,32 @@ UpnpWebFileHandle cUPnPWebServer::open(const char* filename, UpnpOpenFileMode mo else { return NULL; } - MESSAGE("Open the file handle"); + MESSAGE(VERBOSE_WEBSERVER, "Open the file handle"); WebFileHandle->FileHandle->open(mode); return (UpnpWebFileHandle)WebFileHandle; } int cUPnPWebServer::write(UpnpWebFileHandle fh, char* buf, size_t buflen){ cWebFileHandle* FileHandle = (cWebFileHandle*)fh; - MESSAGE("Writing to %s", *FileHandle->Filename); + MESSAGE(VERBOSE_BUFFERS, "Writing to %s", *FileHandle->Filename); return FileHandle->FileHandle->write(buf, buflen); } int cUPnPWebServer::read(UpnpWebFileHandle fh, char* buf, size_t buflen){ cWebFileHandle* FileHandle = (cWebFileHandle*)fh; - MESSAGE("Reading from %s", *FileHandle->Filename); + MESSAGE(VERBOSE_BUFFERS, "Reading from %s", *FileHandle->Filename); return FileHandle->FileHandle->read(buf, buflen); } int cUPnPWebServer::seek(UpnpWebFileHandle fh, off_t offset, int origin){ cWebFileHandle* FileHandle = (cWebFileHandle*)fh; - MESSAGE("Seeking on %s", *FileHandle->Filename); + MESSAGE(VERBOSE_BUFFERS, "Seeking on %s", *FileHandle->Filename); return FileHandle->FileHandle->seek(offset, origin); } int cUPnPWebServer::close(UpnpWebFileHandle fh){ cWebFileHandle *FileHandle = (cWebFileHandle *)fh; - MESSAGE("Closing file %s", *FileHandle->Filename); + MESSAGE(VERBOSE_WEBSERVER, "Closing file %s", *FileHandle->Filename); FileHandle->FileHandle->close(); delete FileHandle->FileHandle; delete FileHandle; diff --git a/upnpcomponents/upnpwebserver.h b/upnpcomponents/upnpwebserver.h index 613f97b..0a49cf9 100644 --- a/upnpcomponents/upnpwebserver.h +++ b/upnpcomponents/upnpwebserver.h @@ -11,6 +11,13 @@ #include "../common.h" #include <upnp/upnp.h> +/** + * The internal webserver + * + * This is the internal webserver. It distributes all the contents of the + * UPnP-Server. + * + */ class cUPnPWebServer { friend class cUPnPServer; private: @@ -19,11 +26,40 @@ private: const char* mRootdir; cUPnPWebServer(const char* root = "/"); protected: - bool enable(bool enable); public: + /** + * Initializes the webserver + * + * It enables the webserver which comes with the <em>Intel SDK</em> and creates + * virtual directories for shares media. + * + * @return returns + * - \bc true, if initializing was successful + * - \bc false, otherwise + */ bool init(); + /** + * Uninitializes the webserver + * + * This stops the webserver. + * + * @return returns + * - \bc true, if initializing was successful + * - \bc false, otherwise + */ bool uninit(); - static cUPnPWebServer* getInstance(const char* rootdir = "/"); + /** + * Returns the instance of the webserver + * + * Returns the instance of the webserver. This will create a single + * instance of none is existing on the very first call. A subsequent call + * will return the same instance. + * + * @return the instance of webserver + */ + static cUPnPWebServer* getInstance( + const char* rootdir = "/" /**< the root directory of the webserver */ + ); virtual ~cUPnPWebServer(); //}; @@ -32,6 +68,7 @@ public: * The callback functions for the webserver * ****************************************************/ + /** * Retrieve file information * @@ -48,8 +85,8 @@ public: * Opens a file in a virtual directory with the specified mode. * * Possible modes are: - * - UPNP_READ : Opens the file for reading - * - UPNP_WRITE: Opens the file for writing + * - \b UPNP_READ, Opens the file for reading + * - \b UPNP_WRITE, Opens the file for writing * * It returns a file handle to the opened file, NULL otherwise * |