make video stream type for running service accessible from python
[enigma2.git] / lib / service / servicedvb.cpp
index 76abbd3805120669c331898da1420e9ff533d08d..e9aeb8a9545758998eff1e0fadca0e5f52c4db34 100644 (file)
@@ -5,18 +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>
 
 #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
@@ -175,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;
@@ -191,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 "";
        }
@@ -223,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;
@@ -238,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;
 }
 
@@ -310,31 +349,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;
@@ -351,6 +365,91 @@ 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())
+//   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)
@@ -528,13 +627,18 @@ 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_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;
        
        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()
@@ -591,6 +695,11 @@ void eDVBServicePlay::serviceEvent(int event)
                        updateTimeshiftPids();
                if (!m_timeshift_active)
                        updateDecoder();
+               if (m_first_program_info && m_is_pvr)
+               {
+                       m_first_program_info = 0;
+                       seekTo(0);
+               }
                m_event((iPlayableService*)this, evUpdatedInfo);
                break;
        }
@@ -611,6 +720,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;
@@ -623,8 +735,41 @@ 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();
-       r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr, m_cue);
+       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;
@@ -632,15 +777,20 @@ RESULT eDVBServicePlay::start()
 
 RESULT eDVBServicePlay::stop()
 {
-       eDebug("stop timeshift");
        stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
-       
-       eDebug("free ts handler");
+
        m_service_handler_timeshift.free();
-       eDebug("stop service handler");
        m_service_handler.free();
-       eDebug("ok");
        
+       if (m_is_pvr && m_cuesheet_changed)
+               saveCuesheet();
+       
+       return 0;
+}
+
+RESULT eDVBServicePlay::setTarget(int target)
+{
+       m_is_primary = !target;
        return 0;
 }
 
@@ -784,12 +934,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;
 }
 
@@ -803,7 +959,18 @@ RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
        if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
-       return pvr_channel->getCurrentPosition(m_decode_demux, pos, 1);
+       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)
@@ -909,6 +1076,9 @@ int eDVBServicePlay::getInfo(int w)
 {
        eDVBServicePMTHandler::program program;
 
+       if (w == sCAIDs)
+               return resIsPyObject;
+
        if (m_service_handler.getProgramInfo(program))
                return -1;
        
@@ -955,6 +1125,7 @@ int eDVBServicePlay::getInfo(int w)
                return -1;
        case sIsCrypted: return program.isCrypted;
        case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
+       case 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;
@@ -970,15 +1141,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()
@@ -1082,6 +1266,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;
@@ -1183,17 +1413,61 @@ 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;
 }
 
-RESULT eDVBServicePlay::addCut(const pts_t &when, int what)
+void eDVBServicePlay::setCutList(PyObject *list)
 {
-       return -1;
+       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);
 }
 
-RESULT eDVBServicePlay::removeCut(const pts_t &when, int what)
+void eDVBServicePlay::setCutListEnable(int enable)
 {
-       return -1;
+       m_cutlist_enabled = enable;
+       cutlistToCuesheet();
 }
 
 void eDVBServicePlay::updateTimeshiftPids()
@@ -1249,8 +1523,11 @@ 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;
@@ -1267,6 +1544,7 @@ void eDVBServicePlay::switchToTimeshift()
        
        m_decode_demux = 0;
        m_decoder = 0;
+       m_teletext_parser = 0;
        
        m_timeshift_active = 1;
 
@@ -1275,14 +1553,22 @@ void eDVBServicePlay::switchToTimeshift()
        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;
        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.");
@@ -1297,7 +1583,10 @@ void eDVBServicePlay::updateDecoder()
                                i != program.videoStreams.end(); ++i)
                        {
                                if (vpid == -1)
+                               {
                                        vpid = i->pid;
+                                       vpidtype = i->type;
+                               }
                                if (i != program.videoStreams.begin())
                                        eDebugNoNewLine(", ");
                                eDebugNoNewLine("%04x", i->pid);
@@ -1312,10 +1601,13 @@ void eDVBServicePlay::updateDecoder()
                                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(", ");
@@ -1333,21 +1625,32 @@ 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);
+#endif
        }
 
        if (m_decoder)
        {
-               m_decoder->setVideoPID(vpid);
+               m_decoder->setVideoPID(vpid, vpidtype);
                m_current_audio_stream = 0;
                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);
                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();
 // how we can do this better?
 // update cache pid when the user changed the audio track or video track
@@ -1367,12 +1670,134 @@ void eDVBServicePlay::updateDecoder()
                                m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
                        }
                        m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
+                       m_dvb_service->setCachePID(eDVBService::cVTYPE, vpidtype);
                        m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
                        m_dvb_service->setCachePID(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");