From 5e0c1dbc3610b703f59fa798748b1676c0f356cc Mon Sep 17 00:00:00 2001 From: hschang Date: Mon, 27 Aug 2018 15:52:33 +0900 Subject: [PATCH] Update EPG Cache(thanks to open source community) --- data/encoding.conf | 110 +- data/menu.xml | 1 + data/setup.xml | 11 + lib/base/Makefile.am | 6 +- lib/base/encoding.cpp | 23 +- lib/base/encoding.h | 24 +- lib/base/estring.cpp | 511 +++--- lib/base/estring.h | 7 +- lib/base/freesatv2.cpp | 288 +++ lib/base/freesatv2.h | 20 + lib/dvb/db.cpp | 15 + lib/dvb/db.h | 1 + lib/dvb/dvbtime.cpp | 47 +- lib/dvb/dvbtime.h | 4 +- lib/dvb/epgcache.cpp | 2482 +++++++++++++++++--------- lib/dvb/epgcache.h | 282 ++- lib/dvb/lowlevel/eit.h | 32 +- lib/dvb/lowlevel/mhw.h | 3 + lib/python/Components/Converter/EventName.py | 127 +- lib/python/Components/Converter/Makefile.am | 2 +- lib/python/Components/Converter/genre.py | 170 ++ lib/python/Components/UsageConfig.py | 88 +- lib/python/Screens/InfoBarGenerics.py | 5 +- lib/service/event.cpp | 239 ++- lib/service/event.h | 69 +- lib/service/iservice.h | 3 +- lib/service/servicedvbrecord.cpp | 41 +- lib/service/servicemp3.cpp | 96 +- lib/service/servicemp3.h | 7 + 29 files changed, 3300 insertions(+), 1414 deletions(-) mode change 100755 => 100644 data/encoding.conf mode change 100755 => 100644 data/menu.xml mode change 100755 => 100644 data/setup.xml create mode 100644 lib/base/freesatv2.cpp create mode 100644 lib/base/freesatv2.h mode change 100755 => 100644 lib/dvb/db.cpp create mode 100644 lib/python/Components/Converter/genre.py mode change 100755 => 100644 lib/python/Screens/InfoBarGenerics.py mode change 100755 => 100644 lib/service/servicemp3.cpp diff --git a/data/encoding.conf b/data/encoding.conf old mode 100755 new mode 100644 index 36f619d..7e900e9 --- a/data/encoding.conf +++ b/data/encoding.conf @@ -1,41 +1,56 @@ #Fallback encoding when in dvb-text no encoding table is given -#Countycode ISO8859-X or ISO6397 +#Countycode encoding, encoding may be ISO8859-X, ISO6937, UTF8, +# UTF-8,GB18030(GB2312/GBK/CP936), BIG5(CP950), +# UNICODE(UTF16BE), UTF16LE ara ISO8859-6 tur ISO8859-9 gre ISO8859-7 +ell ISO8859-7 pol ISO8859-2 rus ISO8859-5 bul ISO8859-5 tha ISO8859-11 -cze ISO6397 -ces ISO6397 -slo ISO6397 -slk ISO6397 +cze ISO6937 +ces ISO6937 +slo ISO6937 +slk ISO6937 +chi GB18030 +zho GB18030 +chn GB18030 + +#Fallback encoding when in dvb-text no encoding table is given +#and no Countrycode and no transponders configs in this config file +* ISO8859-1 + #Sorry for that.. in DVB Spec this is the default behavior #when no other encoding is given in dvb-texts.. #but this breaks too much providers yet.. #so our default is ISO8859-1 without two char byte encoding #So all transponders which needs this must be listed here #TSID ONID -0x447 0x1 # Astra 19.2°E 12.304 H - UPC Direct -0x427 0x1 # Astra 19.2°E 10.920 H - UPC Direct -0x44b 0x1 # Astra 19.2°E 12.382 H - UPC Direct -0x4ff 0x1 # Astra 19.2°E 11.992 H - UPC Direct -0x407 0x1 # Astra 19.2°E 11.671 H - UPC Direct +0x2c1 0x600 #Thor 0.8°W +0x2c2 0x600 #Thor 0.8°W +0x2c3 0x600 #Thor 0.8°W +0x2c4 0x600 #Thor 0.8°W +0x2be 0x600 #Thor 0.8°W +0x2bf 0x600 #Thor 0.8°W +0x2bd 0x600 #Thor 0.8°W +0x2c0 0x600 #Thor 0.8°W +0x2c2 0x600 #Thor 0.8°W +0x2c3 0x600 #Thor 0.8°W +0x2c4 0x600 #Thor 0.8°W +0x2c5 0x600 #Thor 0.8°W +0x2c6 0x600 #Thor 0.8°W +0x2c8 0x600 #Thor 0.8°W +0xaf2 0xbb #Thor 0.8°W 0x436 0x1 # Astra 19.2°E 11.973 V - MTV Networks Europe 0x42a 0x1 # Astra 19.2°E 11.739 V - MTV Networks Europe -0xc23 0x3 # Astra 23.5°E 10.803 H - CS Link / SkyLink -0xc85 0x3 # Astra 23.5°E 11.797 H - CS Link / SkyLink -0xc89 0x3 # Astra 23.5°E 11.876 H - CS Link / SkyLink -0xc8f 0x3 # Astra 23.5°E 11.992 H - CS Link / SkyLink -0xc93 0x3 # Astra 23.5°E 12.070 H - CS Link / SkyLink -0xc95 0x3 # Astra 23.5°E 12.109 H - SkyLink -0xbc6 0x3 # Astra 23.5°E 12.525 V - CS Link / SkyLink -0xbc7 0x3 # Astra 23.5°E 12.565 H - SkyLink 200 318 #Hotbird Eutelsat (Eurosport) 300 318 #Hotbird Eutelsat (Eurosport, Animal Pl.HD) 400 318 #Hotbird 13.0 Cyfra+ +700 318 #Hotbird 13.0 Cyfrowy Polsat 1000 318 #Hotbird 13.0 Grupa ITI +1100 318 #Hotbird 13.0 Cyfra+ 1500 318 #Hotbird 13.0 Cyfra+ 1600 318 #Hotbird 13.0 Cyfra+ 2800 318 #Hotbird 13.0 MTV Networks (Comedy Central) @@ -44,6 +59,7 @@ slk ISO6397 7900 113 #Hotbird 13.0 Cyfrowy Polsat 8100 113 #Hotbird 13.0 Universal (Cyfra+) 8100 318 #Hotbird 13.0 Eutelsat (Universal) +8900 318 #Hotbird 13.0 BBC Brit HD Poland 11000 318 #Hotbird 13.0 Cyfra+ 11400 318 #Hotbird 13.0 Cyfra+ 11600 318 #Hotbird 13.0 BBC HD, ITI @@ -54,19 +70,49 @@ slk ISO6397 13000 318 #Hotbird 13.0 BBC Polska and other 13100 318 #Hotbird 13.0 Crime and Investigation 13200 113 #Hotbird 13.0 Cyfrowy Polsat +15700 318 #Hotbird 13.0 Belgium Satellite Services - TV DISCO +0x801 0x600 # Ziggo NL +0x802 0x600 # Ziggo NL +0x803 0x600 # Ziggo NL +0x806 0x600 # Ziggo NL +0x809 0x600 # Ziggo NL +0x80a 0x600 # Ziggo NL +0x80d 0x600 # Ziggo NL +0x80e 0x600 # Ziggo NL +0x80f 0x600 # Ziggo NL +0x810 0x600 # Ziggo NL +0x812 0x600 # Ziggo NL +0x813 0x600 # Ziggo NL +0x815 0x600 # Ziggo NL +0x817 0x600 # Ziggo NL +0x819 0x600 # Ziggo NL +0x81a 0x600 # Ziggo NL +0x81c 0x600 # Ziggo NL +0x831 0x600 # Ziggo NL +0x832 0x600 # Ziggo NL +0x833 0x600 # Ziggo NL +0x834 0x600 # Ziggo NL +0x835 0x600 # Ziggo NL +0x836 0x600 # Ziggo NL +0x83a 0x600 # Ziggo NL +0x840 0x600 # Ziggo NL #Fallback encoding table for single transponders -#ISO6397 also enables two byte char encoding +#ISO6937 also enables two byte char encoding #TSID ONID ISO8859-X -#12800 318 ISO6397 #Viacom ... MTV / VH1 Polska -21100 126 ISO8859-9 # Digital Platform 7°E 10.928 H 30.000 2/3 8PSK -41200 126 ISO8859-9 # Digital Platform 7°E 11.451 V 25.066 2/3 -50100 126 ISO8859-9 # Digital Platform 7°E 11.471 H 30.000 3/4 -50200 126 ISO8859-9 # Digital Platform 7°E 11.492 V 30.000 3/4 -50300 126 ISO8859-9 # Digital Platform 7°E 11.639 H 30.000 3/4 -50400 126 ISO8859-9 # Digital Platform 7°E 11.534 V 30.000 3/4 -50500 126 ISO8859-9 # Digital Platform 7°E 11.262 H 27.500 3/4 -50600 126 ISO8859-9 # Digital Platform 7°E 11.575 V and 42°E 11.729 V 15.555 5/6 -50700 126 ISO8859-9 # Digital Platform 7°E 11.596 H 30.000 3/4 -50800 126 ISO8859-9 # Digital Platform 7°E 11.678 H 30.000 3/4 -50900 126 ISO8859-9 # Digital Platform 7°E 11.513 H 27.500 3/4 -51000 126 ISO8859-9 # Digital Platform 7°E 11.617 V 30.000 3/4 +20600 126 ISO8859-9 # Digital Platform 7°E +20700 126 ISO8859-9 # Digital Platform 7°E +20800 126 ISO8859-9 # Digital Platform 7°E +20900 126 ISO8859-9 # Digital Platform 7°E +21000 126 ISO8859-9 # Digital Platform 7°E +21100 126 ISO8859-9 # Digital Platform 7°E +41200 126 ISO8859-9 # Digital Platform 7°E +50100 126 ISO8859-9 # Digital Platform 7°E +50200 126 ISO8859-9 # Digital Platform 7°E +50300 126 ISO8859-9 # Digital Platform 7°E +50400 126 ISO8859-9 # Digital Platform 7°E +50500 126 ISO8859-9 # Digital Platform 7°E +50600 126 ISO8859-9 # Digital Platform 7°E +50700 126 ISO8859-9 # Digital Platform 7°E +50800 126 ISO8859-9 # Digital Platform 7°E +50900 126 ISO8859-9 # Digital Platform 7°E +51000 126 ISO8859-9 # Digital Platform 7°E diff --git a/data/menu.xml b/data/menu.xml old mode 100755 new mode 100644 index ed0b415..7ab36e7 --- a/data/menu.xml +++ b/data/menu.xml @@ -54,6 +54,7 @@ + diff --git a/data/setup.xml b/data/setup.xml old mode 100755 new mode 100644 index 9fb4a56..aabb58f --- a/data/setup.xml +++ b/data/setup.xml @@ -64,6 +64,17 @@ config.recording.always_ecm config.recording.never_decrypt + + config.autolanguage.epglanguage + config.autolanguage.epglanguage_alternative + config.epg.eit + config.epg.mhw + config.epg.freesat + config.epg.viasat + config.epg.netmed + config.epg.virgin + config.epg.histminutes + config.subtitles.subtitle_fontcolor config.subtitles.subtitle_fontsize diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index ca9483d..bbd8434 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -26,7 +26,8 @@ libenigma_base_a_SOURCES = \ smartptr.cpp \ thread.cpp \ httpstream.cpp \ - socketbase.cpp + socketbase.cpp \ + freesatv2.cpp EXTRA_DIST = \ eenv.cpp.in @@ -57,4 +58,5 @@ baseinclude_HEADERS = \ smartptr.h \ thread.h \ httpstream.h \ - socketbase.h + socketbase.h \ + freesatv2.h diff --git a/lib/base/encoding.cpp b/lib/base/encoding.cpp index 361acce..5609be4 100644 --- a/lib/base/encoding.cpp +++ b/lib/base/encoding.cpp @@ -5,15 +5,16 @@ #include eDVBTextEncodingHandler encodingHandler; // the one and only instance +int defaultEncodingTable = 1; // ISO8859-1 / Latin1 inline char toupper(char c) { - switch (c) - { - case 'a' ... 'z': - return c-32; - } - return c; + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +inline char tolower(char c) +{ + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; } eDVBTextEncodingHandler::eDVBTextEncodingHandler() @@ -41,10 +42,10 @@ eDVBTextEncodingHandler::eDVBTextEncodingHandler() countrycode[2]=toupper(countrycode[2]); m_CountryCodeDefaultMapping[countrycode]=encoding; } - else if ( (sscanf( line, "0x%x 0x%x ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6397 ) - ||(sscanf( line, "%d %d ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6397 ) ) + else if ( (sscanf( line, "0x%x 0x%x ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6937 ) + ||(sscanf( line, "%d %d ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6937 ) ) m_TransponderDefaultMapping[(tsid<<16)|onid]=0; - else if ( sscanf( line, "%s ISO%d", countrycode, &encoding ) == 2 && encoding == 6397 ) + else if ( sscanf( line, "%s ISO%d", countrycode, &encoding ) == 2 && encoding == 6937 ) { m_CountryCodeDefaultMapping[countrycode]=0; countrycode[0]=toupper(countrycode[0]); @@ -67,7 +68,7 @@ eDVBTextEncodingHandler::eDVBTextEncodingHandler() void eDVBTextEncodingHandler::getTransponderDefaultMapping(int tsidonid, int &table) { - std::map::iterator it = + std::map::iterator it = m_TransponderDefaultMapping.find(tsidonid); if ( it != m_TransponderDefaultMapping.end() ) table = it->second; @@ -84,5 +85,5 @@ int eDVBTextEncodingHandler::getCountryCodeDefaultMapping( const std::string &co m_CountryCodeDefaultMapping.find(country_code); if ( it != m_CountryCodeDefaultMapping.end() ) return it->second; - return 1; // ISO8859-1 / Latin1 + return defaultEncodingTable; } diff --git a/lib/base/encoding.h b/lib/base/encoding.h index 2a58da2..76f9393 100644 --- a/lib/base/encoding.h +++ b/lib/base/encoding.h @@ -5,10 +5,30 @@ #include #include +#define ISO8859_5 0x01 // Latin/Cyrillic +#define ISO8859_6 0x02 // Latin/Arabic +#define ISO8859_7 0x03 // Latin/Greek +#define ISO8859_8 0x04 // Latin/Gebrew +#define ISO8859_9 0x05 // Latin 5 +#define ISO8859_10 0x06 // Latin 6 +#define ISO8859_11 0x07 // Latin/Thai +#define ISO8859_12 0x08 // Reserved +#define ISO8859_13 0x09 // Latin 7 +#define ISO8859_14 0x0A // Latin 8 (Celtic) +#define ISO8859_15 0x0B // Latin 9 +#define ISO8859_xx 0x10 // encoded in next two bytes +#define UNICODE_ENCODING 0x11 // ISO10646 Basic Multilingual Plane +#define KSX1001_ENCODING 0x12 // KSX1001 Korean +#define GB18030_ENCODING 0x13 // ISO10646 Simplified Chinese +#define BIG5_ENCODING 0x14 // ISO10646 Big5 Traditional Chineese +#define UTF8_ENCODING 0x15 // ISO10646 Basic Multilingual Plane in UTF8 encoding +#define UTF16BE_ENCODING 0x16 +#define UTF16LE_ENCODING 0x17 + class eDVBTextEncodingHandler { std::map m_CountryCodeDefaultMapping; - std::map m_TransponderDefaultMapping; + std::map m_TransponderDefaultMapping; std::set m_TransponderUseTwoCharMapping; public: eDVBTextEncodingHandler(); @@ -18,5 +38,5 @@ public: }; extern eDVBTextEncodingHandler encodingHandler; - +extern int defaultEncodingTable; #endif // __lib_base_encoding_h__ diff --git a/lib/base/estring.cpp b/lib/base/estring.cpp index f2ac732..10bc89c 100644 --- a/lib/base/estring.cpp +++ b/lib/base/estring.cpp @@ -5,18 +5,19 @@ #include #include #include +#include "freesatv2.h" std::string buildShortName( const std::string &str ) { std::string tmp; - static char stropen[3] = { 0xc2, 0x86, 0x00 }; - static char strclose[3] = { 0xc2, 0x87, 0x00 }; - size_t open=std::string::npos-1; - while ( (open = str.find(stropen, open+2)) != std::string::npos ) + static char stropen[] = "\xc2\x86"; + static char strclose[] = "\xc2\x87"; + size_t open = std::string::npos-1; + while ((open = str.find(stropen, open+2)) != std::string::npos) { size_t close = str.find(strclose, open); - if ( close != std::string::npos ) - tmp+=str.substr( open+2, close-(open+2) ); + if (close != std::string::npos) + tmp += str.substr(open+2, close-(open+2)); } return tmp.length() ? tmp : str; } @@ -30,8 +31,8 @@ std::string getNum(int val, int sys) if (sys == 10) snprintf(buf, 12, "%i", val); else if (sys == 16) - snprintf(buf, 12, "%X", val); - + snprintf(buf, 12, "%X", val); + std::string res; res.assign(buf); return res; @@ -151,7 +152,9 @@ static unsigned long c885916[96]={ 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF}; -static unsigned long iso6397[96]={ +static freesatHuffmanDecoder huffmanDecoder; + +static unsigned long iso6937[96]={ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0000, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x0000, 0xE002, 0xE003, 0xE004, 0xE005, 0xE006, 0xE007, 0xE008, 0xE009, 0xE00C, 0xE00A, 0xE00B, 0x0000, 0xE00D, 0xE00E, 0xE00F, @@ -159,7 +162,7 @@ static unsigned long iso6397[96]={ 0x2126, 0x00C6, 0x0110, 0x00AA, 0x0126, 0x0000, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149, 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x00AD}; -// Two Char Mapping (aka ISO6397) ( many polish services and UPC Direct/HBO services) +// Two Char Mapping (aka ISO6937) ( many polish services and UPC Direct/HBO services) // get from http://mitglied.lycos.de/buran/charsets/videotex-suppl.html static inline unsigned int doVideoTexSuppl(int c1, int c2) { @@ -325,43 +328,49 @@ static inline unsigned int recode(unsigned char d, int cp) return d; switch (cp) { - case 0: // ISO6397 - return iso6397[d-0xA0]; - case 1: // 8859-1 <-> unicode mapping - return d; - case 2: // 8859-2 -> unicode mapping - return c88592[d-0xA0]; - case 3: // 8859-3 -> unicode mapping - return c88593[d-0xA0]; - case 4: // 8859-2 -> unicode mapping - return c88594[d-0xA0]; - case 5: // 8859-5 -> unicode mapping - return c88595[d-0xA0]; - case 6: // 8859-6 -> unicode mapping - return c88596[d-0xA0]; - case 7: // 8859-7 -> unicode mapping - return c88597[d-0xA0]; - case 8: // 8859-8 -> unicode mapping - return c88598[d-0xA0]; - case 9: // 8859-9 -> unicode mapping - return c88599[d-0xA0]; - case 10:// 8859-10 -> unicode mapping - return c885910[d-0xA0]; - case 11:// 8859-11 -> unicode mapping - return c885911[d-0xA0]; -/* case 12:// 8859-12 -> unicode mapping // reserved for indian use.. - return c885912[d-0xA0];*/ - case 13:// 8859-13 -> unicode mapping - return c885913[d-0xA0]; - case 14:// 8859-14 -> unicode mapping - return c885914[d-0xA0]; - case 15:// 8859-15 -> unicode mapping - return c885915[d-0xA0]; - case 16:// 8859-16 -> unicode mapping - return c885916[d-0xA0]; - default: - return d; + case 0: return iso6937[d-0xA0]; // ISO6937 + case 1: return d; // 8859-1 -> unicode mapping + case 2: return c88592[d-0xA0]; // 8859-2 -> unicode mapping + case 3: return c88593[d-0xA0]; // 8859-3 -> unicode mapping + case 4: return c88594[d-0xA0]; // 8859-2 -> unicode mapping + case 5: return c88595[d-0xA0]; // 8859-5 -> unicode mapping + case 6: return c88596[d-0xA0]; // 8859-6 -> unicode mapping + case 7: return c88597[d-0xA0]; // 8859-7 -> unicode mapping + case 8: return c88598[d-0xA0]; // 8859-8 -> unicode mapping + case 9: return c88599[d-0xA0]; // 8859-9 -> unicode mapping + case 10: return c885910[d-0xA0]; // 8859-10 -> unicode mapping + case 11: return c885911[d-0xA0]; // 8859-11 -> unicode mapping +// case 12: return c885912[d-0xA0]; // 8859-12 -> unicode mapping // reserved for indian use.. + case 13: return c885913[d-0xA0]; // 8859-13 -> unicode mapping + case 14: return c885914[d-0xA0]; // 8859-14 -> unicode mapping + case 15: return c885915[d-0xA0]; // 8859-15 -> unicode mapping + case 16: return c885916[d-0xA0]; // 8859-16 -> unicode mapping + default: return d; + } +} + +std::string UnicodeToUTF8(long c) +{ + if ( c < 0x80 ) { + char utf[2] = {static_cast(c), 0}; + return std::string(utf, 1); + } + else if ( c < 0x800) { + char utf[3] = { static_cast(0xc0 | (c >> 6)), static_cast(0x80 | (c & 0x3f)), 0}; + return std::string(utf, 2); + } + else if ( c < 0x10000) { + char utf[4] = { static_cast(0xe0 | (c >> 12)), static_cast(0x80 | ((c >> 6) & 0x3f)), + static_cast(0x80 | (c & 0x3f)), 0}; + return std::string(utf, 3); + } + else if ( c < 0x200000) { + char utf[5] = { static_cast(0xf0 | (c >> 18)), static_cast(0x80 | ((c >> 12) & 0x3f)), + static_cast(0x80 | ((c >> 6) & 0x3f)), static_cast(0x80 | (c & 0x3f)), 0}; + return std::string(utf, 4); } + eDebug("[UnicodeToUTF8] invalid unicode character: code=0x%08lx", c); // not a valid unicode + return ""; } std::string convertDVBUTF8(const unsigned char *data, int len, int table, int tsidonid) @@ -369,57 +378,82 @@ std::string convertDVBUTF8(const unsigned char *data, int len, int table, int ts if (!len) return ""; - int i=0, t=0; + int i = 0; + std::string output = ""; - if ( tsidonid ) + if (tsidonid) encodingHandler.getTransponderDefaultMapping(tsidonid, table); + // first byte in strings may override general encoding table. switch(data[0]) { - case 1 ... 11: - table=data[i++]+4; -// eDebug("(1..11)text encoded in ISO-8859-%d",table); + case ISO8859_5 ... ISO8859_15: + // For Thai providers, encoding char is present but faulty. + if (table != 11) + table = data[i] + 4; + ++i; +// eDebug("[convertDVBUTF8] (1..11)text encoded in ISO-8859-%d", table); break; - case 0x10: + case ISO8859_xx: { - int n=(data[++i]<<8); + int n = data[++i] << 8; n |= (data[++i]); -// eDebug("(0x10)text encoded in ISO-8859-%d",n); +// eDebug("[convertDVBUTF8] (0x10)text encoded in ISO-8859-%d",n); ++i; switch(n) { - case 12: - eDebug("unsup. ISO8859-12 enc."); + case 0x0C: // ETSI EN 300 468 Table A.4: Reserved for future use + eDebug("[convertDVBUTF8] ISO 8859-12 encoding unsupported"); break; default: - table=n; + table = n; break; } break; } - case 0x11: // Basic Multilingual Plane of ISO/IEC 10646-1 enc (UTF-16... Unicode) - table = 65; + case UNICODE_ENCODING: // Basic Multilingual Plane of ISO/IEC 10646-1 enc (UTF-16... Unicode) + table = UNICODE_ENCODING; tsidonid = 0; ++i; break; - case 0x12: + case KSX1001_ENCODING: + ++i; + eDebug("[convertDVBUTF8] KSC 5601 encoding unsupported."); + break; + case GB18030_ENCODING: ++i; - eDebug("unsup. KSC 5601 enc."); + eDebug("[convertDVBUTF8] GB-2312-1980 encoding unsupported."); break; - case 0x13: + case BIG5_ENCODING: ++i; - eDebug("unsup. GB-2312-1980 enc."); + eDebug("[convertDVBUTF8] Big5 subset of ISO/IEC 10646-1 encoding unsupported."); break; - case 0x14: + case UTF8_ENCODING: // UTF-8 encoding of ISO/IEC 10646-1 + ++i; + table = UTF8_ENCODING; + break; + case UTF16BE_ENCODING: + ++i; + table = UTF16BE_ENCODING; + break; + case UTF16LE_ENCODING: + ++i; + table = UTF16LE_ENCODING; + break; + case 0x1F: + { + // Attempt to decode Freesat Huffman encoded string + std::string decoded_string = huffmanDecoder.decode(data, len); + if (!decoded_string.empty()) + return decoded_string; + } ++i; - eDebug("unsup. Big5 subset of ISO/IEC 10646-1 enc."); + eDebug("[convertDVBUTF8] failed to decode bbc freesat huffman"); break; - case 0x15: // UTF-8 encoding of ISO/IEC 10646-1 - return std::string((char*)data+1, len-1); case 0x0: case 0xC ... 0xF: - case 0x16 ... 0x1F: - eDebug("reserved %d", data[0]); + case 0x18 ... 0x1E: + eDebug("[convertDVBUTF8] reserved %d", data[0]); ++i; break; } @@ -427,54 +461,75 @@ std::string convertDVBUTF8(const unsigned char *data, int len, int table, int ts bool useTwoCharMapping = !table || (tsidonid && encodingHandler.getTransponderUseTwoCharMapping(tsidonid)); if (useTwoCharMapping && table == 5) { // i hope this dont break other transponders which realy use ISO8859-5 and two char byte mapping... -// eDebug("Cyfra / Cyfrowy Polsat HACK... override given ISO8859-5 with ISO6397"); +// eDebug("[convertDVBUTF8] Cyfra / Cyfrowy Polsat HACK... override given ISO8859-5 with ISO6937"); table = 0; } + else if ( table == -1 ) + table = defaultEncodingTable; - unsigned char res[2048]; - while (i < len) + switch(table) { - unsigned long code=0; - if ( useTwoCharMapping && i+1 < len && (code=doVideoTexSuppl(data[i], data[i+1])) ) - i+=2; - if (!code) { - if (table == 65) { // unicode - if (i+1 < len) { - code=(data[i] << 8) | data[i+1]; + case UTF8_ENCODING: + output = std::string((char*)data + i, len - i); + break; + default: + std::string res = ""; + while (i < len) + { + unsigned long code = 0; + if (useTwoCharMapping && i+1 < len && (code = doVideoTexSuppl(data[i], data[i+1]))) i += 2; + else if (table == UTF16BE_ENCODING || table == UNICODE_ENCODING) { + if (i+2 > len) + break; + unsigned long w1 = ((unsigned long)(data[i])<<8) | ((unsigned long)(data[i+1])); + if (w1 < 0xD800UL || w1 > 0xDFFFUL) { + code = w1; + i += 2; + } + else if (w1 > 0xDBFFUL) + break; + else if (i+4 < len) { + unsigned long w2 = ((unsigned long)(data[i+2]) << 8) | ((unsigned long)(data[i+3])); + if (w2 < 0xDC00UL || w2 > 0xDFFFUL) + return std::string(""); + code = 0x10000UL + (((w1 & 0x03FFUL) << 10 ) | (w2 & 0x03FFUL)); + i += 4; + } + else + break; } + else if (table == UTF16LE_ENCODING) { + if ((i+2) > len) + break; + unsigned long w1 = ((unsigned long)(data[i+1]) << 8) | ((unsigned long)(data[i])); + if (w1 < 0xD800UL || w1 > 0xDFFFUL) { + code = w1; + i += 2; + } + else if (w1 > 0xDBFFUL) + break; + else if (i+4 < len) { + unsigned long w2 = ((unsigned long)(data[i+3]) << 8) | ((unsigned long)(data[i+2])); + if (w2 < 0xDC00UL || w2 > 0xDFFFUL) + break; + code = 0x10000UL + (((w2 & 0x03FFUL) << 10 ) | (w1 & 0x03FFUL)); + i += 4; + } + else + break; + } + if (!code) + code = recode(data[i++], table); + + if (!code) + continue; + res += UnicodeToUTF8(code); } - else - code=recode(data[i++], table); - } - if (!code) - continue; - // Unicode->UTF8 encoding - if (code < 0x80) // identity ascii <-> utf8 mapping - res[t++]=char(code); - else if (code < 0x800) // two byte mapping - { - res[t++]=(code>>6)|0xC0; - res[t++]=(code&0x3F)|0x80; - } else if (code < 0x10000) // three bytes mapping - { - res[t++]=(code>>12)|0xE0; - res[t++]=((code>>6)&0x3F)|0x80; - res[t++]=(code&0x3F)|0x80; - } else - { - res[t++]=(code>>18)|0xF0; - res[t++]=((code>>12)&0x3F)|0x80; - res[t++]=((code>>6)&0x3F)|0x80; - res[t++]=(code&0x3F)|0x80; - } - if (t+4 > 2047) - { - eDebug("convertDVBUTF8 buffer to small.. break now"); + output = res; break; - } } - return std::string((char*)res, t); + return output; } std::string convertUTF8DVB(const std::string &string, int table) @@ -485,171 +540,141 @@ std::string convertUTF8DVB(const std::string &string, int table) unsigned char buf[len]; - for(int i=0;iUTF8 encoding - if (code < 0x80) // identity latin <-> utf8 mapping - res[t++]=char(code); - else if (code < 0x800) // two byte mapping - { - res[t++]=(code>>6)|0xC0; - res[t++]=(code&0x3F)|0x80; - } else if (code < 0x10000) // three bytes mapping - { - res[t++]=(code>>12)|0xE0; - res[t++]=((code>>6)&0x3F)|0x80; - res[t++]=(code&0x3F)|0x80; - } else - { - res[t++]=(code>>18)|0xF0; - res[t++]=((code>>12)&0x3F)|0x80; - res[t++]=((code>>6)&0x3F)|0x80; - res[t++]=(code&0x3F)|0x80; - } - if (t+4 > 2047) - { - eDebug("convertLatin1UTF8 buffer to small.. break now"); - break; - } + unsigned long code = (unsigned char)string[i++]; + res += UnicodeToUTF8(code); } - return std::string((char*)res, t); + return res; } int isUTF8(const std::string &string) { - unsigned int len=string.size(); - - for (unsigned int i=0; i < len; ++i) + unsigned int len = string.size(); + + // Unicode chars: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // (i.e. any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + // Avoid "compatibility characters", as defined in section 2.3 of The Unicode Standard, Version 5.0.0. + // Following characters are also discouraged. They are either control characters or permanently + // undefined Unicode characters: + //[#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDEF], + //[#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF], + //[#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF], + //[#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF], + //[#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF], + //[#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF], + //[#x10FFFE-#x10FFFF]. + + for (unsigned int i = 0; i < len; ++i) { - if (!(string[i]&0x80)) // normal ASCII + if (!(string[i] & 0x80)) // normal ASCII continue; - if ((string[i] & 0xE0) == 0xC0) // one char following. - { - // first, length check: - if (i+1 >= len) - return 0; // certainly NOT utf-8 - i++; - if ((string[i]&0xC0) != 0x80) - return 0; // no, not UTF-8. - } else if ((string[i] & 0xF0) == 0xE0) - { - if ((i+1) >= len) - return 0; - i++; - if ((string[i]&0xC0) != 0x80) - return 0; - i++; - if ((string[i]&0xC0) != 0x80) + int l = 0; + if ((string[i] & 0xE0) == 0xC0) // 2-byte + l = 1; + else if ((string[i] & 0xF0) == 0xE0) // 3-byte + l = 2; + else if ((string[i] & 0xF8) == 0xF0) // 4-byte + l = 3; + if (l == 0 || i + l >= len) // no UTF leader or not enough bytes + return 0; + + while (l-- > 0) { + if ((string[++i] & 0xC0) != 0x80) return 0; } } return 1; // can be UTF8 (or pure ASCII, at least no non-UTF-8 8bit characters) } +unsigned int truncateUTF8(std::string &s, unsigned int newsize) +{ + unsigned int len = s.size(); + + // Assume s is a real UTF8 string!!! + while (len > newsize) { + while (len-- > 0 && (s[len] & 0xC0) == 0x80) + ; // remove UTF data bytes, e.g. range 0x80 - 0xBF + if (len > 0) // remove the UTF startbyte, or normal ascii character + --len; + } + s.resize(len); + return len; +} + std::string removeDVBChars(const std::string &s) { std::string res; int len = s.length(); - for(int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { unsigned char c1 = s[i]; unsigned int c; /* UTF8? decode (but only simple) */ - if((c1 > 0x80) && (i < len-1)) + if ((c1 > 0x80) && (i < len-1)) { unsigned char c2 = s[i + 1]; c = ((c1&0x3F)<<6) + (c2&0x3F); @@ -659,10 +684,9 @@ std::string removeDVBChars(const std::string &s) continue; } } - res += s[i]; } - + return res; } @@ -671,11 +695,50 @@ void makeUpper(std::string &s) std::transform(s.begin(), s.end(), s.begin(), (int(*)(int)) toupper); } -std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol) +std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol, int table) { std::string out = in; std::string::size_type loc = 0; - while (( loc = out.find(entity, loc)) != std::string::npos ) - out.replace(loc, entity.length(), symbol); + if( table == -1 ) + table = defaultEncodingTable; + switch(table){ + case UTF8_ENCODING: + while (loc < out.length()) { + if ( (entity.length() + loc) <= out.length() && !out.compare(loc, entity.length(), entity)) { + out.replace(loc, entity.length(), symbol); + loc += symbol.length(); + continue; + } + if (out.at(loc) < 0x80) + ++loc; + else if ((out.at(loc) & 0xE0) == 0xC0) + loc += 2; + else if ((out.at(loc) & 0xF0) == 0xE0) + loc += 3; + else if ((out.at(loc) & 0xF8) == 0xF0) + loc += 4; + } + break; + + case UTF16BE_ENCODING: + case UTF16LE_ENCODING: + while (loc + +#define START '\0' +#define STOP '\0' +#define ESCAPE '\1' + +#ifndef DATADIR +# define DATADIR "/usr/share" +#endif + +#ifndef FREESAT_DATA_DIRECTORY +#define FREESAT_DATA_DIRECTORY DATADIR +#endif +#define TABLE1_FILENAME FREESAT_DATA_DIRECTORY "/enigma2/freesat.t1" +#define TABLE2_FILENAME FREESAT_DATA_DIRECTORY "/enigma2/freesat.t2" + +static void loadFile(huffTableEntry **table, const char *filename); + + +struct huffTableEntry +{ + uint32_t value; + uint16_t bits; + char next; + huffTableEntry * nextEntry; + + huffTableEntry(unsigned int value, short bits, char next) : value(value), bits(bits), next(next), nextEntry(NULL) + { } +}; + +freesatHuffmanDecoder::freesatHuffmanDecoder() +{ + memset(m_tables, 0, sizeof(m_tables)); + loadFile(&m_tables[0][0], TABLE1_FILENAME); + loadFile(&m_tables[1][0], TABLE2_FILENAME); +} + +freesatHuffmanDecoder::~freesatHuffmanDecoder() +{ + int i, j; + huffTableEntry *currentEntry, *nextEntry; + for ( j = 0 ; j < 2; j++ ) + { + for ( i = 0 ; i < 256; i++ ) + { + currentEntry = m_tables[j][i]; + while ( currentEntry != NULL ) + { + nextEntry = currentEntry->nextEntry; + delete currentEntry; + currentEntry = nextEntry; + } + m_tables[j][i] = NULL; + } + } +} + + +/** \brief Convert a textual character description into a value +* +* \param str - Encoded (in someway) string +* +* \return Raw character +*/ +static unsigned char resolveChar(const char *str) +{ + const char *p = str; + unsigned c0 = *p++, c1 = *p++; + if (c1) + switch(c0|c1<<8) + { + case '0'|'x'<<8: + if ( sscanf(p,"%02x", &c1) == 1 ) + c0 = c1; + break; + case 'E'|'S'<<8: + if ( !strcmp(p,"CAPE") ) + c0 = ESCAPE; + break; + case 'S'|'T'<<8: + if ( !strcmp(p,"OP") ) + c0 = STOP; + else if ( !strcmp(p,"ART") ) + c0 = START; + break; + } + return c0; +} + + +/** \brief Decode a binary string into a value +* +* \param binary - Binary string to decode +* +* \return Decoded value +*/ +static unsigned long decodeBinary(const char *binary) +{ + unsigned long mask = 0x80000000; + unsigned long val = 0; + + while (*binary) + { + if ( *binary == '1' ) + { + val |= mask; + } + mask >>= 1; + ++binary; + } + return val; +} + +static void loadFile(huffTableEntry **table, const char *filename) +{ + char buf[1024]; + char *from; + char *to; + char *binary; + char *colon; + + FILE *fp = fopen(filename, "r"); + if ( fp ) + { + while ( fgets(buf,sizeof(buf),fp) != NULL ) + { + // Tokenize string "in place" + from = buf; + colon = strchr(buf, ':'); + if (colon == NULL) + continue; + binary = colon + 1; + *colon = 0; + colon = strchr(binary, ':'); + if (colon == NULL) + continue; + *colon = 0; + to = colon + 1; + colon = strchr(to, ':'); + if (colon != NULL) + *colon = 0; + { + int bin_len = strlen(binary); + int from_char = resolveChar(from); + char to_char = resolveChar(to); + unsigned long bin = decodeBinary(binary); + + // Add entry to end of bucket + huffTableEntry **pCurrent = &table[from_char]; + while ( *pCurrent != NULL ) + { + pCurrent = &((*pCurrent)->nextEntry); + } + *pCurrent = new huffTableEntry(bin, bin_len, to_char); + } + } + fclose(fp); + } +#ifdef FREESATV2_DEBUG + else + { + eDebug("[FREESAT] Cannot load '%s'",filename); + } +#endif +} + + +/** \brief Decode an EPG string as necessary +* +* \param src - Possibly encoded string +* \param size - Size of the buffer +* +* \retval NULL - Can't decode +* \return A decoded string +*/ +std::string freesatHuffmanDecoder::decode(const unsigned char *src, size_t size) +{ + std::string uncompressed; + + if (src[0] != 0x1f) + return uncompressed; + + const unsigned int table_index = src[1] - 1; + + if (table_index <= 1) + { + huffTableEntry **table = &m_tables[table_index][0]; + unsigned int value = 0; + unsigned int byte = 2; + unsigned int bit = 0; + int lastch = START; + + while (byte < 6 && byte < size) + { + value |= src[byte] << ((5-byte) * 8); + byte++; + } + + do + { + int found = 0; + unsigned bitShift = 0; + if (lastch == ESCAPE) + { + char nextCh = (value >> 24) & 0xff; + found = 1; + // Encoded in the next 8 bits. + // Terminated by the first ASCII character. + bitShift = 8; + if ((nextCh & 0x80) == 0) + lastch = nextCh; + uncompressed.append(&nextCh, 1); + } + else + { + huffTableEntry * currentEntry = table[lastch]; + while ( currentEntry != NULL ) + { + unsigned mask = 0, maskbit = 0x80000000; + short kk; + for ( kk = 0; kk < currentEntry->bits; kk++) + { + mask |= maskbit; + maskbit >>= 1; + } + if ((value & mask) == currentEntry->value) + { + char nextCh = currentEntry->next; + bitShift = currentEntry->bits; + if (nextCh != STOP && nextCh != ESCAPE) + { + uncompressed.append(&nextCh, 1); + } + found = 1; + lastch = nextCh; + break; + } + currentEntry = currentEntry->nextEntry; + } + } + if (found) + { + // Shift up by the number of bits. + unsigned b; + for ( b = 0; b < bitShift; b++) + { + value = (value << 1) & 0xfffffffe; + if (byte < size) + value |= (src[byte] >> (7-bit)) & 1; + if (bit == 7) + { + bit = 0; + byte++; + } + else bit++; + } + } + else + { +#ifdef FREESATV2_DEBUG + eDebug("[FREESAT] Missing table %d entry: <%s>", table_index + 1, uncompressed.c_str()); +#endif + return uncompressed; + } + } while (lastch != STOP && value != 0); + } + return uncompressed; +} diff --git a/lib/base/freesatv2.h b/lib/base/freesatv2.h new file mode 100644 index 0000000..6c17a12 --- /dev/null +++ b/lib/base/freesatv2.h @@ -0,0 +1,20 @@ +#ifndef FREESAT_H +#define FREESAT_H + +#include +#include +#include + +struct huffTableEntry; + +class freesatHuffmanDecoder +{ +private: + huffTableEntry *m_tables[2][256]; +public: + freesatHuffmanDecoder(); + ~freesatHuffmanDecoder(); + std::string decode(const unsigned char *src, size_t size); +}; +#endif + diff --git a/lib/dvb/db.cpp b/lib/dvb/db.cpp old mode 100755 new mode 100644 index b2d21f4..dadca4d --- a/lib/dvb/db.cpp +++ b/lib/dvb/db.cpp @@ -1526,6 +1526,21 @@ eServiceReference eDVBDB::searchReference(int tsid, int onid, int sid) return eServiceReference(); } +void eDVBDB::searchAllReferences(std::vector &result, int tsid, int onid, int sid) +{ + eServiceID Sid(sid); + eTransportStreamID Tsid(tsid); + eOriginalNetworkID Onid(onid); + for (std::map >::iterator sit(m_services.begin()); + sit != m_services.end(); ++sit) + { + if (sit->first.getTransportStreamID() == Tsid && + sit->first.getOriginalNetworkID() == Onid && + sit->first.getServiceID() == Sid) + result.push_back(sit->first); + } +} + DEFINE_REF(eDVBDBQueryBase); eDVBDBQueryBase::eDVBDBQueryBase(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query) diff --git a/lib/dvb/db.h b/lib/dvb/db.h index 098ee03..dd1585f 100644 --- a/lib/dvb/db.h +++ b/lib/dvb/db.h @@ -61,6 +61,7 @@ public: ////// void loadBouquet(const char *path); eServiceReference searchReference(int tsid, int onid, int sid); + void searchAllReferences(std::vector &result, int tsid, int onid, int sid); eDVBDB(); virtual ~eDVBDB(); #endif diff --git a/lib/dvb/dvbtime.cpp b/lib/dvb/dvbtime.cpp index 26b6767..2db7c8e 100644 --- a/lib/dvb/dvbtime.cpp +++ b/lib/dvb/dvbtime.cpp @@ -67,13 +67,8 @@ time_t getRTC() return rtc_time != prev_time ? rtc_time : 0; } -time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash) +static void parseDVBdate(tm& t, int mjd) { - tm t; - t.tm_sec=fromBCD(t5); - t.tm_min=fromBCD(t4); - t.tm_hour=fromBCD(t3); - int mjd=(t1<<8)|t2; int k; t.tm_year = (int) ((mjd - 15078.2) / 365.25); @@ -86,12 +81,39 @@ time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash) t.tm_isdst = 0; t.tm_gmtoff = 0; +} - if (hash) { - *hash = t.tm_hour * 60 + t.tm_min; - *hash |= t.tm_mday << 11; - } +static inline void parseDVBtime_impl(tm& t, const uint8_t *data) +{ + parseDVBdate(t, (data[0] << 8) | data[1]); + t.tm_hour = fromBCD(data[2]); + t.tm_min = fromBCD(data[3]); + t.tm_sec = fromBCD(data[4]); +} +time_t parseDVBtime(uint16_t mjd, uint32_t stime_bcd) +{ + tm t; + parseDVBdate(t, mjd); + t.tm_hour = fromBCD(stime_bcd >> 16); + t.tm_min = fromBCD((stime_bcd >> 8)&0xFF); + t.tm_sec = fromBCD(stime_bcd & 0xFF); + return timegm(&t); +} + +time_t parseDVBtime(const uint8_t *data) +{ + tm t; + parseDVBtime_impl(t, data); + return timegm(&t); +} + +time_t parseDVBtime(const uint8_t *data, uint16_t *hash) +{ + tm t; + parseDVBtime_impl(t, data); + *hash = t.tm_hour * 60 + t.tm_min; + *hash |= t.tm_mday << 11; return timegm(&t); } @@ -116,7 +138,7 @@ int TDT::createTable(unsigned int nr, const __u8 *data, unsigned int max) int length = ((data[1] & 0x0F) << 8) | data[2]; if ( length >= 5 ) { - time_t tptime = parseDVBtime(data[3], data[4], data[5], data[6], data[7]); + time_t tptime = parseDVBtime(&data[3]); if (tptime && tptime != -1) eDVBLocalTimeHandler::getInstance()->updateTime(tptime, chan, update_count); error=0; @@ -264,9 +286,6 @@ void eDVBLocalTimeHandler::updateTime( time_t tp_time, eDVBChannel *chan, int up else if (tp_time == -1) { restart_tdt = true; - /*if ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7020 || - ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7000 - && eSystemInfo::getInstance()->hasStandbyWakeupTimer() ) ) TODO !!!!!!! */ { eDebug("[eDVBLocalTimerHandler] no transponder tuned... or no TDT/TOT avail .. try to use RTC :)"); time_t rtc_time = getRTC(); diff --git a/lib/dvb/dvbtime.h b/lib/dvb/dvbtime.h index c49e67e..e8f4ae3 100644 --- a/lib/dvb/dvbtime.h +++ b/lib/dvb/dvbtime.h @@ -25,7 +25,9 @@ inline int toBCD(int dec) return int(dec/10)*0x10 + dec%10; } -time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash=0); +time_t parseDVBtime(uint16_t mjd, uint32_t stime_bcd); +time_t parseDVBtime(const uint8_t* data); +time_t parseDVBtime(const uint8_t* data, uint16_t *hash); class TDT: public eGTable { diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index 4d32474..0d2545f 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -1,26 +1,78 @@ #include #include +#include -#undef EPG_DEBUG +#undef EPG_DEBUG #ifdef EPG_DEBUG #include #endif +#include +#include #include #include // for usleep #include // for statfs -// #include #include +#include #include #include #include #include #include -int eventData::CacheSize=0; -descriptorMap eventData::descriptors; -__u8 eventData::data[4108]; +/* Interval between "garbage collect" cycles */ +#define CLEAN_INTERVAL 60000 // 1 min +/* Restart EPG data capture */ +#define UPDATE_INTERVAL 3600000 // 60 min +/* Time to wait after tuning in before EPG data capturing starts */ +#define ZAP_DELAY 2000 // 2 sec + +struct DescriptorPair +{ + int reference_count; + uint8_t* data; + + DescriptorPair() {} + DescriptorPair(int c, uint8_t* d): reference_count(c), data(d) {} +}; + +typedef std::tr1::unordered_map DescriptorMap; + +struct eventData +{ + uint8_t rawEITdata[10]; + uint8_t n_crc; + uint8_t type; + uint32_t *crc_list; + static DescriptorMap descriptors; + static uint8_t data[]; + static unsigned int CacheSize; + static bool isCacheCorrupt; + eventData(const eit_event_struct* e = NULL, int size = 0, int type = 0, int tsidonid = 0); + ~eventData(); + static void load(FILE *); + static void save(FILE *); + static void cacheCorrupt(const char* context); + const eit_event_struct* get() const; + int getEventID() const + { + return (rawEITdata[0] << 8) | rawEITdata[1]; + } + time_t getStartTime() const + { + return parseDVBtime(&rawEITdata[2]); + } + int getDuration() const + { + return fromBCD(rawEITdata[7])*3600+fromBCD(rawEITdata[8])*60+fromBCD(rawEITdata[9]); + } +}; + +unsigned int eventData::CacheSize = 0; +bool eventData::isCacheCorrupt = 0; +DescriptorMap eventData::descriptors; +uint8_t eventData::data[2 * 4096 + 12]; extern const uint32_t crc32_table[256]; const eServiceReference &handleGroup(const eServiceReference &ref) @@ -33,7 +85,7 @@ const eServiceReference &handleGroup(const eServiceReference &ref) ePtr db; if (!res->getChannelList(db)) { - eBouquet *bouquet=0; + eBouquet *bouquet = NULL; if (!db->getBouquet(ref, bouquet)) { std::list::iterator it(bouquet->m_services.begin()); @@ -46,22 +98,30 @@ const eServiceReference &handleGroup(const eServiceReference &ref) return ref; } -eventData::eventData(const eit_event_struct* e, int size, int type) - :ByteSize(size&0xFF), type(type&0xFF) +static uint32_t calculate_crc_hash(const uint8_t *data, int size) +{ + uint32_t crc = 0; + for (int i = 0; i < size; ++i) + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[i]) & 0xFF]; + return crc; +} + +eventData::eventData(const eit_event_struct* e, int size, int _type, int tsidonid) + :n_crc(0), type(_type & 0xFF), crc_list(NULL) { if (!e) - return; + return; /* Used when loading from file */ - __u32 descr[65]; - __u32 *pdescr=descr; + uint32_t descr[65]; + uint32_t *pdescr=descr; - __u8 *data = (__u8*)e; + uint8_t *data = (uint8_t*)e; int ptr=12; size -= 12; while(size > 1) { - __u8 *descr = data+ptr; + uint8_t *descr = data + ptr; int descr_len = descr[1]; descr_len += 2; if (size >= descr_len) @@ -69,69 +129,158 @@ eventData::eventData(const eit_event_struct* e, int size, int type) switch (descr[0]) { case EXTENDED_EVENT_DESCRIPTOR: - case SHORT_EVENT_DESCRIPTOR: case LINKAGE_DESCRIPTOR: case COMPONENT_DESCRIPTOR: + case CONTENT_DESCRIPTOR: + case PARENTAL_RATING_DESCRIPTOR: + case PDC_DESCRIPTOR: { - __u32 crc = 0; - int cnt=0; - while(cnt++ < descr_len) - crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[ptr++]) & 0xFF]; - - descriptorMap::iterator it = - descriptors.find(crc); + uint32_t crc = calculate_crc_hash(descr, descr_len); + DescriptorMap::iterator it = descriptors.find(crc); if ( it == descriptors.end() ) { CacheSize+=descr_len; - __u8 *d = new __u8[descr_len]; + uint8_t *d = new uint8_t[descr_len]; memcpy(d, descr, descr_len); - descriptors[crc] = descriptorPair(1, d); + descriptors[crc] = DescriptorPair(1, d); } else - ++it->second.first; - *pdescr++=crc; + ++it->second.reference_count; + *pdescr++ = crc; + break; + } + case SHORT_EVENT_DESCRIPTOR: + { + //parse the data out from the short event descriptor + //get the country code, which will be used for converting to UTF8 + std::string cc( (const char*)&descr[2], 3); + std::transform(cc.begin(), cc.end(), cc.begin(), tolower); + int table = encodingHandler.getCountryCodeDefaultMapping(cc); + + int eventNameLen = descr[5]; + int eventTextLen = descr[6 + eventNameLen]; + + //convert our strings to UTF8 + std::string eventNameUTF8 = convertDVBUTF8((const unsigned char*)&descr[6], eventNameLen, table, tsidonid); + std::string textUTF8 = convertDVBUTF8((const unsigned char*)&descr[7 + eventNameLen], eventTextLen, table, tsidonid); + unsigned int eventNameUTF8len = eventNameUTF8.length(); + unsigned int textUTF8len = textUTF8.length(); + + //Rebuild the short event descriptor with UTF-8 strings + + //Save the title first + if( eventNameUTF8len > 0 ) //only store the data if there is something to store + { + /*this will actually cause us to save some memory + previously some descriptors didnt match because there text was different and titles the same. + Now that we store them seperatly we can save some space on title data some rough calculation show anywhere from 20 - 40% savings + */ + eventNameUTF8len = truncateUTF8(eventNameUTF8, 255 - 6); + int title_len = 6 + eventNameUTF8len; + uint8_t *title_data = new uint8_t[title_len + 2]; + title_data[0] = SHORT_EVENT_DESCRIPTOR; + title_data[1] = title_len; + title_data[2] = descr[2]; + title_data[3] = descr[3]; + title_data[4] = descr[4]; + title_data[5] = eventNameUTF8len + 1; + title_data[6] = 0x15; //identify event name as UTF-8 + memcpy(&title_data[7], eventNameUTF8.data(), eventNameUTF8len); + title_data[7 + eventNameUTF8len] = 0; + + //Calculate the CRC, based on our new data + title_len += 2; //add 2 the length to include the 2 bytes in the header + uint32_t title_crc = calculate_crc_hash(title_data, title_len); + + DescriptorMap::iterator it = descriptors.find(title_crc); + if ( it == descriptors.end() ) + { + CacheSize += title_len; + descriptors[title_crc] = DescriptorPair(1, title_data); + } + else + { + ++it->second.reference_count; + delete [] title_data; + } + *pdescr++ = title_crc; + } + + //save the text + if( textUTF8len > 0 ) //only store the data if there is something to store + { + textUTF8len = truncateUTF8(textUTF8, 255 - 6); + int text_len = 6 + textUTF8len; + uint8_t *text_data = new uint8_t[text_len + 2]; + text_data[0] = SHORT_EVENT_DESCRIPTOR; + text_data[1] = text_len; + text_data[2] = descr[2]; + text_data[3] = descr[3]; + text_data[4] = descr[4]; + text_data[5] = 0; + text_data[6] = textUTF8len + 1; //identify text as UTF-8 + text_data[7] = 0x15; //identify text as UTF-8 + memcpy(&text_data[8], textUTF8.data(), textUTF8len); + + text_len += 2; //add 2 the length to include the 2 bytes in the header + uint32_t text_crc = calculate_crc_hash(text_data, text_len); + + DescriptorMap::iterator it = descriptors.find(text_crc); + if ( it == descriptors.end() ) + { + CacheSize += text_len; + descriptors[text_crc] = DescriptorPair(1, text_data); + } + else + { + ++it->second.reference_count; + delete [] text_data; + } + *pdescr++ = text_crc; + } break; } default: // do not cache all other descriptors - ptr += descr_len; break; } + ptr += descr_len; size -= descr_len; } else break; } + memcpy(rawEITdata, (uint8_t*)e, 10); ASSERT(pdescr <= &descr[65]); - ByteSize = 10+((pdescr-descr)*4); - EITdata = new __u8[ByteSize]; - CacheSize+=ByteSize; - memcpy(EITdata, (__u8*) e, 10); - memcpy(EITdata+10, descr, ByteSize-10); + n_crc = pdescr - descr; + if (n_crc) + { + crc_list = new uint32_t[n_crc]; + memcpy(crc_list, descr, n_crc * sizeof(uint32_t)); + } + CacheSize += sizeof(*this) + n_crc * sizeof(uint32_t); } const eit_event_struct* eventData::get() const { - int pos = 12; - int tmp = ByteSize-10; - memcpy(data, EITdata, 10); - int descriptors_length=0; - __u32 *p = (__u32*)(EITdata+10); - while(tmp>3) + unsigned int pos = 12; + memcpy(data, rawEITdata, 10); + unsigned int descriptors_length = 0; + for (uint8_t i = 0; i < n_crc; ++i) { - descriptorMap::iterator it = - descriptors.find(*p++); - if ( it != descriptors.end() ) + DescriptorMap::iterator it = descriptors.find(crc_list[i]); + if (it != descriptors.end()) { - int b = it->second.second[1]+2; - memcpy(data+pos, it->second.second, b ); - pos += b; - descriptors_length += b; + unsigned int b = it->second.data[1] + 2; + if (pos + b < sizeof(data)) + { + memcpy(data + pos, it->second.data, b); + pos += b; + descriptors_length += b; + } } else - eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(p-1)); - tmp-=4; + cacheCorrupt("eventData::get"); } - ASSERT(pos <= 4108); data[10] = (descriptors_length >> 8) & 0x0F; data[11] = descriptors_length & 0xFF; return (eit_event_struct*)data; @@ -139,83 +288,95 @@ const eit_event_struct* eventData::get() const eventData::~eventData() { - if ( ByteSize ) + for ( uint8_t i = 0; i < n_crc; ++i ) { - CacheSize -= ByteSize; - __u32 *d = (__u32*)(EITdata+10); - ByteSize -= 10; - while(ByteSize>3) + DescriptorMap::iterator it = descriptors.find(crc_list[i]); + if ( it != descriptors.end() ) { - descriptorMap::iterator it = - descriptors.find(*d++); - if ( it != descriptors.end() ) + DescriptorPair &p = it->second; + if (!--p.reference_count) // no more used descriptor { - descriptorPair &p = it->second; - if (!--p.first) // no more used descriptor - { - CacheSize -= it->second.second[1]; - delete [] it->second.second; // free descriptor memory - descriptors.erase(it); // remove entry from descriptor map - } + CacheSize -= it->second.data[1]; + delete [] it->second.data; // free descriptor memory + descriptors.erase(it); // remove entry from descriptor map } - else - eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(d-1)); - ByteSize -= 4; } - delete [] EITdata; + else + { + cacheCorrupt("eventData::~eventData"); + } } + delete [] crc_list; + CacheSize -= sizeof(*this) + n_crc * sizeof(uint32_t); } void eventData::load(FILE *f) { - int size=0; + int size = 0; int id=0; - __u8 header[2]; - descriptorPair p; + DescriptorPair p; + uint8_t header[2]; fread(&size, sizeof(int), 1, f); + descriptors.rehash(size); while(size) { - fread(&id, sizeof(__u32), 1, f); - fread(&p.first, sizeof(int), 1, f); + fread(&id, sizeof(uint32_t), 1, f); + fread(&p.reference_count, sizeof(int), 1, f); fread(header, 2, 1, f); int bytes = header[1]+2; - p.second = new __u8[bytes]; - p.second[0] = header[0]; - p.second[1] = header[1]; - fread(p.second+2, bytes-2, 1, f); - descriptors[id]=p; + p.data = new uint8_t[bytes]; + p.data[0] = header[0]; + p.data[1] = header[1]; + fread(p.data+2, bytes-2, 1, f); + descriptors[id] = p; --size; - CacheSize+=bytes; } } void eventData::save(FILE *f) { + if (isCacheCorrupt) + return; int size=descriptors.size(); - descriptorMap::iterator it(descriptors.begin()); + DescriptorMap::iterator it(descriptors.begin()); fwrite(&size, sizeof(int), 1, f); while(size) { - fwrite(&it->first, sizeof(__u32), 1, f); - fwrite(&it->second.first, sizeof(int), 1, f); - fwrite(it->second.second, it->second.second[1]+2, 1, f); + fwrite(&it->first, sizeof(uint32_t), 1, f); + fwrite(&it->second.reference_count, sizeof(int), 1, f); + fwrite(it->second.data, it->second.data[1]+2, 1, f); ++it; --size; } } +void eventData::cacheCorrupt(const char* context) +{ + + eDebug("[eventData] EPG Cache is corrupt (%s), you should restart Enigma!", context); + if (!isCacheCorrupt) + { + isCacheCorrupt = true; + if (!eEPGCache::instance->m_filename.empty()) + unlink(eEPGCache::instance->m_filename.c_str()); // Remove corrupt EPG data + } +} + eEPGCache* eEPGCache::instance; -pthread_mutex_t eEPGCache::cache_lock= +static pthread_mutex_t cache_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; -pthread_mutex_t eEPGCache::channel_map_lock= +static pthread_mutex_t channel_map_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; DEFINE_REF(eEPGCache) eEPGCache::eEPGCache() - :messages(this,1), cleanTimer(eTimer::create(this)), m_running(0)//, paused(0) + :messages(this,1), cleanTimer(eTimer::create(this)), m_running(false) { - eDebug("[EPGC] Initialized EPGCache (wait for setCacheFile call now)"); + eDebug("[eEPGCache] Initialized EPGCache (wait for setCacheFile call now)"); + + enabledSources = 0; + historySeconds = 0; CONNECT(messages.recv_msg, eEPGCache::gotMessage); CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated); @@ -229,16 +390,15 @@ eEPGCache::eEPGCache() res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn); instance=this; - memset(m_filename, 0, sizeof(m_filename)); } void eEPGCache::setCacheFile(const char *path) { - bool inited = !!strlen(m_filename); - strncpy(m_filename, path, 1024); + bool inited = !m_filename.empty(); + m_filename = path; if (!inited) { - eDebug("[EPGC] setCacheFile read/write epg data from/to '%s'", m_filename); + eDebug("[eEPGCache] setCacheFile read/write epg data from/to '%s'", m_filename.c_str()); if (eDVBLocalTimeHandler::getInstance()->ready()) timeUpdated(); } @@ -246,15 +406,15 @@ void eEPGCache::setCacheFile(const char *path) void eEPGCache::timeUpdated() { - if (strlen(m_filename)) + if (!m_filename.empty()) { - if (!sync()) + if (!m_running) { - eDebug("[EPGC] time updated.. start EPG Mainloop"); + eDebug("[eEPGCache] time updated.. start EPG Mainloop"); run(); + m_running = true; singleLock s(channel_map_lock); - channelMapIterator it = m_knownChannels.begin(); - for (; it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it = m_knownChannels.begin(); it != m_knownChannels.end(); ++it) { if (it->second->state == -1) { it->second->state=0; @@ -265,7 +425,7 @@ void eEPGCache::timeUpdated() messages.send(Message(Message::timeChanged)); } else - eDebug("[EPGC] time updated.. but cache file not set yet.. dont start epg!!"); + eDebug("[eEPGCache] time updated.. but cache file not set yet.. dont start epg!!"); } void eEPGCache::DVBChannelAdded(eDVBChannel *chan) @@ -292,8 +452,7 @@ void eEPGCache::DVBChannelAdded(eDVBChannel *chan) void eEPGCache::DVBChannelRunning(iDVBChannel *chan) { - channelMapIterator it = - m_knownChannels.find(chan); + ChannelMap::const_iterator it = m_knownChannels.find(chan); if ( it == m_knownChannels.end() ) eDebug("[eEPGCache] will start non existing channel %p !!!", chan); else @@ -333,6 +492,36 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) return; } +#ifdef ENABLE_VIRGIN + res = demux->createSectionReader( this, data.m_VirginNowNextReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize virgin nownext reader!!"); + return; + } + + res = demux->createSectionReader( this, data.m_VirginScheduleReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize virgin schedule reader!!"); + return; + } +#endif +#ifdef ENABLE_NETMED + res = demux->createSectionReader( this, data.m_NetmedScheduleReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize netmed schedule reader!!"); + return; + } + + res = demux->createSectionReader( this, data.m_NetmedScheduleOtherReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize netmed schedule other reader!!"); + return; + } +#endif res = demux->createSectionReader( this, data.m_ViasatReader ); if ( res ) { @@ -361,8 +550,23 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) return; } #endif - if (m_running) { - data.state=0; +#if ENABLE_FREESAT + res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize FreeSat reader!!"); + return; + } + res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader2 ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize FreeSat reader 2!!"); + return; + } +#endif + if (m_running) + { + data.state = 0; messages.send(Message(Message::startChannel, chan)); // -> gotMessage -> changedService } @@ -375,8 +579,7 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) { - channelMapIterator it = - m_knownChannels.find(chan); + ChannelMap::iterator it = m_knownChannels.find(chan); if ( it != m_knownChannels.end() ) { int state=0; @@ -396,14 +599,16 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) eDebug("[eEPGCache] remove channel %p", chan); if (it->second->state >= 0) messages.send(Message(Message::leaveChannel, chan)); - pthread_mutex_lock(&it->second->channel_active); - singleLock s(channel_map_lock); - m_knownChannels.erase(it); - pthread_mutex_unlock(&it->second->channel_active); - delete it->second; - it->second=0; + channel_data* cd = it->second; + pthread_mutex_lock(&cd->channel_active); + { + singleLock s(channel_map_lock); + m_knownChannels.erase(it); + } + pthread_mutex_unlock(&cd->channel_active); + delete cd; // -> gotMessage -> abortEPG - break; + return; } default: // ignore all other events return; @@ -414,46 +619,46 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) } } -bool eEPGCache::FixOverlapping(std::pair &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service) +bool eEPGCache::FixOverlapping(EventCacheItem &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service) { bool ret = false; timeMap::iterator tmp = tm_it; - while ((tmp->first+tmp->second->getDuration()-300) > TM) + while ((tmp->first + tmp->second->getDuration() - 300) > TM) { - if(tmp->first != TM + if(tmp->first != TM #ifdef ENABLE_PRIVATE_EPG - && tmp->second->type != PRIVATE + && tmp->second->type != PRIVATE #endif -#ifdef ENABLE_MHW +#ifdef ENABLE_MHW_EPG && tmp->second->type != MHW #endif ) { - __u16 event_id = tmp->second->getEventID(); - servicemap.first.erase(event_id); + uint16_t event_id = tmp->second->getEventID(); + servicemap.byEvent.erase(event_id); #ifdef EPG_DEBUG Event evt((uint8_t*)tmp->second->get()); eServiceEvent event; event.parseFrom(&evt, service.sid<<16|service.onid); - eDebug("(1)erase no more used event %04x %d\n%s %s\n%s", + eDebug("[eEPGCache] (1)erase no more used event %04x %d\n%s %s\n%s", service.sid, event_id, event.getBeginTimeString().c_str(), event.getEventName().c_str(), event.getExtendedDescription().c_str()); #endif delete tmp->second; - if (tmp == servicemap.second.begin()) + if (tmp == servicemap.byTime.begin()) { - servicemap.second.erase(tmp); + servicemap.byTime.erase(tmp); break; } else - servicemap.second.erase(tmp--); + servicemap.byTime.erase(tmp--); ret = true; } else { - if (tmp == servicemap.second.begin()) + if (tmp == servicemap.byTime.begin()) break; --tmp; } @@ -464,97 +669,107 @@ bool eEPGCache::FixOverlapping(std::pair &servicemap, time_t T { if (tmp->first != TM && tmp->second->type != PRIVATE) { - __u16 event_id = tmp->second->getEventID(); - servicemap.first.erase(event_id); -#ifdef EPG_DEBUG + uint16_t event_id = tmp->second->getEventID(); + servicemap.byEvent.erase(event_id); +#ifdef EPG_DEBUG Event evt((uint8_t*)tmp->second->get()); eServiceEvent event; event.parseFrom(&evt, service.sid<<16|service.onid); - eDebug("(2)erase no more used event %04x %d\n%s %s\n%s", + eDebug("[eEPGCache] (2)erase no more used event %04x %d\n%s %s\n%s", service.sid, event_id, event.getBeginTimeString().c_str(), event.getEventName().c_str(), event.getExtendedDescription().c_str()); #endif delete tmp->second; - servicemap.second.erase(tmp++); + servicemap.byTime.erase(tmp++); ret = true; } else ++tmp; - if (tmp == servicemap.second.end()) + if (tmp == servicemap.byTime.end()) break; } return ret; } -void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) +void eEPGCache::sectionRead(const uint8_t *data, int source, channel_data *channel) { - eit_t *eit = (eit_t*) data; + const eit_t *eit = (const eit_t*) data; - int len=HILO(eit->section_length)-1;//+3-4; - int ptr=EIT_SIZE; + int len = eit->getSectionLength()-1;//+3-4; + int ptr = EIT_SIZE; if ( ptr >= len ) return; +#if 0 + /* + * disable for now, as this hack breaks EIT parsing for + * services with a low segment_last_table_id + * + * Multichoice should be the exception, not the rule... + */ + // This fixed the EPG on the Multichoice irdeto systems // the EIT packet is non-compliant.. their EIT packet stinks if ( data[ptr-1] < 0x40 ) --ptr; +#endif + + int onid = eit->getOriginalNetworkId(); + int tsid = eit->getTransportStreamId(); // Cablecom HACK .. tsid / onid in eit data are incorrect.. so we use // it from running channel (just for current transport stream eit data) - bool use_transponder_chid = source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E); - eDVBChannelID chid = channel->channel->getChannelID(); - uniqueEPGKey service( HILO(eit->service_id), - use_transponder_chid ? chid.original_network_id.get() : HILO(eit->original_network_id), - use_transponder_chid ? chid.transport_stream_id.get() : HILO(eit->transport_stream_id)); + /* + * Make an exception for BEV (onid 0x100, 0x101), which doesn't use + * SCHEDULE_OTHER. As a result SCHEDULE will contain data for different tsid's, + * so we should not replace it with the current tsid. + */ + bool use_transponder_chid = onid != 0x101 && onid != 0x100 && (source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E)); + + if (use_transponder_chid && channel) + { + eDVBChannelID chid = channel->channel->getChannelID(); + + onid = chid.original_network_id.get(); + tsid = chid.transport_stream_id.get(); + } + + uniqueEPGKey service( eit->getServiceID(), onid, tsid); eit_event_struct* eit_event = (eit_event_struct*) (data+ptr); int eit_event_size; int duration; - time_t TM = parseDVBtime( - eit_event->start_time_1, - eit_event->start_time_2, - eit_event->start_time_3, - eit_event->start_time_4, - eit_event->start_time_5); + time_t TM = parseDVBtime((const uint8_t*)eit_event + 2); time_t now = ::time(0); - if ( TM != 3599 && TM > -1) + if ( TM != 3599 && TM > -1 && channel) channel->haveData |= source; singleLock s(cache_lock); - // hier wird immer eine eventMap zurück gegeben.. entweder eine vorhandene.. + // hier wird immer eine eventMap zurck gegeben.. entweder eine vorhandene.. // oder eine durch [] erzeugte - std::pair &servicemap = eventDB[service]; - eventMap::iterator prevEventIt = servicemap.first.end(); - timeMap::iterator prevTimeIt = servicemap.second.end(); + EventCacheItem &servicemap = eventDB[service]; + eventMap::iterator prevEventIt = servicemap.byEvent.end(); + timeMap::iterator prevTimeIt = servicemap.byTime.end(); while (ptrdescriptors_loop_length)+EIT_LOOP_SIZE; + uint16_t event_hash; + eit_event_size = eit_event->getDescriptorsLoopLength()+EIT_LOOP_SIZE; duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3); - TM = parseDVBtime( - eit_event->start_time_1, - eit_event->start_time_2, - eit_event->start_time_3, - eit_event->start_time_4, - eit_event->start_time_5, - &event_hash); - - if ( TM == 3599 ) - goto next; + TM = parseDVBtime((const uint8_t*)eit_event + 2, &event_hash); - if ( TM != 3599 && (TM+duration < now || TM > now+14*24*60*60) ) - goto next; - - if ( now <= (TM+duration) || TM == 3599 /*NVOD Service*/ ) // old events should not be cached + if ( (TM != 3599) && // NVOD Service + (now <= (TM+duration)) && // skip old events + (TM < (now+28*24*60*60)) && // no more than 4 weeks in future + ( (onid != 1714) || (duration != (24*3600-1)) ) // PlatformaHD invalid event + ) { - __u16 event_id = HILO(eit_event->event_id); + uint16_t event_id = eit_event->getEventId(); eventData *evt = 0; int ev_erase_count = 0; int tm_erase_count = 0; @@ -569,32 +784,31 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) // search in eventmap eventMap::iterator ev_it = - servicemap.first.find(event_id); + servicemap.byEvent.find(event_id); -// eDebug("event_id is %d sid is %04x", event_id, service.sid); + //eDebug("[eEPGCache] event_id : %d, sid : %04x", event_id, service.sid); // entry with this event_id is already exist ? - if ( ev_it != servicemap.first.end() ) + if ( ev_it != servicemap.byEvent.end() ) { if ( source > ev_it->second->type ) // update needed ? goto next; // when not.. then skip this entry // search this event in timemap timeMap::iterator tm_it_tmp = - servicemap.second.find(ev_it->second->getStartTime()); + servicemap.byTime.find(ev_it->second->getStartTime()); - if ( tm_it_tmp != servicemap.second.end() ) + if ( tm_it_tmp != servicemap.byTime.end() ) { if ( tm_it_tmp->first == TM ) // just update eventdata { // exempt memory eventData *tmp = ev_it->second; - ev_it->second = tm_it_tmp->second = - new eventData(eit_event, eit_event_size, source); + ev_it->second = tm_it_tmp->second = new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid); if (FixOverlapping(servicemap, TM, duration, tm_it_tmp, service)) { - prevEventIt = servicemap.first.end(); - prevTimeIt = servicemap.second.end(); + prevEventIt = servicemap.byEvent.end(); + prevTimeIt = servicemap.byTime.end(); } delete tmp; goto next; @@ -603,8 +817,8 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) { tm_erase_count++; // delete the found record from timemap - servicemap.second.erase(tm_it_tmp); - prevTimeIt=servicemap.second.end(); + servicemap.byTime.erase(tm_it_tmp); + prevTimeIt = servicemap.byTime.end(); } } } @@ -612,28 +826,27 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) // search in timemap, for check of a case if new time has coincided with time of other event // or event was is not found in eventmap timeMap::iterator tm_it = - servicemap.second.find(TM); + servicemap.byTime.find(TM); - if ( tm_it != servicemap.second.end() ) + if ( tm_it != servicemap.byTime.end() ) { // event with same start time but another event_id... if ( source > tm_it->second->type && - ev_it == servicemap.first.end() ) + ev_it == servicemap.byEvent.end() ) goto next; // when not.. then skip this entry // search this time in eventmap - eventMap::iterator ev_it_tmp = - servicemap.first.find(tm_it->second->getEventID()); + eventMap::iterator ev_it_tmp = servicemap.byEvent.find(tm_it->second->getEventID()); - if ( ev_it_tmp != servicemap.first.end() ) + if ( ev_it_tmp != servicemap.byEvent.end() ) { ev_erase_count++; // delete the found record from eventmap - servicemap.first.erase(ev_it_tmp); - prevEventIt=servicemap.first.end(); + servicemap.byEvent.erase(ev_it_tmp); + prevEventIt = servicemap.byEvent.end(); } } - evt = new eventData(eit_event, eit_event_size, source); + evt = new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid); #ifdef EPG_DEBUG bool consistencyCheck=true; #endif @@ -649,90 +862,90 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) { // exempt memory delete ev_it->second; - tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair( TM, evt ) ); - ev_it->second=evt; + tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair( TM, evt ) ); + ev_it->second = evt; } else if (ev_erase_count > 0 && tm_erase_count == 0) { // exempt memory delete tm_it->second; - ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair( event_id, evt) ); - tm_it->second=evt; + ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair( event_id, evt) ); + tm_it->second = evt; } else // added new eventData { #ifdef EPG_DEBUG consistencyCheck=false; #endif - ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair( event_id, evt) ); - tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair( TM, evt ) ); + ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair( event_id, evt) ); + tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair( TM, evt ) ); } #ifdef EPG_DEBUG if ( consistencyCheck ) { if ( tm_it->second != evt || ev_it->second != evt ) - eFatal("tm_it->second != ev_it->second"); + eFatal("[eEPGCache] tm_it->second != ev_it->second"); else if ( tm_it->second->getStartTime() != tm_it->first ) - eFatal("event start_time(%d) non equal timemap key(%d)", - tm_it->second->getStartTime(), tm_it->first ); + eFatal("[eEPGCache] event start_time(%d) non equal timemap key(%d)", + (int)tm_it->second->getStartTime(), (int)tm_it->first ); else if ( tm_it->first != TM ) - eFatal("timemap key(%d) non equal TM(%d)", - tm_it->first, TM); + eFatal("[eEPGCache] timemap key(%d) non equal TM(%d)", + (int)tm_it->first, (int)TM); else if ( ev_it->second->getEventID() != ev_it->first ) - eFatal("event_id (%d) non equal event_map key(%d)", - ev_it->second->getEventID(), ev_it->first); + eFatal("[eEPGCache] event_id (%d) non equal event_map key(%d)", + (int)ev_it->second->getEventID(), (int)ev_it->first); else if ( ev_it->first != event_id ) - eFatal("eventmap key(%d) non equal event_id(%d)", - ev_it->first, event_id ); + eFatal("[eEPGCache] eventmap key(%d) non equal event_id(%d)", + (int)ev_it->first, (int)event_id ); } #endif if (FixOverlapping(servicemap, TM, duration, tm_it, service)) { - prevEventIt = servicemap.first.end(); - prevTimeIt = servicemap.second.end(); + prevEventIt = servicemap.byEvent.end(); + prevTimeIt = servicemap.byTime.end(); } } next: #ifdef EPG_DEBUG - if ( servicemap.first.size() != servicemap.second.size() ) + if ( servicemap.byEvent.size() != servicemap.byTime.size() ) { FILE *f = fopen("/hdd/event_map.txt", "w+"); int i=0; - for (eventMap::iterator it(servicemap.first.begin()) - ; it != servicemap.first.end(); ++it ) + for (eventMap::iterator it(servicemap.byEvent.begin()) + ; it != servicemap.byEvent.end(); ++it ) fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second ); fclose(f); f = fopen("/hdd/time_map.txt", "w+"); i=0; - for (timeMap::iterator it(servicemap.second.begin()) - ; it != servicemap.second.end(); ++it ) + for (timeMap::iterator it(servicemap.byTime.begin()) + ; it != servicemap.byTime.end(); ++it ) fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second ); fclose(f); - eFatal("(1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", - service.sid, service.tsid, service.onid, - servicemap.first.size(), servicemap.second.size() ); + eFatal("[eEPGCache] (1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", + service.sid, service.tsid, service.onid, + servicemap.byEvent.size(), servicemap.byTime.size() ); } #endif ptr += eit_event_size; - eit_event=(eit_event_struct*)(((__u8*)eit_event)+eit_event_size); + eit_event = (eit_event_struct*)(((uint8_t*)eit_event) + eit_event_size); } } void eEPGCache::flushEPG(const uniqueEPGKey & s) { - eDebug("[EPGC] flushEPG %d", (int)(bool)s); + eDebug("[eEPGCache] flushEPG %d", (int)(bool)s); singleLock l(cache_lock); if (s) // clear only this service { eventCache::iterator it = eventDB.find(s); if ( it != eventDB.end() ) { - eventMap &evMap = it->second.first; - timeMap &tmMap = it->second.second; + eventMap &evMap = it->second.byEvent; + timeMap &tmMap = it->second.byTime; tmMap.clear(); for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i) delete i->second; @@ -756,8 +969,8 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) for (eventCache::iterator it(eventDB.begin()); it != eventDB.end(); ++it) { - eventMap &evMap = it->second.first; - timeMap &tmMap = it->second.second; + eventMap &evMap = it->second.byEvent; + timeMap &tmMap = it->second.byTime; for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i) delete i->second; evMap.clear(); @@ -769,42 +982,38 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) #endif channelLastUpdated.clear(); singleLock m(channel_map_lock); - for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) it->second->startEPG(); } - eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize); } void eEPGCache::cleanLoop() { - singleLock s(cache_lock); - if (!eventDB.empty()) - { - eDebug("[EPGC] start cleanloop"); - - time_t now = ::time(0); + { /* scope for cache lock */ + time_t now = ::time(0) - historySeconds; + singleLock s(cache_lock); for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++) { bool updated = false; - for (timeMap::iterator It = DBIt->second.second.begin(); It != DBIt->second.second.end() && It->first < now;) + for (timeMap::iterator It = DBIt->second.byTime.begin(); It != DBIt->second.byTime.end() && It->first < now;) { if ( now > (It->first+It->second->getDuration()) ) // outdated normal entry (nvod references to) { // remove entry from eventMap - eventMap::iterator b(DBIt->second.first.find(It->second->getEventID())); - if ( b != DBIt->second.first.end() ) + eventMap::iterator b(DBIt->second.byEvent.find(It->second->getEventID())); + if ( b != DBIt->second.byEvent.end() ) { // release Heap Memory for this entry (new ....) -// eDebug("[EPGC] delete old event (evmap)"); - DBIt->second.first.erase(b); + //eDebug("[eEPGCache] delete old event (evmap)"); + DBIt->second.byEvent.erase(b); } // remove entry from timeMap -// eDebug("[EPGC] release heap mem"); + //eDebug("[eEPGCache] release heap mem"); delete It->second; - DBIt->second.second.erase(It++); -// eDebug("[EPGC] delete old event (timeMap)"); + DBIt->second.byTime.erase(It++); + //eDebug("[eEPGCache] delete old event (timeMap)"); updated = true; } else @@ -817,7 +1026,7 @@ void eEPGCache::cleanLoop() content_time_tables.find( DBIt->first ); if ( x != content_time_tables.end() ) { - timeMap &tmMap = DBIt->second.second; + timeMap &tmMap = DBIt->second.byTime; for ( contentMap::iterator i = x->second.begin(); i != x->second.end(); ) { for ( contentTimeMap::iterator it(i->second.begin()); @@ -837,19 +1046,18 @@ void eEPGCache::cleanLoop() } #endif } - eDebug("[EPGC] stop cleanloop"); - eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize); - } + } /* release lock */ cleanTimer->start(CLEAN_INTERVAL,true); } eEPGCache::~eEPGCache() { + m_running = false; messages.send(Message::quit); kill(); // waiting for thread shutdown singleLock s(cache_lock); for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++) - for (eventMap::iterator It = evIt->second.first.begin(); It != evIt->second.first.end(); It++) + for (eventMap::iterator It = evIt->second.byEvent.begin(); It != evIt->second.byEvent.end(); It++) delete It->second; } @@ -863,8 +1071,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::startChannel: { singleLock s(channel_map_lock); - channelMapIterator channel = - m_knownChannels.find(msg.channel); + ChannelMap::const_iterator channel = m_knownChannels.find(msg.channel); if ( channel != m_knownChannels.end() ) channel->second->startChannel(); break; @@ -872,8 +1079,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::leaveChannel: { singleLock s(channel_map_lock); - channelMapIterator channel = - m_knownChannels.find(msg.channel); + ChannelMap::const_iterator channel = m_knownChannels.find(msg.channel); if ( channel != m_knownChannels.end() ) channel->second->abortEPG(); break; @@ -885,7 +1091,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::got_private_pid: { singleLock s(channel_map_lock); - for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) { eDVBChannel *channel = (eDVBChannel*) it->first; channel_data *data = it->second; @@ -906,9 +1112,9 @@ void eEPGCache::gotMessage( const Message &msg ) update = ZAP_DELAY; data->startPrivateTimer->start(update, 1); if (update >= 60000) - eDebug("[EPGC] next private update in %i min", update/60000); + eDebug("[eEPGCache] next private update in %i min", update/60000); else if (update >= 1000) - eDebug("[EPGC] next private update in %i sec", update/1000); + eDebug("[eEPGCache] next private update in %i sec", update/1000); break; } } @@ -919,7 +1125,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::got_mhw2_channel_pid: { singleLock s(channel_map_lock); - for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) { eDVBChannel *channel = (eDVBChannel*) it->first; channel_data *data = it->second; @@ -928,7 +1134,7 @@ void eEPGCache::gotMessage( const Message &msg ) chid.original_network_id.get() == msg.service.onid ) { data->m_mhw2_channel_pid = msg.pid; - eDebug("[EPGC] got mhw2 channel pid %04x", msg.pid); + eDebug("[eEPGCache] got mhw2 channel pid %04x", msg.pid); break; } } @@ -937,7 +1143,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::got_mhw2_title_pid: { singleLock s(channel_map_lock); - for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) { eDVBChannel *channel = (eDVBChannel*) it->first; channel_data *data = it->second; @@ -946,7 +1152,7 @@ void eEPGCache::gotMessage( const Message &msg ) chid.original_network_id.get() == msg.service.onid ) { data->m_mhw2_title_pid = msg.pid; - eDebug("[EPGC] got mhw2 title pid %04x", msg.pid); + eDebug("[eEPGCache] got mhw2 title pid %04x", msg.pid); break; } } @@ -955,7 +1161,7 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::got_mhw2_summary_pid: { singleLock s(channel_map_lock); - for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) { eDVBChannel *channel = (eDVBChannel*) it->first; channel_data *data = it->second; @@ -964,7 +1170,7 @@ void eEPGCache::gotMessage( const Message &msg ) chid.original_network_id.get() == msg.service.onid ) { data->m_mhw2_summary_pid = msg.pid; - eDebug("[EPGC] got mhw2 summary pid %04x", msg.pid); + eDebug("[eEPGCache] got mhw2 summary pid %04x", msg.pid); break; } } @@ -975,7 +1181,7 @@ void eEPGCache::gotMessage( const Message &msg ) cleanLoop(); break; default: - eDebug("unhandled EPGCache Message!!"); + eDebug("[eEPGCache] unhandled EPGCache Message!!"); break; } } @@ -983,137 +1189,177 @@ void eEPGCache::gotMessage( const Message &msg ) void eEPGCache::thread() { hasStarted(); - m_running=1; nice(4); load(); cleanLoop(); runLoop(); save(); - m_running=0; } +static const char* EPGDAT_IN_FLASH = "/epg.dat"; + void eEPGCache::load() { - FILE *f = fopen(m_filename, "r"); - if (f) + if (m_filename.empty()) + m_filename = "/hdd/epg.dat"; + const char* EPGDAT = m_filename.c_str(); + std::string filenamex = m_filename + ".loading"; + const char* EPGDATX = filenamex.c_str(); + FILE *f = fopen(EPGDAT, "rb"); + int renameResult; + if (f == NULL) + { + /* No EPG on harddisk, so try internal flash */ + eDebug("[eEPGCache] %s not found, try %s", EPGDAT, EPGDAT_IN_FLASH); + EPGDAT = EPGDAT_IN_FLASH; + f = fopen(EPGDAT, "rb"); + if (f == NULL) + return; + renameResult = -1; + } + else + { + unlink(EPGDATX); + renameResult = rename(EPGDAT, EPGDATX); + if (renameResult) eDebug("[eEPGCache] failed to rename %s", EPGDAT); + } { - unlink(m_filename); int size=0; int cnt=0; - + unsigned int magic=0; + unlink(EPGDAT_IN_FLASH);/* Don't keep it around when in flash */ + fread( &magic, sizeof(int), 1, f); + if (magic != 0x98765432) { - unsigned int magic=0; - fread( &magic, sizeof(int), 1, f); - if (magic != 0x98765432) - { - eDebug("[EPGC] epg file has incorrect byte order.. dont read it"); - fclose(f); - return; - } - char text1[13]; - fread( text1, 13, 1, f); - if ( !strncmp( text1, "ENIGMA_EPG_V7", 13) ) + eDebug("[eEPGCache] epg file has incorrect byte order.. dont read it"); + fclose(f); + return; + } + char text1[13]; + fread( text1, 13, 1, f); + if ( !memcmp( text1, "ENIGMA_EPG_V7", 13) ) + { + singleLock s(cache_lock); + fread( &size, sizeof(int), 1, f); + eventDB.rehash(size); /* Reserve buckets in advance */ + while(size--) { - singleLock s(cache_lock); + uniqueEPGKey key; + int size=0; + fread( &key, sizeof(uniqueEPGKey), 1, f); fread( &size, sizeof(int), 1, f); + EventCacheItem& item = eventDB[key]; /* Constructs new entry */ while(size--) { - uniqueEPGKey key; - eventMap evMap; - timeMap tmMap; - int size=0; - fread( &key, sizeof(uniqueEPGKey), 1, f); - fread( &size, sizeof(int), 1, f); - while(size--) + uint8_t len=0; + uint8_t type=0; + eventData *event=0; + fread( &type, sizeof(uint8_t), 1, f); + fread( &len, sizeof(uint8_t), 1, f); + event = new eventData(0, len, type); + event->n_crc = (len-10) / sizeof(uint32_t); + fread( event->rawEITdata, 10, 1, f); + if (event->n_crc) { - __u8 len=0; - __u8 type=0; - eventData *event=0; - fread( &type, sizeof(__u8), 1, f); - fread( &len, sizeof(__u8), 1, f); - event = new eventData(0, len, type); - event->EITdata = new __u8[len]; - eventData::CacheSize+=len; - fread( event->EITdata, len, 1, f); - evMap[ event->getEventID() ]=event; - tmMap[ event->getStartTime() ]=event; - ++cnt; + event->crc_list = new uint32_t[event->n_crc]; + fread( event->crc_list, sizeof(uint32_t), event->n_crc, f); } - eventDB[key]=std::pair(evMap,tmMap); + eventData::CacheSize += sizeof(eventData) + event->n_crc * sizeof(uint32_t); + item.byEvent[event->getEventID()] = event; + item.byTime[event->getStartTime()] = event; + ++cnt; } - eventData::load(f); - eDebug("[EPGC] %d events read from %s", cnt, m_filename); + } + eventData::load(f); + eDebug("[eEPGCache] %d events read from %s", cnt, EPGDAT); #ifdef ENABLE_PRIVATE_EPG - char text2[11]; - fread( text2, 11, 1, f); - if ( !strncmp( text2, "PRIVATE_EPG", 11) ) + char text2[11]; + fread( text2, 11, 1, f); + if ( !memcmp( text2, "PRIVATE_EPG", 11) ) + { + size=0; + fread( &size, sizeof(int), 1, f); + eventDB.rehash(size); /* Reserve buckets in advance */ + while(size--) { - size=0; + int size=0; + uniqueEPGKey key; + fread( &key, sizeof(uniqueEPGKey), 1, f); + eventMap &evMap = eventDB[key].byEvent; fread( &size, sizeof(int), 1, f); while(size--) { - int size=0; - uniqueEPGKey key; - fread( &key, sizeof(uniqueEPGKey), 1, f); - eventMap &evMap=eventDB[key].first; + int size; + int content_id; + fread( &content_id, sizeof(int), 1, f); fread( &size, sizeof(int), 1, f); while(size--) { - int size; - int content_id; - fread( &content_id, sizeof(int), 1, f); - fread( &size, sizeof(int), 1, f); - while(size--) - { - time_t time1, time2; - __u16 event_id; - fread( &time1, sizeof(time_t), 1, f); - fread( &time2, sizeof(time_t), 1, f); - fread( &event_id, sizeof(__u16), 1, f); - content_time_tables[key][content_id][time1]=std::pair(time2, event_id); - eventMap::iterator it = - evMap.find(event_id); - if (it != evMap.end()) - it->second->type = PRIVATE; - } + time_t time1, time2; + uint16_t event_id; + fread( &time1, sizeof(time_t), 1, f); + fread( &time2, sizeof(time_t), 1, f); + fread( &event_id, sizeof(uint16_t), 1, f); + content_time_tables[key][content_id][time1]=std::pair(time2, event_id); + eventMap::iterator it = + evMap.find(event_id); + if (it != evMap.end()) + it->second->type = PRIVATE; } } } -#endif // ENABLE_PRIVATE_EPG } - else - eDebug("[EPGC] don't read old epg database"); - fclose(f); +#endif // ENABLE_PRIVATE_EPG + } + else + eDebug("[eEPGCache] don't read old epg database"); + posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED); + fclose(f); + // We got this far, so the EPG file is okay. + if (renameResult == 0) + { + renameResult = rename(EPGDATX, EPGDAT); + if (renameResult) eDebug("[eEPGCache] failed to rename epg.dat back"); } } } void eEPGCache::save() { - /* create empty file */ - FILE *f = fopen(m_filename, "w"); + const char* EPGDAT = m_filename.c_str(); + if (eventData::isCacheCorrupt) + return; + // only save epg.dat if it's worth the trouble... + if (eventData::CacheSize < 10240) + return; + /* create empty file */ + FILE *f = fopen(EPGDAT, "wb"); if (!f) { - eDebug("[EPGC] couldn't save epg data to '%s'(%m)", m_filename); - return; + eDebug("[eEPGCache] couldn't save epg data to '%s'(%m)", EPGDAT); + EPGDAT = EPGDAT_IN_FLASH; + f = fopen(EPGDAT, "wb"); + if (!f) + return; } - char *buf = realpath(m_filename, NULL); + char *buf = realpath(EPGDAT, NULL); if (!buf) { - eDebug("[EPGC] realpath to '%s' failed in save (%m)", m_filename); + eDebug("[eEPGCache] realpath to '%s' failed in save (%m)", EPGDAT); fclose(f); return; } - eDebug("[EPGC] store epg to realpath '%s'", buf); + eDebug("[eEPGCache] store epg to realpath '%s'", buf); struct statfs s; off64_t tmp; if (statfs(buf, &s) < 0) { - eDebug("[EPGC] statfs '%s' failed in save (%m)", buf); + eDebug("[eEPGCache] statfs '%s' failed in save (%m)", buf); fclose(f); + free(buf); return; } @@ -1124,7 +1370,7 @@ void eEPGCache::save() tmp*=s.f_bsize; if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead { - eDebug("[EPGC] not enough free space at path '%s' %lld bytes availd but %d needed", buf, tmp, (eventData::CacheSize*12)/10); + eDebug("[eEPGCache] not enough free space at '%s' %lld bytes available but %u needed", buf, tmp, (eventData::CacheSize*12)/10); fclose(f); return; } @@ -1138,20 +1384,21 @@ void eEPGCache::save() fwrite( &size, sizeof(int), 1, f ); for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it) { - timeMap &timemap = service_it->second.second; + timeMap &timemap = service_it->second.byTime; fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f); size = timemap.size(); fwrite( &size, sizeof(int), 1, f); for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it) { - __u8 len = time_it->second->ByteSize; - fwrite( &time_it->second->type, sizeof(__u8), 1, f ); - fwrite( &len, sizeof(__u8), 1, f); - fwrite( time_it->second->EITdata, len, 1, f); + uint8_t len = time_it->second->n_crc * sizeof(uint32_t) + 10; + fwrite( &time_it->second->type, sizeof(uint8_t), 1, f ); + fwrite( &len, sizeof(uint8_t), 1, f); + fwrite( time_it->second->rawEITdata, 10, 1, f); + fwrite( time_it->second->crc_list, sizeof(uint32_t), time_it->second->n_crc, f); ++cnt; } } - eDebug("[EPGC] %d events written to %s", cnt, m_filename); + eDebug("[eEPGCache] %d events written to %s", cnt, EPGDAT); eventData::save(f); #ifdef ENABLE_PRIVATE_EPG const char* text3 = "PRIVATE_EPG"; @@ -1174,7 +1421,7 @@ void eEPGCache::save() { fwrite( &it->first, sizeof(time_t), 1, f); fwrite( &it->second.first, sizeof(time_t), 1, f); - fwrite( &it->second.second, sizeof(__u16), 1, f); + fwrite( &it->second.second, sizeof(uint16_t), 1, f); } } } @@ -1209,31 +1456,32 @@ eEPGCache::channel_data::channel_data(eEPGCache *ml) pthread_mutex_init(&channel_active, 0); } -bool eEPGCache::channel_data::finishEPG() +void eEPGCache::channel_data::finishEPG() { if (!isRunning) // epg ready { - eDebug("[EPGC] stop caching events(%ld)", ::time(0)); + eDebug("[eEPGCache] stop caching events(%ld)", ::time(0)); zapTimer->start(UPDATE_INTERVAL, 1); - eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000); + eDebug("[eEPGCache] next update in %i min", UPDATE_INTERVAL / 60000); for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) { seenSections[i].clear(); calcedSections[i].clear(); } - singleLock l(cache->cache_lock); - cache->channelLastUpdated[channel->getChannelID()] = ::time(0); #ifdef ENABLE_MHW_EPG - cleanup(); + cleanupMHW(); #endif - return true; +#ifdef ENABLE_FREESAT + cleanupFreeSat(); +#endif + singleLock l(cache_lock); + cache->channelLastUpdated[channel->getChannelID()] = ::time(0); } - return false; } void eEPGCache::channel_data::startEPG() { - eDebug("[EPGC] start caching events(%ld)", ::time(0)); + eDebug("[eEPGCache] start caching events(%ld)", ::time(0)); state=0; haveData=0; for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) @@ -1241,60 +1489,145 @@ void eEPGCache::channel_data::startEPG() seenSections[i].clear(); calcedSections[i].clear(); } +#ifdef ENABLE_MHW_EPG + cleanupMHW(); +#endif +#ifdef ENABLE_FREESAT + cleanupFreeSat(); +#endif eDVBSectionFilterMask mask; memset(&mask, 0, sizeof(mask)); #ifdef ENABLE_MHW_EPG - mask.pid = 0xD3; - mask.data[0] = 0x91; - mask.mask[0] = 0xFF; - m_MHWReader->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData), m_MHWConn); - m_MHWReader->start(mask); - isRunning |= MHW; - memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask)); - - mask.pid = m_mhw2_channel_pid; - mask.data[0] = 0xC8; - mask.mask[0] = 0xFF; - mask.data[1] = 0; - mask.mask[1] = 0xFF; - m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2); - m_MHWReader2->start(mask); - isRunning |= MHW; - memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask)); - mask.data[1] = 0; - mask.mask[1] = 0; - m_MHWTimeoutet=false; + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::MHW) + { + mask.pid = 0xD3; + mask.data[0] = 0x91; + mask.mask[0] = 0xFF; + m_MHWReader->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData), m_MHWConn); + m_MHWReader->start(mask); + isRunning |= MHW; + memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask)); + + mask.pid = m_mhw2_channel_pid; + mask.data[0] = 0xC8; + mask.mask[0] = 0xFF; + mask.data[1] = 0; + mask.mask[1] = 0xFF; + m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2); + m_MHWReader2->start(mask); + isRunning |= MHW; + memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask)); + mask.data[1] = 0; + mask.mask[1] = 0; + m_MHWTimeoutet=false; + } +#endif +#ifdef ENABLE_FREESAT + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::FREESAT_SCHEDULE_OTHER) + { + mask.pid = 3842; + mask.flags = eDVBSectionFilterMask::rfCRC; + mask.data[0] = 0x60; + mask.mask[0] = 0xFE; + m_FreeSatScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn); + m_FreeSatScheduleOtherReader->start(mask); + + /* + * faster pid, available on ITV HD transponder. + * We rely on the fact that we have either of the two, + * never both. (both readers share the same data callback + * and status maps) + */ + mask.pid = 3003; + m_FreeSatScheduleOtherReader2->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn2); + m_FreeSatScheduleOtherReader2->start(mask); + isRunning |= FREESAT_SCHEDULE_OTHER; + } #endif - mask.pid = 0x12; mask.flags = eDVBSectionFilterMask::rfCRC; - mask.data[0] = 0x4E; - mask.mask[0] = 0xFE; - m_NowNextReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_NowNextConn); - m_NowNextReader->start(mask); - isRunning |= NOWNEXT; + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NOWNEXT) + { + mask.data[0] = 0x4E; + mask.mask[0] = 0xFE; + m_NowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NOWNEXT), m_NowNextConn); + m_NowNextReader->start(mask); + isRunning |= NOWNEXT; + } + + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE) + { + mask.data[0] = 0x50; + mask.mask[0] = 0xF0; + m_ScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE), m_ScheduleConn); + m_ScheduleReader->start(mask); + isRunning |= SCHEDULE; + } + + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE_OTHER) + { + mask.data[0] = 0x60; + mask.mask[0] = 0xF0; + m_ScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE_OTHER), m_ScheduleOtherConn); + m_ScheduleOtherReader->start(mask); + isRunning |= SCHEDULE_OTHER; + } - mask.data[0] = 0x50; - mask.mask[0] = 0xF0; - m_ScheduleReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleConn); - m_ScheduleReader->start(mask); - isRunning |= SCHEDULE; +#ifdef ENABLE_VIRGIN + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_NOWNEXT) + { + mask.pid = 0x2bc; + mask.data[0] = 0x4E; + mask.mask[0] = 0xFE; + m_VirginNowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_NOWNEXT), m_VirginNowNextConn); + m_VirginNowNextReader->start(mask); + isRunning |= VIRGIN_NOWNEXT; + } - mask.data[0] = 0x60; - m_ScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleOtherConn); - m_ScheduleOtherReader->start(mask); - isRunning |= SCHEDULE_OTHER; + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_SCHEDULE) + { + mask.pid = 0x2bc; + mask.data[0] = 0x50; + mask.mask[0] = 0xFE; + m_VirginScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_SCHEDULE), m_VirginScheduleConn); + m_VirginScheduleReader->start(mask); + isRunning |= VIRGIN_SCHEDULE; + } +#endif +#ifdef ENABLE_NETMED + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE) + { + mask.pid = 0x1388; + mask.data[0] = 0x50; + mask.mask[0] = 0xF0; + m_NetmedScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE), m_NetmedScheduleConn); + m_NetmedScheduleReader->start(mask); + isRunning |= NETMED_SCHEDULE; + } - mask.pid = 0x39; + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE_OTHER) + { + mask.pid = 0x1388; + mask.data[0] = 0x60; + mask.mask[0] = 0xF0; + m_NetmedScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE_OTHER), m_NetmedScheduleOtherConn); + m_NetmedScheduleOtherReader->start(mask); + isRunning |= NETMED_SCHEDULE_OTHER; + } +#endif + if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIASAT) + { + mask.pid = 0x39; - mask.data[0] = 0x40; - mask.mask[0] = 0x40; - m_ViasatReader->connectRead(slot(*this, &eEPGCache::channel_data::readDataViasat), m_ViasatConn); - m_ViasatReader->start(mask); - isRunning |= VIASAT; + mask.data[0] = 0x40; + mask.mask[0] = 0x40; + m_ViasatReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIASAT), m_ViasatConn); + m_ViasatReader->start(mask); + isRunning |= VIASAT; + } abortTimer->start(7000,true); } @@ -1305,28 +1638,72 @@ void eEPGCache::channel_data::abortNonAvail() { if ( !(haveData&NOWNEXT) && (isRunning&NOWNEXT) ) { - eDebug("[EPGC] abort non avail nownext reading"); + eDebug("[eEPGCache] abort non avail nownext reading"); isRunning &= ~NOWNEXT; m_NowNextReader->stop(); m_NowNextConn=0; } if ( !(haveData&SCHEDULE) && (isRunning&SCHEDULE) ) { - eDebug("[EPGC] abort non avail schedule reading"); + eDebug("[eEPGCache] abort non avail schedule reading"); isRunning &= ~SCHEDULE; m_ScheduleReader->stop(); m_ScheduleConn=0; } if ( !(haveData&SCHEDULE_OTHER) && (isRunning&SCHEDULE_OTHER) ) { - eDebug("[EPGC] abort non avail schedule other reading"); + eDebug("[eEPGCache] abort non avail schedule other reading"); isRunning &= ~SCHEDULE_OTHER; m_ScheduleOtherReader->stop(); m_ScheduleOtherConn=0; } +#ifdef ENABLE_VIRGIN + if ( !(haveData&VIRGIN_NOWNEXT) && (isRunning&VIRGIN_NOWNEXT) ) + { + eDebug("[eEPGCache] abort non avail virgin nownext reading"); + isRunning &= ~VIRGIN_NOWNEXT; + m_VirginNowNextReader->stop(); + m_VirginNowNextConn=0; + } + if ( !(haveData&VIRGIN_SCHEDULE) && (isRunning&VIRGIN_SCHEDULE) ) + { + eDebug("[eEPGCache] abort non avail virgin schedule reading"); + isRunning &= ~VIRGIN_SCHEDULE; + m_VirginScheduleReader->stop(); + m_VirginScheduleConn=0; + } +#endif +#ifdef ENABLE_NETMED + if ( !(haveData&NETMED_SCHEDULE) && (isRunning&NETMED_SCHEDULE) ) + { + eDebug("[eEPGCache] abort non avail netmed schedule reading"); + isRunning &= ~NETMED_SCHEDULE; + m_NetmedScheduleReader->stop(); + m_NetmedScheduleConn=0; + } + if ( !(haveData&NETMED_SCHEDULE_OTHER) && (isRunning&NETMED_SCHEDULE_OTHER) ) + { + eDebug("[eEPGCache] abort non avail netmed schedule other reading"); + isRunning &= ~NETMED_SCHEDULE_OTHER; + m_NetmedScheduleOtherReader->stop(); + m_NetmedScheduleOtherConn=0; + } +#endif +#ifdef ENABLE_FREESAT + if ( !(haveData&FREESAT_SCHEDULE_OTHER) && (isRunning&FREESAT_SCHEDULE_OTHER) ) + { + eDebug("[eEPGCache] abort non avail FreeSat schedule_other reading"); + isRunning &= ~FREESAT_SCHEDULE_OTHER; + m_FreeSatScheduleOtherReader->stop(); + m_FreeSatScheduleOtherReader2->stop(); + m_FreeSatScheduleOtherConn=0; + m_FreeSatScheduleOtherConn2=0; + cleanupFreeSat(); + } +#endif if ( !(haveData&VIASAT) && (isRunning&VIASAT) ) { - eDebug("[EPGC] abort non avail viasat reading"); + eDebug("[eEPGCache] abort non avail viasat reading"); isRunning &= ~VIASAT; m_ViasatReader->stop(); m_ViasatConn=0; @@ -1334,7 +1711,7 @@ void eEPGCache::channel_data::abortNonAvail() #ifdef ENABLE_MHW_EPG if ( !(haveData&MHW) && (isRunning&MHW) ) { - eDebug("[EPGC] abort non avail mhw reading"); + eDebug("[eEPGCache] abort non avail mhw reading"); isRunning &= ~MHW; m_MHWReader->stop(); m_MHWConn=0; @@ -1354,6 +1731,12 @@ void eEPGCache::channel_data::abortNonAvail() seenSections[i].clear(); calcedSections[i].clear(); } +#ifdef ENABLE_MHW_EPG + cleanupMHW(); +#endif +#ifdef ENABLE_FREESAT + cleanupFreeSat(); +#endif } } ++state; @@ -1371,9 +1754,9 @@ void eEPGCache::channel_data::startChannel() zapTimer->start(update, 1); if (update >= 60000) - eDebug("[EPGC] next update in %i min", update/60000); + eDebug("[eEPGCache] next update in %i min", update/60000); else if (update >= 1000) - eDebug("[EPGC] next update in %i sec", update/1000); + eDebug("[eEPGCache] next update in %i sec", update/1000); } void eEPGCache::channel_data::abortEPG() @@ -1383,11 +1766,17 @@ void eEPGCache::channel_data::abortEPG() seenSections[i].clear(); calcedSections[i].clear(); } +#ifdef ENABLE_MHW_EPG + cleanupMHW(); +#endif +#ifdef ENABLE_FREESAT + cleanupFreeSat(); +#endif abortTimer->stop(); zapTimer->stop(); if (isRunning) { - eDebug("[EPGC] abort caching events !!"); + eDebug("[eEPGCache] abort caching events !!"); if (isRunning & SCHEDULE) { isRunning &= ~SCHEDULE; @@ -1406,6 +1795,44 @@ void eEPGCache::channel_data::abortEPG() m_ScheduleOtherReader->stop(); m_ScheduleOtherConn=0; } +#ifdef ENABLE_VIRGIN + if (isRunning & VIRGIN_NOWNEXT) + { + isRunning &= ~VIRGIN_NOWNEXT; + m_VirginNowNextReader->stop(); + m_VirginNowNextConn=0; + } + if (isRunning & VIRGIN_SCHEDULE) + { + isRunning &= ~VIRGIN_SCHEDULE; + m_VirginScheduleReader->stop(); + m_VirginScheduleConn=0; + } +#endif +#ifdef ENABLE_NETMED + if (isRunning & NETMED_SCHEDULE) + { + isRunning &= ~NETMED_SCHEDULE; + m_NetmedScheduleReader->stop(); + m_NetmedScheduleConn=0; + } + if (isRunning & NETMED_SCHEDULE_OTHER) + { + isRunning &= ~NETMED_SCHEDULE_OTHER; + m_NetmedScheduleOtherReader->stop(); + m_NetmedScheduleOtherConn=0; + } +#endif +#ifdef ENABLE_FREESAT + if (isRunning & FREESAT_SCHEDULE_OTHER) + { + isRunning &= ~FREESAT_SCHEDULE_OTHER; + m_FreeSatScheduleOtherReader->stop(); + m_FreeSatScheduleOtherReader2->stop(); + m_FreeSatScheduleOtherConn=0; + m_FreeSatScheduleOtherConn2=0; + } +#endif if (isRunning & VIASAT) { isRunning &= ~VIASAT; @@ -1432,52 +1859,57 @@ void eEPGCache::channel_data::abortEPG() pthread_mutex_unlock(&channel_active); } - -void eEPGCache::channel_data::readDataViasat( const __u8 *data) -{ - __u8 *d=0; - memcpy(&d, &data, sizeof(__u8*)); - d[0] |= 0x80; - readData(data); -} - -void eEPGCache::channel_data::readData( const __u8 *data) +void eEPGCache::channel_data::readData( const uint8_t *data, int source) { - int source; int map; - iDVBSectionReader *reader=NULL; - switch(data[0]) + iDVBSectionReader *reader = NULL; + switch (source) { - case 0x4E ... 0x4F: - reader=m_NowNextReader; - source=NOWNEXT; - map=0; + case NOWNEXT: + reader = m_NowNextReader; + map = 0; + break; + case SCHEDULE: + reader = m_ScheduleReader; + map = 1; + break; + case SCHEDULE_OTHER: + reader = m_ScheduleOtherReader; + map = 2; + break; + case VIASAT: + reader = m_ViasatReader; + map = 3; break; - case 0x50 ... 0x5F: - reader=m_ScheduleReader; - source=SCHEDULE; - map=1; +#ifdef ENABLE_NETMED + case NETMED_SCHEDULE: + reader = m_NetmedScheduleReader; + map = 1; break; - case 0x60 ... 0x6F: - reader=m_ScheduleOtherReader; - source=SCHEDULE_OTHER; - map=2; + case NETMED_SCHEDULE_OTHER: + reader = m_NetmedScheduleOtherReader; + map = 2; break; - case 0xD0 ... 0xDF: - case 0xE0 ... 0xEF: - reader=m_ViasatReader; - source=VIASAT; - map=3; +#endif +#ifdef ENABLE_VIRGIN + case VIRGIN_NOWNEXT: + reader = m_VirginNowNextReader; + map = 0; + break; + case VIRGIN_SCHEDULE: + reader = m_VirginScheduleReader; + map = 1; break; +#endif default: - eDebug("[EPGC] unknown table_id !!!"); + eDebug("[eEPGCache] unknown source"); return; } tidMap &seenSections = this->seenSections[map]; tidMap &calcedSections = this->calcedSections[map]; if ( (state == 1 && calcedSections == seenSections) || state > 1 ) { - eDebugNoNewLine("[EPGC] "); + eDebugNoNewLine("[eEPGCache] "); switch (source) { case NOWNEXT: @@ -1496,9 +1928,29 @@ void eEPGCache::channel_data::readData( const __u8 *data) m_ViasatConn=0; eDebugNoNewLine("viasat"); break; +#ifdef ENABLE_NETMED + case NETMED_SCHEDULE: + m_NetmedScheduleConn=0; + eDebugNoNewLine("netmed schedule"); + break; + case NETMED_SCHEDULE_OTHER: + m_NetmedScheduleOtherConn=0; + eDebugNoNewLine("netmed schedule other"); + break; +#endif +#ifdef ENABLE_VIRGIN + case VIRGIN_NOWNEXT: + m_VirginNowNextConn=0; + eDebugNoNewLine("virgin nownext"); + break; + case VIRGIN_SCHEDULE: + m_VirginScheduleConn=0; + eDebugNoNewLine("virgin schedule"); + break; +#endif default: eDebugNoNewLine("unknown");break; } - eDebug(" finished(%ld)", ::time(0)); + eDebugNoNewLine(" finished(%ld)\n", ::time(0)); if ( reader ) reader->stop(); isRunning &= ~source; @@ -1508,7 +1960,7 @@ void eEPGCache::channel_data::readData( const __u8 *data) else { eit_t *eit = (eit_t*) data; - __u32 sectionNo = data[0] << 24; + uint32_t sectionNo = data[0] << 24; sectionNo |= data[3] << 16; sectionNo |= data[4] << 8; sectionNo |= eit->section_number; @@ -1520,44 +1972,165 @@ void eEPGCache::channel_data::readData( const __u8 *data) { seenSections.insert(sectionNo); calcedSections.insert(sectionNo); - __u32 tmpval = sectionNo & 0xFFFFFF00; - __u8 incr = source == NOWNEXT ? 1 : 8; + uint32_t tmpval = sectionNo & 0xFFFFFF00; + uint8_t incr = source == NOWNEXT ? 1 : 8; for ( int i = 0; i <= eit->last_section_number; i+=incr ) { if ( i == eit->section_number ) { for (int x=i; x <= eit->segment_last_section_number; ++x) + { calcedSections.insert(tmpval|(x&0xFF)); + } } else + { calcedSections.insert(tmpval|(i&0xFF)); + } } cache->sectionRead(data, source, this); } } } -RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction) -// if t == -1 we search the current event... +#if ENABLE_FREESAT + +freesatEITSubtableStatus::freesatEITSubtableStatus(u_char version, uint8_t maxSection) : version(version) { - singleLock s(cache_lock); - uniqueEPGKey key(handleGroup(service)); + initMap(maxSection); +} - // check if EPG for this service is ready... +void freesatEITSubtableStatus::initMap(uint8_t maxSection) +{ + int i, maxSectionIdx = maxSection / 8; + for (i = 0; i < 32; i++) + { + sectionMap[i] = (i <= maxSectionIdx ? 0x0100 : 0x0000 ); + } +} + +bool freesatEITSubtableStatus::isSectionPresent(uint8_t sectionNo) +{ + uint8_t sectionIdx = sectionNo / 8; + uint8_t bitOffset = sectionNo % 8; + + return ((sectionMap[sectionIdx] & (1 << bitOffset)) != 0); +} + +bool freesatEITSubtableStatus::isCompleted() +{ + uint32_t i = 0; + uint8_t calc; + + while ( i < 32 ) + { + calc = sectionMap[i] >> 8; + if (! calc) return true; // Last segment passed + if (calc ^ ( sectionMap[i] & 0xFF ) ) // Segment not fully found + return false; + i++; + } + return true; // All segments ok +} + +void freesatEITSubtableStatus::seen(uint8_t sectionNo, uint8_t maxSegmentSection) +{ + uint8_t sectionIdx = sectionNo / 8; + uint8_t bitOffset = sectionNo % 8; + uint8_t maxBitOffset = maxSegmentSection % 8; + + sectionMap[sectionIdx] &= 0x00FF; // Clear calc map + sectionMap[sectionIdx] |= ((0x01FF << maxBitOffset) & 0xFF00); // Set calc map + sectionMap[sectionIdx] |= (1 << bitOffset); // Set seen map +} + +bool freesatEITSubtableStatus::isVersionChanged(u_char testVersion) +{ + return version != testVersion; +} + +void freesatEITSubtableStatus::updateVersion(u_char newVersion, uint8_t maxSection) +{ + version = newVersion; + initMap(maxSection); +} + +void eEPGCache::channel_data::cleanupFreeSat() +{ + m_FreeSatSubTableStatus.clear(); + m_FreesatTablesToComplete = 0; +} + +void eEPGCache::channel_data::readFreeSatScheduleOtherData( const uint8_t *data) +{ + eit_t *eit = (eit_t*) data; + uint32_t subtableNo = data[0] << 24; // Table ID + subtableNo |= data[3] << 16; // Service ID Hi + subtableNo |= data[4] << 8; // Service ID Lo + + // Check for sub-table version in map + std::map &freeSatSubTableStatus = this->m_FreeSatSubTableStatus; + std::map::iterator itmap = freeSatSubTableStatus.find(subtableNo); + + freesatEITSubtableStatus *fsstatus; + if ( itmap == freeSatSubTableStatus.end() ) + { + // New sub table. Store version. + //eDebug("[eEPGCache] New subtable (%x) version (%d) now/next (%d) tsid (%x/%x) onid (%x/%x)", subtableNo, eit->version_number, eit->current_next_indicator, eit->transport_stream_id_hi, eit->transport_stream_id_lo, eit->original_network_id_hi, eit->original_network_id_lo); + fsstatus = new freesatEITSubtableStatus(eit->version_number, eit->last_section_number); + m_FreesatTablesToComplete++; + freeSatSubTableStatus.insert(std::pair(subtableNo, *fsstatus)); + } + else + { + fsstatus = &itmap->second; + // Existing subtable. Check version. Should check current / next as well? Seems to always be current for Freesat + if ( fsstatus->isVersionChanged(eit->version_number) ) + { + eDebug("[eEPGCache] FS subtable (%x) version changed (%d) now/next (%d)", subtableNo, eit->version_number, eit->current_next_indicator); + m_FreesatTablesToComplete++; + fsstatus->updateVersion(eit->version_number, eit->last_section_number); + } + else + { + if ( fsstatus->isSectionPresent(eit->section_number) ) + { +// eDebug("[eEPGCache] DUP FS sub/sec/ver (%x/%d/%d)", subtableNo, eit->section_number, eit->version_number); + return; + } + } + } + +// eDebug("[eEPGCache] New FS sub/sec/ls/lss/ver (%x/%d/%d/%d/%d)", subtableNo, eit->section_number, eit->last_section_number, eit->segment_last_section_number, eit->version_number); + fsstatus->seen(eit->section_number, eit->segment_last_section_number); + if (fsstatus->isCompleted()) + { + m_FreesatTablesToComplete--; + } + cache->sectionRead(data, FREESAT_SCHEDULE_OTHER, this); +} +#endif + +RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction) +// if t == -1 we search the current event... +{ + uniqueEPGKey key(handleGroup(service)); + + // check if EPG for this service is ready... eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ? + if ( It != eventDB.end() && !It->second.byEvent.empty() ) // entrys cached ? { if (t==-1) t = ::time(0); - timeMap::iterator i = direction <= 0 ? It->second.second.lower_bound(t) : // find > or equal - It->second.second.upper_bound(t); // just > - if ( i != It->second.second.end() ) + timeMap::iterator i = direction <= 0 ? It->second.byTime.lower_bound(t) : // find > or equal + It->second.byTime.upper_bound(t); // just > + if ( i != It->second.byTime.end() ) { if ( direction < 0 || (direction == 0 && i->first > t) ) { timeMap::iterator x = i; --x; - if ( x != It->second.second.end() ) + if ( x != It->second.byTime.end() ) { time_t start_time = x->first; if (direction >= 0) @@ -1579,16 +2152,6 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, co return -1; } -RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eit_event_struct *&result, int direction) -{ - singleLock s(cache_lock); - const eventData *data=0; - RESULT ret = lookupEventTime(service, t, data, direction); - if ( !ret && data ) - result = data->get(); - return ret; -} - RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result, int direction) { singleLock s(cache_lock); @@ -1604,6 +2167,7 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, eP singleLock s(cache_lock); const eventData *data=0; RESULT ret = lookupEventTime(service, t, data, direction); + result = NULL; if ( !ret && data ) { Event ev((uint8_t*)data->get()); @@ -1616,14 +2180,13 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, eP RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result ) { - singleLock s(cache_lock); uniqueEPGKey key(handleGroup(service)); - eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? + eventCache::iterator It = eventDB.find(key); + if (It != eventDB.end()) { - eventMap::iterator i( It->second.first.find( event_id )); - if ( i != It->second.first.end() ) + eventMap::iterator i = It->second.byEvent.find(event_id); + if ( i != It->second.byEvent.end() ) { result = i->second; return 0; @@ -1631,19 +2194,48 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, else { result = 0; - eDebug("[EPGC] event %04x not found in epgcache", event_id); + eDebug("[eEPGCache] event %04x not found in epgcache", event_id); } } return -1; } -RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&result) +RESULT eEPGCache::saveEventToFile(const char* filename, const eServiceReference &service, int eit_event_id, time_t begTime, time_t endTime) { + RESULT ret = -1; singleLock s(cache_lock); - const eventData *data=0; - RESULT ret = lookupEventId(service, event_id, data); - if ( !ret && data ) - result = data->get(); + const eventData *data = NULL; + if ( eit_event_id != -1 ) + { + eDebug("[eEPGCache] %s epg event id %x", __func__, eit_event_id); + ret = lookupEventId(service, eit_event_id, data); + } + if ( (ret != 0) && (begTime != -1) ) + { + time_t queryTime = begTime; + if (endTime != -1) + queryTime += (endTime - begTime) / 2; + ret = lookupEventTime(service, queryTime, data); + } + if (ret == 0) + { + int fd = open(filename, O_CREAT|O_WRONLY, 0666); + if (fd < 0) + { + eDebug("[eEPGCache] Failed to create file: %s", filename); + return fd; + } + const eit_event_struct *event = data->get(); + int evLen = event->getDescriptorsLoopLength() + 12/*EIT_LOOP_SIZE*/; + int wr = ::write( fd, event, evLen ); + ::close(fd); + if ( wr != evLen ) + { + ::unlink(filename); /* Remove faulty file */ + eDebug("[eEPGCache] eit write error on %s: %m", filename); + ret = (wr < 0) ? wr : -1; + } + } return ret; } @@ -1662,6 +2254,7 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, singleLock s(cache_lock); const eventData *data=0; RESULT ret = lookupEventId(service, event_id, data); + result = NULL; if ( !ret && data ) { Event ev((uint8_t*)data->get()); @@ -1679,16 +2272,16 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, if (begin == -1) begin = ::time(0); eventCache::iterator It = eventDB.find(ref); - if ( It != eventDB.end() && It->second.second.size() ) + if ( It != eventDB.end() && !It->second.byTime.empty() ) { - m_timemap_cursor = It->second.second.lower_bound(begin); - if ( m_timemap_cursor != It->second.second.end() ) + m_timemap_cursor = It->second.byTime.lower_bound(begin); + if ( m_timemap_cursor != It->second.byTime.end() ) { if ( m_timemap_cursor->first != begin ) { timeMap::iterator x = m_timemap_cursor; --x; - if ( x != It->second.second.end() ) + if ( x != It->second.byTime.end() ) { time_t start_time = x->first; if ( begin > start_time && begin < (start_time+x->second->getDuration())) @@ -1698,9 +2291,9 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, } if (minutes != -1) - m_timemap_end = It->second.second.lower_bound(begin+minutes*60); + m_timemap_end = It->second.byTime.lower_bound(begin+minutes*60); else - m_timemap_end = It->second.second.end(); + m_timemap_end = It->second.byTime.end(); currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get(); return m_timemap_cursor == m_timemap_end ? -1 : 0; @@ -1708,26 +2301,6 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, return -1; } -RESULT eEPGCache::getNextTimeEntry(const eventData *& result) -{ - if ( m_timemap_cursor != m_timemap_end ) - { - result = m_timemap_cursor++->second; - return 0; - } - return -1; -} - -RESULT eEPGCache::getNextTimeEntry(const eit_event_struct *&result) -{ - if ( m_timemap_cursor != m_timemap_end ) - { - result = m_timemap_cursor++->second->get(); - return 0; - } - return -1; -} - RESULT eEPGCache::getNextTimeEntry(Event *&result) { if ( m_timemap_cursor != m_timemap_end ) @@ -1749,8 +2322,9 @@ RESULT eEPGCache::getNextTimeEntry(ePtr &result) return -1; } -void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service, eServiceEvent *ptr, ePyObject nowTime, ePyObject service_name ) +void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service_reference, eServiceEvent *ptr, ePyObject service_name, ePyObject nowTime, eventData *evData ) { + //eDebug("[eEPGCache] fillTuple arg=%s argcnt=%d, ptr=%d evData=%d", argstring, argcount, ptr ? 1 : 0, evData ? 1 : 0); ePyObject tmp; int spos=0, tpos=0; char c; @@ -1763,13 +2337,13 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s tmp = PyLong_FromLong(0); break; case 'I': // Event Id - tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject(); + tmp = evData ? PyLong_FromLong(evData->getEventID()) : (ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject()); break; case 'B': // Event Begin Time - tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject(); + tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : (evData ? PyLong_FromLong(evData->getStartTime()) : ePyObject()); break; case 'D': // Event Duration - tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject(); + tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : (evData ? PyLong_FromLong(evData->getDuration()) : ePyObject()); break; case 'T': // Event Title tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject(); @@ -1780,12 +2354,18 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s case 'E': // Event Extended Description tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject(); break; + case 'P': // Event Parental Rating + tmp = ptr ? ePyObject(ptr->getParentalData()) : ePyObject(); + break; + case 'W': // Event Content Description + tmp = ptr ? ePyObject(ptr->getGenreData()) : ePyObject(); + break; case 'C': // Current Time tmp = nowTime; inc_refcount = true; break; case 'R': // service reference string - tmp = service; + tmp = service_reference; inc_refcount = true; break; case 'n': // short service name @@ -1798,7 +2378,7 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s continue; default: // ignore unknown tmp = ePyObject(); - eDebug("fillTuple unknown '%c'... insert 'None' in result", c); + eDebug("[eEPGCache] fillTuple unknown '%c'... insert 'None' in result", c); } if (!tmp) { @@ -1815,7 +2395,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, { if (convertFunc) { - fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name); + fillTuple(convertFuncArgs, argstring, argcount, service, ptr, service_name, nowTime, 0); ePyObject result = PyObject_CallObject(convertFunc, convertFuncArgs); if (!result) { @@ -1827,7 +2407,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, Py_DECREF(dest_list); PyErr_SetString(PyExc_StandardError, "error in convertFunc execute"); - eDebug("error in convertFunc execute"); + eDebug("[eEPGCache] handleEvent: error in convertFunc execute"); return -1; } PyList_Append(dest_list, result); @@ -1836,7 +2416,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, else { ePyObject tuple = PyTuple_New(argcount); - fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name); + fillTuple(tuple, argstring, argcount, service, ptr, service_name, nowTime, 0); PyList_Append(dest_list, tuple); Py_DECREF(tuple); } @@ -1852,6 +2432,8 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, // T = Event Title // S = Event Short Description // E = Event Extended Description +// P = Event Parental Rating +// W = Event Content Description ('W'hat) // C = Current Time // R = Service Reference // N = Service Name @@ -1880,7 +2462,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("no list"); + eDebug("[eEPGCache] no list"); return NULL; } int listIt=0; @@ -1888,11 +2470,11 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) if (!listSize) { PyErr_SetString(PyExc_StandardError, - "not params given"); - eDebug("not params given"); + "no params given"); + eDebug("[eEPGCache] no params given"); return NULL; } - else + else { ePyObject argv=PyList_GET_ITEM(list, 0); // borrowed reference! if (PyString_Check(argv)) @@ -1903,7 +2485,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) else argstring = "I"; // just event id as default argcount = strlen(argstring); -// eDebug("have %d args('%s')", argcount, argstring); +// eDebug("[eEPGCache] have %d args('%s')", argcount, argstring); } bool forceReturnOne = strchr(argstring, 'X') ? true : false; @@ -1916,7 +2498,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) { PyErr_SetString(PyExc_StandardError, "convertFunc must be callable"); - eDebug("convertFunc is not callable"); + eDebug("[eEPGCache] convertFunc is not callable"); return NULL; } convertFuncArgs = PyTuple_New(argcount); @@ -1952,7 +2534,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) { if (!PyString_Check(entry)) { - eDebug("tuple entry 0 is no a string"); + eDebug("[eEPGCache] tuple entry 0 is no a string"); goto skip_entry; } service = entry; @@ -1962,7 +2544,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) type=PyInt_AsLong(entry); if (type < -1 || type > 2) { - eDebug("unknown type %d", type); + eDebug("[eEPGCache] unknown type %d", type); goto skip_entry; } break; @@ -1973,7 +2555,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) minutes=PyInt_AsLong(entry); break; default: - eDebug("unneeded extra argument"); + eDebug("[eEPGCache] unneeded extra argument"); break; } } @@ -1982,12 +2564,6 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) stime = ::time(0); eServiceReference ref(handleGroup(eServiceReference(PyString_AS_STRING(service)))); - if (ref.type != eServiceReference::idDVB) - { - eDebug("service reference for epg query is not valid"); - continue; - } - // redirect subservice querys to parent service eServiceReferenceDVB &dvb_ref = (eServiceReferenceDVB&)ref; if (dvb_ref.getParentTransportStreamID().get()) // linkage subservice @@ -2096,63 +2672,302 @@ skip_entry: return dest_list; } -void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData *evData, eServiceEvent *ptr, ePyObject service_name, ePyObject service_reference) +static void fill_eit_start(eit_event_struct *evt, time_t t) { - ePyObject tmp; - int pos=0; - while(pos < argcount) + tm *time = gmtime(&t); + + int l = 0; + int month = time->tm_mon + 1; + if (month == 1 || month == 2) + l = 1; + int mjd = 14956 + time->tm_mday + (int)((time->tm_year - l) * 365.25) + (int)((month + 1 + l*12) * 30.6001); + evt->start_time_1 = mjd >> 8; + evt->start_time_2 = mjd & 0xFF; + + evt->start_time_3 = toBCD(time->tm_hour); + evt->start_time_4 = toBCD(time->tm_min); + evt->start_time_5 = toBCD(time->tm_sec); + +} + +static void fill_eit_duration(eit_event_struct *evt, int time) +{ + //time is given in second + //convert to hour, minutes, seconds + evt->duration_1 = toBCD(time / 3600); + evt->duration_2 = toBCD((time % 3600) / 60); + evt->duration_3 = toBCD((time % 3600) % 60); +} + +// convert from set of strings to DVB format (EIT) +void eEPGCache::submitEventData(const std::vector& serviceRefs, long start, + long duration, const char* title, const char* short_summary, + const char* long_description, char event_type) +{ + std::vector sids; + std::vector chids; + for (std::vector::const_iterator serviceRef = serviceRefs.begin(); + serviceRef != serviceRefs.end(); + ++serviceRef) { - bool inc_refcount=false; - switch(argstring[pos]) + eDVBChannelID chid; + serviceRef->getChannelID(chid); + chids.push_back(chid); + sids.push_back(serviceRef->getServiceID().get()); + } + submitEventData(sids, chids, start, duration, title, short_summary, long_description, event_type, EPG_IMPORT); +} + +void eEPGCache::submitEventData(const std::vector& sids, const std::vector& chids, long start, + long duration, const char* title, const char* short_summary, + const char* long_description, char event_type, int source) +{ + if (!title) + return; + if (sids.size() != chids.size()) + return; + static const int EIT_LENGTH = 4108; + static const uint8_t codePage = 0x15; // UTF-8 encoding + uint8_t data[EIT_LENGTH]; + + eit_t *packet = (eit_t *) data; + packet->table_id = 0x50; + packet->section_syntax_indicator = 1; + + packet->version_number = 0; // eEPGCache::sectionRead() will dig this for the moment + packet->current_next_indicator = 0; + packet->section_number = 0; // eEPGCache::sectionRead() will dig this for the moment + packet->last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment + + packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment + packet->segment_last_table_id = 0x50; + + eit_event_t *evt_struct = (eit_event_t*) (data + EIT_SIZE); + + uint16_t eventId = start & 0xFFFF; + evt_struct->setEventId(eventId); + + //6 bytes start time, 3 bytes duration + fill_eit_start(evt_struct, start); + fill_eit_duration(evt_struct, duration); + + evt_struct->running_status = 0; + evt_struct->free_CA_mode = 0; + + //no support for different code pages, only DVB's latin1 character set + //TODO: convert text to correct character set (data is probably passed in as UTF-8) + uint8_t *x = (uint8_t *) evt_struct; + x += EIT_LOOP_SIZE; + int nameLength = strnlen(title, 246); + int descLength = short_summary ? strnlen(short_summary, 246 - nameLength) : 0; + + eit_short_event_descriptor_struct *short_evt = (eit_short_event_descriptor_struct*) x; + short_evt->descriptor_tag = SHORT_EVENT_DESCRIPTOR; + short_evt->descriptor_length = EIT_SHORT_EVENT_DESCRIPTOR_SIZE + nameLength + descLength + 1 - 2; //+1 for length of short description, -2 for tag and length + if (nameLength) ++short_evt->descriptor_length; // +1 for codepage byte + if (descLength) ++short_evt->descriptor_length; + short_evt->language_code_1 = 'e'; + short_evt->language_code_2 = 'n'; + short_evt->language_code_3 = 'g'; + short_evt->event_name_length = nameLength ? nameLength + 1 : 0; + x = (uint8_t *) short_evt; + x += EIT_SHORT_EVENT_DESCRIPTOR_SIZE; + *x = codePage; + ++x; + memcpy(x, title, nameLength); + x += nameLength; + if (descLength) + { + *x = descLength + 1; + ++x; + *x = codePage; + ++x; + memcpy(x, short_summary, descLength); + x += descLength; + } + else + { + *x = 0; + ++x; + } + + //Content type + if (event_type != 0) + { + x[0] = 0x54; + x[1] = 2; + x[2] = event_type; + x[3] = 0; + x += 4; + } + + //Long description + int currentLoopLength = x - (uint8_t*)short_evt; + static const int overheadPerDescriptor = 9; //increase if codepages are added!!! + static const int MAX_LEN = 256 - overheadPerDescriptor; + + int textLength = long_description ? strnlen(long_description, EIT_LENGTH) : 0;//EIT_LENGTH is a bit too much, but it's only here as a reasonable end point + int lastDescriptorNumber = (textLength + MAX_LEN-1) / MAX_LEN - 1; + int remainingTextLength = textLength - lastDescriptorNumber * MAX_LEN; + + //if long description is too long, just try to fill as many descriptors as possible + while ( (lastDescriptorNumber+1) * 256 + currentLoopLength > EIT_LENGTH - EIT_LOOP_SIZE) + { + lastDescriptorNumber--; + remainingTextLength = MAX_LEN; + } + + for (int descrIndex = 0; descrIndex <= lastDescriptorNumber; ++descrIndex) + { + eit_extended_descriptor_struct *ext_evt = (eit_extended_descriptor_struct*) x; + ext_evt->descriptor_tag = EIT_EXTENDED_EVENT_DESCRIPOR; + //descriptor header length is 6, including the 2 tag and length bytes + //so the length field must be: stringlength + 1 (2 4-bits numbers) + 3 (lang code) + 2 bytes for item info length field and text length field + int currentTextLength = descrIndex < lastDescriptorNumber ? MAX_LEN : remainingTextLength; + ext_evt->descriptor_length = 6 + currentTextLength + 1; + + ext_evt->descriptor_number = descrIndex; + ext_evt->last_descriptor_number = lastDescriptorNumber; + ext_evt->iso_639_2_language_code_1 = 'e'; + ext_evt->iso_639_2_language_code_2 = 'n'; + ext_evt->iso_639_2_language_code_3 = 'g'; + + x[6] = 0; //item information (car, year, director, etc. Unsupported for now) + x[7] = currentTextLength + 1; //length of description string (part in this message) + x[8] = codePage; + memcpy(x + 9, &long_description[descrIndex*MAX_LEN], currentTextLength); + + x += 2 + ext_evt->descriptor_length; + } + + //TODO: add age and more + int desc_loop_length = x - ((uint8_t*)evt_struct + EIT_LOOP_SIZE); + evt_struct->setDescriptorsLoopLength(desc_loop_length); + + int packet_length = (x - data) - 3; //should add 1 for crc.... + packet->setSectionLength(packet_length); + // Add channelrefs and submit data. + for (unsigned int i = 0; i < chids.size(); i++) + { + packet->setServiceId(sids[i]); + packet->setTransportStreamId(chids[i].transport_stream_id.get()); + packet->setOriginalNetworkId(chids[i].original_network_id.get()); + sectionRead(data, source, 0); + } +} + +void eEPGCache::setEpgHistorySeconds(time_t seconds) +{ + historySeconds = seconds; +} + +void eEPGCache::setEpgSources(unsigned int mask) +{ + enabledSources = mask; +} + +unsigned int eEPGCache::getEpgSources() +{ + return enabledSources; +} + +static const char* getStringFromPython(ePyObject obj) +{ + char *result = 0; + if (PyString_Check(obj)) + { + result = PyString_AS_STRING(obj); + } + return result; +} + +void eEPGCache::importEvent(ePyObject serviceReference, ePyObject list) +{ + importEvents(serviceReference, list); +} + +//here we get a python tuple of tuples ;) +// consider it an array of objects with the following data +// 1. start time (long) +// 2. duration (int) +// 3. event title (string) +// 4. short description (string) +// 5. extended description (string) +// 6. event type (byte) +void eEPGCache::importEvents(ePyObject serviceReferences, ePyObject list) +{ + std::vector refs; + + if (PyString_Check(serviceReferences)) + { + char *refstr; + refstr = PyString_AS_STRING(serviceReferences); + if (!refstr) { - case '0': // PyLong 0 - tmp = PyLong_FromLong(0); - break; - case 'I': // Event Id - tmp = PyLong_FromLong(evData->getEventID()); - break; - case 'B': // Event Begin Time - if (ptr) - tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject(); - else - tmp = PyLong_FromLong(evData->getStartTime()); - break; - case 'D': // Event Duration - if (ptr) - tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject(); - else - tmp = PyLong_FromLong(evData->getDuration()); - break; - case 'T': // Event Title - tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject(); - break; - case 'S': // Event Short Description - tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : ePyObject(); - break; - case 'E': // Event Extended Description - tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject(); - break; - case 'R': // service reference string - tmp = service_reference; - inc_refcount = true; - break; - case 'n': // short service name - case 'N': // service name - tmp = service_name; - inc_refcount = true; - break; - default: // ignore unknown - tmp = ePyObject(); - eDebug("fillTuple2 unknown '%c'... insert None in Result", argstring[pos]); + eDebug("[eEPGCache:import] serviceReference string is 0, aborting"); + return; } - if (!tmp) + refs.push_back(eServiceReferenceDVB(refstr)); + } + else if (PyList_Check(serviceReferences)) + { + int nRefs = PyList_Size(serviceReferences); + for (int i = 0; i < nRefs; ++i) { - tmp = Py_None; - inc_refcount = true; + PyObject* item = PyList_GET_ITEM(serviceReferences, i); + char *refstr; + refstr = PyString_AS_STRING(item); + if (!refstr) + { + eDebug("[eEPGCache:import] a serviceref item is not a string"); + } + else + { + refs.push_back(eServiceReferenceDVB(refstr)); + } } - if (inc_refcount) - Py_INCREF(tmp); - PyTuple_SET_ITEM(tuple, pos++, tmp); + } + else + { + eDebug("[eEPGCache:import] serviceReference string is neither string nor list, aborting"); + return; + } + + bool isTuple = PyTuple_Check(list); + if (!isTuple && !PyList_Check(list)) + { + + eDebug("[eEPGCache:import] argument 'list' is neither list nor tuple."); + return; + } + + int numberOfEvents = isTuple ? PyTuple_Size(list) : PyList_Size(list); + + for (int i = 0; i < numberOfEvents; ++i) + { + ePyObject singleEvent = isTuple ? PyTuple_GET_ITEM(list, i) : PyList_GET_ITEM(list, i); + if (!PyTuple_Check(singleEvent)) + { + eDebug("[eEPGCache:import] eventdata tuple does not pass PyTuple_Check, aborting"); + return; + } + int tupleSize = PyTuple_Size(singleEvent); + if (tupleSize < 5) + { + eDebug("[eEPGCache:import] eventdata tuple does not contain enough fields, aborting"); + return; + } + + long start = PyLong_AsLong(PyTuple_GET_ITEM(singleEvent, 0)); + long duration = PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 1)); + const char *title = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 2)); + const char *short_summary = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 3)); + const char *long_description = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 4)); + char event_type = (char) PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 5)); + + Py_BEGIN_ALLOW_THREADS; + submitEventData(refs, start, duration, title, short_summary, long_description, event_type); + Py_END_ALLOW_THREADS; } } @@ -2163,6 +2978,8 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData // D = Event Duration // T = Event Title // S = Event Short Description +// P = Event Parental Rating +// W = Event Content Description // E = Event Extended Description // R = Service Reference // N = Service Name @@ -2170,12 +2987,13 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData // the second tuple entry is the MAX matches value // the third tuple entry is the type of query // 0 = search for similar broadcastings (SIMILAR_BROADCASTINGS_SEARCH) -// 1 = search events with exactly title name (EXAKT_TITLE_SEARCH) +// 1 = search events with exactly title name (EXACT_TITLE_SEARCH) // 2 = search events with text in title name (PARTIAL_TITLE_SEARCH) +// 3 = search events starting with title name (START_TITLE_SEARCH) // when type is 0 (SIMILAR_BROADCASTINGS_SEARCH) // the fourth is the servicereference string // the fifth is the eventid -// when type is 1 or 2 (EXAKT_TITLE_SEARCH or PARTIAL_TITLE_SEARCH) +// when type > 0 (*_TITLE_SEARCH) // the fourth is the search text // the fifth is // 0 = case sensitive (CASE_CHECK) @@ -2184,8 +3002,7 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData PyObject *eEPGCache::search(ePyObject arg) { ePyObject ret; - int descridx = -1; - __u32 descr[512]; + std::deque descr; int eventid = -1; const char *argstring=0; char *refstr=0; @@ -2211,11 +3028,14 @@ PyObject *eEPGCache::search(ePyObject arg) #endif argstring = PyString_AS_STRING(obj); for (int i=0; i < argcount; ++i) + { switch(argstring[i]) { case 'S': case 'E': case 'T': + case 'P': + case 'W': needServiceEvent=true; break; case 'N': @@ -2230,20 +3050,24 @@ PyObject *eEPGCache::search(ePyObject arg) default: break; } + } } else { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("tuple arg 0 is not a string"); + eDebug("[eEPGCache] tuple arg 0 is not a string"); return NULL; } } if (tuplesize > 1) + { maxmatches = PyLong_AsLong(PyTuple_GET_ITEM(arg, 1)); + } if (tuplesize > 2) { querytype = PyLong_AsLong(PyTuple_GET_ITEM(arg, 2)); + if (tuplesize > 4 && querytype == 0) { ePyObject obj = PyTuple_GET_ITEM(arg, 3); @@ -2259,47 +3083,44 @@ PyObject *eEPGCache::search(ePyObject arg) lookupEventId(ref, eventid, evData); if (evData) { - __u8 *data = evData->EITdata; - int tmp = evData->ByteSize-10; - __u32 *p = (__u32*)(data+10); - // search short and extended event descriptors - while(tmp>3) + // search short and extended event descriptors + for (uint8_t i = 0; i < evData->n_crc; ++i) { - __u32 crc = *p++; - descriptorMap::iterator it = + uint32_t crc = evData->crc_list[i]; + DescriptorMap::iterator it = eventData::descriptors.find(crc); if (it != eventData::descriptors.end()) { - __u8 *descr_data = it->second.second; + uint8_t *descr_data = it->second.data; switch(descr_data[0]) { case 0x4D ... 0x4E: - descr[++descridx]=crc; + descr.push_back(crc); + break; default: break; } } - tmp-=4; } } - if (descridx<0) - eDebug("event not found"); + if (descr.empty()) + eDebug("[eEPGCache] event not found"); } else { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("tuple arg 4 is not a valid service reference string"); + eDebug("[eEPGCache] tuple arg 4 is not a valid service reference string"); return NULL; } } else { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("tuple arg 4 is not a string"); + eDebug("[eEPGCache] tuple arg 4 is not a string"); return NULL; } } - else if (tuplesize > 4 && (querytype == 1 || querytype == 2) ) + else if (tuplesize > 4 && (querytype > 0) ) { ePyObject obj = PyTuple_GET_ITEM(arg, 3); if (PyString_Check(obj)) @@ -2311,83 +3132,85 @@ PyObject *eEPGCache::search(ePyObject arg) #else int textlen = PyString_Size(obj); #endif - if (querytype == 1) - eDebug("lookup for events with '%s' as title(%s)", str, casetype?"ignore case":"case sensitive"); - else - eDebug("lookup for events with '%s' in title(%s)", str, casetype?"ignore case":"case sensitive"); + switch (querytype) + { + case 1: + eDebug("[eEPGCache] lookup events with '%s' as title (%s)", str, casetype?"ignore case":"case sensitive"); + break; + case 2: + eDebug("[eEPGCache] lookup events with '%s' in title (%s)", str, casetype?"ignore case":"case sensitive"); + break; + case 3: + eDebug("[eEPGCache] lookup events, title starting with '%s' (%s)", str, casetype?"ignore case":"case sensitive"); + break; + } + Py_BEGIN_ALLOW_THREADS; /* No Python code in this section, so other threads can run */ singleLock s(cache_lock); - for (descriptorMap::iterator it(eventData::descriptors.begin()); - it != eventData::descriptors.end() && descridx < 511; ++it) + std::string title; + for (DescriptorMap::iterator it(eventData::descriptors.begin()); + it != eventData::descriptors.end(); ++it) { - __u8 *data = it->second.second; - if ( data[0] == 0x4D ) // short event descriptor + uint8_t *data = it->second.data; + if ( data[0] == SHORT_EVENT_DESCRIPTOR ) { + const char *titleptr = (const char*)&data[6]; int title_len = data[5]; - if ( querytype == 1 ) + if (data[6] < 0x20) { - int offs = 6; - // skip DVB-Text Encoding! - if (data[6] == 0x10) - { - offs+=3; - title_len-=3; - } - else if(data[6] > 0 && data[6] < 0x20) - { - offs+=1; - title_len-=1; - } + /* custom encoding */ + title = convertDVBUTF8((unsigned char*)titleptr, title_len, 0x40, 0); + titleptr = title.data(); + title_len = title.length(); + } + if (title_len < textlen) + /*Doesn't fit, so cannot match anything */ + continue; + if (querytype == 1) + { + /* require exact title match */ if (title_len != textlen) continue; - if ( casetype ) + } + else if (querytype == 3) + { + /* Do a "startswith" match by pretending the text isn't that long */ + title_len = textlen; + } + if (casetype) + { + while (title_len >= textlen) { - if ( !strncasecmp((const char*)data+offs, str, title_len) ) + if (!strncasecmp(titleptr, str, textlen)) { -// std::string s((const char*)data+offs, title_len); -// eDebug("match1 %s %s", str, s.c_str() ); - descr[++descridx] = it->first; + descr.push_back(it->first); + break; } - } - else if ( !strncmp((const char*)data+offs, str, title_len) ) - { -// std::string s((const char*)data+offs, title_len); -// eDebug("match2 %s %s", str, s.c_str() ); - descr[++descridx] = it->first; + title_len--; + titleptr++; } } else { - int idx=0; - while((title_len-idx) >= textlen) + while (title_len >= textlen) { - if (casetype) - { - if (!strncasecmp((const char*)data+6+idx, str, textlen) ) - { - descr[++descridx] = it->first; -// std::string s((const char*)data+6, title_len); -// eDebug("match 3 %s %s", str, s.c_str() ); - break; - } - } - else if (!strncmp((const char*)data+6+idx, str, textlen) ) + if (!memcmp(titleptr, str, textlen)) { - descr[++descridx] = it->first; -// std::string s((const char*)data+6, title_len); -// eDebug("match 4 %s %s", str, s.c_str() ); + descr.push_back(it->first); break; } - ++idx; + title_len--; + titleptr++; } } } } + Py_END_ALLOW_THREADS; } else { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("tuple arg 4 is not a string"); + eDebug("[eEPGCache] tuple arg 4 is not a string"); return NULL; } } @@ -2395,7 +3218,7 @@ PyObject *eEPGCache::search(ePyObject arg) { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("tuple arg 3(%d) is not a known querytype(0, 1, 2)", querytype); + eDebug("[eEPGCache] tuple arg 3(%d) is not a known querytype(0..3)", querytype); return NULL; } } @@ -2403,7 +3226,7 @@ PyObject *eEPGCache::search(ePyObject arg) { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("not enough args in tuple"); + eDebug("[eEPGCache] not enough args in tuple"); return NULL; } } @@ -2411,11 +3234,11 @@ PyObject *eEPGCache::search(ePyObject arg) { PyErr_SetString(PyExc_StandardError, "type error"); - eDebug("arg 0 is not a tuple"); + eDebug("[eEPGCache] arg 0 is not a tuple"); return NULL; } - if (descridx > -1) + if (!descr.empty()) { int maxcount=maxmatches; eServiceReferenceDVB ref(refstr?(const eServiceReferenceDVB&)handleGroup(eServiceReference(refstr)):eServiceReferenceDVB("")); @@ -2432,105 +3255,118 @@ PyObject *eEPGCache::search(ePyObject arg) ++cit; continue; } - ePyObject service_name; - ePyObject service_reference; - timeMap &evmap = cit->second.second; + timeMap &evmap = cit->second.byTime; // check all events for (timeMap::iterator evit(evmap.begin()); evit != evmap.end() && maxcount; ++evit) { - int evid = evit->second->getEventID(); - if ( evid == eventid) - continue; - __u8 *data = evit->second->EITdata; - int tmp = evit->second->ByteSize-10; - __u32 *p = (__u32*)(data+10); + if (querytype == 0) + { + /* ignore the current event, when looking for similar events */ + if (evit->second->getEventID() == eventid) + continue; + } // check if any of our descriptor used by this event - int cnt=-1; - while(tmp>3) + unsigned int cnt = 0; + for (uint8_t i = 0; i < evit->second->n_crc; ++i) { - __u32 crc32 = *p++; - for ( int i=0; i <= descridx; ++i) + uint32_t crc32 = evit->second->crc_list[i]; + for (std::deque::const_iterator it = descr.begin(); + it != descr.end(); ++it) { - if (descr[i] == crc32) // found... + if (*it == crc32) // found... + { ++cnt; + if (querytype) + { + /* we need only one match, when we're not looking for similar broadcasting events */ + i = evit->second->n_crc; + break; + } + } } - tmp-=4; } - if ( (querytype == 0 && cnt == descridx) || - ((querytype == 1 || querytype == 2) && cnt != -1) ) + if ( (querytype == 0 && cnt == descr.size()) || + ((querytype > 0) && cnt != 0) ) { const uniqueEPGKey &service = cit->first; - eServiceReference ref = - eDVBDB::getInstance()->searchReference(service.tsid, service.onid, service.sid); - if (ref.valid()) + std::vector refs; + eDVBDB::getInstance()->searchAllReferences(refs, service.tsid, service.onid, service.sid); + for (unsigned int i = 0; i < refs.size(); i++) { - // create servive event - eServiceEvent ptr; - const eventData *ev_data=0; - if (needServiceEvent) + eServiceReference ref = refs[i]; + if (ref.valid()) { - if (lookupEventId(ref, evid, ev_data)) - eDebug("event not found !!!!!!!!!!!"); - else + ePyObject service_name; + ePyObject service_reference; + // create servive event + eServiceEvent ptr; + const eventData *ev_data=0; + if (needServiceEvent) { - const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref; - Event ev((uint8_t*)ev_data->get()); - ptr.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get()); + if (lookupEventId(ref, evit->second->getEventID(), ev_data)) + eDebug("[eEPGCache] event not found !!!!!!!!!!!"); + else + { + const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref; + Event ev((uint8_t*)ev_data->get()); + ptr.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get()); + } } - } - // create service name - if (must_get_service_name && !service_name) - { - ePtr sptr; - eServiceCenterPtr service_center; - eServiceCenter::getPrivInstance(service_center); - if (service_center) + // create service name + if (must_get_service_name && !service_name) { - service_center->info(ref, sptr); - if (sptr) + ePtr sptr; + eServiceCenterPtr service_center; + eServiceCenter::getPrivInstance(service_center); + if (service_center) { - std::string name; - sptr->getName(ref, name); - - if (must_get_service_name == 1) + service_center->info(ref, sptr); + if (sptr) { - size_t pos; - // filter short name brakets - while((pos = name.find("\xc2\x86")) != std::string::npos) - name.erase(pos,2); - while((pos = name.find("\xc2\x87")) != std::string::npos) - name.erase(pos,2); - } - else - name = buildShortName(name); + std::string name; + sptr->getName(ref, name); - if (name.length()) - service_name = PyString_FromString(name.c_str()); + if (must_get_service_name == 1) + { + size_t pos; + // filter short name brakets + while((pos = name.find("\xc2\x86")) != std::string::npos) + name.erase(pos,2); + while((pos = name.find("\xc2\x87")) != std::string::npos) + name.erase(pos,2); + } + else + name = buildShortName(name); + + if (name.length()) + service_name = PyString_FromString(name.c_str()); + } } + if (!service_name) + service_name = PyString_FromString(""); } - if (!service_name) - service_name = PyString_FromString(""); + // create servicereference string + if (must_get_service_reference && !service_reference) + service_reference = PyString_FromString(ref.toString().c_str()); + // create list + if (!ret) + ret = PyList_New(0); + // create tuple + ePyObject tuple = PyTuple_New(argcount); + // fill tuple + ePyObject tmp = ePyObject(); + fillTuple(tuple, argstring, argcount, service_reference, ev_data ? &ptr : 0, service_name, tmp, evit->second); + PyList_Append(ret, tuple); + Py_DECREF(tuple); + if (service_name) + Py_DECREF(service_name); + if (service_reference) + Py_DECREF(service_reference); + --maxcount; } - // create servicereference string - if (must_get_service_reference && !service_reference) - service_reference = PyString_FromString(ref.toString().c_str()); - // create list - if (!ret) - ret = PyList_New(0); - // create tuple - ePyObject tuple = PyTuple_New(argcount); - // fill tuple - fillTuple2(tuple, argstring, argcount, evit->second, ev_data ? &ptr : 0, service_name, service_reference); - PyList_Append(ret, tuple); - Py_DECREF(tuple); - --maxcount; } } } - if (service_name) - Py_DECREF(service_name); - if (service_reference) - Py_DECREF(service_reference); if (first) { // now start at first service in epgcache database ( only in SIMILAR BROADCASTING SEARCH ) @@ -2576,11 +3412,11 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) switch ((*desc)->getTag()) { case 0xC2: // user defined - if ((*desc)->getLength() == 8) + if ((*desc)->getLength() == 8) { - __u8 buffer[10]; + uint8_t buffer[10]; (*desc)->writeToBuffer(buffer); - if (!strncmp((const char *)buffer+2, "EPGDATA", 7)) + if (!memcmp((const char *)buffer+2, "EPGDATA", 7)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) @@ -2589,7 +3425,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) messages.send(Message(Message::got_mhw2_channel_pid, ref, pid)); } } - else if(!strncmp((const char *)buffer+2, "FICHAS", 6)) + else if(!memcmp((const char *)buffer+2, "FICHAS", 6)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) @@ -2598,7 +3434,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) messages.send(Message(Message::got_mhw2_summary_pid, ref, pid)); } } - else if(!strncmp((const char *)buffer+2, "GENEROS", 7)) + else if(!memcmp((const char *)buffer+2, "GENEROS", 7)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) @@ -2625,7 +3461,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) break; case 0x90: { - UnknownDescriptor *descr = (UnknownDescriptor*)*desc; + Descriptor *descr = (Descriptor*)*desc; int descr_len = descr->getLength(); if (descr_len == 4) { @@ -2657,27 +3493,27 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) } } else - eDebug("PMTready but no pmt!!"); + eDebug("[eEPGCache] PMTready but no pmt!!"); } struct date_time { - __u8 data[5]; + uint8_t data[5]; time_t tm; date_time( const date_time &a ) { memcpy(data, a.data, 5); tm = a.tm; } - date_time( const __u8 data[5]) + date_time( const uint8_t data[5]) { memcpy(this->data, data, 5); - tm = parseDVBtime(data[0], data[1], data[2], data[3], data[4]); + tm = parseDVBtime(data); } date_time() { } - const __u8& operator[](int pos) const + const uint8_t& operator[](int pos) const { return data[pos]; } @@ -2691,42 +3527,44 @@ struct less_datetime } }; -void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __u8 *data) +void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const uint8_t *data) { contentMap &content_time_table = content_time_tables[current_service]; singleLock s(cache_lock); std::map< date_time, std::list, less_datetime > start_times; - eventMap &evMap = eventDB[current_service].first; - timeMap &tmMap = eventDB[current_service].second; - int ptr=8; + EventCacheItem &eventDBitem = eventDB[current_service]; + eventMap &evMap = eventDBitem.byEvent; + timeMap &tmMap = eventDBitem.byTime; + int ptr = 8; int content_id = data[ptr++] << 24; content_id |= data[ptr++] << 16; content_id |= data[ptr++] << 8; content_id |= data[ptr++]; - contentTimeMap &time_event_map = - content_time_table[content_id]; + contentTimeMap &time_event_map = content_time_table[content_id]; for ( contentTimeMap::iterator it( time_event_map.begin() ); it != time_event_map.end(); ++it ) { eventMap::iterator evIt( evMap.find(it->second.second) ); if ( evIt != evMap.end() ) { + // time_event_map can have other timestamp -> get timestamp from eventData + time_t ev_time = evIt->second->getStartTime(); delete evIt->second; evMap.erase(evIt); + tmMap.erase(ev_time); } - tmMap.erase(it->second.first); } time_event_map.clear(); - __u8 duration[3]; + uint8_t duration[3]; memcpy(duration, data+ptr, 3); ptr+=3; int duration_sec = fromBCD(duration[0])*3600+fromBCD(duration[1])*60+fromBCD(duration[2]); - const __u8 *descriptors[65]; - const __u8 **pdescr = descriptors; + const uint8_t *descriptors[65]; + const uint8_t **pdescr = descriptors; int descriptors_length = (data[ptr++]&0x0F) << 8; descriptors_length |= data[ptr++]; @@ -2768,7 +3606,7 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ descr_len -= 6; while( descr_len > 2 ) { - __u8 datetime[5]; + uint8_t datetime[5]; datetime[0] = data[ptr++]; datetime[1] = data[ptr++]; int tmp_len = data[ptr++]; @@ -2795,13 +3633,13 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ } } ASSERT(pdescr <= &descriptors[65]); - __u8 event[4098]; + uint8_t event[4098]; eit_event_struct *ev_struct = (eit_event_struct*) event; ev_struct->running_status = 0; ev_struct->free_CA_mode = 1; memcpy(event+7, duration, 3); ptr = 12; - const __u8 **d=descriptors; + const uint8_t **d=descriptors; while ( d < pdescr ) { memcpy(event+ptr, *d, ((*d)[1])+2); @@ -2820,7 +3658,7 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ for (std::list::iterator i(it->second.begin()); i != it->second.end(); ++i) { event[bptr++] = 0x4A; - __u8 *len = event+(bptr++); + uint8_t *len = event+(bptr++); event[bptr++] = (i->tsid & 0xFF00) >> 8; event[bptr++] = (i->tsid & 0xFF); event[bptr++] = (i->onid & 0xFF00) >> 8; @@ -2839,12 +3677,12 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ while( tmMap.find(stime) != tmMap.end() ) ++stime; event[6] += (stime - it->first.tm); - __u16 event_id = 0; + uint16_t event_id = 0; while( evMap.find(event_id) != evMap.end() ) ++event_id; event[0] = (event_id & 0xFF00) >> 8; event[1] = (event_id & 0xFF); - time_event_map[it->first.tm]=std::pair(stime, event_id); + time_event_map[it->first.tm]=std::pair(stime, event_id); eventData *d = new eventData( ev_struct, bptr, PRIVATE ); evMap[event_id] = d; tmMap[stime] = d; @@ -2860,7 +3698,7 @@ void eEPGCache::channel_data::startPrivateReader() mask.flags = eDVBSectionFilterMask::rfCRC; mask.data[0] = 0xA0; mask.mask[0] = 0xFF; - eDebug("[EPGC] start privatefilter for pid %04x and version %d", m_PrivatePid, m_PrevVersion); + eDebug("[eEPGCache] start privatefilter for pid %04x and version %d", m_PrivatePid, m_PrevVersion); if (m_PrevVersion != -1) { mask.data[3] = m_PrevVersion << 1; @@ -2873,7 +3711,7 @@ void eEPGCache::channel_data::startPrivateReader() m_PrivateReader->start(mask); } -void eEPGCache::channel_data::readPrivateData( const __u8 *data) +void eEPGCache::channel_data::readPrivateData( const uint8_t *data) { if ( seenPrivateSections.find(data[6]) == seenPrivateSections.end() ) { @@ -2882,7 +3720,7 @@ void eEPGCache::channel_data::readPrivateData( const __u8 *data) } if ( seenPrivateSections.size() == (unsigned int)(data[7] + 1) ) { - eDebug("[EPGC] private finished"); + eDebug("[eEPGCache] private finished"); eDVBChannelID chid = channel->getChannelID(); int tmp = chid.original_network_id.get(); tmp |= 0x80000000; // we use highest bit as private epg indicator @@ -2896,15 +3734,16 @@ void eEPGCache::channel_data::readPrivateData( const __u8 *data) #endif // ENABLE_PRIVATE_EPG #ifdef ENABLE_MHW_EPG -void eEPGCache::channel_data::cleanup() +void eEPGCache::channel_data::cleanupMHW() { + m_MHWTimeoutTimer->stop(); m_channels.clear(); m_themes.clear(); m_titles.clear(); m_program_ids.clear(); } -__u8 *eEPGCache::channel_data::delimitName( __u8 *in, __u8 *out, int len_in ) +uint8_t *eEPGCache::channel_data::delimitName( uint8_t *in, uint8_t *out, int len_in ) { // Names in mhw structs are not strings as they are not '\0' terminated. // This function converts the mhw name into a string. @@ -2939,7 +3778,7 @@ void eEPGCache::channel_data::timeMHW2DVB( u_char day, u_char hours, u_char minu { char tz_saved[1024]; // Remove offset in mhw time. - __u8 local_hours = hours; + uint8_t local_hours = hours; if ( hours >= 16 ) local_hours -= 4; else if ( hours >= 8 ) @@ -2952,7 +3791,7 @@ void eEPGCache::channel_data::timeMHW2DVB( u_char day, u_char hours, u_char minu char *old_tz = getenv( "TZ" ); if (old_tz) strcpy(tz_saved, old_tz); - putenv("TZ=CET-1CEST,M3.5.0/2,M10.5.0/3"); + putenv((char*)"TZ=CET-1CEST,M3.5.0/2,M10.5.0/3"); tzset(); tm localnow; @@ -2990,10 +3829,10 @@ void eEPGCache::channel_data::timeMHW2DVB( u_char day, u_char hours, u_char minu timeMHW2DVB( recdate.tm_hour, minutes, return_time+2 ); } -void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator itTitle, std::string sumText, const __u8 *data) +void eEPGCache::channel_data::storeMHWTitle(std::map::iterator itTitle, std::string sumText, const uint8_t *data) // data is borrowed from calling proc to save memory space. { - __u8 name[34]; + uint8_t name[34]; // For each title a separate EIT packet will be sent to eEPGCache::sectionRead() bool isMHW2 = itTitle->second.mhw2_mjd_hi || itTitle->second.mhw2_mjd_lo || @@ -3016,7 +3855,7 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment packet->segment_last_table_id = 0x50; - __u8 *title = isMHW2 ? ((__u8*)(itTitle->second.title))-4 : (__u8*)itTitle->second.title; + uint8_t *title = isMHW2 ? ((uint8_t*)(itTitle->second.title))-4 : (uint8_t*)itTitle->second.title; std::string prog_title = (char *) delimitName( title, name, isMHW2 ? 35 : 23 ); int prog_title_length = prog_title.length(); @@ -3035,13 +3874,13 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator data[4] = itTitle->second.mhw2_hours; data[5] = itTitle->second.mhw2_minutes; data[6] = itTitle->second.mhw2_seconds; - timeMHW2DVB( HILO(itTitle->second.mhw2_duration), data+7 ); + timeMHW2DVB( itTitle->second.getMhw2Duration(), data+7 ); } else { timeMHW2DVB( itTitle->second.dh.day, itTitle->second.dh.hours, itTitle->second.ms.minutes, (u_char *) event_data + 2 ); - timeMHW2DVB( HILO(itTitle->second.duration), (u_char *) event_data+7 ); + timeMHW2DVB( itTitle->second.getDuration(), (u_char *) event_data+7 ); } event_data->running_status = 0; @@ -3136,21 +3975,21 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator cache->sectionRead( data, MHW, this ); } -void eEPGCache::channel_data::startTimeout(int msec) +void eEPGCache::channel_data::startMHWTimeout(int msec) { m_MHWTimeoutTimer->start(msec,true); m_MHWTimeoutet=false; } -void eEPGCache::channel_data::startMHWReader(__u16 pid, __u8 tid) +void eEPGCache::channel_data::startMHWReader(uint16_t pid, uint8_t tid) { m_MHWFilterMask.pid = pid; m_MHWFilterMask.data[0] = tid; m_MHWReader->start(m_MHWFilterMask); -// eDebug("start 0x%02x 0x%02x", pid, tid); +// eDebug("[eEPGCache] start 0x%02x 0x%02x", pid, tid); } -void eEPGCache::channel_data::startMHWReader2(__u16 pid, __u8 tid, int ext) +void eEPGCache::channel_data::startMHWReader2(uint16_t pid, uint8_t tid, int ext) { m_MHWFilterMask2.pid = pid; m_MHWFilterMask2.data[0] = tid; @@ -3158,18 +3997,18 @@ void eEPGCache::channel_data::startMHWReader2(__u16 pid, __u8 tid, int ext) { m_MHWFilterMask2.data[1] = ext; m_MHWFilterMask2.mask[1] = 0xFF; -// eDebug("start 0x%03x 0x%02x 0x%02x", pid, tid, ext); +// eDebug("[eEPGCache] start 0x%03x 0x%02x 0x%02x", pid, tid, ext); } else { m_MHWFilterMask2.data[1] = 0; m_MHWFilterMask2.mask[1] = 0; -// eDebug("start 0x%02x 0x%02x", pid, tid); +// eDebug("[eEPGCache] start 0x%02x 0x%02x", pid, tid); } m_MHWReader2->start(m_MHWFilterMask2); } -void eEPGCache::channel_data::readMHWData(const __u8 *data) +void eEPGCache::channel_data::readMHWData(const uint8_t *data) { if ( m_MHWReader2 ) m_MHWReader2->stop(); @@ -3178,7 +4017,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) // have si data.. so we dont read mhw data (haveData & (SCHEDULE|SCHEDULE_OTHER|VIASAT)) ) { - eDebug("[EPGC] mhw aborted %d", state); + eDebug("[eEPGCache] mhw aborted %d", state); } else if (m_MHWFilterMask.pid == 0xD3 && m_MHWFilterMask.data[0] == 0x91) // Channels table @@ -3195,7 +4034,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) } haveData |= MHW; - eDebug("[EPGC] mhw %d channels found", m_channels.size()); + eDebug("[eEPGCache] mhw %d channels found", m_channels.size()); // Channels table has been read, start reading the themes table. startMHWReader(0xD3, 0x92); @@ -3208,9 +4047,9 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) int record_size = sizeof( mhw_theme_name_t ); int nbr_records = int (len/record_size); int idx_ptr = 0; - __u8 next_idx = (__u8) *(data + 3 + idx_ptr); - __u8 idx = 0; - __u8 sub_idx = 0; + uint8_t next_idx = (uint8_t) *(data + 3 + idx_ptr); + uint8_t idx = 0; + uint8_t sub_idx = 0; for ( int i = 0; i < nbr_records; i++ ) { mhw_theme_name_t *theme = (mhw_theme_name_t*) &data[19 + i*record_size]; @@ -3218,7 +4057,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) { idx = (idx_ptr<<4); idx_ptr++; - next_idx = (__u8) *(data + 3 + idx_ptr); + next_idx = (uint8_t) *(data + 3 + idx_ptr); sub_idx = 0; } else @@ -3226,30 +4065,32 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) m_themes[idx+sub_idx] = *theme; } - eDebug("[EPGC] mhw %d themes found", m_themes.size()); + eDebug("[eEPGCache] mhw %d themes found", m_themes.size()); // Themes table has been read, start reading the titles table. startMHWReader(0xD2, 0x90); - startTimeout(4000); + startMHWTimeout(4000); return; } else if (m_MHWFilterMask.pid == 0xD2 && m_MHWFilterMask.data[0] == 0x90) // Titles table { mhw_title_t *title = (mhw_title_t*) data; + uint8_t name[24]; + std::string prog_title = (char *) delimitName( title->title, name, 23 ); - if ( title->channel_id == 0xFF ) // Separator + if ( title->channel_id == 0xFF || prog_title.substr(0,7) == "BIENTOT" ) // Separator or BIENTOT record return; // Continue reading of the current table. else { // Create unique key per title - __u32 title_id = ((title->channel_id)<<16)|((title->dh.day)<<13)|((title->dh.hours)<<8)| + uint32_t title_id = ((title->channel_id)<<16)|((title->dh.day)<<13)|((title->dh.hours)<<8)| (title->ms.minutes); - __u32 program_id = ((title->program_id_hi)<<24)|((title->program_id_mh)<<16)| + uint32_t program_id = ((title->program_id_hi)<<24)|((title->program_id_mh)<<16)| ((title->program_id_ml)<<8)|(title->program_id_lo); if ( m_titles.find( title_id ) == m_titles.end() ) { - startTimeout(4000); + startMHWTimeout(4000); title->mhw2_mjd_hi = 0; title->mhw2_mjd_lo = 0; title->mhw2_duration_hi = 0; @@ -3257,10 +4098,10 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) m_titles[ title_id ] = *title; if ( (title->ms.summary_available) && (m_program_ids.find(program_id) == m_program_ids.end()) ) // program_ids will be used to gather summaries. - m_program_ids.insert(std::pair<__u32,__u32>(program_id,title_id)); + m_program_ids.insert(std::pair(program_id,title_id)); return; // Continue reading of the current table. } - else if (!checkTimeout()) + else if (!checkMHWTimeout()) return; } if ( !m_program_ids.empty()) @@ -3268,10 +4109,10 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) // Titles table has been read, there are summaries to read. // Start reading summaries, store corresponding titles on the fly. startMHWReader(0xD3, 0x90); - eDebug("[EPGC] mhw %d titles(%d with summary) found", + eDebug("[eEPGCache] mhw %d titles(%d with summary) found", m_titles.size(), m_program_ids.size()); - startTimeout(4000); + startMHWTimeout(4000); return; } } @@ -3281,20 +4122,20 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) mhw_summary_t *summary = (mhw_summary_t*) data; // Create unique key per record - __u32 program_id = ((summary->program_id_hi)<<24)|((summary->program_id_mh)<<16)| + uint32_t program_id = ((summary->program_id_hi)<<24)|((summary->program_id_mh)<<16)| ((summary->program_id_ml)<<8)|(summary->program_id_lo); int len = ((data[1]&0xf)<<8) + data[2]; - // ugly workaround to convert const __u8* to char* + // ugly workaround to convert const uint8_t* to char* char *tmp=0; memcpy(&tmp, &data, sizeof(void*)); tmp[len+3] = 0; // Terminate as a string. - std::multimap<__u32, __u32>::iterator itProgid( m_program_ids.find( program_id ) ); + std::multimap::iterator itProgid( m_program_ids.find( program_id ) ); if ( itProgid == m_program_ids.end() ) { /* This part is to prevent to looping forever if some summaries are not received yet. There is a timeout of 4 sec. after the last successfully read summary. */ - if (!m_program_ids.empty() && !checkTimeout()) + if (!m_program_ids.empty() && !checkMHWTimeout()) return; // Continue reading of the current table. } else @@ -3306,11 +4147,11 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) the_text.replace(pos, 2, " "); // Find corresponding title, store title and summary in epgcache. - std::map<__u32, mhw_title_t>::iterator itTitle( m_titles.find( itProgid->second ) ); + std::map::iterator itTitle( m_titles.find( itProgid->second ) ); if ( itTitle != m_titles.end() ) { - startTimeout(4000); - storeTitle( itTitle, the_text, data ); + startMHWTimeout(4000); + storeMHWTitle( itTitle, the_text, data ); m_titles.erase( itTitle ); } m_program_ids.erase( itProgid ); @@ -3318,13 +4159,13 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) return; // Continue reading of the current table. } } - eDebug("[EPGC] mhw finished(%ld) %d summaries not found", + eDebug("[eEPGCache] mhw finished(%ld) %d summaries not found", ::time(0), m_program_ids.size()); // Summaries have been read, titles that have summaries have been stored. // Now store titles that do not have summaries. - for (std::map<__u32, mhw_title_t>::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) - storeTitle( itTitle, "", data ); + for (std::map::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) + storeMHWTitle( itTitle, "", data ); isRunning &= ~MHW; m_MHWConn=0; if ( m_MHWReader ) @@ -3333,7 +4174,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) finishEPG(); } -void eEPGCache::channel_data::readMHWData2(const __u8 *data) +void eEPGCache::channel_data::readMHWData2(const uint8_t *data) { int dataLen = (((data[1]&0xf) << 8) | data[2]) + 3; @@ -3344,7 +4185,7 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) // have si data.. so we dont read mhw data (haveData & (SCHEDULE|SCHEDULE_OTHER|VIASAT)) ) { - eDebug("[EPGC] mhw2 aborted %d", state); + eDebug("[eEPGCache] mhw2 aborted %d", state); } else if (m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 0) // Channels table @@ -3369,7 +4210,7 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) else goto abort; // data seems consistent... - const __u8 *tmp = data+121; + const uint8_t *tmp = data+121; for (int i=0; i < num_channels; ++i) { mhw_channel_name_t channel; @@ -3380,7 +4221,7 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) channel.channel_id_hi = *(tmp++); channel.channel_id_lo = *(tmp++); m_channels[i]=channel; -// eDebug("%d(%02x) %04x: %02x %02x", i, i, (channel.channel_id_hi << 8) | channel.channel_id_lo, *tmp, *(tmp+1)); +// eDebug("[eEPGCache] %d(%02x) %04x: %02x %02x", i, i, (channel.channel_id_hi << 8) | channel.channel_id_lo, *tmp, *(tmp+1)); tmp+=2; } for (int i=0; i < num_channels; ++i) @@ -3391,15 +4232,15 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) for (; x < channel_name_len; ++x) channel.name[x]=*(tmp++); channel.name[x+1]=0; -// eDebug("%d(%02x) %s", i, i, channel.name); +// eDebug("[eEPGCache] %d(%02x) %s", i, i, channel.name); } haveData |= MHW; - eDebug("[EPGC] mhw2 %d channels found", m_channels.size()); + eDebug("[eEPGCache] mhw2 %d channels found", m_channels.size()); } else if (m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 1) { // Themes table - eDebug("[EPGC] mhw2 themes nyi"); + eDebug("[eEPGCache] mhw2 themes nyi"); } else if (m_MHWFilterMask2.pid == m_mhw2_title_pid && m_MHWFilterMask2.data[0] == 0xe6) // Titles table @@ -3408,7 +4249,7 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) bool valid=false; bool finish=false; -// eDebug("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", +// eDebug("[eEPGCache] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", // data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], // data[11], data[12], data[13], data[14], data[15], data[16], data[17] ); @@ -3423,11 +4264,11 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) if (!valid) { if (dataLen > 18) - eDebug("mhw2 title table invalid!!"); - if (checkTimeout()) + eDebug("[eEPGCache] mhw2 title table invalid!!"); + if (checkMHWTimeout()) goto abort; if (!m_MHWTimeoutTimer->isActive()) - startTimeout(5000); + startMHWTimeout(5000); return; // continue reading } @@ -3436,7 +4277,7 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) pos = 18; while (pos < dataLen) { -// eDebugNoNewLine(" [%02x] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x [%02x %02x %02x %02x %02x %02x %02x] LL - DESCR - ", +// eDebugNoNewLine("[eEPGCache] [%02x] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x [%02x %02x %02x %02x %02x %02x %02x] LL - DESCR - ", // data[pos], data[pos+1], data[pos+2], data[pos+3], data[pos+4], data[pos+5], data[pos+6], data[pos+7], // data[pos+8], data[pos+9], data[pos+10], data[pos+11], data[pos+12], data[pos+13], data[pos+14], data[pos+15], data[pos+16], data[pos+17]); title.channel_id = data[pos]+1; @@ -3450,37 +4291,37 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) title.mhw2_duration_lo = duration&0xFF; // Create unique key per title - __u32 title_id = (data[pos+7] << 24) | (data[pos+8] << 16) | (data[pos+9] << 8) | data[pos+10]; + uint32_t title_id = (data[pos+7] << 24) | (data[pos+8] << 16) | (data[pos+9] << 8) | data[pos+10]; - __u8 slen = data[pos+18] & 0x3f; - __u8 *dest = ((__u8*)title.title)-4; + uint8_t slen = data[pos+18] & 0x3f; + uint8_t *dest = ((uint8_t*)title.title)-4; memcpy(dest, &data[pos+19], slen>35 ? 35 : slen); memset(dest+slen, 0, 35-slen); pos += 19 + slen; // eDebug("%02x [%02x %02x]: %s", data[pos], data[pos+1], data[pos+2], dest); // not used theme id (data[7] & 0x3f) + (data[pos] & 0x3f); - __u32 summary_id = (data[pos+1] << 8) | data[pos+2]; + uint32_t summary_id = (data[pos+1] << 8) | data[pos+2]; // if (title.channel_id > m_channels.size()) -// eDebug("channel_id(%d %02x) to big!!", title.channel_id); +// eDebug("[eEPGCache] channel_id(%d %02x) to big!!", title.channel_id); -// eDebug("pos %d prog_id %02x %02x chid %02x summary_id %04x dest %p len %d\n", +// eDebug("[eEPGCache] pos %d prog_id %02x %02x chid %02x summary_id %04x dest %p len %d\n", // pos, title.program_id_ml, title.program_id_lo, title.channel_id, summary_id, dest, slen); -// eDebug("title_id %08x -> summary_id %04x\n", title_id, summary_id); +// eDebug("[eEPGCache] title_id %08x -> summary_id %04x\n", title_id, summary_id); pos += 3; - std::map<__u32, mhw_title_t>::iterator it = m_titles.find( title_id ); + std::map::iterator it = m_titles.find( title_id ); if ( it == m_titles.end() ) { - startTimeout(5000); + startMHWTimeout(5000); m_titles[ title_id ] = title; if (summary_id != 0xFFFF) { bool add=true; - std::multimap<__u32, __u32>::iterator it(m_program_ids.lower_bound(summary_id)); + std::multimap::iterator it(m_program_ids.lower_bound(summary_id)); while (it != m_program_ids.end() && it->first == summary_id) { if (it->second == title_id) { @@ -3490,27 +4331,26 @@ void eEPGCache::channel_data::readMHWData2(const __u8 *data) ++it; } if (add) - m_program_ids.insert(std::pair<__u32,__u32>(summary_id,title_id)); + m_program_ids.insert(std::pair(summary_id,title_id)); } } else { - if ( !checkTimeout() ) + if ( !checkMHWTimeout() ) continue; // Continue reading of the current table. finish=true; break; } } -start_summary: if (finish) { - eDebug("[EPGC] mhw2 %d titles(%d with summary) found", m_titles.size(), m_program_ids.size()); + eDebug("[eEPGCache] mhw2 %d titles(%d with summary) found", m_titles.size(), m_program_ids.size()); if (!m_program_ids.empty()) { // Titles table has been read, there are summaries to read. // Start reading summaries, store corresponding titles on the fly. startMHWReader2(m_mhw2_summary_pid, 0x96); - startTimeout(15000); + startMHWTimeout(15000); return; } } @@ -3520,7 +4360,7 @@ start_summary: else if (m_MHWFilterMask2.pid == m_mhw2_summary_pid && m_MHWFilterMask2.data[0] == 0x96) // Summaries table { - if (!checkTimeout()) + if (!checkMHWTimeout()) { int len, loop, pos, lenline; bool valid; @@ -3555,11 +4395,11 @@ start_summary: if (valid) { // data seems consistent... - __u32 summary_id = (data[3]<<8)|data[4]; -// eDebug ("summary id %04x\n", summary_id); -// eDebug("[%02x %02x] %02x %02x %02x %02x %02x %02x %02x %02x XX\n", data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13] ); + uint32_t summary_id = (data[3]<<8)|data[4]; +// eDebug ("[eEPGCache] summary id %04x\n", summary_id); +// eDebug("[eEPGCache] [%02x %02x] %02x %02x %02x %02x %02x %02x %02x %02x XX\n", data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13] ); - // ugly workaround to convert const __u8* to char* + // ugly workaround to convert const uint8_t* to char* char *tmp=0; memcpy(&tmp, &data, sizeof(void*)); @@ -3579,7 +4419,7 @@ start_summary: else tmp[pos+1] = 0; - std::multimap<__u32, __u32>::iterator itProgId( m_program_ids.lower_bound(summary_id) ); + std::multimap::iterator itProgId( m_program_ids.lower_bound(summary_id) ); if ( itProgId == m_program_ids.end() || itProgId->first != summary_id) { /* This part is to prevent to looping forever if some summaries are not received yet. There is a timeout of 4 sec. after the last successfully read summary. */ @@ -3588,19 +4428,19 @@ start_summary: } else { - startTimeout(15000); + startMHWTimeout(15000); std::string the_text = (char *) (data + pos + 1); -// eDebug ("summary id %04x : %s\n", summary_id, data+pos+1); +// eDebug ("[eEPGCache] summary id %04x : %s\n", summary_id, data+pos+1); while( itProgId != m_program_ids.end() && itProgId->first == summary_id ) { -// eDebug("."); +// eDebug("[eEPGCache] ."); // Find corresponding title, store title and summary in epgcache. - std::map<__u32, mhw_title_t>::iterator itTitle( m_titles.find( itProgId->second ) ); + std::map::iterator itTitle( m_titles.find( itProgId->second ) ); if ( itTitle != m_titles.end() ) { - storeTitle( itTitle, the_text, data ); + storeMHWTitle( itTitle, the_text, data ); m_titles.erase( itTitle ); } m_program_ids.erase( itProgId++ ); @@ -3631,9 +4471,9 @@ start_summary: { // Summaries have been read, titles that have summaries have been stored. // Now store titles that do not have summaries. - for (std::map<__u32, mhw_title_t>::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) - storeTitle( itTitle, "", data ); - eDebug("[EPGC] mhw2 finished(%ld) %d summaries not found", + for (std::map::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) + storeMHWTitle( itTitle, "", data ); + eDebug("[eEPGCache] mhw2 finished(%ld) %d summaries not found", ::time(0), m_program_ids.size()); } diff --git a/lib/dvb/epgcache.h b/lib/dvb/epgcache.h index 90aff6c..2b47977 100644 --- a/lib/dvb/epgcache.h +++ b/lib/dvb/epgcache.h @@ -2,32 +2,20 @@ #define __epgcache_h_ #define ENABLE_PRIVATE_EPG 1 -//#define ENABLE_MHW_EPG 1 +#define ENABLE_MHW_EPG 1 +#define ENABLE_FREESAT 1 +#define ENABLE_NETMED 1 +#define ENABLE_VIRGIN 1 #ifndef SWIG -/* Test for gcc >= maj.min, as per __GNUC_PREREQ in glibc */ -#if defined (__GNUC__) && defined (__GNUC_MINOR__) -#define __GNUC_PREREQ(maj, min) \ - ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) -#else -#define __GNUC_PREREQ(maj, min) 0 -#endif - #include #include -#if 0 && __GNUC_PREREQ(4,3) -#include -#include -#else -#include -#include -#endif +#include #include #include -#include #ifdef ENABLE_MHW_EPG #include #endif @@ -40,12 +28,6 @@ #include #include -#define CLEAN_INTERVAL 60000 // 1 min -#define UPDATE_INTERVAL 3600000 // 60 min -#define ZAP_DELAY 2000 // 2 sek - -#define HILO(x) (x##_hi << 8 | x##_lo) - class eventData; class eServiceReferenceDVB; class eDVBServicePMTHandler; @@ -69,32 +51,39 @@ struct uniqueEPGKey } bool operator <(const uniqueEPGKey &a) const { - return memcmp( &sid, &a.sid, sizeof(int)*3)<0; + if (sid < a.sid) + return true; + if (sid != a.sid) + return false; + if (onid < a.onid) + return true; + if (onid != a.onid) + return false; + return (tsid < a.tsid); } operator bool() const - { - return !(sid == -1 && onid == -1 && tsid == -1); + { + return !(sid == -1 && onid == -1 && tsid == -1); } bool operator==(const uniqueEPGKey &a) const { - return !memcmp( &sid, &a.sid, sizeof(int)*3); + return (tsid == a.tsid) && (onid == a.onid) && (sid == a.sid); } struct equal { bool operator()(const uniqueEPGKey &a, const uniqueEPGKey &b) const { - return !memcmp( &a.sid, &b.sid, sizeof(int)*3); + return (a.tsid == b.tsid) && (a.onid == b.onid) && (a.sid == b.sid); } }; }; //eventMap is sorted by event_id -#define eventMap std::map<__u16, eventData*> +typedef std::map eventMap; //timeMap is sorted by beginTime -#define timeMap std::map +typedef std::map timeMap; -#define channelMapIterator std::map::iterator -#define updateMap std::map +typedef std::map updateMap; struct hash_uniqueEPGKey { @@ -104,65 +93,38 @@ struct hash_uniqueEPGKey } }; -#define tidMap std::set<__u32> -#if 0 && __GNUC_PREREQ(4,3) - #define eventCache std::unordered_map, hash_uniqueEPGKey, uniqueEPGKey::equal> - #ifdef ENABLE_PRIVATE_EPG - #define contentTimeMap std::unordered_map > - #define contentMap std::unordered_map - #define contentMaps std::unordered_map - #endif -#elif __GNUC_PREREQ(3,1) - #define eventCache __gnu_cxx::hash_map, hash_uniqueEPGKey, uniqueEPGKey::equal> - #ifdef ENABLE_PRIVATE_EPG - #define contentTimeMap __gnu_cxx::hash_map > - #define contentMap __gnu_cxx::hash_map - #define contentMaps __gnu_cxx::hash_map - #endif -#else // for older gcc use following - #define eventCache std::hash_map, hash_uniqueEPGKey, uniqueEPGKey::equal > - #ifdef ENABLE_PRIVATE_EPG - #define contentTimeMap std::hash_map > - #define contentMap std::hash_map - #define contentMaps std::hash_map - #endif +struct EventCacheItem { + eventMap byEvent; + timeMap byTime; +}; + +typedef std::set tidMap; + +typedef std::tr1::unordered_map eventCache; +#ifdef ENABLE_PRIVATE_EPG + typedef std::tr1::unordered_map > contentTimeMap; + typedef std::tr1::unordered_map contentMap; + typedef std::tr1::unordered_map contentMaps; #endif -#define descriptorPair std::pair -#define descriptorMap std::map<__u32, descriptorPair > +#endif -class eventData +#ifdef ENABLE_FREESAT +#include +class freesatEITSubtableStatus { - friend class eEPGCache; private: - __u8* EITdata; - __u8 ByteSize; - __u8 type; - static descriptorMap descriptors; - static __u8 data[4108]; - static int CacheSize; - static void load(FILE *); - static void save(FILE *); + u_char version; + uint16_t sectionMap[32]; + void initMap(uint8_t maxSection); + public: - eventData(const eit_event_struct* e=NULL, int size=0, int type=0); - ~eventData(); - const eit_event_struct* get() const; - operator const eit_event_struct*() const - { - return get(); - } - int getEventID() - { - return (EITdata[0] << 8) | EITdata[1]; - } - time_t getStartTime() - { - return parseDVBtime(EITdata[2], EITdata[3], EITdata[4], EITdata[5], EITdata[6]); - } - int getDuration() - { - return fromBCD(EITdata[7])*3600+fromBCD(EITdata[8])*60+fromBCD(EITdata[9]); - } + freesatEITSubtableStatus(u_char version, uint8_t maxSection); + bool isSectionPresent(uint8_t sectionNo); + void seen(uint8_t sectionNo, uint8_t maxSegmentSection); + bool isVersionChanged(u_char testVersion); + void updateVersion(u_char newVersion, uint8_t maxSection); + bool isCompleted(); }; #endif @@ -178,11 +140,27 @@ class eEPGCache: public eMainloop, private eThread, public Object ePtr abortTimer, zapTimer; int prevChannelState; int state; - __u8 isRunning, haveData; + unsigned int isRunning, haveData; ePtr channel; ePtr m_stateChangedConn, m_NowNextConn, m_ScheduleConn, m_ScheduleOtherConn, m_ViasatConn; ePtr m_NowNextReader, m_ScheduleReader, m_ScheduleOtherReader, m_ViasatReader; tidMap seenSections[4], calcedSections[4]; +#ifdef ENABLE_VIRGIN + ePtr m_VirginNowNextConn, m_VirginScheduleConn; + ePtr m_VirginNowNextReader, m_VirginScheduleReader; +#endif +#ifdef ENABLE_NETMED + ePtr m_NetmedScheduleConn, m_NetmedScheduleOtherConn; + ePtr m_NetmedScheduleReader, m_NetmedScheduleOtherReader; +#endif +#ifdef ENABLE_FREESAT + ePtr m_FreeSatScheduleOtherConn, m_FreeSatScheduleOtherConn2; + ePtr m_FreeSatScheduleOtherReader, m_FreeSatScheduleOtherReader2; + std::map m_FreeSatSubTableStatus; + uint32_t m_FreesatTablesToComplete; + void readFreeSatScheduleOtherData(const uint8_t *data); + void cleanupFreeSat(); +#endif #ifdef ENABLE_PRIVATE_EPG ePtr startPrivateTimer; int m_PrevVersion; @@ -190,51 +168,44 @@ class eEPGCache: public eMainloop, private eThread, public Object uniqueEPGKey m_PrivateService; ePtr m_PrivateConn; ePtr m_PrivateReader; - std::set<__u8> seenPrivateSections; - void readPrivateData(const __u8 *data); + std::set seenPrivateSections; + void readPrivateData(const uint8_t *data); void startPrivateReader(); #endif #ifdef ENABLE_MHW_EPG std::vector m_channels; - std::map<__u8, mhw_theme_name_t> m_themes; - std::map<__u32, mhw_title_t> m_titles; - std::multimap<__u32, __u32> m_program_ids; + std::map m_themes; + std::map m_titles; + std::multimap m_program_ids; ePtr m_MHWConn, m_MHWConn2; ePtr m_MHWReader, m_MHWReader2; eDVBSectionFilterMask m_MHWFilterMask, m_MHWFilterMask2; ePtr m_MHWTimeoutTimer; - __u16 m_mhw2_channel_pid, m_mhw2_title_pid, m_mhw2_summary_pid; + uint16_t m_mhw2_channel_pid, m_mhw2_title_pid, m_mhw2_summary_pid; bool m_MHWTimeoutet; void MHWTimeout() { m_MHWTimeoutet=true; } - void readMHWData(const __u8 *data); - void readMHWData2(const __u8 *data); - void startMHWReader(__u16 pid, __u8 tid); - void startMHWReader2(__u16 pid, __u8 tid, int ext=-1); - void startTimeout(int msek); - bool checkTimeout() { return m_MHWTimeoutet; } - void cleanup(); - __u8 *delimitName( __u8 *in, __u8 *out, int len_in ); + void readMHWData(const uint8_t *data); + void readMHWData2(const uint8_t *data); + void startMHWReader(uint16_t pid, uint8_t tid); + void startMHWReader2(uint16_t pid, uint8_t tid, int ext=-1); + void startMHWTimeout(int msek); + bool checkMHWTimeout() { return m_MHWTimeoutet; } + void cleanupMHW(); + uint8_t *delimitName( uint8_t *in, uint8_t *out, int len_in ); void timeMHW2DVB( u_char hours, u_char minutes, u_char *return_time); void timeMHW2DVB( int minutes, u_char *return_time); void timeMHW2DVB( u_char day, u_char hours, u_char minutes, u_char *return_time); - void storeTitle(std::map<__u32, mhw_title_t>::iterator itTitle, std::string sumText, const __u8 *data); + void storeMHWTitle(std::map::iterator itTitle, std::string sumText, const uint8_t *data); #endif - void readData(const __u8 *data); - void readDataViasat(const __u8 *data); + void readData(const uint8_t *data, int source); void startChannel(); void startEPG(); - bool finishEPG(); + void finishEPG(); void abortEPG(); void abortNonAvail(); }; - bool FixOverlapping(std::pair &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service); + bool FixOverlapping(EventCacheItem &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service); public: - enum {PRIVATE=0, NOWNEXT=1, SCHEDULE=2, SCHEDULE_OTHER=4 -#ifdef ENABLE_MHW_EPG - ,MHW=8 -#endif - ,VIASAT=16 - }; struct Message { enum @@ -274,15 +245,22 @@ public: eFixedMessagePump messages; private: friend class channel_data; + friend class eventData; static eEPGCache *instance; + typedef std::map ChannelMap; + ePtr cleanTimer; - std::map m_knownChannels; + ChannelMap m_knownChannels; ePtr m_chanAddedConn; + unsigned int enabledSources; + unsigned int historySeconds; + eventCache eventDB; updateMap channelLastUpdated; - static pthread_mutex_t cache_lock, channel_map_lock; + std::string m_filename; + bool m_running; #ifdef ENABLE_PRIVATE_EPG contentMaps content_time_tables; @@ -290,21 +268,15 @@ private: void thread(); // thread function -// called from epgcache thread - int m_running; - char m_filename[1024]; - void save(); - void load(); #ifdef ENABLE_PRIVATE_EPG - void privateSectionRead(const uniqueEPGKey &, const __u8 *); + void privateSectionRead(const uniqueEPGKey &, const uint8_t *); #endif - void sectionRead(const __u8 *data, int source, channel_data *channel); + void sectionRead(const uint8_t *data, int source, channel_data *channel); void gotMessage(const Message &message); - void flushEPG(const uniqueEPGKey & s=uniqueEPGKey()); void cleanLoop(); + void submitEventData(const std::vector& sids, const std::vector& chids, long start, long duration, const char* title, const char* short_summary, const char* long_description, char event_type, int source); // called from main thread - void timeUpdated(); void DVBChannelAdded(eDVBChannel*); void DVBChannelStateChanged(iDVBChannel*); void DVBChannelRunning(iDVBChannel *); @@ -317,6 +289,11 @@ private: #endif // SWIG public: static eEPGCache *getInstance() { return instance; } + + void save(); + void load(); + void timeUpdated(); + void flushEPG(const uniqueEPGKey & s=uniqueEPGKey()); #ifndef SWIG eEPGCache(); ~eEPGCache(); @@ -331,28 +308,21 @@ public: // must be called once! void setCacheFile(const char *filename); - // called from main thread - inline void Lock(); - inline void Unlock(); - // at moment just for one service.. RESULT startTimeQuery(const eServiceReference &service, time_t begin=-1, int minutes=-1); #ifndef SWIG - // eventData's are plain entrys out of the cache.. it's not safe to use them after cache unlock - // but its faster in use... its not allowed to delete this pointers via delete or free.. +private: + // For internal use only. Acquire the cache lock before calling. RESULT lookupEventId(const eServiceReference &service, int event_id, const eventData *&); RESULT lookupEventTime(const eServiceReference &service, time_t, const eventData *&, int direction=0); - RESULT getNextTimeEntry(const eventData *&); - // eit_event_struct's are plain dvb eit_events .. it's not safe to use them after cache unlock - // its not allowed to delete this pointers via delete or free.. - RESULT lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&); - RESULT lookupEventTime(const eServiceReference &service, time_t , const eit_event_struct *&, int direction=0); - RESULT getNextTimeEntry(const eit_event_struct *&); +public: + /* Only used by servicedvbrecord.cpp to write the EIT file */ + RESULT saveEventToFile(const char* filename, const eServiceReference &service, int eit_event_id, time_t begTime, time_t endTime); - // Event's are parsed epg events.. it's safe to use them after cache unlock - // after use this Events must be deleted (memleaks) + // Events are parsed epg events.. it's safe to use them after cache unlock + // after use the Event pointer must be released using "delete". RESULT lookupEventId(const eServiceReference &service, int event_id, Event* &); RESULT lookupEventTime(const eServiceReference &service, time_t, Event* &, int direction=0); RESULT getNextTimeEntry(Event *&); @@ -360,7 +330,8 @@ public: enum { SIMILAR_BROADCASTINGS_SEARCH, EXAKT_TITLE_SEARCH, - PARTIAL_TITLE_SEARCH + PARTIAL_TITLE_SEARCH, + START_TITLE_SEARCH }; enum { CASE_CHECK, @@ -374,18 +345,33 @@ public: SWIG_VOID(RESULT) lookupEventId(const eServiceReference &service, int event_id, ePtr &SWIG_OUTPUT); SWIG_VOID(RESULT) lookupEventTime(const eServiceReference &service, time_t, ePtr &SWIG_OUTPUT, int direction=0); SWIG_VOID(RESULT) getNextTimeEntry(ePtr &SWIG_OUTPUT); -}; -#ifndef SWIG -inline void eEPGCache::Lock() -{ - pthread_mutex_lock(&cache_lock); -} - -inline void eEPGCache::Unlock() -{ - pthread_mutex_unlock(&cache_lock); -} + enum {PRIVATE=0, NOWNEXT=1, SCHEDULE=2, SCHEDULE_OTHER=4 +#ifdef ENABLE_MHW_EPG + ,MHW=8 +#endif +#ifdef ENABLE_FREESAT + ,FREESAT_NOWNEXT=16 + ,FREESAT_SCHEDULE=32 + ,FREESAT_SCHEDULE_OTHER=64 +#endif + ,VIASAT=256 +#ifdef ENABLE_NETMED + ,NETMED_SCHEDULE=512 + ,NETMED_SCHEDULE_OTHER=1024 #endif +#ifdef ENABLE_VIRGIN + ,VIRGIN_NOWNEXT=2048 + ,VIRGIN_SCHEDULE=4096 +#endif + ,EPG_IMPORT=0x80000000 + }; + void setEpgHistorySeconds(time_t seconds); + void setEpgSources(unsigned int mask); + unsigned int getEpgSources(); + void submitEventData(const std::vector& serviceRefs, long start, long duration, const char* title, const char* short_summary, const char* long_description, char event_type); + void importEvents(SWIG_PYOBJECT(ePyObject) serviceReferences, SWIG_PYOBJECT(ePyObject) list); + void importEvent(SWIG_PYOBJECT(ePyObject) serviceReference, SWIG_PYOBJECT(ePyObject) list); +}; #endif diff --git a/lib/dvb/lowlevel/eit.h b/lib/dvb/lowlevel/eit.h index affd06c..7dd3927 100644 --- a/lib/dvb/lowlevel/eit.h +++ b/lib/dvb/lowlevel/eit.h @@ -8,16 +8,16 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * + * * The author may be reached as dent@cosy.sbg.ac.at, or * Thomas Mirlacher, Jakob-Haringerstr. 2, A-5020 Salzburg, * Austria @@ -68,6 +68,16 @@ typedef struct { u_char original_network_id_lo : 8; u_char segment_last_section_number : 8; u_char segment_last_table_id : 8; + + int getSectionLength() const { return section_length_hi << 8 | section_length_lo; }; + int getServiceID() const { return service_id_hi << 8 | service_id_lo; }; + int getTransportStreamId() const { return transport_stream_id_hi << 8 | transport_stream_id_lo; }; + int getOriginalNetworkId() const { return original_network_id_hi << 8 | original_network_id_lo; }; + + void setSectionLength(int length) { section_length_hi = length >> 8; section_length_lo = length & 0xFF; }; + void setServiceId(int serviceId) { service_id_hi = serviceId >> 8; service_id_lo = serviceId & 0xFF; }; + void setTransportStreamId(int tsi) { transport_stream_id_hi = tsi >> 8; transport_stream_id_lo = tsi & 0xFF; }; + void setOriginalNetworkId(int oni) { original_network_id_hi = oni >> 8; original_network_id_lo = oni & 0xFF; }; } eit_t; #define EIT_SIZE 14 @@ -103,20 +113,20 @@ struct eit_loop_struct1 { struct eit_short_event_descriptor_struct { u_char descriptor_tag : 8; u_char descriptor_length : 8; - + u_char language_code_1 : 8; u_char language_code_2 : 8; u_char language_code_3 : 8; u_char event_name_length : 8; }; - + #define EIT_EXTENDED_EVENT_DESCRIPOR 0x4e typedef struct eit_event_struct { u_char event_id_hi : 8; u_char event_id_lo : 8; - + u_char start_time_1 : 8; u_char start_time_2 : 8; u_char start_time_3 : 8; @@ -138,7 +148,13 @@ typedef struct eit_event_struct { #endif u_char descriptors_loop_length_lo : 8; - + + uint16_t getEventId() const { return event_id_hi << 8 | event_id_lo; }; + int getDescriptorsLoopLength() const { return descriptors_loop_length_hi << 8 | descriptors_loop_length_lo; }; + + void setEventId(uint16_t eventId) { event_id_hi = eventId >> 8; event_id_lo = eventId & 0xFF; }; + void setDescriptorsLoopLength(int dll) { descriptors_loop_length_hi = dll >> 8; descriptors_loop_length_lo = dll & 0xFF; }; + } eit_event_t; #define EIT_LOOP_SIZE 12 @@ -160,4 +176,4 @@ struct eit_extended_descriptor_struct { }; -#endif +#endif diff --git a/lib/dvb/lowlevel/mhw.h b/lib/dvb/lowlevel/mhw.h index f06c86e..f9f2f70 100644 --- a/lib/dvb/lowlevel/mhw.h +++ b/lib/dvb/lowlevel/mhw.h @@ -85,6 +85,9 @@ typedef struct { u_char mhw2_mjd_lo :8; u_char mhw2_duration_hi :8; u_char mhw2_duration_lo :8; + + int getDuration() const { return duration_hi << 8 | duration_lo; }; + int getMhw2Duration() const { return mhw2_duration_hi << 8 | mhw2_duration_lo; }; } mhw_title_t; typedef struct mhw_summary { diff --git a/lib/python/Components/Converter/EventName.py b/lib/python/Components/Converter/EventName.py index b1ec62d..6ccf328 100644 --- a/lib/python/Components/Converter/EventName.py +++ b/lib/python/Components/Converter/EventName.py @@ -1,36 +1,153 @@ from Components.Converter.Converter import Converter from Components.Element import cached +from Components.Converter.genre import getGenreStringSub class EventName(Converter, object): NAME = 0 SHORT_DESCRIPTION = 1 EXTENDED_DESCRIPTION = 2 - ID = 3 - + FULL_DESCRIPTION = 3 + ID = 4 + NAME_NOW = 5 + NAME_NEXT = 6 + GENRE = 7 + RATING = 8 + SRATING = 9 + PDC = 10 + PDCTIME = 11 + PDCTIMESHORT = 12 + ISRUNNINGSTATUS = 13 + def __init__(self, type): Converter.__init__(self, type) if type == "Description": self.type = self.SHORT_DESCRIPTION elif type == "ExtendedDescription": self.type = self.EXTENDED_DESCRIPTION + elif type == "FullDescription": + self.type = self.FULL_DESCRIPTION elif type == "ID": self.type = self.ID + elif type == "NameNow": + self.type = self.NAME_NOW + elif type == "NameNext": + self.type = self.NAME_NEXT + elif type == "Genre": + self.type = self.GENRE + elif type == "Rating": + self.type = self.RATING + elif type == "SmallRating": + self.type = self.SRATING + elif type == "Pdc": + self.type = self.PDC + elif type == "PdcTime": + self.type = self.PDCTIME + elif type == "PdcTimeShort": + self.type = self.PDCTIMESHORT + elif type == "IsRunningStatus": + self.type = self.ISRUNNINGSTATUS else: self.type = self.NAME @cached + def getBoolean(self): + event = self.source.event + if event is None: + return False + if self.type == self.PDC: + if event.getPdcPil(): + return True + return False + + boolean = property(getBoolean) + + @cached def getText(self): event = self.source.event if event is None: return "" - + if self.type == self.NAME: return event.getEventName() + elif self.type == self.SRATING: + rating = event.getParentalData() + if rating is None: + return "" + else: + country = rating.getCountryCode() + age = rating.getRating() + if age == 0: + return _("All ages") + elif age > 15: + return _("bc%s") % age + else: + age += 3 + return " %d+" % age + elif self.type == self.RATING: + rating = event.getParentalData() + if rating is None: + return "" + else: + country = rating.getCountryCode() + age = rating.getRating() + if age == 0: + return _("Rating undefined") + elif age > 15: + return _("Rating defined by broadcaster - %d") % age + else: + age += 3 + return _("Minimum age %d years") % age + elif self.type == self.GENRE: + genre = event.getGenreData() + if genre is None: + return "" + else: + return getGenreStringSub(genre.getLevel1(), genre.getLevel2()) + elif self.type == self.NAME_NOW: + return pgettext("now/next: 'now' event label", "Now") + ": " + event.getEventName() + elif self.type == self.NAME_NEXT: + return pgettext("now/next: 'next' event label", "Next") + ": " + event.getEventName() elif self.type == self.SHORT_DESCRIPTION: return event.getShortDescription() elif self.type == self.EXTENDED_DESCRIPTION: - return event.getExtendedDescription() + return event.getExtendedDescription() or event.getShortDescription() + elif self.type == self.FULL_DESCRIPTION: + description = event.getShortDescription() + extended = event.getExtendedDescription() + if description and extended: + if description.replace('\n','') == extended.replace('\n',''): + return extended + description += '\n' + return description + extended elif self.type == self.ID: return str(event.getEventId()) - + elif self.type == self.PDC: + if event.getPdcPil(): + return _("PDC") + return "" + elif self.type in (self.PDCTIME, self.PDCTIMESHORT): + pil = event.getPdcPil() + if pil: + if self.type == self.PDCTIMESHORT: + return _("%02d:%02d") % ((pil & 0x7C0) >> 6, (pil & 0x3F)) + return _("%d.%02d. %02d:%02d") % ((pil & 0xF8000) >> 15, (pil & 0x7800) >> 11, (pil & 0x7C0) >> 6, (pil & 0x3F)) + return "" + elif self.type == self.ISRUNNINGSTATUS: + if event.getPdcPil(): + running_status = event.getRunningStatus() + if running_status == 1: + return _("not running") + if running_status == 2: + return _("starts in a few seconds") + if running_status == 3: + return _("pausing") + if running_status == 4: + return _("running") + if running_status == 5: + return _("service off-air") + if running_status in (6,7): + return _("reserved for future use") + return _("undefined") + return "" + text = property(getText) diff --git a/lib/python/Components/Converter/Makefile.am b/lib/python/Components/Converter/Makefile.am index ee817d4..a9871d5 100644 --- a/lib/python/Components/Converter/Makefile.am +++ b/lib/python/Components/Converter/Makefile.am @@ -6,4 +6,4 @@ install_PYTHON = \ ConditionalShowHide.py ServicePosition.py ValueRange.py RdsInfo.py Streaming.py \ StaticMultiList.py ServiceTime.py MovieInfo.py MenuEntryCompare.py StringListSelection.py \ ValueBitTest.py TunerInfo.py ConfigEntryTest.py TemplatedMultiContent.py ProgressToText.py \ - Combine.py SensorToText.py ValueToPixmap.py HbbtvApplicationInfo.py + Combine.py SensorToText.py ValueToPixmap.py HbbtvApplicationInfo.py genre.py diff --git a/lib/python/Components/Converter/genre.py b/lib/python/Components/Converter/genre.py new file mode 100644 index 0000000..50d4be7 --- /dev/null +++ b/lib/python/Components/Converter/genre.py @@ -0,0 +1,170 @@ +# +# Genre types taken from DVB standards documentation +# +# some broadcaster do define other types so this list +# may grow or be replaced.. +# +maintype = [ _("Reserved"), + _("Movie/Drama"), + _("News Current Affairs"), + _("Show Games show"), + _("Sports"), + _("Children/Youth"), + _("Music/Ballet/Dance"), + _("Arts/Culture"), + _("Social/Political/Economics"), + _("Education/Science/..."), + _("Leisure hobbies"), + _("Other")] + + +subtype = {} +# Movie/Drama +subtype[1] = [ + _("movie/drama (general)"), + _("detective/thriller"), + _("adventure/western/war"), + _("science fiction/fantasy/horror"), + _("comedy"), + _("soap/melodram/folkloric"), + _("romance"), + _("serious/classical/religious/historical movie/drama"), + _("adult movie/drama")] + +# News Current Affairs +subtype[2] = [ + _("news/current affairs (general)"), + _("news/weather report"), + _("news magazine"), + _("documentary"), + _("discussion/interview/debate")] + +# Show Games show +subtype[3] = [ + _("show/game show (general)"), + _("game show/quiz/contest"), + _("variety show"), + _("talk show")] + +# Sports +subtype[4] = [ + _("sports (general)"), + _("special events"), + _("sports magazine"), + _("football/soccer"), + _("tennis/squash"), + _("team sports"), + _("athletics"), + _("motor sport"), + _("water sport"), + _("winter sport"), + _("equestrian"), + _("martial sports")] + +# Children/Youth +subtype[5] = [ + _("childrens's/youth program (general)"), + _("pre-school children's program"), + _("entertainment (6-14 year old)"), + _("entertainment (10-16 year old)"), + _("information/education/school program"), + _("cartoon/puppets")] + +# Music/Ballet/Dance +subtype[6] = [ + _("music/ballet/dance (general)"), + _("rock/pop"), + _("serious music/classic music"), + _("folk/traditional music"), + _("jazz"), + _("musical/opera"), + _("ballet")] + +# Arts/Culture +subtype[7] = [ + _("arts/culture (without music, general)"), + _("performing arts"), + _("fine arts"), + _("religion"), + _("popular culture/traditional arts"), + _("literature"), + _("film/cinema"), + _("experimental film/video"), + _("broadcasting/press"), + _("new media"), + _("arts/culture magazine"), + _("fashion")] + +# Social/Political/Economics +subtype[8] = [ + _("social/political issues/economics (general)"), + _("magazines/reports/documentary"), + _("economics/social advisory"), + _("remarkable people")] + +# Education/Science/... +subtype[9] = [ + _("education/science/factual topics (general)"), + _("nature/animals/environment"), + _("technology/natural science"), + _("medicine/physiology/psychology"), + _("foreign countries/expeditions"), + _("social/spiritual science"), + _("further education"), + _("languages")] + +# Leisure hobies +subtype[10] = [ + _("leisure hobbies (general)"), + _("tourism/travel"), + _("handicraft"), + _("motoring"), + _("fitness & health"), + _("cooking"), + _("advertisement/shopping"), + _("gardening")] + +# Other +subtype[11] = [ + _("original language"), + _("black & white"), + _("unpublished"), + _("live broadcast")] + +def getGenreStringMain(hn, ln): +# if hn == 0: +# return _("Undefined content") + if hn == 15: + return _("User defined") + if 0 < hn < len(maintype): + return maintype[hn] +# return _("Reserved") + " " + str(hn) + return "" + +def getGenreStringSub(hn, ln): +# if hn == 0: +# return _("Undefined content") + " " + str(ln) + if hn == 15: + return _("User defined") + " " + str(ln) + if 0 < hn < len(maintype): + if ln == 15: + return _("User defined") + if ln < len(subtype[hn]): + return subtype[hn][ln] +# return _("Reserved") " " + str(ln) +# return _("Reserved") + " " + str(hn) + "," + str(ln) + return "" + +def getGenreStringLong(hn, ln): +# if hn == 0: +# return _("Undefined content") + " " + str(ln) + if hn == 15: + return _("User defined") + " " + str(ln) + if 0 < hn < len(maintype): + return maintype[hn] + ": " + getGenreStringSub(hn, ln) +# return _("Reserved") + " " + str(hn) + "," + str(ln) + return "" + +# +# The End +# diff --git a/lib/python/Components/UsageConfig.py b/lib/python/Components/UsageConfig.py index 36db2ad..52476ed 100644 --- a/lib/python/Components/UsageConfig.py +++ b/lib/python/Components/UsageConfig.py @@ -1,10 +1,9 @@ from Components.Harddisk import harddiskmanager from Components.NimManager import nimmanager -from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigNumber, ConfigSet, ConfigLocations +from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigNumber, ConfigSet, ConfigLocations, ConfigSelectionNumber from Tools.Directories import defaultRecordingLocation from Tools.Directories import resolveFilename, SCOPE_HDD -from enigma import Misc_Options, eEnv -from enigma import setTunerTypePriorityOrder, setPreferredTuner +from enigma import Misc_Options, eEnv, setTunerTypePriorityOrder, setPreferredTuner, eServiceEvent from SystemInfo import SystemInfo import os @@ -94,6 +93,42 @@ def InitUsageConfig(): setPreferredTuner(int(configElement.value)) config.usage.frontend_priority.addNotifier(PreferredTunerChanged) + config.epg = ConfigSubsection() + config.epg.eit = ConfigYesNo(default = True) + config.epg.mhw = ConfigYesNo(default = False) + config.epg.freesat = ConfigYesNo(default = True) + config.epg.viasat = ConfigYesNo(default = True) + config.epg.netmed = ConfigYesNo(default = True) + config.epg.virgin = ConfigYesNo(default = False) + def EpgSettingsChanged(configElement): + from enigma import eEPGCache + mask = 0xffffffff + if not config.epg.eit.value: + mask &= ~(eEPGCache.NOWNEXT | eEPGCache.SCHEDULE | eEPGCache.SCHEDULE_OTHER) + if not config.epg.mhw.value: + mask &= ~eEPGCache.MHW + if not config.epg.freesat.value: + mask &= ~(eEPGCache.FREESAT_NOWNEXT | eEPGCache.FREESAT_SCHEDULE | eEPGCache.FREESAT_SCHEDULE_OTHER) + if not config.epg.viasat.value: + mask &= ~eEPGCache.VIASAT + #if not config.epg.netmed.value: + # mask &= ~(eEPGCache.NETMED_SCHEDULE | eEPGCache.NETMED_SCHEDULE_OTHER) + #if not config.epg.virgin.value: + # mask &= ~(eEPGCache.VIRGIN_NOWNEXT | eEPGCache.VIRGIN_SCHEDULE) + eEPGCache.getInstance().setEpgSources(mask) + config.epg.eit.addNotifier(EpgSettingsChanged) + config.epg.mhw.addNotifier(EpgSettingsChanged) + config.epg.freesat.addNotifier(EpgSettingsChanged) + config.epg.viasat.addNotifier(EpgSettingsChanged) + config.epg.netmed.addNotifier(EpgSettingsChanged) + config.epg.virgin.addNotifier(EpgSettingsChanged) + + config.epg.histminutes = ConfigSelectionNumber(min = 0, max = 120, stepwidth = 15, default = 0, wraparound = True) + def EpgHistorySecondsChanged(configElement): + from enigma import eEPGCache + eEPGCache.getInstance().setEpgHistorySeconds(config.epg.histminutes.getValue()*60) + config.epg.histminutes.addNotifier(EpgHistorySecondsChanged) + def setHDDStandby(configElement): for hdd in harddiskmanager.HDDList(): hdd[1].setIdleTime(int(configElement.value)) @@ -213,6 +248,53 @@ def InitUsageConfig(): ("30000", _("30"))]) config.subtitles.pango_autoturnon = ConfigYesNo(default = True) + config.autolanguage = ConfigSubsection() + epg_language_choices=[ + ("---", _("None")), + ("eng qaa", _("English")), + ("deu ger", _("German")), + ("ara", _("Arabic")), + ("eus baq", _("Basque")), + ("bul", _("Bulgarian")), + ("hrv", _("Croatian")), + ("ces cze", _("Czech")), + ("dan", _("Danish")), + ("dut ndl nld", _("Dutch")), + ("est", _("Estonian")), + ("fin", _("Finnish")), + ("fra fre", _("French")), + ("ell gre", _("Greek")), + ("heb", _("Hebrew")), + ("hun", _("Hungarian")), + ("ita", _("Italian")), + ("lav", _("Latvian")), + ("lit", _("Lithuanian")), + ("ltz", _("Luxembourgish")), + ("nor", _("Norwegian")), + ("fas per fa pes", _("Persian")), + ("pol", _("Polish")), + ("por dub Dub DUB ud1", _("Portuguese")), + ("ron rum", _("Romanian")), + ("rus", _("Russian")), + ("srp", _("Serbian")), + ("slk slo", _("Slovak")), + ("slv", _("Slovenian")), + ("spa", _("Spanish")), + ("swe", _("Swedish")), + ("tha", _("Thai")), + ("tur Audio_TUR", _("Turkish")), + ("ukr Ukr", _("Ukrainian"))] + + def setEpgLanguage(configElement): + eServiceEvent.setEPGLanguage(configElement.value) + config.autolanguage.epglanguage = ConfigSelection(epg_language_choices, default="---") + config.autolanguage.epglanguage.addNotifier(setEpgLanguage) + + def setEpgLanguageAlternative(configElement): + eServiceEvent.setEPGLanguageAlternative(configElement.value) + config.autolanguage.epglanguage_alternative = ConfigSelection(epg_language_choices, default="---") + config.autolanguage.epglanguage_alternative.addNotifier(setEpgLanguageAlternative) + def updateChoices(sel, choices): if choices: defval = None diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py old mode 100755 new mode 100644 index d1c6c84..bc66f1f --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -44,6 +44,9 @@ from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath # hack alert! from Menu import MainMenu, mdom +def isStandardInfoBar(self): + return self.__class__.__name__ == "InfoBar" + class InfoBarDish: def __init__(self): self.dishDialog = self.session.instantiateDialog(Dish) @@ -852,7 +855,7 @@ class InfoBarSeek: return seek def isSeekable(self): - if self.getSeek() is None: + if self.getSeek() is None or (isStandardInfoBar(self) and not self.timeshift_enabled): return False return True diff --git a/lib/service/event.cpp b/lib/service/event.cpp index 2a4cb0d..596e327 100644 --- a/lib/service/event.cpp +++ b/lib/service/event.cpp @@ -8,70 +8,30 @@ #include #include #include +#include +#include #include +#include #include #include // static members / methods -std::string eServiceEvent::m_language = "de_DE"; +std::string eServiceEvent::m_language = "---"; +std::string eServiceEvent::m_language_alternative = "---"; -void eServiceEvent::setEPGLanguage( const std::string language ) -{ - m_language = language; -} /////////////////////////// DEFINE_REF(eServiceEvent); DEFINE_REF(eComponentData); - -const char MAX_LANG = 37; -/* OSD language (see /share/locales/locales) to iso639 conversion table */ -std::string ISOtbl[MAX_LANG][2] = -{ - {"ar_AE","ara"}, - {"C","eng"}, - {"cs_CZ","ces"}, /* or 'cze' */ - {"cs_CZ","cze"}, - {"da_DK","dan"}, - {"de_DE","deu"}, /* also 'ger' is valid iso639 code!! */ - {"de_DE","ger"}, - {"el_GR","gre"}, /* also 'ell' is valid */ - {"el_GR","ell"}, - {"es_ES","esl"}, /* also 'spa' is ok */ - {"es_ES","spa"}, - {"et_EE","est"}, - {"fi_FI","fin"}, - {"fr_FR","fra"}, - {"hr_HR","hrv"}, /* or 'scr' */ - {"hr_HR","scr"}, - {"hu_HU","hun"}, - {"is_IS","isl"}, /* or 'ice' */ - {"is_IS","ice"}, - {"it_IT","ita"}, - {"lt_LT","lit"}, - {"nl_NL","nld"}, /* or 'dut' */ - {"nl_NL","dut"}, - {"no_NO","nor"}, - {"pl_PL","pol"}, - {"pt_PT","por"}, - {"ro_RO","ron"}, /* or 'rum' */ - {"ro_RO","rum"}, - {"ru_RU","rus"}, - {"sk_SK","slk"}, /* or 'slo' */ - {"sk_SK","slo"}, - {"sl_SI","slv"}, - {"sr_YU","srp"}, /* or 'scc' */ - {"sr_YU","scc"}, - {"sv_SE","swe"}, - {"tr_TR","tur"}, - {"ur_IN","urd"} -}; +DEFINE_REF(eGenreData); +DEFINE_REF(eParentalData); /* search for the presence of language from given EIT event descriptors*/ -bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) +bool eServiceEvent::loadLanguage(Event *evt, const std::string &lang, int tsidonid) { bool retval=0; + std::string language = lang; for (DescriptorConstIterator desc = evt->getDescriptors()->begin(); desc != evt->getDescriptors()->end(); ++desc) { switch ((*desc)->getTag()) @@ -82,14 +42,15 @@ bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) case SHORT_EVENT_DESCRIPTOR: { const ShortEventDescriptor *sed = (ShortEventDescriptor*)*desc; - const std::string &cc = sed->getIso639LanguageCode(); + std::string cc = sed->getIso639LanguageCode(); + std::transform(cc.begin(), cc.end(), cc.begin(), tolower); int table=encodingHandler.getCountryCodeDefaultMapping(cc); - if (lang.empty()) - lang = cc; // use first found language - if (!strncasecmp(lang.c_str(), cc.c_str(), 3)) + if (language == "---" || language.find(cc) != std::string::npos) { - m_event_name = convertDVBUTF8(replace_all(replace_all(sed->getEventName(), "\n", " "), "\t", " "), table, tsidonid); - m_short_description = convertDVBUTF8(sed->getText(), table, tsidonid); + /* stick to this language, avoid merging or mixing descriptors of different languages */ + language = cc; + m_event_name += replace_all(replace_all(convertDVBUTF8(sed->getEventName(), table, tsidonid), "\n", " ",table), "\t", " ",table); + m_short_description += convertDVBUTF8(sed->getText(), table, tsidonid); retval=1; } break; @@ -97,13 +58,37 @@ bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) case EXTENDED_EVENT_DESCRIPTOR: { const ExtendedEventDescriptor *eed = (ExtendedEventDescriptor*)*desc; - const std::string &cc = eed->getIso639LanguageCode(); + std::string cc = eed->getIso639LanguageCode(); + std::transform(cc.begin(), cc.end(), cc.begin(), tolower); int table=encodingHandler.getCountryCodeDefaultMapping(cc); - if (lang.empty()) - lang = cc; // use first found language - if (!strncasecmp(lang.c_str(), cc.c_str(), 3)) + if (language == "---" || language.find(cc) != std::string::npos) { - m_extended_description += convertDVBUTF8(eed->getText(), table, tsidonid); + /* stick to this language, avoid merging or mixing descriptors of different languages */ + language = cc; + /* + * Bit of a hack, some providers put the event description partly in the short descriptor, + * and the remainder in extended event descriptors. + * In that case, we cannot really treat short/extended description as separate descriptions. + * Unfortunately we cannot recognise this, but we'll use the length of the short description + * to guess whether we should concatenate both descriptions (without any spaces) + */ + if (eed->getText().empty() && m_short_description.size() >= 180) + { + m_extended_description = m_short_description; + m_short_description = ""; + } + if (table == 0) // Two Char Mapping EED must be processed in one pass + { + m_tmp_extended_description += eed->getText(); + if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) + { + m_extended_description += convertDVBUTF8(m_tmp_extended_description, table, tsidonid); + } + } + else + { + m_extended_description += convertDVBUTF8(eed->getText(), table, tsidonid); + } retval=1; } #if 0 @@ -136,6 +121,7 @@ bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) data.m_componentType = cp->getComponentType(); data.m_componentTag = cp->getComponentTag(); data.m_iso639LanguageCode = cp->getIso639LanguageCode(); + std::transform(data.m_iso639LanguageCode.begin(), data.m_iso639LanguageCode.end(), data.m_iso639LanguageCode.begin(), tolower); int table=encodingHandler.getCountryCodeDefaultMapping(data.m_iso639LanguageCode); data.m_text = convertDVBUTF8(cp->getText(),table,tsidonid); m_component_data.push_back(data); @@ -146,19 +132,53 @@ bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) const LinkageDescriptor *ld = (LinkageDescriptor*)*desc; if ( ld->getLinkageType() == 0xB0 ) { - eServiceReference ref; - ref.type = eServiceReference::idDVB; - eServiceReferenceDVB &dvb_ref = (eServiceReferenceDVB&) ref; + eServiceReferenceDVB dvb_ref; + dvb_ref.type = eServiceReference::idDVB; dvb_ref.setServiceType(1); dvb_ref.setTransportStreamID(ld->getTransportStreamId()); dvb_ref.setOriginalNetworkID(ld->getOriginalNetworkId()); dvb_ref.setServiceID(ld->getServiceId()); const PrivateDataByteVector *privateData = ld->getPrivateDataBytes(); dvb_ref.name = convertDVBUTF8((const unsigned char*)&((*privateData)[0]), privateData->size(), 1, tsidonid); - m_linkage_services.push_back(ref); + m_linkage_services.push_back(dvb_ref); + } + break; + } + case CONTENT_DESCRIPTOR: + { + const ContentDescriptor *cd = (ContentDescriptor *)*desc; + const ContentClassificationList *con = cd->getClassifications(); + for (ContentClassificationConstIterator it = con->begin(); it != con->end(); ++it) + { + eGenreData data; + data.m_level1 = (*it)->getContentNibbleLevel1(); + data.m_level2 = (*it)->getContentNibbleLevel2(); + data.m_user1 = (*it)->getUserNibble1(); + data.m_user2 = (*it)->getUserNibble2(); + m_genres.push_back(data); } break; } + case PARENTAL_RATING_DESCRIPTOR: + { + const ParentalRatingDescriptor *prd = (ParentalRatingDescriptor *)*desc; + const ParentalRatingList *par = prd->getParentalRatings(); + for (ParentalRatingConstIterator it = par->begin(); it != par->end(); ++it) + { + eParentalData data; + + data.m_country_code = (*it)->getCountryCode(); + data.m_rating = (*it)->getRating(); + m_ratings.push_back(data); + } + break; + } + case PDC_DESCRIPTOR: + { + const PdcDescriptor *pdcd = (PdcDescriptor *)*desc; + m_pdc_pil = pdcd->getProgrammeIdentificationLabel(); + break; + } } } } @@ -169,37 +189,29 @@ bool eServiceEvent::loadLanguage(Event *evt, std::string lang, int tsidonid) RESULT eServiceEvent::parseFrom(Event *evt, int tsidonid) { - uint16_t stime_mjd = evt->getStartTimeMjd(); - uint32_t stime_bcd = evt->getStartTimeBcd(); - uint32_t duration = evt->getDuration(); - m_begin = parseDVBtime( - stime_mjd >> 8, - stime_mjd&0xFF, - stime_bcd >> 16, - (stime_bcd >> 8)&0xFF, - stime_bcd & 0xFF - ); + m_begin = parseDVBtime(evt->getStartTimeMjd(), evt->getStartTimeBcd()); m_event_id = evt->getEventId(); + uint32_t duration = evt->getDuration(); m_duration = fromBCD(duration>>16)*3600+fromBCD(duration>>8)*60+fromBCD(duration); - for (int i=0; i < MAX_LANG; i++) - if (m_language==ISOtbl[i][0]) - if (loadLanguage(evt, ISOtbl[i][1], tsidonid)) - return 0; - if (loadLanguage(evt, "eng", tsidonid)) + uint8_t running_status = evt->getRunningStatus(); + m_running_status = running_status; + if (m_language != "---" && loadLanguage(evt, m_language, tsidonid)) return 0; - if (loadLanguage(evt, std::string(), tsidonid)) + if (m_language_alternative != "---" && loadLanguage(evt, m_language_alternative, tsidonid)) + return 0; + if (loadLanguage(evt, "---", tsidonid)) return 0; return 0; } -RESULT eServiceEvent::parseFrom(const std::string filename, int tsidonid) +RESULT eServiceEvent::parseFrom(const std::string &filename, int tsidonid) { if (!filename.empty()) { int fd = ::open( filename.c_str(), O_RDONLY ); if ( fd > -1 ) { - __u8 buf[4096]; + uint8_t buf[4096]; int rd = ::read(fd, buf, 4096); ::close(fd); if ( rd > 12 /*EIT_LOOP_SIZE*/ ) @@ -224,6 +236,60 @@ std::string eServiceEvent::getBeginTimeString() const return std::string(tmp, 12); } +RESULT eServiceEvent::getGenreData(ePtr &dest) const +{ + std::list::const_iterator it = m_genres.begin(); + for(;it != m_genres.end(); ++it) { + dest = new eGenreData(*it); + // for now just return the first item on the list + return 0; + } + dest = 0; + return -1; +} + +PyObject *eServiceEvent::getGenreData() const +{ + ePyObject ret = PyList_New(m_genres.size()); + int cnt=0; + for (std::list::const_iterator it(m_genres.begin()); it != m_genres.end(); ++it) + { + ePyObject tuple = PyTuple_New(4); + PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(it->getLevel1())); + PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->getLevel2())); + PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(it->getUser1())); + PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(it->getUser2())); + PyList_SET_ITEM(ret, cnt++, tuple); + } + return ret; +} + +RESULT eServiceEvent::getParentalData(ePtr &dest) const +{ + std::list::const_iterator it = m_ratings.begin(); + for(;it != m_ratings.end(); ++it) { + dest = new eParentalData(*it); + // for now just return the first item on the list + return 0; + } + dest = 0; + return -1; +} + +PyObject *eServiceEvent::getParentalData() const +{ + ePyObject ret = PyList_New(m_ratings.size()); + int cnt = 0; + for (std::list::const_iterator it(m_ratings.begin()); it != m_ratings.end(); ++it) + { + ePyObject tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, PyString_FromString(it->getCountryCode().c_str())); + PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->getRating())); + PyList_SET_ITEM(ret, cnt++, tuple); + } + return ret; +} + RESULT eServiceEvent::getComponentData(ePtr &dest, int tagnum) const { std::list::const_iterator it = @@ -236,14 +302,14 @@ RESULT eServiceEvent::getComponentData(ePtr &dest, int tagnum) c return 0; } } - dest=0; + dest = 0; return -1; } PyObject *eServiceEvent::getComponentData() const { ePyObject ret = PyList_New(m_component_data.size()); - int cnt=0; + int cnt = 0; for (std::list::const_iterator it(m_component_data.begin()); it != m_component_data.end(); ++it) { ePyObject tuple = PyTuple_New(5); @@ -291,9 +357,4 @@ RESULT eServiceEvent::getLinkageService(eServiceReference &service, eServiceRefe return -1; } -void setServiceEventLanguage(const std::string language) -{ - eServiceEvent::setEPGLanguage(language); -} - DEFINE_REF(eDebugClass); diff --git a/lib/service/event.h b/lib/service/event.h index 4c35407..e637b55 100644 --- a/lib/service/event.h +++ b/lib/service/event.h @@ -22,7 +22,7 @@ struct eComponentData std::string m_iso639LanguageCode; std::string m_text; public: - eComponentData(const eComponentData& d) { *this = d; } + eComponentData(const eComponentData& d) { *this = d; } eComponentData() { m_streamContent = m_componentType = m_componentTag = 0; } int getStreamContent(void) const { return m_streamContent; } int getComponentType(void) const { return m_componentType; } @@ -32,30 +32,72 @@ public: }; SWIG_TEMPLATE_TYPEDEF(ePtr, eComponentDataPtr); +SWIG_IGNORE(eGenreData); +struct eGenreData +{ + friend class eServiceEvent; + DECLARE_REF(eGenreData); + uint8_t m_level1; + uint8_t m_level2; + uint8_t m_user1; + uint8_t m_user2; +public: + eGenreData(const eGenreData& d) { *this = d; } + eGenreData() { m_level1 = m_level2 = m_user1 = m_user2 = 0; } + int getLevel1(void) const { return m_level1; } + int getLevel2(void) const { return m_level2; } + int getUser1(void) const { return m_user1; } + int getUser2(void) const { return m_user2; } +}; +SWIG_TEMPLATE_TYPEDEF(ePtr, eGenreDataPtr); + +SWIG_IGNORE(eParentalData); +struct eParentalData +{ + friend class eServiceEvent; + DECLARE_REF(eParentalData); + std::string m_country_code; + uint8_t m_rating; +public: + eParentalData(const eParentalData& d) { *this = d; } + eParentalData() { m_country_code = ""; m_rating = 0; } + std::string getCountryCode(void) const { return m_country_code; } + int getRating(void) const { return m_rating; } +}; +SWIG_TEMPLATE_TYPEDEF(ePtr, eParentalDataPtr); + + SWIG_ALLOW_OUTPUT_SIMPLE(eServiceReference); // needed for SWIG_OUTPUT in eServiceEvent::getLinkageService SWIG_IGNORE(eServiceEvent); class eServiceEvent: public iObject { DECLARE_REF(eServiceEvent); - bool loadLanguage(Event *event, std::string lang, int tsidonid); + bool loadLanguage(Event *event, const std::string &lang, int tsidonid); std::list m_component_data; std::list m_linkage_services; + std::list m_genres; + std::list m_ratings; time_t m_begin; int m_duration; int m_event_id; - std::string m_event_name, m_short_description, m_extended_description; - static std::string m_language; + int m_pdc_pil; + int m_running_status; + std::string m_event_name, m_short_description, m_extended_description, m_tmp_extended_description; + static std::string m_language, m_language_alternative; // .. additional info public: #ifndef SWIG RESULT parseFrom(Event *evt, int tsidonid=0); - RESULT parseFrom(const std::string filename, int tsidonid=0); - static void setEPGLanguage( const std::string language ); + RESULT parseFrom(const std::string &filename, int tsidonid=0); + static void setEPGLanguage(const std::string &language) { m_language = language; } + static void setEPGLanguageAlternative(const std::string &language) { m_language_alternative = language; } #endif time_t getBeginTime() const { return m_begin; } int getDuration() const { return m_duration; } int getEventId() const { return m_event_id; } + int getPdcPil() const { return m_pdc_pil; } + int getRunningStatus() const { return m_running_status; } std::string getEventName() const { return m_event_name; } std::string getShortDescription() const { return m_short_description; } std::string getExtendedDescription() const { return m_extended_description; } @@ -64,13 +106,22 @@ public: PyObject *getComponentData() const; int getNumOfLinkageServices() const { return m_linkage_services.size(); } SWIG_VOID(RESULT) getLinkageService(eServiceReference &SWIG_OUTPUT, eServiceReference &parent, int num) const; + SWIG_VOID(RESULT) getGenreData(ePtr &SWIG_OUTPUT) const; + PyObject *getGenreData() const; + SWIG_VOID(RESULT) getParentalData(ePtr &SWIG_OUTPUT) const; + PyObject *getParentalData() const; }; SWIG_TEMPLATE_TYPEDEF(ePtr, eServiceEvent); SWIG_EXTEND(ePtr, - static void setEPGLanguage( const std::string language ) + static void setEPGLanguage(const std::string &language) + { + eServiceEvent::setEPGLanguage(language); + } +); +SWIG_EXTEND(ePtr, + static void setEPGLanguageAlternative(const std::string &language) { - extern void setServiceEventLanguage(const std::string language); - setServiceEventLanguage(language); + eServiceEvent::setEPGLanguageAlternative(language); } ); diff --git a/lib/service/iservice.h b/lib/service/iservice.h index bc0f872..74ec80e 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -19,7 +19,8 @@ public: idStructure, // service_id == 0 is root idDVB, idFile, - idUser=0x1000 + idUser=0x1000, + idServiceMP3=0x1001 }; int type; diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index 6ce66ae..b3d0992 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -171,43 +171,10 @@ RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t e ret = meta.updateMeta(m_filename) ? -255 : 0; if (!ret) { - const eit_event_struct *event = 0; - eEPGCache::getInstance()->Lock(); - if ( eit_event_id != -1 ) - { - eDebug("query epg event id %d", eit_event_id); - eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event); - } - if ( !event && (begTime != -1 && endTime != -1) ) - { - time_t queryTime = begTime + ((endTime-begTime)/2); - tm beg, end, query; - localtime_r(&begTime, &beg); - localtime_r(&endTime, &end); - localtime_r(&queryTime, &query); - eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d", - beg.tm_hour, beg.tm_min, beg.tm_sec, - end.tm_hour, end.tm_min, end.tm_sec, - query.tm_hour, query.tm_min, query.tm_sec); - eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event); - } - if ( event ) - { - eDebug("found event.. store to disc"); - std::string fname = m_filename; - fname.erase(fname.length()-2, 2); - fname+="eit"; - int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777); - if (fd>-1) - { - int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/; - int wr = ::write( fd, (unsigned char*)event, evLen ); - if ( wr != evLen ) - eDebug("eit write error (%m)"); - ::close(fd); - } - } - eEPGCache::getInstance()->Unlock(); + std::string fname = filename; + fname.erase(fname.length()-2, 2); + fname += "eit"; + eEPGCache::getInstance()->saveEventToFile(fname.c_str(), ref, eit_event_id, begTime, endTime); } } return ret; diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp old mode 100755 new mode 100644 index b3281a9..fbc8811 --- a/lib/service/servicemp3.cpp +++ b/lib/service/servicemp3.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -228,14 +229,28 @@ int eStaticServiceMP3Info::getInfo(const eServiceReference &ref, int w) } return iServiceInformation::resNA; } - + +RESULT eStaticServiceMP3Info::getEvent(const eServiceReference &ref, ePtr &evt, time_t start_time) +{ + if (ref.path.find("://") != std::string::npos) + { + eServiceReference equivalentref(ref); + equivalentref.type = eServiceFactoryMP3::id; + equivalentref.path.clear(); + return eEPGCache::getInstance()->lookupEventTime(equivalentref, start_time, evt); + } + evt = 0; + return -1; +} // eServiceMP3 int eServiceMP3::ac3_delay, eServiceMP3::pcm_delay; eServiceMP3::eServiceMP3(eServiceReference ref) - :m_ref(ref), m_pump(eApp, 1) + :m_ref(ref), + m_pump(eApp, 1), + m_nownext_timer(eTimer::create(eApp)) { m_subtitle_sync_timer = eTimer::create(eApp); m_streamingsrc_timeout = 0; @@ -252,6 +267,7 @@ eServiceMP3::eServiceMP3(eServiceReference ref) audioSink = videoSink = NULL; CONNECT(m_subtitle_sync_timer->timeout, eServiceMP3::pushSubtitles); CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll); + CONNECT(m_nownext_timer->timeout, eServiceMP3::updateEpgCacheNowNext); m_aspect = m_width = m_height = m_framerate = m_progressive = -1; m_state = stIdle; @@ -462,6 +478,53 @@ eServiceMP3::~eServiceMP3() } } +void eServiceMP3::updateEpgCacheNowNext() +{ + bool update = false; + ePtr next = 0; + ePtr ptr = 0; + eServiceReference ref(m_ref); + ref.type = eServiceFactoryMP3::id; + ref.path.clear(); + + if (eEPGCache::getInstance() && eEPGCache::getInstance()->lookupEventTime(ref, -1, ptr) >= 0) + { + ePtr current = m_event_now; + if (!current || !ptr || current->getEventId() != ptr->getEventId()) + { + update = true; + m_event_now = ptr; + time_t next_time = ptr->getBeginTime() + ptr->getDuration(); + if (eEPGCache::getInstance()->lookupEventTime(ref, next_time, ptr) >= 0) + { + next = ptr; + m_event_next = ptr; + } + } + } + + int refreshtime = 60; + if (!next) + { + next = m_event_next; + } + if (next) + { + time_t now = ::time(0); + refreshtime = (int)(next->getBeginTime() - now) + 3; + if (refreshtime <= 0 || refreshtime > 60) + { + refreshtime = 60; + } + } + + m_nownext_timer->startLongTimer(refreshtime); + if (update) + { + m_event((iPlayableService*)this, evUpdatedEventInfo); + } +} + DEFINE_REF(eServiceMP3); RESULT eServiceMP3::connectEvent(const Slot2 &event, ePtr &connection) @@ -504,6 +567,7 @@ RESULT eServiceMP3::stop() //eDebug("eServiceMP3::stop %s", m_ref.path.c_str()); gst_element_set_state(m_gst_playbin, GST_STATE_NULL); m_state = stStopped; + m_nownext_timer->stop(); return 0; } @@ -786,6 +850,14 @@ RESULT eServiceMP3::getName(std::string &name) return 0; } +RESULT eServiceMP3::getEvent(ePtr &evt, int nownext) +{ + evt = nownext ? m_event_next : m_event_now; + if (!evt) + return -1; + return 0; +} + int eServiceMP3::getInfo(int w) { const gchar *tag = 0; @@ -888,6 +960,24 @@ int eServiceMP3::getInfo(int w) std::string eServiceMP3::getInfoString(int w) { + if ( m_sourceinfo.is_streaming ) + { + switch (w) + { + case sProvider: + return "IPTV"; + case sServiceref: + { + eServiceReference ref(m_ref); + ref.type = eServiceFactoryMP3::id; + ref.path.clear(); + return ref.toString(); + } + default: + break; + } + } + if ( !m_stream_tags && w < sUser && w > 26 ) return ""; const gchar *tag = 0; @@ -1373,6 +1463,8 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) setAC3Delay(ac3_delay); setPCMDelay(pcm_delay); + updateEpgCacheNowNext(); + } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: { diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h index 8ada1b5..4cbedf5 100644 --- a/lib/service/servicemp3.h +++ b/lib/service/servicemp3.h @@ -41,6 +41,8 @@ public: RESULT getName(const eServiceReference &ref, std::string &name); int getLength(const eServiceReference &ref); int getInfo(const eServiceReference &ref, int w); + int isPlayable(const eServiceReference &ref, const eServiceReference &ignore, bool simulate) { return 1; } + RESULT getEvent(const eServiceReference &ref, ePtr &ptr, time_t start_time); }; typedef struct _GstElement GstElement; @@ -101,6 +103,7 @@ public: // iServiceInformation RESULT getName(std::string &name); + RESULT getEvent(ePtr &evt, int nownext); int getInfo(int w); std::string getInfoString(int w); PyObject *getInfoObject(int w); @@ -181,6 +184,10 @@ public: std::string missing_codec; }; +protected: + ePtr m_nownext_timer; + ePtr m_event_now, m_event_next; + void updateEpgCacheNowNext(); private: static int pcm_delay; static int ac3_delay; -- 2.7.4