reenable old teletext subtitles
[enigma2.git] / lib / service / servicedvb.cpp
index 5f5aae65502472f789f797301117e1ba8d4c4bbd..89301de471f25fbaddbdeadc1ecc5d7536f4e9a2 100644 (file)
@@ -5,21 +5,29 @@
 #include <lib/service/service.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/components/file_eraser.h>
 #include <lib/service/servicedvbrecord.h>
+#include <lib/service/event.h>
 #include <lib/dvb/metaparser.h>
 #include <lib/dvb/tstools.h>
 #include <lib/python/python.h>
 
+               /* for subtitles */
+#include <lib/gui/esubtitle.h>
+
 #include <sys/vfs.h>
+#include <sys/stat.h>
 
 #include <byteswap.h>
 #include <netinet/in.h>
 
+#define INTERNAL_TELETEXT
+
 #ifndef BYTE_ORDER
 #error no byte order defined!
 #endif
@@ -140,7 +148,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 getInfo(const eServiceReference &ref, int w);
        std::string getInfoString(const eServiceReference &ref,int w);
 };
@@ -182,6 +191,8 @@ int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w
        {
        case iServiceInformation::sDescription:
                return iServiceInformation::resIsString;
+       case iServiceInformation::sServiceref:
+               return iServiceInformation::resIsString;
        case iServiceInformation::sTimeCreate:
                if (m_parser.m_time_create)
                        return m_parser.m_time_create;
@@ -198,11 +209,31 @@ std::string eStaticServiceDVBPVRInformation::getInfoString(const eServiceReferen
        {
        case iServiceInformation::sDescription:
                return m_parser.m_description;
+       case iServiceInformation::sServiceref:
+               return m_parser.m_ref.toString();
        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);
@@ -230,11 +261,17 @@ RESULT eDVBPVRServiceOfflineOperations::deleteFromDisk(int simulate)
                if (getListOfFilenames(res))
                        return -1;
                
-                               /* TODO: deferred removing.. */
+               eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
+               if (!eraser)
+                       eDebug("FATAL !! can't get background file eraser");
+               
                for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
                {
                        eDebug("Removing %s...", i->c_str());
-                       ::unlink(i->c_str());
+                       if (eraser)
+                               eraser->erase(i->c_str());
+                       else
+                               ::unlink(i->c_str());
                }
                
                return 0;
@@ -245,7 +282,25 @@ RESULT eDVBPVRServiceOfflineOperations::getListOfFilenames(std::list<std::string
 {
        res.clear();
        res.push_back(m_ref.path);
+
+// handling for old splitted recordings (enigma 1)
+       char buf[255];
+       int slice=1;
+       while(true)
+       {
+               snprintf(buf, 255, "%s.%03d", m_ref.path.c_str(), slice++);
+               struct stat s;
+               if (stat(buf, &s) < 0)
+                       break;
+               res.push_back(buf);
+       }       
+
        res.push_back(m_ref.path + ".meta");
+       res.push_back(m_ref.path + ".ap");
+       res.push_back(m_ref.path + ".cuts");
+       std::string tmp = m_ref.path;
+       tmp.erase(m_ref.path.length()-3);
+       res.push_back(tmp + ".eit");
        return 0;
 }
 
@@ -258,6 +313,9 @@ eServiceFactoryDVB::eServiceFactoryDVB()
        eServiceCenter::getPrivInstance(sc);
        if (sc)
                sc->addServiceFactory(eServiceFactoryDVB::id, this);
+
+       m_StaticServiceDVBInfo = new eStaticServiceDVBInformation;
+       m_StaticServiceDVBBouquetInfo = new eStaticServiceDVBBouquetInformation;
 }
 
 eServiceFactoryDVB::~eServiceFactoryDVB()
@@ -317,31 +375,6 @@ RESULT eDVBServiceList::startQuery()
        return 0;
 }
 
-RESULT eDVBServiceList::getContent(PyObject *list, bool sorted)
-{
-       eServiceReferenceDVB ref;
-
-       if (!m_query || !list || !PyList_Check(list))
-               return -1;
-
-       std::list<eServiceReferenceDVB> tmplist;
-
-       while (!m_query->getNextResult(ref))
-               tmplist.push_back(ref);
-
-       if (sorted)
-               tmplist.sort(iListableServiceCompare(this));
-
-       for (std::list<eServiceReferenceDVB>::iterator it(tmplist.begin());
-               it != tmplist.end(); ++it)
-       {
-               PyObject *refobj = New_eServiceReference(*it);
-               PyList_Append(list, refobj);
-               Py_DECREF(refobj);
-       }
-       return 0;
-}
-
 RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
 {
        eServiceReferenceDVB ref;
@@ -358,6 +391,95 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sort
        return 0;
 }
 
+//   The first argument of this function is a format string to specify the order and
+//   the content of the returned list
+//   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)
+//   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
+//   python tuples
+//   unknown format string chars are returned as python None values !
+PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
+{
+       PyObject *ret=0;
+       std::list<eServiceReference> tmplist;
+       int retcount=1;
+
+       if (!format || !(retcount=strlen(format)))
+               format = "R"; // just return service reference swig object ...
+
+       if (!getContent(tmplist, sorted))
+       {
+               int services=tmplist.size();
+               ePtr<iStaticServiceInformation> sptr;
+               eServiceCenterPtr service_center;
+
+               if (strchr(format, 'N'))
+                       eServiceCenter::getPrivInstance(service_center);
+
+               ret = PyList_New(services);
+               std::list<eServiceReference>::iterator it(tmplist.begin());
+
+               for (int cnt=0; cnt < services; ++cnt)
+               {
+                       eServiceReference &ref=*it++;
+                       PyObject *tuple = retcount > 1 ? PyTuple_New(retcount) : 0;
+                       for (int i=0; i < retcount; ++i)
+                       {
+                               PyObject *tmp=0;
+                               switch(format[i])
+                               {
+                               case 'R':  // service reference (swig)object
+                                       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());
+                                       break;
+                               case 'N':  // service name
+                                       if (service_center)
+                                       {
+                                               service_center->info(ref, sptr);
+                                               if (sptr)
+                                               {
+                                                       std::string name;
+                                                       sptr->getName(ref, name);
+                                                       if (name.length())
+                                                               tmp = PyString_FromString(name.c_str());
+                                               }
+                                       }
+                                       if (!tmp)
+                                               tmp = PyString_FromString("<n/a>");
+                                       break;
+                               default:
+                                       if (tuple)
+                                       {
+                                               tmp = Py_None;
+                                               Py_INCREF(Py_None);
+                                       }
+                                       break;
+                               }
+                               if (tmp)
+                               {
+                                       if (tuple)
+                                               PyTuple_SET_ITEM(tuple, i, tmp);
+                                       else
+                                               PyList_SET_ITEM(ret, cnt, tmp);
+                               }
+                       }
+                       if (tuple)
+                               PyList_SET_ITEM(ret, cnt, tuple);
+               }
+       }
+       return ret ? ret : PyList_New(0);
+}
+
 RESULT eDVBServiceList::getNext(eServiceReference &ref)
 {
        if (!m_query)
@@ -392,11 +514,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)
@@ -470,9 +592,9 @@ RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServic
        if ((ref.flags & eServiceReference::flagDirectory) == eServiceReference::flagDirectory) // 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);
@@ -480,7 +602,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;
@@ -533,9 +655,10 @@ 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)
 {
-       m_is_pvr = !ref.path.empty();
+       m_is_primary = 1;
+       m_is_pvr = !m_reference.path.empty();
        
        m_timeshift_enabled = m_timeshift_active = 0;
        m_skipmode = 0;
@@ -545,13 +668,16 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
        CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
 
        m_cuesheet_changed = 0;
+       m_cutlist_enabled = 1;
        
-       if (m_is_pvr)
-               loadCuesheet();
+       m_subtitle_widget = 0;
+       
+       CONNECT(m_subtitle_sync_timer.timeout, eDVBServicePlay::checkSubtitleTiming);
 }
 
 eDVBServicePlay::~eDVBServicePlay()
 {
+       delete m_subtitle_widget;
 }
 
 void eDVBServicePlay::gotNewEvent()
@@ -629,6 +755,9 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                if (m_timeshift_active)
                        updateDecoder();
                break;
+       case eDVBServicePMTHandler::eventSOF:
+               m_event((iPlayableService*)this, evSOF);
+               break;
        case eDVBServicePMTHandler::eventEOF:
                switchToLive();
                break;
@@ -641,9 +770,31 @@ RESULT eDVBServicePlay::start()
                /* in pvr mode, we only want to use one demux. in tv mode, we're using 
                   two (one for decoding, one for data source), as we must be prepared
                   to start recording from the data demux. */
-       m_cue = new eCueSheet();
+       if (m_is_pvr)
+               m_cue = new eCueSheet();
+
        m_first_program_info = 1;
-       r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr, m_cue);
+       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";
+               ePtr<eServiceEvent> event = new eServiceEvent;
+               if (!event->parseFrom(filename, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get()))
+               {
+                       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);
        return 0;
@@ -651,17 +802,51 @@ RESULT eDVBServicePlay::start()
 
 RESULT eDVBServicePlay::stop()
 {
+               /* add bookmark for last play position */
+       if (m_is_pvr)
+       {
+               pts_t play_position;
+               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;
+                       }
+                       
+                       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();
+       }
        
        return 0;
 }
 
+RESULT eDVBServicePlay::setTarget(int target)
+{
+       m_is_primary = !target;
+       return 0;
+}
+
 RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
 {
        connection = new eConnection((iPlayableService*)this, m_event.connect(event));
@@ -802,12 +987,18 @@ RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
        if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
+       int mode = 1;
+       
+                       /* HACK until we have skip-AP api */
+       if ((to > 0) && (to < 100))
+               mode = 2;
+       
        to *= direction;
        
        if (!m_cue)
                return 0;
        
-       m_cue->seekTo(1, to);
+       m_cue->seekTo(mode, to);
        return 0;
 }
 
@@ -847,7 +1038,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;
@@ -859,6 +1050,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;
@@ -874,7 +1071,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)
                {
@@ -909,6 +1107,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::radioText(ePtr<iRadioText> &ptr)
+{
+       ptr = this;
+       return 0;
+}
+
 RESULT eDVBServicePlay::getName(std::string &name)
 {
        if (m_is_pvr)
@@ -938,7 +1154,12 @@ int eDVBServicePlay::getInfo(int w)
 {
        eDVBServicePMTHandler::program program;
 
-       if (m_service_handler.getProgramInfo(program))
+       if (w == sCAIDs)
+               return resIsPyObject;
+
+       eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+       
+       if (h.getProgramInfo(program))
                return -1;
        
        switch (w)
@@ -982,9 +1203,10 @@ int eDVBServicePlay::getInfo(int w)
                        }
                }
                return -1;
-       case sIsCrypted: return program.isCrypted;
+       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 sVideoType: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].type;
+       case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[0].pid;
        case sPCRPID: return program.pcrPid;
        case sPMTPID: return program.pmtPid;
        case sTXTPID: return program.textPid;
@@ -999,21 +1221,35 @@ int eDVBServicePlay::getInfo(int w)
 }
 
 std::string eDVBServicePlay::getInfoString(int w)
-{      
+{
        switch (w)
        {
        case sProvider:
                if (!m_dvb_service) return "";
                return m_dvb_service->m_provider_name;
        default:
-               return "";
+               break;
+       }
+       return iServiceInformation::getInfoString(w);
+}
+
+PyObject *eDVBServicePlay::getInfoObject(int w)
+{
+       switch (w)
+       {
+       case sCAIDs:
+               return m_service_handler.getCaIds();
+       default:
+               break;
        }
+       return iServiceInformation::getInfoObject(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();
 }
@@ -1031,8 +1267,9 @@ 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())
@@ -1042,6 +1279,8 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int
                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
@@ -1067,8 +1306,9 @@ 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())
@@ -1080,28 +1320,60 @@ int eDVBServicePlay::selectAudioStream(int i)
        if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
                return -4;
 
+       if (m_radiotext_parser)
+               m_radiotext_parser->start(program.audioStreams[i].pid);
+
        if (m_dvb_service && !m_is_pvr)
        {
                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->setCacheEntry(eDVBService::cAPID, program.audioStreams[i].pid);
+                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
+               }
+               else
                {
-                       m_dvb_service->setCachePID(eDVBService::cAPID, -1);
-                       m_dvb_service->setCachePID(eDVBService::cAC3PID, program.audioStreams[i].pid);
+                       m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
+                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, program.audioStreams[i].pid);
                }
        }
 
-       m_current_audio_stream = i;
+       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;
 }
 
-int eDVBServicePlay::getFrontendInfo(int w)
+std::string eDVBServicePlay::getRadioText(int x)
+{
+       if (m_radiotext_parser)
+               switch(x)
+               {
+                       case 0:
+                               return m_radiotext_parser->getCurrentText();
+               }
+       return "";
+}
+
+void eDVBServicePlay::radioTextUpdated()
+{
+       m_event((iPlayableService*)this, evUpdatedRadioText);
+}
+
+int eDVBServiceBase::getFrontendInfo(int w)
 {
-       if (m_is_pvr)
-               return 0;
        eUsePtr<iDVBChannel> channel;
        if(m_service_handler.getChannel(channel))
                return 0;
@@ -1111,6 +1383,52 @@ int eDVBServicePlay::getFrontendInfo(int w)
        return fe->readFrontendData(w);
 }
 
+PyObject *eDVBServiceBase::getFrontendData(bool original)
+{
+       PyObject *ret=0;
+
+       eUsePtr<iDVBChannel> channel;
+       if(!m_service_handler.getChannel(channel))
+       {
+               ePtr<iDVBFrontend> fe;
+               if(!channel->getFrontend(fe))
+               {
+                       ret = fe->readTransponderData(original);
+                       if (ret)
+                       {
+                               ePtr<iDVBFrontendParameters> feparm;
+                               channel->getCurrentFrontendParameters(feparm);
+                               if (feparm)
+                               {
+                                       eDVBFrontendParametersSatellite osat;
+                                       if (!feparm->getDVBS(osat))
+                                       {
+                                               void PutToDict(PyObject *, const char*, long);
+                                               void PutToDict(PyObject *, const char*, const char*);
+                                               PutToDict(ret, "orbital_position", osat.orbital_position);
+                                               const char *tmp = "UNKNOWN";
+                                               switch(osat.polarisation)
+                                               {
+                                                       case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
+                                                       case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
+                                                       case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR_LEFT"; break;
+                                                       case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR_RIGHT"; break;
+                                                       default:break;
+                                               }
+                                               PutToDict(ret, "polarization", tmp);
+                                       }
+                               }
+                       }
+               }
+       }
+       if (!ret)
+       {
+               ret = Py_None;
+               Py_INCREF(ret);
+       }
+       return ret;
+}
+
 int eDVBServicePlay::getNumberOfSubservices()
 {
        ePtr<eServiceEvent> evt;
@@ -1258,6 +1576,15 @@ void eDVBServicePlay::setCutList(PyObject *list)
                eDebug("adding %08llx, %d", pts, type);
        }
        m_cuesheet_changed = 1;
+       
+       cutlistToCuesheet();
+       m_event((iPlayableService*)this, evCuesheetChanged);
+}
+
+void eDVBServicePlay::setCutListEnable(int enable)
+{
+       m_cutlist_enabled = enable;
+       cutlistToCuesheet();
 }
 
 void eDVBServicePlay::updateTimeshiftPids()
@@ -1266,7 +1593,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
        {
@@ -1313,14 +1642,22 @@ void eDVBServicePlay::switchToLive()
        if (!m_timeshift_active)
                return;
        
+       m_cue = 0;
        m_decoder = 0;
        m_decode_demux = 0;
+       m_teletext_parser = 0;
+       m_radiotext_parser = 0;
+       m_subtitle_parser = 0;
+       m_new_dvb_subtitle_page_connection = 0;
+       m_new_subtitle_page_connection = 0;
+       m_radiotext_updated_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();
 }
 
@@ -1328,25 +1665,40 @@ void eDVBServicePlay::switchToTimeshift()
 {
        if (m_timeshift_active)
                return;
-       
+
        m_decode_demux = 0;
        m_decoder = 0;
-       
+       m_teletext_parser = 0;
+       m_radiotext_parser = 0;
+       m_subtitle_parser = 0;
+       m_new_subtitle_page_connection = 0;
+       m_new_dvb_subtitle_page_connection = 0;
+       m_radiotext_updated_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 */
 }
 
 void eDVBServicePlay::updateDecoder()
 {
-       int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
+       int vpid = -1, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1;
+
        eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
 
+       bool defaultac3=false;
+       std::string default_ac3;
+
+       if (!ePythonConfigQuery::getConfigValue("config.av.defaultac3", default_ac3))
+               defaultac3 = default_ac3 == "True";
+
        eDVBServicePMTHandler::program program;
        if (h.getProgramInfo(program))
                eDebug("getting program info failed.");
@@ -1357,11 +1709,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);
@@ -1373,13 +1728,16 @@ 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)
+                               if (apid == -1 || (apidtype == eDVBAudio::aMPEG && defaultac3))
                                {
-                                       apid = i->pid;
-                                       apidtype = i->type;
+                                       if ( apid == -1 || (i->type != eDVBAudio::aMPEG) )
+                                       {
+                                               apid = i->pid;
+                                               apidtype = i->type;
+                                       }
                                }
                                if (i != program.audioStreams.begin())
                                        eDebugNoNewLine(", ");
@@ -1397,22 +1755,92 @@ void eDVBServicePlay::updateDecoder()
        {
                h.getDecodeDemux(m_decode_demux);
                if (m_decode_demux)
-                       m_decode_demux->getMPEGDecoder(m_decoder);
+                       m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
                if (m_cue)
-                       m_cue->setDecodingDemux(m_decode_demux);
+                       m_cue->setDecodingDemux(m_decode_demux, m_decoder);
+#ifdef INTERNAL_TELETEXT
+               m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
+               m_teletext_parser->connectNewPage(slot(*this, &eDVBServicePlay::newSubtitlePage), m_new_subtitle_page_connection);
+#endif
+               m_subtitle_parser = new eDVBSubtitleParser(m_decode_demux);
+               m_subtitle_parser->connectNewPage(slot(*this, &eDVBServicePlay::newDVBSubtitlePage), m_new_dvb_subtitle_page_connection);
        }
 
        if (m_decoder)
        {
-               m_decoder->setVideoPID(vpid);
-               m_current_audio_stream = 0;
+               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);
                m_decoder->setAudioPID(apid, apidtype);
-               if (!(m_is_pvr || m_timeshift_active))
+               if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+               {
                        m_decoder->setSyncPCR(pcrpid);
+                       if (apid != -1)
+                       {
+                               ePtr<iDVBDemux> data_demux;
+                               if (!h.getDataDemux(data_demux))
+                               {
+                                       m_radiotext_parser = new eDVBRadioTextParser(data_demux);
+                                       m_radiotext_parser->connectUpdatedRadiotext(slot(*this, &eDVBServicePlay::radioTextUpdated), m_radiotext_updated_connection);
+                                       m_radiotext_parser->start(apid);
+                               }
+                       }
+               }
                else
                        m_decoder->setSyncPCR(-1);
+
                m_decoder->setTextPID(tpid);
+
+               m_teletext_parser->start(program.textPid);
+
+               if (!m_is_primary)
+                       m_decoder->setTrickmode(1);
+
                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);
+
 // 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..
@@ -1422,19 +1850,21 @@ void eDVBServicePlay::updateDecoder()
                {
                        if (apidtype == eDVBAudio::aMPEG)
                        {
-                               m_dvb_service->setCachePID(eDVBService::cAPID, apid);
-                               m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
+                               m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
+                               m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
                        }
                        else
                        {
-                               m_dvb_service->setCachePID(eDVBService::cAPID, -1);
-                               m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
+                               m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
+                               m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
                        }
-                       m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
-                       m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
-                       m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
+                       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()
@@ -1463,7 +1893,7 @@ void eDVBServicePlay::loadCuesheet()
 #endif
                        what = ntohl(what);
                        
-                       if (what > 2)
+                       if (what > 3)
                                break;
                        
                        m_cue_entries.insert(cueEntry(where, what));
@@ -1474,6 +1904,8 @@ void eDVBServicePlay::loadCuesheet()
                eDebug("cutfile not found!");
        
        m_cuesheet_changed = 0;
+       cutlistToCuesheet();
+       m_event((iPlayableService*)this, evCuesheetChanged);
 }
 
 void eDVBServicePlay::saveCuesheet()
@@ -1505,6 +1937,267 @@ void eDVBServicePlay::saveCuesheet()
        m_cuesheet_changed = 0;
 }
 
+void eDVBServicePlay::cutlistToCuesheet()
+{
+       if (!m_cue)
+       {
+               eDebug("no cue sheet");
+               return;
+       }       
+       m_cue->clear();
+       
+       if (!m_cutlist_enabled)
+       {
+               m_cue->commitSpans();
+               eDebug("cutlists were disabled");
+               return;
+       }
+
+       pts_t in = 0, out = 0, length = 0;
+       
+       getLength(length);
+               
+       std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
+       
+       while (1)
+       {
+               if (i == m_cue_entries.end())
+                       out = length;
+               else {
+                       if (i->what == 0) /* in */
+                       {
+                               in = i++->where;
+                               continue;
+                       } else if (i->what == 1) /* out */
+                               out = i++->where;
+                       else /* mark (2) or last play position (3) */
+                       {
+                               i++;
+                               continue;
+                       }
+               }
+               
+               if (in != out)
+                       m_cue->addSourceSpan(in, out);
+               
+               in = length;
+               
+               if (i == m_cue_entries.end())
+                       break;
+       }
+       m_cue->commitSpans();
+}
+
+RESULT eDVBServicePlay::enableSubtitles(eWidget *parent, PyObject *entry)
+{
+       if (m_subtitle_widget)
+               disableSubtitles(parent);
+       
+       if (!PyInt_Check(entry))
+               return -1;
+
+       int page = PyInt_AsLong(entry);
+
+       if (page > 0 && !m_teletext_parser)
+               return -1;
+       if (page < 0 && !m_subtitle_parser)
+               return -1;
+
+       m_subtitle_widget = new eSubtitleWidget(parent);
+       m_subtitle_widget->resize(parent->size()); /* full size */
+
+       if (page > 0)
+       {
+/*             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
+               {
+                       eDebug("start teletext on pid %04x, page %d", program.textPid, page);
+                       m_teletext_parser->start(program.textPid);*/
+                       m_teletext_parser->setPage(page);
+//             }
+       }
+       else
+       {
+               int pid = -page;
+               eDebug("start dvb subtitles on pid %04x", pid);
+               m_subtitle_parser->start(pid);
+       }
+
+       return 0;
+}
+
+RESULT eDVBServicePlay::disableSubtitles(eWidget *parent)
+{
+       delete m_subtitle_widget;
+       m_subtitle_widget = 0;
+       return 0;
+}
+
+PyObject *eDVBServicePlay::getSubtitleList()
+{
+       if (!m_teletext_parser)
+       {
+               Py_INCREF(Py_None);
+               return Py_None;
+       }
+       
+       PyObject *l = PyList_New(0);
+       
+       for (std::set<int>::iterator i(m_teletext_parser->m_found_subtitle_pages.begin()); i != m_teletext_parser->m_found_subtitle_pages.end(); ++i)
+       {
+               PyObject *tuple = PyTuple_New(2);
+               char desc[20];
+               sprintf(desc, "Page %x", *i);
+               PyTuple_SetItem(tuple, 0, PyString_FromString(desc));
+               PyTuple_SetItem(tuple, 1, PyInt_FromLong(*i));
+               PyList_Append(l, tuple);
+               Py_DECREF(tuple);
+       }
+
+       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)
+               {
+                       PyObject *tuple = PyTuple_New(2);
+                       char desc[20];
+                       sprintf(desc, "DVB %s", it->language_code.c_str());
+                       PyTuple_SetItem(tuple, 0, PyString_FromString(desc));
+                       PyTuple_SetItem(tuple, 1, PyInt_FromLong(-it->pid));
+                       PyList_Append(l, tuple);
+                       Py_DECREF(tuple);
+               }
+       }
+
+       return l;
+}
+
+void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
+{
+       if (m_subtitle_widget)
+       {
+               m_subtitle_pages.push_back(page);
+               checkSubtitleTiming();
+       }
+}
+
+void eDVBServicePlay::checkSubtitleTiming()
+{
+//     eDebug("checkSubtitleTiming");
+       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)
+               {
+                       if (type == TELETEXT)
+                       {
+                               eDebug("display teletext subtitle page");
+                               m_subtitle_widget->setPage(page);
+                               m_subtitle_pages.pop_front();
+                       }
+                       else
+                       {
+                               eDebug("display dvb subtitle Page");
+                               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)
+       {
+               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);
+}
+
 DEFINE_REF(eDVBServicePlay)
 
 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");