do not store default values in cache
[enigma2.git] / lib / service / servicedvb.cpp
index fc35b5217e0952ca7707ae513fdbbc8c39e35a46..b99357c55c2781867731b29a6693995b9b6548a3 100644 (file)
@@ -5,13 +5,30 @@
 #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>
+
+#include <sys/vfs.h>
+
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#include <dvbsi++/event_information_section.h>
+
+#ifndef BYTE_ORDER
+#error no byte order defined!
+#endif
+
+#define TSPATH "/media/hdd"
 
 class eStaticServiceDVBInformation: public iStaticServiceInformation
 {
@@ -169,6 +186,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;
@@ -185,6 +204,8 @@ 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 "";
        }
@@ -217,11 +238,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;
@@ -232,7 +259,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;
 }
 
@@ -304,7 +349,7 @@ RESULT eDVBServiceList::startQuery()
        return 0;
 }
 
-RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list)
+RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
 {
        eServiceReferenceDVB ref;
        
@@ -313,9 +358,98 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list)
        
        while (!m_query->getNextResult(ref))
                list.push_back(ref);
+
+       if (sorted)
+               list.sort(iListableServiceCompare(this));
+
        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())
+//   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 '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)
@@ -378,6 +512,13 @@ RESULT eDVBServiceList::flushChanges()
        return m_bouquet->flushChanges();
 }
 
+RESULT eDVBServiceList::setListName(const std::string &name)
+{
+       if (!m_bouquet)
+               return -1;
+       return m_bouquet->setListName(name);
+}
+
 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
 {
        ePtr<eDVBService> service;
@@ -484,13 +625,20 @@ RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServ
 }
 
 eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): 
-       m_reference(ref), m_dvb_service(service), m_service_handler(0), m_is_paused(0)
+       m_reference(ref), m_dvb_service(service), m_is_paused(0)
 {
-       m_is_pvr = !ref.path.empty();
-       m_timeshift_enabled = 0;
+       m_is_primary = 1;
+       m_is_pvr = !m_reference.path.empty();
+       
+       m_timeshift_enabled = m_timeshift_active = 0;
+       m_skipmode = 0;
        
        CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
+       CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
        CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
+
+       m_cuesheet_changed = 0;
+       m_cutlist_enabled = 1;
 }
 
 eDVBServicePlay::~eDVBServicePlay()
@@ -520,7 +668,7 @@ void eDVBServicePlay::serviceEvent(int event)
        case eDVBServicePMTHandler::eventTuned:
        {
                ePtr<iDVBDemux> m_demux;
-               if (!m_service_handler.getDemux(m_demux))
+               if (!m_service_handler.getDataDemux(m_demux))
                {
                        eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_reference;
                        int sid = ref.getParentServiceID().get();
@@ -542,101 +690,107 @@ void eDVBServicePlay::serviceEvent(int event)
        }
        case eDVBServicePMTHandler::eventNewProgramInfo:
        {
-               int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1;
-               eDVBServicePMTHandler::program program;
-               if (m_service_handler.getProgramInfo(program))
-                       eDebug("getting program info failed.");
-               else
+               eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active);
+               if (m_timeshift_enabled)
+                       updateTimeshiftPids();
+               if (!m_timeshift_active)
+                       updateDecoder();
+               if (m_first_program_info && m_is_pvr)
                {
-                       eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
-                       if (!program.videoStreams.empty())
-                       {
-                               eDebugNoNewLine(" (");
-                               for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
-                                       i(program.videoStreams.begin()); 
-                                       i != program.videoStreams.end(); ++i)
-                               {
-                                       if (vpid == -1)
-                                               vpid = i->pid;
-                                       if (i != program.videoStreams.begin())
-                                               eDebugNoNewLine(", ");
-                                       eDebugNoNewLine("%04x", i->pid);
-                               }
-                               eDebugNoNewLine(")");
-                       }
-                       eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
-                       if (!program.audioStreams.empty())
-                       {
-                               eDebugNoNewLine(" (");
-                               for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
-                                       i(program.audioStreams.begin()); 
-                                       i != program.audioStreams.end(); ++i)
-                               {
-                                       if (apid == -1)
-                                       {
-                                               apid = i->pid;
-                                               apidtype = i->type;
-                                       }
-                                       if (i != program.audioStreams.begin())
-                                               eDebugNoNewLine(", ");
-                                       eDebugNoNewLine("%04x", i->pid);
-                               }
-                               eDebugNoNewLine(")");
-                       }
-                       eDebug(", and the pcr pid is %04x", program.pcrPid);
-                       if (program.pcrPid != 0x1fff)
-                               pcrpid = program.pcrPid;
-               }
-               
-               if (!m_decoder)
-               {
-                       ePtr<iDVBDemux> demux;
-                       m_service_handler.getDemux(demux);
-                       if (demux)
-                               demux->getMPEGDecoder(m_decoder);
+                       m_first_program_info = 0;
+                       seekTo(0);
                }
-
-               if (m_decoder)
-               {
-                       m_decoder->setVideoPID(vpid);
-                       m_current_audio_stream = 0;
-                       m_decoder->setAudioPID(apid, apidtype);
-                       if (!m_is_pvr)
-                               m_decoder->setSyncPCR(pcrpid);
-                       else
-                               m_decoder->setSyncPCR(-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..
-                               
-                               /* don't worry about non-existing services, nor pvr services */
-                       if (m_dvb_service && !m_is_pvr)
-                       {
-                               m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
-                               m_dvb_service->setCachePID(eDVBService::cAPID, apid);
-                               m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
-                       }
-               }
-               
+               m_event((iPlayableService*)this, evUpdatedInfo);
                break;
        }
+       case eDVBServicePMTHandler::eventEOF:
+               m_event((iPlayableService*)this, evEOF);
+               break;
+       case eDVBServicePMTHandler::eventSOF:
+               m_event((iPlayableService*)this, evSOF);
+               break;
+       }
+}
+
+void eDVBServicePlay::serviceEventTimeshift(int event)
+{
+       switch (event)
+       {
+       case eDVBServicePMTHandler::eventNewProgramInfo:
+               if (m_timeshift_active)
+                       updateDecoder();
+               break;
+       case eDVBServicePMTHandler::eventSOF:
+               m_event((iPlayableService*)this, evSOF);
+               break;
+       case eDVBServicePMTHandler::eventEOF:
+               switchToLive();
+               break;
        }
 }
 
 RESULT eDVBServicePlay::start()
 {
        int r;
-       eDebug("starting DVB service");
-       r = m_service_handler.tune((eServiceReferenceDVB&)m_reference);
-       eDebug("tune result: %d", r);
+               /* 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. */
+       if (m_is_pvr)
+               m_cue = new eCueSheet();
+
+       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 )
+               {
+                       __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");
+                       }
+               }
+       }
+       
+       if (m_is_pvr)
+               loadCuesheet();
+
        m_event(this, evStart);
+       m_event((iPlayableService*)this, evSeekableStatusChanged);
        return 0;
 }
 
 RESULT eDVBServicePlay::stop()
 {
-       eDebug("stopping..");
+       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();
+       
+       return 0;
+}
+
+RESULT eDVBServicePlay::setTarget(int target)
+{
+       m_is_primary = !target;
        return 0;
 }
 
@@ -648,7 +802,10 @@ RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &ev
 
 RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
 {
-       if (!m_is_pvr)
+               /* note: we check for timeshift to be enabled,
+                  not neccessary active. if you pause when timeshift
+                  is not active, you should activate it when unpausing */
+       if ((!m_is_pvr) && (!m_timeshift_enabled))
        {
                ptr = 0;
                return -1;
@@ -668,15 +825,44 @@ RESULT eDVBServicePlay::setSlowMotion(int ratio)
 
 RESULT eDVBServicePlay::setFastForward(int ratio)
 {
-       if (m_decoder)
-               return m_decoder->setFastForward(ratio);
-       else
+       int skipmode, ffratio;
+       
+       if (ratio > 8)
+       {
+               skipmode = ratio;
+               ffratio = 1;
+       } else if (ratio > 0)
+       {
+               skipmode = 0;
+               ffratio = ratio;
+       } else if (!ratio)
+       {
+               skipmode = 0;
+               ffratio = 0;
+       } else // if (ratio < 0)
+       {
+               skipmode = ratio;
+               ffratio = 1;
+       }
+
+       if (m_skipmode != skipmode)
+       {
+               eDebug("setting cue skipmode to %d", skipmode);
+               if (m_cue)
+                       m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
+       }
+       
+       m_skipmode = skipmode;
+       
+       if (!m_decoder)
                return -1;
+
+       return m_decoder->setFastForward(ffratio);
 }
     
 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
 {
-       if (m_is_pvr)
+       if (m_is_pvr || m_timeshift_enabled)
        {
                ptr = this;
                return 0;
@@ -686,15 +872,13 @@ RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
        return -1;
 }
 
+       /* TODO: when timeshift is enabled but not active, this doesn't work. */
 RESULT eDVBServicePlay::getLength(pts_t &len)
 {
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
-       {
-               eDebug("getPVRChannel failed!");
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
-       }
        
        return pvr_channel->getLength(len);
 }
@@ -721,50 +905,86 @@ RESULT eDVBServicePlay::unpause()
 
 RESULT eDVBServicePlay::seekTo(pts_t to)
 {
-       return -1;
+       eDebug("eDVBServicePlay::seekTo: jump %lld", to);
+       
+       if (!m_decode_demux)
+               return -1;
+
+       ePtr<iDVBPVRChannel> pvr_channel;
+       
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
+               return -1;
+       
+       if (!m_cue)
+               return -1;
+       
+       m_cue->seekTo(0, to);
+       return 0;
 }
 
 RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
 {
        eDebug("eDVBServicePlay::seekRelative: jump %d, %lld", direction, to);
+       
+       if (!m_decode_demux)
+               return -1;
 
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
+       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;
        
-       ePtr<iDVBDemux> demux;
-       m_service_handler.getDemux(demux);
-       if (!demux)
-               return -1;
+       if (!m_cue)
+               return 0;
        
-       return pvr_channel->seekTo(demux, 1, to);
+       m_cue->seekTo(mode, to);
+       return 0;
 }
 
 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
 {
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
+       if (!m_decode_demux)
                return -1;
        
-       ePtr<iDVBDemux> demux;
-       m_service_handler.getDemux(demux);
-       if (!demux)
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
-       return pvr_channel->getCurrentPosition(demux, pos);
+       int r = 0;
+
+               /* if there is a decoder, use audio or video PTS */
+       if (m_decoder)
+       {
+               r = m_decoder->getPTS(0, pos);
+               if (r)
+                       return r;
+       }
+       
+               /* fixup */
+       return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
 }
 
-RESULT eDVBServicePlay::setTrickmode(int trick=0)
+RESULT eDVBServicePlay::setTrickmode(int trick)
 {
        if (m_decoder)
                m_decoder->setTrickmode(trick);
        return 0;
 }
 
+RESULT eDVBServicePlay::isCurrentlySeekable()
+{
+       return m_is_pvr || m_timeshift_active;
+}
+
 RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
 {
        ptr = this;
@@ -777,6 +997,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;
@@ -791,7 +1017,34 @@ RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
 
 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
 {
+       ptr = 0;
        if (m_timeshift_enabled || !m_is_pvr)
+       {
+               if (!m_timeshift_enabled)
+               {
+                               /* we need enough diskspace */
+                       struct statfs fs;
+                       if (statfs(TSPATH "/.", &fs) < 0)
+                       {
+                               eDebug("statfs failed!");
+                               return -2;
+                       }
+               
+                       if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
+                       {
+                               eDebug("not enough diskspace for timeshift! (less than 1GB)");
+                               return -3;
+                       }
+               }
+               ptr = this;
+               return 0;
+       }
+       return -1;
+}
+
+RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
+{
+       if (m_is_pvr)
        {
                ptr = this;
                return 0;
@@ -802,6 +1055,11 @@ RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
 
 RESULT eDVBServicePlay::getName(std::string &name)
 {
+       if (m_is_pvr)
+       {
+               ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
+               return i->getName(m_reference, name);
+       }
        if (m_dvb_service)
        {
                m_dvb_service->getName(m_reference, name);
@@ -824,17 +1082,60 @@ int eDVBServicePlay::getInfo(int w)
 {
        eDVBServicePMTHandler::program program;
 
+       if (w == sCAIDs)
+               return resIsPyObject;
+
        if (m_service_handler.getProgramInfo(program))
                return -1;
        
        switch (w)
        {
+       case sAspect:
+               if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
+               {
+                       ePtr<eServiceEvent> evt;
+                       if (!m_event_handler.getEvent(evt, 0))
+                       {
+                               ePtr<eComponentData> data;
+                               if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
+                               {
+                                       if ( data->getStreamContent() == 1 )
+                                       {
+                                               switch(data->getComponentType())
+                                               {
+                                                       // SD
+                                                       case 1: // 4:3 SD PAL
+                                                       case 2:
+                                                       case 3: // 16:9 SD PAL
+                                                       case 4: // > 16:9 PAL
+                                                       case 5: // 4:3 SD NTSC
+                                                       case 6: 
+                                                       case 7: // 16:9 SD NTSC
+                                                       case 8: // > 16:9 NTSC
+
+                                                       // HD
+                                                       case 9: // 4:3 HD PAL
+                                                       case 0xA:
+                                                       case 0xB: // 16:9 HD PAL
+                                                       case 0xC: // > 16:9 HD PAL
+                                                       case 0xD: // 4:3 HD NTSC
+                                                       case 0xE:
+                                                       case 0xF: // 16:9 HD NTSC
+                                                       case 0x10: // > 16:9 HD PAL
+                                                               return data->getComponentType();
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return -1;
+       case sIsCrypted: return program.isCrypted;
        case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].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[m_current_audio_stream].pid;
        case sPCRPID: return program.pcrPid;
        case sPMTPID: return program.pmtPid;
-       case sTXTPID: return -1;
-               
+       case sTXTPID: 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();
@@ -846,15 +1147,28 @@ 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()
@@ -889,6 +1203,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
@@ -927,11 +1243,42 @@ int eDVBServicePlay::selectAudioStream(int i)
        if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
                return -4;
 
+       if (m_dvb_service && !m_is_pvr)
+       {
+               if (program.audioStreams[i].type == eDVBAudio::aMPEG)
+               {
+                       m_dvb_service->setCacheEntry(eDVBService::cAPID, program.audioStreams[i].pid);
+                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
+               }       else
+               {
+                       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()
+{
+       int curChannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL);
+       return curChannel == -1 ? STEREO : curChannel;
+}
+
+RESULT eDVBServicePlay::selectChannel(int i)
+{
+       if (i < iAudioChannelSelection::LEFT || i > iAudioChannelSelection::RIGHT)
+               i = -1;  // Stereo
+       if (m_dvb_service->getCacheEntry(eDVBService::cACHANNEL) != i)
+       {
+               m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
+               if (m_decoder)
+                       m_decoder->setAudioChannel(i);
+       }
+}
+
 int eDVBServicePlay::getFrontendInfo(int w)
 {
        if (m_is_pvr)
@@ -945,6 +1292,52 @@ int eDVBServicePlay::getFrontendInfo(int w)
        return fe->readFrontendData(w);
 }
 
+PyObject *eDVBServicePlay::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;
@@ -958,29 +1351,8 @@ RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
        ePtr<eServiceEvent> evt;
        if (!m_event_handler.getEvent(evt, 0))
        {
-               if (!evt->getLinkageService(sub, n))
-               {
-                       eServiceReferenceDVB &subservice = (eServiceReferenceDVB&) sub;
-                       eServiceReferenceDVB &current = (eServiceReferenceDVB&) m_reference;
-                       subservice.setDVBNamespace(current.getDVBNamespace());
-                       if ( current.getParentTransportStreamID().get() )
-                       {
-                               subservice.setParentTransportStreamID( current.getParentTransportStreamID() );
-                               subservice.setParentServiceID( current.getParentServiceID() );
-                       }
-                       else
-                       {
-                               subservice.setParentTransportStreamID( current.getTransportStreamID() );
-                               subservice.setParentServiceID( current.getServiceID() );
-                       }
-                       if ( subservice.getParentTransportStreamID() == subservice.getTransportStreamID() &&
-                               subservice.getParentServiceID() == subservice.getServiceID() )
-                       {
-                               subservice.setParentTransportStreamID( eTransportStreamID(0) );
-                               subservice.setParentServiceID( eServiceID(0) );
-                       }
+               if (!evt->getLinkageService(sub, m_reference, n))
                        return 0;
-               }
        }
        sub.type=eServiceReference::idInvalid;
        return -1;
@@ -988,9 +1360,40 @@ RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
 
 RESULT eDVBServicePlay::startTimeshift()
 {
+       ePtr<iDVBDemux> demux;
+       
+       eDebug("Start timeshift!");
+       
        if (m_timeshift_enabled)
                return -1;
-       eDebug("TIMESHIFT - start!");
+       
+               /* start recording with the data demux. */
+       if (m_service_handler.getDataDemux(demux))
+               return -2;
+
+       demux->createTSRecorder(m_record);
+       if (!m_record)
+               return -3;
+
+       char templ[]=TSPATH "/timeshift.XXXXXX";
+       m_timeshift_fd = mkstemp(templ);
+       m_timeshift_file = templ;
+       
+       eDebug("recording to %s", templ);
+       
+       if (m_timeshift_fd < 0)
+       {
+               m_record = 0;
+               return -4;
+       }
+               
+       m_record->setTargetFD(m_timeshift_fd);
+
+       m_timeshift_enabled = 1;
+       
+       updateTimeshiftPids();
+       m_record->start();
+
        return 0;
 }
 
@@ -998,11 +1401,434 @@ RESULT eDVBServicePlay::stopTimeshift()
 {
        if (!m_timeshift_enabled)
                return -1;
+       
+       switchToLive();
+       
        m_timeshift_enabled = 0;
-       eDebug("timeshift disabled");
+       
+       m_record->stop();
+       m_record = 0;
+       
+       close(m_timeshift_fd);
+       eDebug("remove timeshift file");
+       remove(m_timeshift_file.c_str());
+       
        return 0;
 }
 
+int eDVBServicePlay::isTimeshiftActive()
+{
+       return m_timeshift_enabled && m_timeshift_active;
+}
+
+RESULT eDVBServicePlay::activateTimeshift()
+{
+       if (!m_timeshift_enabled)
+               return -1;
+       
+       if (!m_timeshift_active)
+       {
+               switchToTimeshift();
+               return 0;
+       }
+       
+       return -2;
+}
+
+PyObject *eDVBServicePlay::getCutList()
+{
+       PyObject *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);
+               PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
+               PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
+               PyList_Append(list, tuple);
+               Py_DECREF(tuple);
+       }
+       
+       return list;
+}
+
+void eDVBServicePlay::setCutList(PyObject *list)
+{
+       if (!PyList_Check(list))
+               return;
+       int size = PyList_Size(list);
+       int i;
+       
+       m_cue_entries.clear();
+       
+       for (i=0; i<size; ++i)
+       {
+               PyObject *tuple = PyList_GetItem(list, i);
+               if (!PyTuple_Check(tuple))
+               {
+                       eDebug("non-tuple in cutlist");
+                       continue;
+               }
+               if (PyTuple_Size(tuple) != 2)
+               {
+                       eDebug("cutlist entries need to be a 2-tuple");
+                       continue;
+               }
+               PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(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));
+                       continue;
+               }
+               pts_t pts = PyLong_AsLongLong(ppts);
+               int type = PyInt_AsLong(ptype);
+               m_cue_entries.insert(cueEntry(pts, type));
+               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()
+{
+       if (!m_record)
+               return;
+       
+       eDVBServicePMTHandler::program program;
+       if (m_service_handler.getProgramInfo(program))
+               return;
+       else
+       {
+               std::set<int> pids_to_record;
+               pids_to_record.insert(0); // PAT
+               if (program.pmtPid != -1)
+                       pids_to_record.insert(program.pmtPid); // PMT
+
+               if (program.textPid != -1)
+                       pids_to_record.insert(program.textPid); // Videotext
+
+               for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
+                       i(program.videoStreams.begin()); 
+                       i != program.videoStreams.end(); ++i)
+                       pids_to_record.insert(i->pid);
+
+               for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
+                       i(program.audioStreams.begin()); 
+                       i != program.audioStreams.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(), 
+                               m_pids_active.begin(), m_pids_active.end(),
+                               std::inserter(new_pids, new_pids.begin()));
+               
+               std::set_difference(
+                               m_pids_active.begin(), m_pids_active.end(),
+                               pids_to_record.begin(), pids_to_record.end(), 
+                               std::inserter(new_pids, new_pids.begin())
+                               );
+
+               for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
+                       m_record->addPID(*i);
+
+               for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
+                       m_record->removePID(*i);
+       }
+}
+
+void eDVBServicePlay::switchToLive()
+{
+       if (!m_timeshift_active)
+               return;
+       
+       m_cue = 0;
+       m_decoder = 0;
+       m_decode_demux = 0;
+       m_teletext_parser = 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();
+}
+
+void eDVBServicePlay::switchToTimeshift()
+{
+       if (m_timeshift_active)
+               return;
+       
+       m_decode_demux = 0;
+       m_decoder = 0;
+       m_teletext_parser = 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, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1, achannel = -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 == "enable";
+
+       eDVBServicePMTHandler::program program;
+       if (h.getProgramInfo(program))
+               eDebug("getting program info failed.");
+       else
+       {
+               eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
+               if (!program.videoStreams.empty())
+               {
+                       eDebugNoNewLine(" (");
+                       for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
+                               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);
+                       }
+                       eDebugNoNewLine(")");
+               }
+               eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
+               if (!program.audioStreams.empty())
+               {
+                       eDebugNoNewLine(" (");
+                       for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
+                               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);
+                       }
+                       eDebugNoNewLine(")");
+               }
+               eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
+               pcrpid = program.pcrPid;
+               eDebug(", and the text pid is %04x", program.textPid);
+               tpid = program.textPid;
+               achannel = program.audioChannel;
+       }
+
+       if (!m_decoder)
+       {
+               h.getDecodeDemux(m_decode_demux);
+               if (m_decode_demux)
+                       m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
+               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, vpidtype);
+               m_current_audio_stream = 0;
+               m_decoder->setAudioPID(apid, apidtype);
+               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
+
+               if (!m_is_primary)
+                       m_decoder->setTrickmode(1);
+
+               m_decoder->start();
+
+               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..
+                               
+               /* don't worry about non-existing services, nor pvr services */
+               if (m_dvb_service && !m_is_pvr)
+               {
+                       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);
+                       }
+                       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);
+               }
+       }
+}
+
+void eDVBServicePlay::loadCuesheet()
+{
+       std::string filename = m_reference.path + ".cuts";
+       
+       m_cue_entries.clear();
+
+       FILE *f = fopen(filename.c_str(), "rb");
+
+       if (f)
+       {
+               eDebug("loading cuts..");
+               while (1)
+               {
+                       unsigned long long where;
+                       unsigned int what;
+                       
+                       if (!fread(&where, sizeof(where), 1, f))
+                               break;
+                       if (!fread(&what, sizeof(what), 1, f))
+                               break;
+                       
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       where = bswap_64(where);
+#endif
+                       what = ntohl(what);
+                       
+                       if (what > 2)
+                               break;
+                       
+                       m_cue_entries.insert(cueEntry(where, what));
+               }
+               fclose(f);
+               eDebug("%d entries", m_cue_entries.size());
+       } else
+               eDebug("cutfile not found!");
+       
+       m_cuesheet_changed = 0;
+       cutlistToCuesheet();
+       m_event((iPlayableService*)this, evCuesheetChanged);
+}
+
+void eDVBServicePlay::saveCuesheet()
+{
+       std::string filename = m_reference.path + ".cuts";
+       
+       FILE *f = fopen(filename.c_str(), "wb");
+
+       if (f)
+       {
+               unsigned long long where;
+               int what;
+
+               for (std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
+               {
+#if BYTE_ORDER == BIG_ENDIAN
+                       where = i->where;
+#else
+                       where = bswap_64(i->where);
+#endif
+                       what = htonl(i->what);
+                       fwrite(&where, sizeof(where), 1, f);
+                       fwrite(&what, sizeof(what), 1, f);
+                       
+               }
+               fclose(f);
+       }
+       
+       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 where 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 */
+                       {
+                               i++;
+                               continue;
+                       }
+               }
+               
+               if (in != out)
+                       m_cue->addSourceSpan(in, out);
+               
+               in = length;
+               
+               if (i == m_cue_entries.end())
+                       break;
+       }
+       m_cue->commitSpans();
+}
+
 DEFINE_REF(eDVBServicePlay)
 
 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");