whitespace fixes, fix infobar summary to include progressbar (patch by Sat_Man)
[enigma2.git] / lib / service / servicedvb.cpp
index 192d3a64024d63b88f999d1b8d8dad418154eded..c66b3da15b235b46cee5b1318b29a97a4dac3c69 100644 (file)
@@ -3,9 +3,9 @@
 #include <string>
 #include <lib/service/servicedvb.h>
 #include <lib/service/service.h>
+#include <lib/base/estring.h>
 #include <lib/base/init_num.h>
 #include <lib/base/init.h>
-#include <lib/base/nconfig.h> // access to python config
 #include <lib/dvb/dvb.h>
 #include <lib/dvb/db.h>
 #include <lib/dvb/decoder.h>
 #include <lib/dvb/metaparser.h>
 #include <lib/dvb/tstools.h>
 #include <lib/python/python.h>
+#include <lib/base/nconfig.h> // access to python config
+
+               /* for subtitles */
+#include <lib/gui/esubtitle.h>
 
 #include <sys/vfs.h>
+#include <sys/stat.h>
 
 #include <byteswap.h>
 #include <netinet/in.h>
 
-#include <dvbsi++/event_information_section.h>
-
 #ifndef BYTE_ORDER
 #error no byte order defined!
 #endif
@@ -36,6 +39,8 @@ class eStaticServiceDVBInformation: public iStaticServiceInformation
 public:
        RESULT getName(const eServiceReference &ref, std::string &name);
        int getLength(const eServiceReference &ref);
+       int isPlayable(const eServiceReference &ref, const eServiceReference &ignore);
+       PyObject *getInfoObject(const eServiceReference &ref, int);
 };
 
 DEFINE_REF(eStaticServiceDVBInformation);
@@ -60,16 +65,7 @@ RESULT eStaticServiceDVBInformation::getName(const eServiceReference &ref, std::
                                if (!service_center->info(parent, service_info))
                                {
                                        if (!service_info->getName(parent, name))
-                                       {
-                                               // just show short name
-                                               unsigned int pos = name.find("\xc2\x86");
-                                               if ( pos != std::string::npos )
-                                                       name.erase(0, pos+2);
-                                               pos = name.find("\xc2\x87");
-                                               if ( pos != std::string::npos )
-                                                       name.erase(pos);
-                                               name+=" - ";
-                                       }
+                                               name=buildShortName(name) + " - ";
                                }
                        }
                }
@@ -87,13 +83,289 @@ int eStaticServiceDVBInformation::getLength(const eServiceReference &ref)
        return -1;
 }
 
-class eStaticServiceDVBBouquetInformation: public iStaticServiceInformation
+int eStaticServiceDVBInformation::isPlayable(const eServiceReference &ref, const eServiceReference &ignore)
 {
-       DECLARE_REF(eStaticServiceDVBBouquetInformation);
-public:
-       RESULT getName(const eServiceReference &ref, std::string &name);
-       int getLength(const eServiceReference &ref);
-};
+       ePtr<eDVBResourceManager> res_mgr;
+       if ( eDVBResourceManager::getInstance( res_mgr ) )
+               eDebug("isPlayable... no res manager!!");
+       else
+       {
+               eDVBChannelID chid, chid_ignore;
+               ((const eServiceReferenceDVB&)ref).getChannelID(chid);
+               ((const eServiceReferenceDVB&)ignore).getChannelID(chid_ignore);
+               return res_mgr->canAllocateChannel(chid, chid_ignore);
+       }
+       return false;
+}
+
+static void PutToDict(ePyObject &dict, const char*key, long value)
+{
+       ePyObject item = PyString_FromFormat("%d", value);
+       if (item)
+       {
+               if (PyDict_SetItemString(dict, key, item))
+                       eDebug("put %s to dict failed", key);
+               Py_DECREF(item);
+       }
+       else
+               eDebug("could not create PyObject for %s", key);
+}
+
+extern void PutToDict(ePyObject &dict, const char*key, const char *value);
+
+void PutSatelliteDataToDict(ePyObject &dict, eDVBFrontendParametersSatellite &feparm)
+{
+       const char *tmp=0;
+       PutToDict(dict, "type", "Satellite");
+       PutToDict(dict, "frequency", feparm.frequency);
+       PutToDict(dict, "symbolrate", feparm.symbol_rate);
+       PutToDict(dict, "orbital position", feparm.orbital_position);
+       switch (feparm.inversion)
+       {
+               case eDVBFrontendParametersSatellite::Inversion::On: tmp="ON"; break;
+               case eDVBFrontendParametersSatellite::Inversion::Off: tmp="OFF"; break;
+               default:
+               case eDVBFrontendParametersSatellite::Inversion::Unknown: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "inversion", tmp);
+       switch (feparm.fec)
+       {
+               case eDVBFrontendParametersSatellite::FEC::fNone: tmp="NONE"; break;
+               case eDVBFrontendParametersSatellite::FEC::f1_2: tmp="1/2"; break;
+               case eDVBFrontendParametersSatellite::FEC::f2_3: tmp="2/3"; break;
+               case eDVBFrontendParametersSatellite::FEC::f3_4: tmp="3/4"; break;
+               case eDVBFrontendParametersSatellite::FEC::f5_6: tmp="5/6"; break;
+               case eDVBFrontendParametersSatellite::FEC::f7_8: tmp="7/8"; break;
+               case eDVBFrontendParametersSatellite::FEC::f3_5: tmp="3/5"; break;
+               case eDVBFrontendParametersSatellite::FEC::f4_5: tmp="4/5"; break;
+               case eDVBFrontendParametersSatellite::FEC::f8_9: tmp="8/9"; break;
+               case eDVBFrontendParametersSatellite::FEC::f9_10: tmp="9/10"; break;
+               default:
+               case eDVBFrontendParametersSatellite::FEC::fAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "fec inner", tmp);
+       switch (feparm.modulation)
+       {
+               case eDVBFrontendParametersSatellite::Modulation::Auto: tmp="AUTO"; break;
+               case eDVBFrontendParametersSatellite::Modulation::QPSK: tmp="QPSK"; break;
+               case eDVBFrontendParametersSatellite::Modulation::M8PSK: tmp="8PSK"; break;
+               case eDVBFrontendParametersSatellite::Modulation::QAM_16: tmp="QAM16"; break;
+       }
+       PutToDict(dict, "modulation", tmp);
+       switch(feparm.polarisation)
+       {
+               case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
+               case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
+               case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR LEFT"; break;
+               default:
+               case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR RIGHT"; break;
+       }
+       PutToDict(dict, "polarization", tmp);
+       switch(feparm.system)
+       {
+               default:
+               case eDVBFrontendParametersSatellite::System::DVB_S: tmp="DVB-S"; break;
+               case eDVBFrontendParametersSatellite::System::DVB_S2:
+                       switch(feparm.roll_off)
+                       {
+                               case eDVBFrontendParametersSatellite::RollOff::alpha_0_35: tmp="0.35"; break;
+                               case eDVBFrontendParametersSatellite::RollOff::alpha_0_25: tmp="0.25"; break;
+                               case eDVBFrontendParametersSatellite::RollOff::alpha_0_20: tmp="0.20"; break;
+                               default:
+                               case eDVBFrontendParametersSatellite::RollOff::alpha_auto: tmp="AUTO"; break;
+                       }
+                       PutToDict(dict, "roll off", tmp);
+                       tmp="DVB-S2";
+                       break;
+       }
+       PutToDict(dict, "system", tmp);
+}
+
+void PutTerrestrialDataToDict(ePyObject &dict, eDVBFrontendParametersTerrestrial &feparm)
+{
+       PutToDict(dict, "type", "Terrestrial");
+       PutToDict(dict, "frequency", feparm.frequency);
+       const char *tmp=0;
+       switch (feparm.bandwidth)
+       {
+       case eDVBFrontendParametersTerrestrial::Bandwidth::Bw8MHz: tmp="8 MHz"; break;
+       case eDVBFrontendParametersTerrestrial::Bandwidth::Bw7MHz: tmp="7 MHz"; break;
+       case eDVBFrontendParametersTerrestrial::Bandwidth::Bw6MHz: tmp="6 MHz"; break;
+       default:
+       case eDVBFrontendParametersTerrestrial::Bandwidth::BwAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "bandwidth", tmp);
+       switch (feparm.code_rate_LP)
+       {
+       case eDVBFrontendParametersTerrestrial::FEC::f1_2: tmp="1/2"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f2_3: tmp="2/3"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f3_4: tmp="3/4"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f5_6: tmp="5/6"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f7_8: tmp="7/8"; break;
+       default:
+       case eDVBFrontendParametersTerrestrial::FEC::fAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "code rate lp", tmp);
+       switch (feparm.code_rate_HP)
+       {
+       case eDVBFrontendParametersTerrestrial::FEC::f1_2: tmp="1/2"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f2_3: tmp="2/3"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f3_4: tmp="3/4"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f5_6: tmp="5/6"; break;
+       case eDVBFrontendParametersTerrestrial::FEC::f7_8: tmp="7/8"; break;
+       default:
+       case eDVBFrontendParametersTerrestrial::FEC::fAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "code rate hp", tmp);
+       switch (feparm.modulation)
+       {
+       case eDVBFrontendParametersTerrestrial::Modulation::QPSK: tmp="QPSK"; break;
+       case eDVBFrontendParametersTerrestrial::Modulation::QAM16: tmp="QAM16"; break;
+       case eDVBFrontendParametersTerrestrial::Modulation::QAM64: tmp="QAM64"; break;
+       default:
+       case eDVBFrontendParametersTerrestrial::Modulation::Auto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "constellation", tmp);
+       switch (feparm.transmission_mode)
+       {
+       case eDVBFrontendParametersTerrestrial::TransmissionMode::TM2k: tmp="2k"; break;
+       case eDVBFrontendParametersTerrestrial::TransmissionMode::TM8k: tmp="8k"; break;
+       default:
+       case eDVBFrontendParametersTerrestrial::TransmissionMode::TMAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "transmission mode", tmp);
+       switch (feparm.guard_interval)
+       {
+               case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_32: tmp="1/32"; break;
+               case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_16: tmp="1/16"; break;
+               case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_8: tmp="1/8"; break;
+               case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_4: tmp="1/4"; break;
+               default:
+               case eDVBFrontendParametersTerrestrial::GuardInterval::GI_Auto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "guard interval", tmp);
+       switch (feparm.hierarchy)
+       {
+               case eDVBFrontendParametersTerrestrial::Hierarchy::HNone: tmp="NONE"; break;
+               case eDVBFrontendParametersTerrestrial::Hierarchy::H1: tmp="1"; break;
+               case eDVBFrontendParametersTerrestrial::Hierarchy::H2: tmp="2"; break;
+               case eDVBFrontendParametersTerrestrial::Hierarchy::H4: tmp="4"; break;
+               default:
+               case eDVBFrontendParametersTerrestrial::Hierarchy::HAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "hierarchy", tmp);
+       switch (feparm.inversion)
+       {
+               case eDVBFrontendParametersSatellite::Inversion::On: tmp="ON"; break;
+               case eDVBFrontendParametersSatellite::Inversion::Off: tmp="OFF"; break;
+               default:
+               case eDVBFrontendParametersSatellite::Inversion::Unknown: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "inversion", tmp);
+}
+
+void PutCableDataToDict(ePyObject &dict, eDVBFrontendParametersCable &feparm)
+{
+       const char *tmp=0;
+       PutToDict(dict, "type", "Cable");
+       PutToDict(dict, "frequency", feparm.frequency);
+       PutToDict(dict, "symbolrate", feparm.symbol_rate);
+       switch (feparm.modulation)
+       {
+       case eDVBFrontendParametersCable::Modulation::QAM16: tmp="QAM16"; break;
+       case eDVBFrontendParametersCable::Modulation::QAM32: tmp="QAM32"; break;
+       case eDVBFrontendParametersCable::Modulation::QAM64: tmp="QAM64"; break;
+       case eDVBFrontendParametersCable::Modulation::QAM128: tmp="QAM128"; break;
+       case eDVBFrontendParametersCable::Modulation::QAM256: tmp="QAM256"; break;
+       default:
+       case eDVBFrontendParametersCable::Modulation::Auto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "modulation", tmp);
+       switch (feparm.inversion)
+       {
+       case eDVBFrontendParametersCable::Inversion::On: tmp="ON"; break;
+       case eDVBFrontendParametersCable::Inversion::Off: tmp="OFF"; break;
+       default:
+       case eDVBFrontendParametersCable::Inversion::Unknown: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "inversion", tmp);
+       switch (feparm.fec_inner)
+       {
+       case eDVBFrontendParametersCable::FEC::fNone: tmp="NONE"; break;
+       case eDVBFrontendParametersCable::FEC::f1_2: tmp="1/2"; break;
+       case eDVBFrontendParametersCable::FEC::f2_3: tmp="2/3"; break;
+       case eDVBFrontendParametersCable::FEC::f3_4: tmp="3/4"; break;
+       case eDVBFrontendParametersCable::FEC::f5_6: tmp="5/6"; break;
+       case eDVBFrontendParametersCable::FEC::f7_8: tmp="7/8"; break;
+       case eDVBFrontendParametersCable::FEC::f8_9: tmp="8/9"; break;
+       default:
+       case eDVBFrontendParametersCable::FEC::fAuto: tmp="AUTO"; break;
+       }
+       PutToDict(dict, "fec inner", tmp);
+}
+
+PyObject *eStaticServiceDVBInformation::getInfoObject(const eServiceReference &r, int what)
+{
+       if (r.type == eServiceReference::idDVB)
+       {
+               const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)r;
+               switch(what)
+               {
+                       case iServiceInformation::sTransponderData:
+                       {
+                               ePtr<eDVBResourceManager> res;
+                               if (!eDVBResourceManager::getInstance(res))
+                               {
+                                       ePtr<iDVBChannelList> db;
+                                       if (!res->getChannelList(db))
+                                       {
+                                               eDVBChannelID chid;
+                                               ref.getChannelID(chid);
+                                               ePtr<iDVBFrontendParameters> feparm;
+                                               if (!db->getChannelFrontendData(chid, feparm))
+                                               {
+                                                       int system;
+                                                       if (!feparm->getSystem(system))
+                                                       {
+                                                               ePyObject dict = PyDict_New();
+                                                               switch(system)
+                                                               {
+                                                                       case iDVBFrontend::feSatellite:
+                                                                       {
+                                                                               eDVBFrontendParametersSatellite s;
+                                                                               feparm->getDVBS(s);
+                                                                               PutSatelliteDataToDict(dict, s);
+                                                                               break;
+                                                                       }
+                                                                       case iDVBFrontend::feTerrestrial:
+                                                                       {
+                                                                               eDVBFrontendParametersTerrestrial t;
+                                                                               feparm->getDVBT(t);
+                                                                               PutTerrestrialDataToDict(dict, t);
+                                                                               break;
+                                                                       }
+                                                                       case iDVBFrontend::feCable:
+                                                                       {
+                                                                               eDVBFrontendParametersCable c;
+                                                                               feparm->getDVBC(c);
+                                                                               PutCableDataToDict(dict, c);
+                                                                               break;
+                                                                       }
+                                                                       default:
+                                                                               eDebug("unknown frontend type %d", system);
+                                                                               Py_DECREF(dict);
+                                                                               break;
+                                                               }
+                                                               return dict;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       Py_RETURN_NONE;
+}
 
 DEFINE_REF(eStaticServiceDVBBouquetInformation);
 
@@ -130,11 +402,64 @@ RESULT eStaticServiceDVBBouquetInformation::getName(const eServiceReference &ref
                return -1;
 }
 
+int eStaticServiceDVBBouquetInformation::isPlayable(const eServiceReference &ref, const eServiceReference &ignore)
+{
+       if (ref.flags & eServiceReference::isGroup)
+       {
+               ePtr<iDVBChannelList> db;
+               ePtr<eDVBResourceManager> res;
+
+               if (eDVBResourceManager::getInstance(res))
+               {
+                       eDebug("eStaticServiceDVBBouquetInformation::isPlayable failed.. no resource manager!");
+                       return 0;
+               }
+
+               if (res->getChannelList(db))
+               {
+                       eDebug("eStaticServiceDVBBouquetInformation::isPlayable failed.. no channel list!");
+                       return 0;
+               }
+
+               eBouquet *bouquet=0;
+               if (db->getBouquet(ref, bouquet))
+               {
+                       eDebug("eStaticServiceDVBBouquetInformation::isPlayable failed.. getBouquet failed!");
+                       return 0;
+               }
+
+               int cur=0;
+               eDVBChannelID chid, chid_ignore;
+               ((const eServiceReferenceDVB&)ignore).getChannelID(chid_ignore);
+               for (std::list<eServiceReference>::iterator it(bouquet->m_services.begin()); it != bouquet->m_services.end(); ++it)
+               {
+                       ((const eServiceReferenceDVB&)*it).getChannelID(chid);
+                       int tmp=res->canAllocateChannel(chid, chid_ignore);
+                       if (tmp > cur)
+                       {
+                               m_playable_service = *it;
+                               cur = tmp;
+                       }
+               }
+               if (cur)
+                       return cur;
+       }
+       m_playable_service = eServiceReference();
+       return 0;
+}
+
 int eStaticServiceDVBBouquetInformation::getLength(const eServiceReference &ref)
 {
        return -1;
 }
 
+#include <lib/dvb/epgcache.h>
+
+RESULT eStaticServiceDVBBouquetInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &ptr, time_t start_time)
+{
+       return eEPGCache::getInstance()->lookupEventTime(ref, start_time, ptr);
+}
+
 class eStaticServiceDVBPVRInformation: public iStaticServiceInformation
 {
        DECLARE_REF(eStaticServiceDVBPVRInformation);
@@ -144,7 +469,8 @@ public:
        eStaticServiceDVBPVRInformation(const eServiceReference &ref);
        RESULT getName(const eServiceReference &ref, std::string &name);
        int getLength(const eServiceReference &ref);
-       
+       RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time);
+       int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; }
        int getInfo(const eServiceReference &ref, int w);
        std::string getInfoString(const eServiceReference &ref,int w);
 };
@@ -160,7 +486,15 @@ eStaticServiceDVBPVRInformation::eStaticServiceDVBPVRInformation(const eServiceR
 RESULT eStaticServiceDVBPVRInformation::getName(const eServiceReference &ref, std::string &name)
 {
        ASSERT(ref == m_ref);
-       name = m_parser.m_name.size() ? m_parser.m_name : ref.path;
+       if (m_parser.m_name.size())
+               name = m_parser.m_name;
+       else
+       {
+               name = ref.path;
+               size_t n = name.rfind('/');
+               if (n != std::string::npos)
+                       name = name.substr(n + 1);
+       }
        return 0;
 }
 
@@ -206,11 +540,31 @@ std::string eStaticServiceDVBPVRInformation::getInfoString(const eServiceReferen
                return m_parser.m_description;
        case iServiceInformation::sServiceref:
                return m_parser.m_ref.toString();
+       case iServiceInformation::sTags:
+               return m_parser.m_tags;
        default:
                return "";
        }
 }
 
+RESULT eStaticServiceDVBPVRInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
+{
+       if (!ref.path.empty())
+       {
+               ePtr<eServiceEvent> event = new eServiceEvent;
+               std::string filename = ref.path;
+               filename.erase(filename.length()-2, 2);
+               filename+="eit";
+               if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
+               {
+                       evt = event;
+                       return 0;
+               }
+       }
+       evt = 0;
+       return -1;
+}
+
 class eDVBPVRServiceOfflineOperations: public iServiceOfflineOperations
 {
        DECLARE_REF(eDVBPVRServiceOfflineOperations);
@@ -290,6 +644,9 @@ eServiceFactoryDVB::eServiceFactoryDVB()
        eServiceCenter::getPrivInstance(sc);
        if (sc)
                sc->addServiceFactory(eServiceFactoryDVB::id, this);
+
+       m_StaticServiceDVBInfo = new eStaticServiceDVBInformation;
+       m_StaticServiceDVBBouquetInfo = new eStaticServiceDVBBouquetInformation;
 }
 
 eServiceFactoryDVB::~eServiceFactoryDVB()
@@ -355,7 +712,7 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sort
        
        if (!m_query)
                return -1;
-       
+
        while (!m_query->getNextResult(ref))
                list.push_back(ref);
 
@@ -370,7 +727,9 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sort
 //   useable format options are
 //   R = Service Reference (as swig object .. this is very slow)
 //   S = Service Reference (as python string object .. same as ref.toString())
+//   C = Service Reference (as python string object .. same as ref.toCompareString())
 //   N = Service Name (as python string object)
+//   n = Short Service Name (short name brakets used) (as python string object)
 //   when exactly one return value per service is selected in the format string,
 //   then each value is directly a list entry
 //   when more than one value is returned per service, then the list is a list of
@@ -378,7 +737,7 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sort
 //   unknown format string chars are returned as python None values !
 PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
 {
-       PyObject *ret=0;
+       ePyObject ret;
        std::list<eServiceReference> tmplist;
        int retcount=1;
 
@@ -391,7 +750,7 @@ PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
                ePtr<iStaticServiceInformation> sptr;
                eServiceCenterPtr service_center;
 
-               if (strchr(format, 'N'))
+               if (strchr(format, 'N') || strchr(format, 'n'))
                        eServiceCenter::getPrivInstance(service_center);
 
                ret = PyList_New(services);
@@ -400,14 +759,17 @@ PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
                for (int cnt=0; cnt < services; ++cnt)
                {
                        eServiceReference &ref=*it++;
-                       PyObject *tuple = retcount > 1 ? PyTuple_New(retcount) : 0;
+                       ePyObject tuple = retcount > 1 ? PyTuple_New(retcount) : ePyObject();
                        for (int i=0; i < retcount; ++i)
                        {
-                               PyObject *tmp=0;
+                               ePyObject tmp;
                                switch(format[i])
                                {
                                case 'R':  // service reference (swig)object
-                                       tmp = New_eServiceReference(ref);
+                                       tmp = NEW_eServiceReference(ref);
+                                       break;
+                               case 'C':  // service reference compare string
+                                       tmp = PyString_FromString(ref.toCompareString().c_str());
                                        break;
                                case 'S':  // service reference string
                                        tmp = PyString_FromString(ref.toString().c_str());
@@ -420,6 +782,30 @@ PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
                                                {
                                                        std::string name;
                                                        sptr->getName(ref, name);
+
+                                                       // filter short name brakets
+                                                       unsigned int pos;
+                                                       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);
+
+                                                       if (name.length())
+                                                               tmp = PyString_FromString(name.c_str());
+                                               }
+                                       }
+                                       if (!tmp)
+                                               tmp = PyString_FromString("<n/a>");
+                                       break;
+                               case 'n':  // short service name
+                                       if (service_center)
+                                       {
+                                               service_center->info(ref, sptr);
+                                               if (sptr)
+                                               {
+                                                       std::string name;
+                                                       sptr->getName(ref, name);
+                                                       name = buildShortName(name);
                                                        if (name.length())
                                                                tmp = PyString_FromString(name.c_str());
                                                }
@@ -447,7 +833,7 @@ PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
                                PyList_SET_ITEM(ret, cnt, tuple);
                }
        }
-       return ret ? ret : PyList_New(0);
+       return ret ? (PyObject*)ret : (PyObject*)PyList_New(0);
 }
 
 RESULT eDVBServiceList::getNext(eServiceReference &ref)
@@ -458,14 +844,9 @@ RESULT eDVBServiceList::getNext(eServiceReference &ref)
        return m_query->getNextResult((eServiceReferenceDVB&)ref);
 }
 
-int eDVBServiceList::compareLessEqual(const eServiceReference &a, const eServiceReference &b)
-{
-       return m_query->compareLessEqual((const eServiceReferenceDVB&)a, (const eServiceReferenceDVB&)b);
-}
-
 RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
 {
-       if (m_parent.flags & eServiceReference::flagDirectory) // bouquet
+       if (m_parent.flags & eServiceReference::canDescent) // bouquet
        {
                ePtr<iDVBChannelList> db;
                ePtr<eDVBResourceManager> resm;
@@ -484,11 +865,11 @@ RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
        return -1;
 }
 
-RESULT eDVBServiceList::addService(eServiceReference &ref)
+RESULT eDVBServiceList::addService(eServiceReference &ref, eServiceReference before)
 {
        if (!m_bouquet)
                return -1;
-       return m_bouquet->addService(ref);
+       return m_bouquet->addService(ref, before);
 }
 
 RESULT eDVBServiceList::removeService(eServiceReference &ref)
@@ -559,12 +940,12 @@ RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableServ
 RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
 {
        /* is a listable service? */
-       if ((ref.flags & eServiceReference::flagDirectory) == eServiceReference::flagDirectory) // bouquet
+       if (ref.flags & eServiceReference::canDescent) // bouquet
        {
                if ( !ref.name.empty() )  // satellites or providers list
-                       ptr = new eStaticServiceDVBInformation;
+                       ptr = m_StaticServiceDVBInfo;
                else // a dvb bouquet
-                       ptr = new eStaticServiceDVBBouquetInformation;
+                       ptr = m_StaticServiceDVBBouquetInfo;
        }
        else if (!ref.path.empty()) /* do we have a PVR service? */
                ptr = new eStaticServiceDVBPVRInformation(ref);
@@ -572,7 +953,7 @@ RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServic
        {
                ePtr<eDVBService> service;
                if (lookupService(service, ref)) // no eDVBService avail for this reference ( Linkage Services... )
-                       ptr = new eStaticServiceDVBInformation;
+                       ptr = m_StaticServiceDVBInfo;
                else
                        /* eDVBService has the iStaticServiceInformation interface, so we pass it here. */
                        ptr = service;
@@ -625,8 +1006,9 @@ RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServ
 }
 
 eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): 
-       m_reference(ref), m_dvb_service(service), m_is_paused(0)
+       m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
 {
+       memset(&m_videoEventData, 0, sizeof(struct iTSMPEGDecoder::videoEvent));
        m_is_primary = 1;
        m_is_pvr = !m_reference.path.empty();
        
@@ -639,10 +1021,17 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
 
        m_cuesheet_changed = 0;
        m_cutlist_enabled = 1;
+       
+       m_subtitle_widget = 0;
+       
+       m_tune_state = -1;
+
+       CONNECT(m_subtitle_sync_timer.timeout, eDVBServicePlay::checkSubtitleTiming);
 }
 
 eDVBServicePlay::~eDVBServicePlay()
 {
+       delete m_subtitle_widget;
 }
 
 void eDVBServicePlay::gotNewEvent()
@@ -663,6 +1052,8 @@ void eDVBServicePlay::gotNewEvent()
 
 void eDVBServicePlay::serviceEvent(int event)
 {
+       m_tune_state = event;
+
        switch (event)
        {
        case eDVBServicePMTHandler::eventTuned:
@@ -682,9 +1073,13 @@ void eDVBServicePlay::serviceEvent(int event)
                }
                break;
        }
+       case eDVBServicePMTHandler::eventNoResources:
+       case eDVBServicePMTHandler::eventNoPAT:
+       case eDVBServicePMTHandler::eventNoPATEntry:
+       case eDVBServicePMTHandler::eventNoPMT:
        case eDVBServicePMTHandler::eventTuneFailed:
        {
-               eDebug("DVB service failed to tune");
+               eDebug("DVB service failed to tune - error %d", event);
                m_event((iPlayableService*)this, evTuneFailed);
                break;
        }
@@ -724,7 +1119,8 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                m_event((iPlayableService*)this, evSOF);
                break;
        case eDVBServicePMTHandler::eventEOF:
-               switchToLive();
+               if ((!m_is_paused) && (m_skipmode >= 0))
+                       switchToLive();
                break;
        }
 }
@@ -737,54 +1133,84 @@ RESULT eDVBServicePlay::start()
                   to start recording from the data demux. */
        if (m_is_pvr)
                m_cue = new eCueSheet();
+       else
+               m_event(this, evStart);
 
        m_first_program_info = 1;
        eServiceReferenceDVB &service = (eServiceReferenceDVB&)m_reference;
        r = m_service_handler.tune(service, m_is_pvr, m_cue);
-       
+
                /* inject EIT if there is a stored one */
        if (m_is_pvr)
        {
                std::string filename = service.path;
                filename.erase(filename.length()-2, 2);
                filename+="eit";
-               int fd = ::open( filename.c_str(), O_RDONLY );
-               if ( fd > -1 )
+               ePtr<eServiceEvent> event = new eServiceEvent;
+               if (!event->parseFrom(filename, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get()))
                {
-                       __u8 buf[4096];
-                       int rd = ::read(fd, buf, 4096);
-                       ::close(fd);
-                       if ( rd > 12 /*EIT_LOOP_SIZE*/ )
-                       {
-                               Event ev(buf);
-                               ePtr<eServiceEvent> event = new eServiceEvent;
-                               ePtr<eServiceEvent> empty;
-                               event->parseFrom(&ev, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get());
-                               m_event_handler.inject(event, 0);
-                               m_event_handler.inject(empty, 1);
-                               eDebug("injected");
-                       }
+                       ePtr<eServiceEvent> empty;
+                       m_event_handler.inject(event, 0);
+                       m_event_handler.inject(empty, 1);
                }
        }
-       
+
        if (m_is_pvr)
+       {
                loadCuesheet();
-
-       m_event(this, evStart);
-       m_event((iPlayableService*)this, evSeekableStatusChanged);
+               m_event(this, evStart);
+       }
        return 0;
 }
 
 RESULT eDVBServicePlay::stop()
 {
+               /* add bookmark for last play position */
+       if (m_is_pvr)
+       {
+               pts_t play_position, length;
+               if (!getPlayPosition(play_position))
+               {
+                               /* remove last position */
+                       for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end();)
+                       {
+                               if (i->what == 3) /* current play position */
+                               {
+                                       m_cue_entries.erase(i);
+                                       i = m_cue_entries.begin();
+                                       continue;
+                               } else
+                                       ++i;
+                       }
+                       
+                       if (getLength(length))
+                               length = 0;
+                       
+                       if (length)
+                       {
+                               int perc = play_position * 100LL / length;
+                       
+                                       /* only store last play position when between 5% and 95% */
+                               if ((5 < perc) && (perc < 95))
+                                       m_cue_entries.insert(cueEntry(play_position, 3)); /* last play position */
+                       }
+                       m_cuesheet_changed = 1;
+               }
+       }
+
        stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
 
        m_service_handler_timeshift.free();
        m_service_handler.free();
        
        if (m_is_pvr && m_cuesheet_changed)
-               saveCuesheet();
-       
+       {
+               struct stat s;
+                               /* save cuesheet only when main file is accessible. */
+               if (!::stat(m_reference.path.c_str(), &s))
+                       saveCuesheet();
+       }
+       m_event((iPlayableService*)this, evStopped);
        return 0;
 }
 
@@ -859,7 +1285,7 @@ RESULT eDVBServicePlay::setFastForward(int ratio)
 
        return m_decoder->setFastForward(ffratio);
 }
-    
+
 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
 {
        if (m_is_pvr || m_timeshift_enabled)
@@ -919,6 +1345,9 @@ RESULT eDVBServicePlay::seekTo(pts_t to)
                return -1;
        
        m_cue->seekTo(0, to);
+       m_dvb_subtitle_pages.clear();
+       m_subtitle_pages.clear();
+
        return 0;
 }
 
@@ -946,6 +1375,8 @@ RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
                return 0;
        
        m_cue->seekTo(mode, to);
+       m_dvb_subtitle_pages.clear();
+       m_subtitle_pages.clear();
        return 0;
 }
 
@@ -985,7 +1416,7 @@ RESULT eDVBServicePlay::isCurrentlySeekable()
        return m_is_pvr || m_timeshift_active;
 }
 
-RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
+RESULT eDVBServicePlay::frontendInfo(ePtr<iFrontendInformation> &ptr)
 {
        ptr = this;
        return 0;
@@ -997,6 +1428,12 @@ RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
        return 0;
 }
 
+RESULT eDVBServicePlay::audioChannel(ePtr<iAudioChannelSelection> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
 RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
 {
        ptr = this;
@@ -1012,7 +1449,8 @@ RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
 {
        ptr = 0;
-       if (m_timeshift_enabled || !m_is_pvr)
+       if (m_have_video_pid &&  // HACK !!! FIXMEE !! temporary no timeshift on radio services !!
+               (m_timeshift_enabled || !m_is_pvr))
        {
                if (!m_timeshift_enabled)
                {
@@ -1047,6 +1485,24 @@ RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
        return -1;
 }
 
+RESULT eDVBServicePlay::subtitle(ePtr<iSubtitleOutput> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
+RESULT eDVBServicePlay::audioDelay(ePtr<iAudioDelay> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
+RESULT eDVBServicePlay::rdsDecoder(ePtr<iRdsDecoder> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
 RESULT eDVBServicePlay::getName(std::string &name)
 {
        if (m_is_pvr)
@@ -1075,17 +1531,42 @@ RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
 int eDVBServicePlay::getInfo(int w)
 {
        eDVBServicePMTHandler::program program;
-
+       
        if (w == sCAIDs)
                return resIsPyObject;
 
-       if (m_service_handler.getProgramInfo(program))
-               return -1;
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+       
+       int no_program_info = 0;
+       
+       if (h.getProgramInfo(program))
+               no_program_info = 1;
        
        switch (w)
        {
+#if HAVE_DVB_API_VERSION >= 3
+       case sVideoHeight:
+               if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+                       return m_videoEventData.height;
+               return -1;
+       case sVideoWidth:
+               if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+                       return m_videoEventData.width;
+               return -1;
+#else
+#warning "FIXMEE implement sVideoHeight, sVideoWidth for old DVB API"
+#endif
        case sAspect:
-               if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
+#if HAVE_DVB_API_VERSION >= 3
+               if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+                       return m_videoEventData.aspect == VIDEO_FORMAT_4_3 ? 1 : 3;
+               else
+#else
+#warning "FIXMEE implement sAspect for old DVB API"
+#endif
+               if (no_program_info)
+                       return -1; 
+               else if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
                {
                        ePtr<eServiceEvent> evt;
                        if (!m_event_handler.getEvent(evt, 0))
@@ -1123,17 +1604,20 @@ int eDVBServicePlay::getInfo(int w)
                        }
                }
                return -1;
-       case sIsCrypted: return program.isCrypted;
-       case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
-       case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
-       case sPCRPID: return program.pcrPid;
-       case sPMTPID: return program.pmtPid;
-       case sTXTPID: return program.textPid;
+       case sIsCrypted: if (no_program_info) return -1; return program.isCrypted();
+       case sVideoPID: if (no_program_info) return -1; if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
+       case sVideoType: if (no_program_info) return -1; if (program.videoStreams.empty()) return -1; return program.videoStreams[0].type;
+       case sAudioPID: if (no_program_info) return -1; if (program.audioStreams.empty()) return -1; return program.audioStreams[0].pid;
+       case sPCRPID: if (no_program_info) return -1; return program.pcrPid;
+       case sPMTPID: if (no_program_info) return -1; return program.pmtPid;
+       case sTXTPID: if (no_program_info) return -1; return program.textPid;
        case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
        case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
        case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
        case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
        case sProvider: if (!m_dvb_service) return -1; return -2;
+       case sServiceref: return resIsString;
+       case sDVBState: return m_tune_state;
        default:
                return -1;
        }
@@ -1146,6 +1630,8 @@ std::string eDVBServicePlay::getInfoString(int w)
        case sProvider:
                if (!m_dvb_service) return "";
                return m_dvb_service->m_provider_name;
+       case sServiceref:
+               return m_reference.toString();
        default:
                break;
        }
@@ -1158,6 +1644,8 @@ PyObject *eDVBServicePlay::getInfoObject(int w)
        {
        case sCAIDs:
                return m_service_handler.getCaIds();
+       case sTransponderData:
+               return eStaticServiceDVBInformation().getInfoObject(m_reference, w);
        default:
                break;
        }
@@ -1167,11 +1655,29 @@ PyObject *eDVBServicePlay::getInfoObject(int w)
 int eDVBServicePlay::getNumberOfTracks()
 {
        eDVBServicePMTHandler::program program;
-       if (m_service_handler.getProgramInfo(program))
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+       if (h.getProgramInfo(program))
                return 0;
        return program.audioStreams.size();
 }
 
+int eDVBServicePlay::getCurrentTrack()
+{
+       eDVBServicePMTHandler::program program;
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+       if (h.getProgramInfo(program))
+               return 0;
+
+       int max = program.audioStreams.size();
+       int i;
+
+       for (i = 0; i < max; ++i)
+               if (program.audioStreams[i].pid == m_current_audio_pid)
+                       return i;
+
+       return 0;
+}
+
 RESULT eDVBServicePlay::selectTrack(unsigned int i)
 {
        int ret = selectAudioStream(i);
@@ -1185,17 +1691,22 @@ RESULT eDVBServicePlay::selectTrack(unsigned int i)
 RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
 {
        eDVBServicePMTHandler::program program;
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
 
-       if (m_service_handler.getProgramInfo(program))
+       if (h.getProgramInfo(program))
                return -1;
        
        if (i >= program.audioStreams.size())
                return -2;
        
+       info.m_pid = program.audioStreams[i].pid;
+
        if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
                info.m_description = "MPEG";
        else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
                info.m_description = "AC3";
+       else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAAC)
+               info.m_description = "AAC";
        else  if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
                info.m_description = "DTS";
        else
@@ -1221,41 +1732,175 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int
 int eDVBServicePlay::selectAudioStream(int i)
 {
        eDVBServicePMTHandler::program program;
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
 
-       if (m_service_handler.getProgramInfo(program))
+       if (h.getProgramInfo(program))
                return -1;
-       
-       if ((unsigned int)i >= program.audioStreams.size())
+
+       if ((i != -1) && ((unsigned int)i >= program.audioStreams.size()))
                return -2;
-       
+
        if (!m_decoder)
                return -3;
-       
-       if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
-               return -4;
 
-       if (m_dvb_service && !m_is_pvr)
+       int stream = i;
+       if (stream == -1)
+               stream = program.defaultAudioStream;
+
+       int apid = -1, apidtype = -1;
+
+       if (((unsigned int)stream) < program.audioStreams.size())
        {
-               if (program.audioStreams[i].type == eDVBAudio::aMPEG)
-               {
-                       m_dvb_service->setCachePID(eDVBService::cAPID, program.audioStreams[i].pid);
-                       m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
-               }       else
-               {
-                       m_dvb_service->setCachePID(eDVBService::cAPID, -1);
-                       m_dvb_service->setCachePID(eDVBService::cAC3PID, program.audioStreams[i].pid);
-               }
+               apid = program.audioStreams[stream].pid;
+               apidtype = program.audioStreams[stream].type;
        }
 
-       m_current_audio_stream = i;
+       m_current_audio_pid = apid;
 
-       return 0;
-}
+       if (m_decoder->setAudioPID(apid, apidtype))
+       {
+               eDebug("set audio pid failed");
+               return -4;
+       }
 
-int eDVBServicePlay::getFrontendInfo(int w)
+               /* if we are not in PVR mode, timeshift is not active and we are not in pip mode, check if we need to enable the rds reader */
+       if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+               if (!m_rds_decoder)
+               {
+                       ePtr<iDVBDemux> data_demux;
+                       if (!h.getDataDemux(data_demux))
+                       {
+                               m_rds_decoder = new eDVBRdsDecoder(data_demux);
+                               m_rds_decoder->connectEvent(slot(*this, &eDVBServicePlay::rdsDecoderEvent), m_rds_decoder_event_connection);
+                       }
+               }
+
+               /* if we decided that we need one, update the pid */
+       if (m_rds_decoder)
+               m_rds_decoder->start(apid);
+
+                       /* store new pid as default only when:
+                               a.) we have an entry in the service db for the current service,
+                               b.) we are not playing back something,
+                               c.) we are not selecting the default entry. (we wouldn't change 
+                                   anything in the best case, or destroy the default setting in
+                                   case the real default is not yet available.)
+                       */
+       if (m_dvb_service && !m_is_pvr && ((i != -1)
+               || ((m_dvb_service->getCacheEntry(eDVBService::cAPID) == -1) && (m_dvb_service->getCacheEntry(eDVBService::cAC3PID)==-1))))
+       {
+               if (apidtype == eDVBAudio::aMPEG)
+               {
+                       m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
+                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
+               }
+               else
+               {
+                       m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
+                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
+               }
+       }
+
+       h.resetCachedProgram();
+
+       return 0;
+}
+
+int eDVBServicePlay::getCurrentChannel()
+{
+       return m_decoder ? m_decoder->getAudioChannel() : STEREO;
+}
+
+RESULT eDVBServicePlay::selectChannel(int i)
+{
+       if (i < LEFT || i > RIGHT || i == STEREO)
+               i = -1;  // Stereo
+       if (m_dvb_service)
+               m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
+       if (m_decoder)
+               m_decoder->setAudioChannel(i);
+       return 0;
+}
+
+std::string eDVBServicePlay::getText(int x)
+{
+       if (m_rds_decoder)
+               switch(x)
+               {
+                       case RadioText:
+                               return convertLatin1UTF8(m_rds_decoder->getRadioText());
+                       case RtpText:
+                               return convertLatin1UTF8(m_rds_decoder->getRtpText());
+               }
+       return "";
+}
+
+void eDVBServicePlay::rdsDecoderEvent(int what)
+{
+       switch(what)
+       {
+               case eDVBRdsDecoder::RadioTextChanged:
+                       m_event((iPlayableService*)this, evUpdatedRadioText);
+                       break;
+               case eDVBRdsDecoder::RtpTextChanged:
+                       m_event((iPlayableService*)this, evUpdatedRtpText);
+                       break;
+               case eDVBRdsDecoder::RassInteractivePicMaskChanged:
+                       m_event((iPlayableService*)this, evUpdatedRassInteractivePicMask);
+                       break;
+               case eDVBRdsDecoder::RecvRassSlidePic:
+                       m_event((iPlayableService*)this, evUpdatedRassSlidePic);
+                       break;
+       }
+}
+
+void eDVBServicePlay::showRassSlidePicture()
+{
+       if (m_rds_decoder)
+       {
+               if (m_decoder)
+               {
+                       std::string rass_slide_pic = m_rds_decoder->getRassSlideshowPicture();
+                       if (rass_slide_pic.length())
+                               m_decoder->showSinglePic(rass_slide_pic.c_str());
+                       else
+                               eDebug("empty filename for rass slide picture received!!");
+               }
+               else
+                       eDebug("no MPEG Decoder to show iframes avail");
+       }
+       else
+               eDebug("showRassSlidePicture called.. but not decoder");
+}
+
+void eDVBServicePlay::showRassInteractivePic(int page, int subpage)
+{
+       if (m_rds_decoder)
+       {
+               if (m_decoder)
+               {
+                       std::string rass_interactive_pic = m_rds_decoder->getRassPicture(page, subpage);
+                       if (rass_interactive_pic.length())
+                               m_decoder->showSinglePic(rass_interactive_pic.c_str());
+                       else
+                               eDebug("empty filename for rass interactive picture %d/%d received!!", page, subpage);
+               }
+               else
+                       eDebug("no MPEG Decoder to show iframes avail");
+       }
+       else
+               eDebug("showRassInteractivePic called.. but not decoder");
+}
+
+ePyObject eDVBServicePlay::getRassInteractiveMask()
+{
+       if (m_rds_decoder)
+               return m_rds_decoder->getRassPictureMask();
+       Py_RETURN_NONE;
+}
+
+int eDVBServiceBase::getFrontendInfo(int w)
 {
-       if (m_is_pvr)
-               return 0;
        eUsePtr<iDVBChannel> channel;
        if(m_service_handler.getChannel(channel))
                return 0;
@@ -1265,19 +1910,54 @@ int eDVBServicePlay::getFrontendInfo(int w)
        return fe->readFrontendData(w);
 }
 
-PyObject *eDVBServicePlay::getFrontendData(bool original)
+PyObject *eDVBServiceBase::getFrontendData()
 {
-       PyObject *ret=0;
+       ePyObject ret = PyDict_New();
+       if (ret)
+       {
+               eUsePtr<iDVBChannel> channel;
+               if(!m_service_handler.getChannel(channel))
+               {
+                       ePtr<iDVBFrontend> fe;
+                       if(!channel->getFrontend(fe))
+                               fe->getFrontendData(ret);
+               }
+       }
+       else
+               Py_RETURN_NONE;
+       return ret;
+}
 
-       eUsePtr<iDVBChannel> channel;
-       if(!m_service_handler.getChannel(channel))
+PyObject *eDVBServiceBase::getFrontendStatus()
+{
+       ePyObject ret = PyDict_New();
+       if (ret)
+       {
+               eUsePtr<iDVBChannel> channel;
+               if(!m_service_handler.getChannel(channel))
+               {
+                       ePtr<iDVBFrontend> fe;
+                       if(!channel->getFrontend(fe))
+                               fe->getFrontendStatus(ret);
+               }
+       }
+       else
+               Py_RETURN_NONE;
+       return ret;
+}
+
+PyObject *eDVBServiceBase::getTransponderData(bool original)
+{
+       ePyObject ret = PyDict_New();
+       if (ret)
        {
-               ePtr<iDVBFrontend> fe;
-               if(!channel->getFrontend(fe))
+               eUsePtr<iDVBChannel> channel;
+               if(!m_service_handler.getChannel(channel))
                {
-                       ret = fe->readTransponderData(original);
-                       if (ret)
+                       ePtr<iDVBFrontend> fe;
+                       if(!channel->getFrontend(fe))
                        {
+                               fe->getTransponderData(ret, original);
                                ePtr<iDVBFrontendParameters> feparm;
                                channel->getCurrentFrontendParameters(feparm);
                                if (feparm)
@@ -1285,8 +1965,8 @@ PyObject *eDVBServicePlay::getFrontendData(bool original)
                                        eDVBFrontendParametersSatellite osat;
                                        if (!feparm->getDVBS(osat))
                                        {
-                                               void PutToDict(PyObject *, const char*, long);
-                                               void PutToDict(PyObject *, const char*, const char*);
+                                               void PutToDict(ePyObject &, const char*, long);
+                                               void PutToDict(ePyObject &, const char*, const char*);
                                                PutToDict(ret, "orbital_position", osat.orbital_position);
                                                const char *tmp = "UNKNOWN";
                                                switch(osat.polarisation)
@@ -1303,10 +1983,26 @@ PyObject *eDVBServicePlay::getFrontendData(bool original)
                        }
                }
        }
-       if (!ret)
+       else
+               Py_RETURN_NONE;
+       return ret;
+}
+
+PyObject *eDVBServiceBase::getAll(bool original)
+{
+       ePyObject ret = getTransponderData(original);
+       if (ret != Py_None)
        {
-               ret = Py_None;
-               Py_INCREF(ret);
+               eUsePtr<iDVBChannel> channel;
+               if(!m_service_handler.getChannel(channel))
+               {
+                       ePtr<iDVBFrontend> fe;
+                       if(!channel->getFrontend(fe))
+                       {
+                               fe->getFrontendData(ret);
+                               fe->getFrontendStatus(ret);
+                       }
+               }
        }
        return ret;
 }
@@ -1384,7 +2080,7 @@ RESULT eDVBServicePlay::stopTimeshift()
        
        close(m_timeshift_fd);
        eDebug("remove timeshift file");
-       remove(m_timeshift_file.c_str());
+       eBackgroundFileEraser::getInstance()->erase(m_timeshift_file.c_str());
        
        return 0;
 }
@@ -1410,11 +2106,11 @@ RESULT eDVBServicePlay::activateTimeshift()
 
 PyObject *eDVBServicePlay::getCutList()
 {
-       PyObject *list = PyList_New(0);
+       ePyObject list = PyList_New(0);
        
        for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
        {
-               PyObject *tuple = PyTuple_New(2);
+               ePyObject tuple = PyTuple_New(2);
                PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
                PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
                PyList_Append(list, tuple);
@@ -1424,7 +2120,7 @@ PyObject *eDVBServicePlay::getCutList()
        return list;
 }
 
-void eDVBServicePlay::setCutList(PyObject *list)
+void eDVBServicePlay::setCutList(ePyObject list)
 {
        if (!PyList_Check(list))
                return;
@@ -1435,7 +2131,7 @@ void eDVBServicePlay::setCutList(PyObject *list)
        
        for (i=0; i<size; ++i)
        {
-               PyObject *tuple = PyList_GetItem(list, i);
+               ePyObject tuple = PyList_GET_ITEM(list, i);
                if (!PyTuple_Check(tuple))
                {
                        eDebug("non-tuple in cutlist");
@@ -1446,7 +2142,7 @@ void eDVBServicePlay::setCutList(PyObject *list)
                        eDebug("cutlist entries need to be a 2-tuple");
                        continue;
                }
-               PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
+               ePyObject ppts = PyTuple_GET_ITEM(tuple, 0), ptype = PyTuple_GET_ITEM(tuple, 1);
                if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
                {
                        eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
@@ -1475,7 +2171,9 @@ void eDVBServicePlay::updateTimeshiftPids()
                return;
        
        eDVBServicePMTHandler::program program;
-       if (m_service_handler.getProgramInfo(program))
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+       if (h.getProgramInfo(program))
                return;
        else
        {
@@ -1497,6 +2195,11 @@ void eDVBServicePlay::updateTimeshiftPids()
                        i != program.audioStreams.end(); ++i)
                                pids_to_record.insert(i->pid);
 
+               for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
+                       i(program.subtitleStreams.begin());
+                       i != program.subtitleStreams.end(); ++i)
+                               pids_to_record.insert(i->pid);
+
                std::set<int> new_pids, obsolete_pids;
                
                std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
@@ -1526,13 +2229,19 @@ void eDVBServicePlay::switchToLive()
        m_decoder = 0;
        m_decode_demux = 0;
        m_teletext_parser = 0;
-       
+       m_rds_decoder = 0;
+       m_subtitle_parser = 0;
+       m_new_dvb_subtitle_page_connection = 0;
+       m_new_subtitle_page_connection = 0;
+       m_rds_decoder_event_connection = 0;
+       m_video_event_connection = 0;
+
                /* free the timeshift service handler, we need the resources */
        m_service_handler_timeshift.free();
        m_timeshift_active = 0;
-       
+
        m_event((iPlayableService*)this, evSeekableStatusChanged);
-       
+
        updateDecoder();
 }
 
@@ -1540,33 +2249,37 @@ void eDVBServicePlay::switchToTimeshift()
 {
        if (m_timeshift_active)
                return;
-       
+
        m_decode_demux = 0;
        m_decoder = 0;
        m_teletext_parser = 0;
-       
+       m_rds_decoder = 0;
+       m_subtitle_parser = 0;
+       m_new_subtitle_page_connection = 0;
+       m_new_dvb_subtitle_page_connection = 0;
+       m_rds_decoder_event_connection = 0;
+       m_video_event_connection = 0;
+
        m_timeshift_active = 1;
 
-       m_event((iPlayableService*)this, evSeekableStatusChanged);
-       
        eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
        r.path = m_timeshift_file;
-       
+
        m_cue = new eCueSheet();
        m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
-       updateDecoder(); /* mainly to switch off PCR */
+
+       eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
+       pause();
+       updateDecoder(); /* mainly to switch off PCR, and to set pause */
+       
+       m_event((iPlayableService*)this, evSeekableStatusChanged);
 }
 
 void eDVBServicePlay::updateDecoder()
 {
-       int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
-       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
-
-       bool defaultac3=false;
-       std::string default_ac3;
+       int vpid = -1, vpidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1;
 
-       if (!ePythonConfigQuery::getConfigValue("config.av.defaultac3", default_ac3))
-               defaultac3 = default_ac3 == "enable";
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
 
        eDVBServicePMTHandler::program program;
        if (h.getProgramInfo(program))
@@ -1578,11 +2291,14 @@ void eDVBServicePlay::updateDecoder()
                {
                        eDebugNoNewLine(" (");
                        for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
-                               i(program.videoStreams.begin()); 
+                               i(program.videoStreams.begin());
                                i != program.videoStreams.end(); ++i)
                        {
                                if (vpid == -1)
+                               {
                                        vpid = i->pid;
+                                       vpidtype = i->type;
+                               }
                                if (i != program.videoStreams.begin())
                                        eDebugNoNewLine(", ");
                                eDebugNoNewLine("%04x", i->pid);
@@ -1594,17 +2310,9 @@ void eDVBServicePlay::updateDecoder()
                {
                        eDebugNoNewLine(" (");
                        for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
-                               i(program.audioStreams.begin()); 
+                               i(program.audioStreams.begin());
                                i != program.audioStreams.end(); ++i)
                        {
-                               if (apid == -1 || (apidtype == eDVBAudio::aMPEG && defaultac3))
-                               {
-                                       if ( apid == -1 || (i->type != eDVBAudio::aMPEG) )
-                                       {
-                                               apid = i->pid;
-                                               apidtype = i->type;
-                                       }
-                               }
                                if (i != program.audioStreams.begin())
                                        eDebugNoNewLine(", ");
                                eDebugNoNewLine("%04x", i->pid);
@@ -1621,55 +2329,102 @@ void eDVBServicePlay::updateDecoder()
        {
                h.getDecodeDemux(m_decode_demux);
                if (m_decode_demux)
+               {
                        m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
+                       if (m_decoder)
+                               m_decoder->connectVideoEvent(slot(*this, &eDVBServicePlay::video_event), m_video_event_connection);
+                       m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
+                       m_teletext_parser->connectNewPage(slot(*this, &eDVBServicePlay::newSubtitlePage), m_new_subtitle_page_connection);
+                       m_subtitle_parser = new eDVBSubtitleParser(m_decode_demux);
+                       m_subtitle_parser->connectNewPage(slot(*this, &eDVBServicePlay::newDVBSubtitlePage), m_new_dvb_subtitle_page_connection);
+               } else
+               {
+                       m_teletext_parser = 0;
+                       m_subtitle_parser = 0;
+               }
+
                if (m_cue)
                        m_cue->setDecodingDemux(m_decode_demux, m_decoder);
-#ifdef INTERNAL_TELETEXT
-               m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
-#endif
        }
 
        if (m_decoder)
        {
-               m_decoder->setVideoPID(vpid);
-               m_current_audio_stream = 0;
-               m_decoder->setAudioPID(apid, apidtype);
+               if (m_dvb_service)
+               {
+                       achannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL);
+                       ac3_delay = m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
+                       pcm_delay = m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
+               }
+               else // subservice or recording
+               {
+                       eServiceReferenceDVB ref;
+                       m_service_handler.getServiceReference(ref);
+                       eServiceReferenceDVB parent = ref.getParentServiceReference();
+                       if (!parent)
+                               parent = ref;
+                       if (parent)
+                       {
+                               ePtr<eDVBResourceManager> res_mgr;
+                               if (!eDVBResourceManager::getInstance(res_mgr))
+                               {
+                                       ePtr<iDVBChannelList> db;
+                                       if (!res_mgr->getChannelList(db))
+                                       {
+                                               ePtr<eDVBService> origService;
+                                               if (!db->getService(parent, origService))
+                                               {
+                                                       ac3_delay = origService->getCacheEntry(eDVBService::cAC3DELAY);
+                                                       pcm_delay = origService->getCacheEntry(eDVBService::cPCMDELAY);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               m_decoder->setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
+               m_decoder->setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
+
+               m_decoder->setVideoPID(vpid, vpidtype);
+               selectAudioStream();
+
                if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
                        m_decoder->setSyncPCR(pcrpid);
                else
                        m_decoder->setSyncPCR(-1);
-#ifndef INTERNAL_TELETEXT
+
                m_decoder->setTextPID(tpid);
-#else
-               if (m_teletext_parser)
-                       m_teletext_parser->start(tpid);
-#endif
+
+               m_teletext_parser->start(program.textPid);
 
                if (!m_is_primary)
                        m_decoder->setTrickmode(1);
-               m_decoder->start();
-// how we can do this better?
-// update cache pid when the user changed the audio track or video track
-// TODO handling of difference audio types.. default audio types..
-                               
+
+               if (m_is_paused)
+                       m_decoder->preroll();
+               else
+                       m_decoder->start();
+
+               if (vpid > 0 && vpid < 0x2000)
+                       ;
+               else
+               {
+                       std::string radio_pic;
+                       if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic))
+                               m_decoder->setRadioPic(radio_pic);
+               }
+
+               m_decoder->setAudioChannel(achannel);
+
                /* don't worry about non-existing services, nor pvr services */
                if (m_dvb_service && !m_is_pvr)
                {
-                       if (apidtype == eDVBAudio::aMPEG)
-                       {
-                               m_dvb_service->setCachePID(eDVBService::cAPID, apid);
-                               m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
-                       }
-                       else
-                       {
-                               m_dvb_service->setCachePID(eDVBService::cAPID, -1);
-                               m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
-                       }
-                       m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
-                       m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
-                       m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
+                               /* (audio pid will be set in selectAudioTrack */
+                       m_dvb_service->setCacheEntry(eDVBService::cVPID, vpid);
+                       m_dvb_service->setCacheEntry(eDVBService::cVTYPE, vpidtype == eDVBVideo::MPEG2 ? -1 : vpidtype);
+                       m_dvb_service->setCacheEntry(eDVBService::cPCRPID, pcrpid);
+                       m_dvb_service->setCacheEntry(eDVBService::cTPID, tpid);
                }
-       }
+       }       
+       m_have_video_pid = (vpid > 0 && vpid < 0x2000);
 }
 
 void eDVBServicePlay::loadCuesheet()
@@ -1698,7 +2453,7 @@ void eDVBServicePlay::loadCuesheet()
 #endif
                        what = ntohl(what);
                        
-                       if (what > 2)
+                       if (what > 3)
                                break;
                        
                        m_cue_entries.insert(cueEntry(where, what));
@@ -1754,7 +2509,7 @@ void eDVBServicePlay::cutlistToCuesheet()
        if (!m_cutlist_enabled)
        {
                m_cue->commitSpans();
-               eDebug("cutlists where disabled");
+               eDebug("cutlists were disabled");
                return;
        }
 
@@ -1775,7 +2530,7 @@ void eDVBServicePlay::cutlistToCuesheet()
                                continue;
                        } else if (i->what == 1) /* out */
                                out = i++->where;
-                       else /* mark */
+                       else /* mark (2) or last play position (3) */
                        {
                                i++;
                                continue;
@@ -1793,6 +2548,406 @@ void eDVBServicePlay::cutlistToCuesheet()
        m_cue->commitSpans();
 }
 
+RESULT eDVBServicePlay::enableSubtitles(eWidget *parent, ePyObject tuple)
+{
+       if (m_subtitle_widget)
+               disableSubtitles(parent);
+
+       ePyObject entry;
+       int tuplesize = PyTuple_Size(tuple);
+       int type = 0;
+
+       if (!PyTuple_Check(tuple))
+               goto error_out;
+
+       if (tuplesize < 1)
+               goto error_out;
+
+       entry = PyTuple_GET_ITEM(tuple, 0);
+
+       if (!PyInt_Check(entry))
+               goto error_out;
+
+       type = PyInt_AsLong(entry);
+
+       if (type == 1)  // teletext subtitles
+       {
+               int page, magazine, pid;
+               if (tuplesize < 4)
+                       goto error_out;
+
+               if (!m_teletext_parser)
+               {
+                       eDebug("enable teletext subtitles.. no parser !!!");
+                       return -1;
+               }
+
+               entry = PyTuple_GET_ITEM(tuple, 1);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               pid = PyInt_AsLong(entry);
+
+               entry = PyTuple_GET_ITEM(tuple, 2);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               page = PyInt_AsLong(entry);
+
+               entry = PyTuple_GET_ITEM(tuple, 3);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               magazine = PyInt_AsLong(entry);
+
+               m_subtitle_widget = new eSubtitleWidget(parent);
+               m_subtitle_widget->resize(parent->size()); /* full size */
+               m_teletext_parser->setPageAndMagazine(page, magazine);
+               if (m_dvb_service)
+                       m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE,((pid&0xFFFF)<<16)|((page&0xFF)<<8)|(magazine&0xFF));
+       }
+       else if (type == 0)
+       {
+               int pid = 0, composition_page_id = 0, ancillary_page_id = 0;
+               if (!m_subtitle_parser)
+               {
+                       eDebug("enable dvb subtitles.. no parser !!!");
+                       return -1;
+               }
+               if (tuplesize < 4)
+                       goto error_out;
+
+               entry = PyTuple_GET_ITEM(tuple, 1);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               pid = PyInt_AsLong(entry);
+
+               entry = PyTuple_GET_ITEM(tuple, 2);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               composition_page_id = PyInt_AsLong(entry);
+
+               entry = PyTuple_GET_ITEM(tuple, 3);
+               if (!PyInt_Check(entry))
+                       goto error_out;
+               ancillary_page_id = PyInt_AsLong(entry);
+
+               m_subtitle_widget = new eSubtitleWidget(parent);
+               m_subtitle_widget->resize(parent->size()); /* full size */
+               m_subtitle_parser->start(pid, composition_page_id, ancillary_page_id);
+               if (m_dvb_service)
+                       m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE, ((pid&0xFFFF)<<16)|((composition_page_id&0xFF)<<8)|(ancillary_page_id&0xFF));
+       }
+       else
+               goto error_out;
+       return 0;
+error_out:
+       eDebug("enableSubtitles needs a tuple as 2nd argument!\n"
+               "for teletext subtitles (0, pid, teletext_page, teletext_magazine)\n"
+               "for dvb subtitles (1, pid, composition_page_id, ancillary_page_id)");
+       return -1;
+}
+
+RESULT eDVBServicePlay::disableSubtitles(eWidget *parent)
+{
+       delete m_subtitle_widget;
+       m_subtitle_widget = 0;
+       if (m_subtitle_parser)
+       {
+               m_subtitle_parser->stop();
+               m_dvb_subtitle_pages.clear();
+       }
+       if (m_teletext_parser)
+       {
+               m_teletext_parser->setPageAndMagazine(-1, -1);
+               m_subtitle_pages.clear();
+       }
+       if (m_dvb_service)
+               m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE, -1);
+       return 0;
+}
+
+PyObject *eDVBServicePlay::getCachedSubtitle()
+{
+       if (m_dvb_service)
+       {
+               int tmp = m_dvb_service->getCacheEntry(eDVBService::cSUBTITLE);
+               if (tmp != -1)
+               {
+                       unsigned int data = (unsigned int)tmp;
+                       int pid = (data&0xFFFF0000)>>16;
+                       ePyObject tuple = PyTuple_New(4);
+                       eDVBServicePMTHandler::program program;
+                       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+                       if (!h.getProgramInfo(program))
+                       {
+                               if (program.textPid==pid) // teletext
+                                       PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1)); // type teletext
+                               else
+                                       PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(0)); // type dvb
+                               PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong((data&0xFFFF0000)>>16)); // pid
+                               PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong((data&0xFF00)>>8)); // composition_page / page
+                               PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(data&0xFF)); // ancillary_page / magazine
+                               return tuple;
+                       }
+               }
+       }
+       Py_RETURN_NONE;
+}
+
+PyObject *eDVBServicePlay::getSubtitleList()
+{
+       if (!m_teletext_parser)
+               Py_RETURN_NONE;
+       
+       ePyObject l = PyList_New(0);
+       std::set<int> added_ttx_pages;
+
+       std::set<eDVBServicePMTHandler::subtitleStream> &subs =
+               m_teletext_parser->m_found_subtitle_pages;
+
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+       eDVBServicePMTHandler::program program;
+       if (h.getProgramInfo(program))
+               eDebug("getting program info failed.");
+       else
+       {
+               for (std::vector<eDVBServicePMTHandler::subtitleStream>::iterator it(program.subtitleStreams.begin());
+                       it != program.subtitleStreams.end(); ++it)
+               {
+                       switch(it->subtitling_type)
+                       {
+                               case 0x01: // ebu teletext subtitles
+                               {
+                                       int page_number = it->teletext_page_number & 0xFF;
+                                       int magazine_number = it->teletext_magazine_number & 7;
+                                       int hash = magazine_number << 8 | page_number;
+                                       if (added_ttx_pages.find(hash) == added_ttx_pages.end())
+                                       {
+                                               ePyObject tuple = PyTuple_New(5);
+                                               PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1));
+                                               PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+                                               PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(page_number));
+                                               PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(magazine_number));
+                                               PyTuple_SET_ITEM(tuple, 4, PyString_FromString(it->language_code.c_str()));
+                                               PyList_Append(l, tuple);
+                                               Py_DECREF(tuple);
+                                               added_ttx_pages.insert(hash);
+                                       }
+                                       break;
+                               }
+                               case 0x10 ... 0x13:
+                               case 0x20 ... 0x23: // dvb subtitles
+                               {
+                                       ePyObject tuple = PyTuple_New(5);
+                                       PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(0));
+                                       PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+                                       PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(it->composition_page_id));
+                                       PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(it->ancillary_page_id));
+                                       PyTuple_SET_ITEM(tuple, 4, PyString_FromString(it->language_code.c_str()));
+                                       PyList_Insert(l, 0, tuple);
+                                       Py_DECREF(tuple);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       for (std::set<eDVBServicePMTHandler::subtitleStream>::iterator it(subs.begin());
+               it != subs.end(); ++it)
+       {
+               int page_number = it->teletext_page_number & 0xFF;
+               int magazine_number = it->teletext_magazine_number & 7;
+               int hash = magazine_number << 8 | page_number;
+               if (added_ttx_pages.find(hash) == added_ttx_pages.end())
+               {
+                       ePyObject tuple = PyTuple_New(5);
+                       PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1));
+                       PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+                       PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(page_number));
+                       PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(magazine_number));
+                       PyTuple_SET_ITEM(tuple, 4, PyString_FromString("und"));  // undetermined
+                       PyList_Append(l, tuple);
+                       Py_DECREF(tuple);
+               }
+       }
+
+       return l;
+}
+
+void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
+{
+       if (m_subtitle_widget)
+       {
+               pts_t pos = 0;
+               if (m_decoder)
+                       m_decoder->getPTS(0, pos);
+               eDebug("got new subtitle page %lld %lld %d", pos, page.m_pts, page.m_have_pts);
+               m_subtitle_pages.push_back(page);
+               checkSubtitleTiming();
+       }
+}
+
+void eDVBServicePlay::checkSubtitleTiming()
+{
+       eDebug("checkSubtitleTiming");
+       if (!m_subtitle_widget)
+               return;
+       while (1)
+       {
+               enum { TELETEXT, DVB } type;
+               eDVBTeletextSubtitlePage page;
+               eDVBSubtitlePage dvb_page;
+               pts_t show_time;
+               if (!m_subtitle_pages.empty())
+               {
+                       page = m_subtitle_pages.front();
+                       type = TELETEXT;
+                       show_time = page.m_pts;
+               }
+               else if (!m_dvb_subtitle_pages.empty())
+               {
+                       dvb_page = m_dvb_subtitle_pages.front();
+                       type = DVB;
+                       show_time = dvb_page.m_show_time;
+               }
+               else
+                       return;
+       
+               pts_t pos = 0;
+       
+               if (m_decoder)
+                       m_decoder->getPTS(0, pos);
+
+               eDebug("%lld %lld", pos, show_time);
+               int diff =  show_time - pos;
+               if (diff < 0)
+               {
+                       eDebug("[late (%d ms)]", -diff / 90);
+                       diff = 0;
+               }
+//             if (diff > 900000)
+//             {
+//                     eDebug("[invalid]");
+//                     diff = 0;
+//             }
+       
+               if ((diff/90)<20)
+               {
+                       if (type == TELETEXT)
+                       {
+                               eDebug("display teletext subtitle page %lld", show_time);
+                               m_subtitle_widget->setPage(page);
+                               m_subtitle_pages.pop_front();
+                       }
+                       else
+                       {
+                               eDebug("display dvb subtitle Page %lld", show_time);
+                               m_subtitle_widget->setPage(dvb_page);
+                               m_dvb_subtitle_pages.pop_front();
+                       }
+               } else
+               {
+                       eDebug("start subtitle delay %d", diff / 90);
+                       m_subtitle_sync_timer.start(diff / 90, 1);
+                       break;
+               }
+       }
+}
+
+void eDVBServicePlay::newDVBSubtitlePage(const eDVBSubtitlePage &p)
+{
+       if (m_subtitle_widget)
+       {
+               pts_t pos = 0;
+               if (m_decoder)
+                       m_decoder->getPTS(0, pos);
+               eDebug("got new subtitle page %lld %lld", pos, p.m_show_time);
+               m_dvb_subtitle_pages.push_back(p);
+               checkSubtitleTiming();
+       }
+}
+
+int eDVBServicePlay::getAC3Delay()
+{
+       if (m_dvb_service)
+               return m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
+       else if (m_decoder)
+               return m_decoder->getAC3Delay();
+       else
+               return 0;
+}
+
+int eDVBServicePlay::getPCMDelay()
+{
+       if (m_dvb_service)
+               return m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
+       else if (m_decoder)
+               return m_decoder->getPCMDelay();
+       else
+               return 0;
+}
+
+void eDVBServicePlay::setAC3Delay(int delay)
+{
+       if (m_dvb_service)
+               m_dvb_service->setCacheEntry(eDVBService::cAC3DELAY, delay ? delay : -1);
+       if (m_decoder)
+               m_decoder->setAC3Delay(delay);
+}
+
+void eDVBServicePlay::setPCMDelay(int delay)
+{
+       if (m_dvb_service)
+               m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
+       if (m_decoder)
+               m_decoder->setPCMDelay(delay);
+}
+
+void eDVBServicePlay::video_event(struct iTSMPEGDecoder::videoEvent event)
+{
+       memcpy(&m_videoEventData, &event, sizeof(iTSMPEGDecoder::videoEvent));
+       m_event((iPlayableService*)this, evVideoSizeChanged);
+}
+
+RESULT eDVBServicePlay::stream(ePtr<iStreamableService> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
+PyObject *eDVBServicePlay::getStreamingData()
+{
+       eDVBServicePMTHandler::program program;
+       if (m_service_handler.getProgramInfo(program))
+       {
+               Py_INCREF(Py_None);
+               return Py_None;
+       }
+
+       PyObject *r = program.createPythonObject();
+       ePtr<iDVBDemux> demux;
+       if (!m_service_handler.getDataDemux(demux))
+       {
+               uint8_t demux_id;
+               demux->getCADemuxID(demux_id);
+               
+               PyDict_SetItemString(r, "demux", PyInt_FromLong(demux_id));
+       }
+
+       return r;
+}
+
+
 DEFINE_REF(eDVBServicePlay)
 
+PyObject *eDVBService::getInfoObject(const eServiceReference &ref, int w)
+{
+       switch (w)
+       {
+       case iServiceInformation::sTransponderData:
+               return eStaticServiceDVBInformation().getInfoObject(ref, w);
+       default:
+               break;
+       }
+       return iStaticServiceInformation::getInfoObject(ref, w);
+}
+
 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");