adding new bouquets added to the context menu of the bouquet list
[enigma2.git] / lib / service / servicedvb.cpp
1 #include <lib/base/eerror.h>
2 #include <lib/base/object.h>
3 #include <string>
4 #include <lib/service/servicedvb.h>
5 #include <lib/service/service.h>
6 #include <lib/base/init_num.h>
7 #include <lib/base/init.h>
8
9 #include <lib/dvb/dvb.h>
10 #include <lib/dvb/db.h>
11 #include <lib/dvb/decoder.h>
12
13 #include <lib/service/servicedvbrecord.h>
14 #include <lib/service/event.h>
15 #include <lib/dvb/metaparser.h>
16 #include <lib/dvb/tstools.h>
17 #include <lib/python/python.h>
18
19 #include <sys/vfs.h>
20
21 #include <byteswap.h>
22 #include <netinet/in.h>
23
24 #include <dvbsi++/event_information_section.h>
25
26 #ifndef BYTE_ORDER
27 #error no byte order defined!
28 #endif
29
30 #define TSPATH "/media/hdd"
31
32 class eStaticServiceDVBInformation: public iStaticServiceInformation
33 {
34         DECLARE_REF(eStaticServiceDVBInformation);
35 public:
36         RESULT getName(const eServiceReference &ref, std::string &name);
37         int getLength(const eServiceReference &ref);
38 };
39
40 DEFINE_REF(eStaticServiceDVBInformation);
41
42 RESULT eStaticServiceDVBInformation::getName(const eServiceReference &ref, std::string &name)
43 {
44         eServiceReferenceDVB &service = (eServiceReferenceDVB&)ref;
45         if ( !ref.name.empty() )
46         {
47                 if (service.getParentTransportStreamID().get()) // linkage subservice
48                 {
49                         ePtr<iServiceHandler> service_center;
50                         if (!eServiceCenter::getInstance(service_center))
51                         {
52                                 eServiceReferenceDVB parent = service;
53                                 parent.setTransportStreamID( service.getParentTransportStreamID() );
54                                 parent.setServiceID( service.getParentServiceID() );
55                                 parent.setParentTransportStreamID(eTransportStreamID(0));
56                                 parent.setParentServiceID(eServiceID(0));
57                                 parent.name="";
58                                 ePtr<iStaticServiceInformation> service_info;
59                                 if (!service_center->info(parent, service_info))
60                                 {
61                                         if (!service_info->getName(parent, name))
62                                         {
63                                                 // just show short name
64                                                 unsigned int pos = name.find("\xc2\x86");
65                                                 if ( pos != std::string::npos )
66                                                         name.erase(0, pos+2);
67                                                 pos = name.find("\xc2\x87");
68                                                 if ( pos != std::string::npos )
69                                                         name.erase(pos);
70                                                 name+=" - ";
71                                         }
72                                 }
73                         }
74                 }
75                 else
76                         name="";
77                 name += ref.name;
78                 return 0;
79         }
80         else
81                 return -1;
82 }
83
84 int eStaticServiceDVBInformation::getLength(const eServiceReference &ref)
85 {
86         return -1;
87 }
88
89 class eStaticServiceDVBBouquetInformation: public iStaticServiceInformation
90 {
91         DECLARE_REF(eStaticServiceDVBBouquetInformation);
92 public:
93         RESULT getName(const eServiceReference &ref, std::string &name);
94         int getLength(const eServiceReference &ref);
95 };
96
97 DEFINE_REF(eStaticServiceDVBBouquetInformation);
98
99 RESULT eStaticServiceDVBBouquetInformation::getName(const eServiceReference &ref, std::string &name)
100 {
101         ePtr<iDVBChannelList> db;
102         ePtr<eDVBResourceManager> res;
103
104         int err;
105         if ((err = eDVBResourceManager::getInstance(res)) != 0)
106         {
107                 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no resource manager!");
108                 return err;
109         }
110         if ((err = res->getChannelList(db)) != 0)
111         {
112                 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no channel list!");
113                 return err;
114         }
115
116         eBouquet *bouquet=0;
117         if ((err = db->getBouquet(ref, bouquet)) != 0)
118         {
119                 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. getBouquet failed!");
120                 return -1;
121         }
122
123         if ( bouquet && bouquet->m_bouquet_name.length() )
124         {
125                 name = bouquet->m_bouquet_name;
126                 return 0;
127         }
128         else
129                 return -1;
130 }
131
132 int eStaticServiceDVBBouquetInformation::getLength(const eServiceReference &ref)
133 {
134         return -1;
135 }
136
137 class eStaticServiceDVBPVRInformation: public iStaticServiceInformation
138 {
139         DECLARE_REF(eStaticServiceDVBPVRInformation);
140         eServiceReference m_ref;
141         eDVBMetaParser m_parser;
142 public:
143         eStaticServiceDVBPVRInformation(const eServiceReference &ref);
144         RESULT getName(const eServiceReference &ref, std::string &name);
145         int getLength(const eServiceReference &ref);
146         
147         int getInfo(const eServiceReference &ref, int w);
148         std::string getInfoString(const eServiceReference &ref,int w);
149 };
150
151 DEFINE_REF(eStaticServiceDVBPVRInformation);
152
153 eStaticServiceDVBPVRInformation::eStaticServiceDVBPVRInformation(const eServiceReference &ref)
154 {
155         m_ref = ref;
156         m_parser.parseFile(ref.path);
157 }
158
159 RESULT eStaticServiceDVBPVRInformation::getName(const eServiceReference &ref, std::string &name)
160 {
161         ASSERT(ref == m_ref);
162         name = m_parser.m_name.size() ? m_parser.m_name : ref.path;
163         return 0;
164 }
165
166 int eStaticServiceDVBPVRInformation::getLength(const eServiceReference &ref)
167 {
168         ASSERT(ref == m_ref);
169         
170         eDVBTSTools tstools;
171         
172         if (tstools.openFile(ref.path.c_str()))
173                 return 0;
174
175         pts_t len;
176         if (tstools.calcLen(len))
177                 return 0;
178
179         return len / 90000;
180 }
181
182 int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w)
183 {
184         switch (w)
185         {
186         case iServiceInformation::sDescription:
187                 return iServiceInformation::resIsString;
188         case iServiceInformation::sTimeCreate:
189                 if (m_parser.m_time_create)
190                         return m_parser.m_time_create;
191                 else
192                         return iServiceInformation::resNA;
193         default:
194                 return iServiceInformation::resNA;
195         }
196 }
197
198 std::string eStaticServiceDVBPVRInformation::getInfoString(const eServiceReference &ref,int w)
199 {
200         switch (w)
201         {
202         case iServiceInformation::sDescription:
203                 return m_parser.m_description;
204         default:
205                 return "";
206         }
207 }
208
209 class eDVBPVRServiceOfflineOperations: public iServiceOfflineOperations
210 {
211         DECLARE_REF(eDVBPVRServiceOfflineOperations);
212         eServiceReferenceDVB m_ref;
213 public:
214         eDVBPVRServiceOfflineOperations(const eServiceReference &ref);
215         
216         RESULT deleteFromDisk(int simulate);
217         RESULT getListOfFilenames(std::list<std::string> &);
218 };
219
220 DEFINE_REF(eDVBPVRServiceOfflineOperations);
221
222 eDVBPVRServiceOfflineOperations::eDVBPVRServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReferenceDVB&)ref)
223 {
224 }
225
226 RESULT eDVBPVRServiceOfflineOperations::deleteFromDisk(int simulate)
227 {
228         if (simulate)
229                 return 0;
230         else
231         {
232                 std::list<std::string> res;
233                 if (getListOfFilenames(res))
234                         return -1;
235                 
236                                 /* TODO: deferred removing.. */
237                 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
238                 {
239                         eDebug("Removing %s...", i->c_str());
240                         ::unlink(i->c_str());
241                 }
242                 
243                 return 0;
244         }
245 }
246
247 RESULT eDVBPVRServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
248 {
249         res.clear();
250         res.push_back(m_ref.path);
251         res.push_back(m_ref.path + ".meta");
252         res.push_back(m_ref.path + ".ap");
253         res.push_back(m_ref.path + ".cuts");
254         res.push_back(m_ref.path + ".eit");
255         return 0;
256 }
257
258 DEFINE_REF(eServiceFactoryDVB)
259
260 eServiceFactoryDVB::eServiceFactoryDVB()
261 {
262         ePtr<eServiceCenter> sc;
263         
264         eServiceCenter::getPrivInstance(sc);
265         if (sc)
266                 sc->addServiceFactory(eServiceFactoryDVB::id, this);
267 }
268
269 eServiceFactoryDVB::~eServiceFactoryDVB()
270 {
271         ePtr<eServiceCenter> sc;
272         
273         eServiceCenter::getPrivInstance(sc);
274         if (sc)
275                 sc->removeServiceFactory(eServiceFactoryDVB::id);
276 }
277
278 DEFINE_REF(eDVBServiceList);
279
280 eDVBServiceList::eDVBServiceList(const eServiceReference &parent): m_parent(parent)
281 {
282 }
283
284 eDVBServiceList::~eDVBServiceList()
285 {
286 }
287
288 RESULT eDVBServiceList::startQuery()
289 {
290         ePtr<iDVBChannelList> db;
291         ePtr<eDVBResourceManager> res;
292         
293         int err;
294         if ((err = eDVBResourceManager::getInstance(res)) != 0)
295         {
296                 eDebug("no resource manager");
297                 return err;
298         }
299         if ((err = res->getChannelList(db)) != 0)
300         {
301                 eDebug("no channel list");
302                 return err;
303         }
304         
305         ePtr<eDVBChannelQuery> q;
306         
307         if (!m_parent.path.empty())
308         {
309                 eDVBChannelQuery::compile(q, m_parent.path);
310                 if (!q)
311                 {
312                         eDebug("compile query failed");
313                         return err;
314                 }
315         }
316         
317         if ((err = db->startQuery(m_query, q, m_parent)) != 0)
318         {
319                 eDebug("startQuery failed");
320                 return err;
321         }
322
323         return 0;
324 }
325
326 RESULT eDVBServiceList::getContent(PyObject *list, bool sorted)
327 {
328         eServiceReferenceDVB ref;
329
330         if (!m_query || !list || !PyList_Check(list))
331                 return -1;
332
333         std::list<eServiceReferenceDVB> tmplist;
334
335         while (!m_query->getNextResult(ref))
336                 tmplist.push_back(ref);
337
338         if (sorted)
339                 tmplist.sort(iListableServiceCompare(this));
340
341         for (std::list<eServiceReferenceDVB>::iterator it(tmplist.begin());
342                 it != tmplist.end(); ++it)
343         {
344                 PyObject *refobj = New_eServiceReference(*it);
345                 PyList_Append(list, refobj);
346                 Py_DECREF(refobj);
347         }
348         return 0;
349 }
350
351 RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
352 {
353         eServiceReferenceDVB ref;
354         
355         if (!m_query)
356                 return -1;
357         
358         while (!m_query->getNextResult(ref))
359                 list.push_back(ref);
360
361         if (sorted)
362                 list.sort(iListableServiceCompare(this));
363
364         return 0;
365 }
366
367 RESULT eDVBServiceList::getNext(eServiceReference &ref)
368 {
369         if (!m_query)
370                 return -1;
371         
372         return m_query->getNextResult((eServiceReferenceDVB&)ref);
373 }
374
375 int eDVBServiceList::compareLessEqual(const eServiceReference &a, const eServiceReference &b)
376 {
377         return m_query->compareLessEqual((const eServiceReferenceDVB&)a, (const eServiceReferenceDVB&)b);
378 }
379
380 RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
381 {
382         if (m_parent.flags & eServiceReference::flagDirectory) // bouquet
383         {
384                 ePtr<iDVBChannelList> db;
385                 ePtr<eDVBResourceManager> resm;
386
387                 if (eDVBResourceManager::getInstance(resm) || resm->getChannelList(db))
388                         return -1;
389
390                 if (db->getBouquet(m_parent, m_bouquet) != 0)
391                         return -1;
392
393                 res = this;
394                 
395                 return 0;
396         }
397         res = 0;
398         return -1;
399 }
400
401 RESULT eDVBServiceList::addService(eServiceReference &ref)
402 {
403         if (!m_bouquet)
404                 return -1;
405         return m_bouquet->addService(ref);
406 }
407
408 RESULT eDVBServiceList::removeService(eServiceReference &ref)
409 {
410         if (!m_bouquet)
411                 return -1;
412         return m_bouquet->removeService(ref);
413 }
414
415 RESULT eDVBServiceList::moveService(eServiceReference &ref, int pos)
416 {
417         if (!m_bouquet)
418                 return -1;
419         return m_bouquet->moveService(ref, pos);
420 }
421
422 RESULT eDVBServiceList::flushChanges()
423 {
424         if (!m_bouquet)
425                 return -1;
426         return m_bouquet->flushChanges();
427 }
428
429 RESULT eDVBServiceList::setListName(const std::string &name)
430 {
431         if (!m_bouquet)
432                 return -1;
433         return m_bouquet->setListName(name);
434 }
435
436 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
437 {
438         ePtr<eDVBService> service;
439         int r = lookupService(service, ref);
440         if (r)
441                 service = 0;
442                 // check resources...
443         ptr = new eDVBServicePlay(ref, service);
444         return 0;
445 }
446
447 RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
448 {
449         if (ref.path.empty())
450         {
451                 ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
452                 return 0;
453         } else
454         {
455                 ptr = 0;
456                 return -1;
457         }
458 }
459
460 RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
461 {
462         ePtr<eDVBServiceList> list = new eDVBServiceList(ref);
463         if (list->startQuery())
464         {
465                 ptr = 0;
466                 return -1;
467         }
468         
469         ptr = list;
470         return 0;
471 }
472
473 RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
474 {
475         /* is a listable service? */
476         if ((ref.flags & eServiceReference::flagDirectory) == eServiceReference::flagDirectory) // bouquet
477         {
478                 if ( !ref.name.empty() )  // satellites or providers list
479                         ptr = new eStaticServiceDVBInformation;
480                 else // a dvb bouquet
481                         ptr = new eStaticServiceDVBBouquetInformation;
482         }
483         else if (!ref.path.empty()) /* do we have a PVR service? */
484                 ptr = new eStaticServiceDVBPVRInformation(ref);
485         else // normal dvb service
486         {
487                 ePtr<eDVBService> service;
488                 if (lookupService(service, ref)) // no eDVBService avail for this reference ( Linkage Services... )
489                         ptr = new eStaticServiceDVBInformation;
490                 else
491                         /* eDVBService has the iStaticServiceInformation interface, so we pass it here. */
492                         ptr = service;
493         }
494         return 0;
495 }
496
497 RESULT eServiceFactoryDVB::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
498 {
499         if (ref.path.empty())
500         {
501                 ptr = 0;
502                 return -1;
503         } else
504         {
505                 ptr = new eDVBPVRServiceOfflineOperations(ref);
506                 return 0;
507         }
508 }
509
510 RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServiceReference &ref)
511 {
512                         // TODO: handle the listing itself
513         // if (ref.... == -1) .. return "... bouquets ...";
514         // could be also done in another serviceFactory (with seperate ID) to seperate actual services and lists
515                         // TODO: cache
516         ePtr<iDVBChannelList> db;
517         ePtr<eDVBResourceManager> res;
518         
519         int err;
520         if ((err = eDVBResourceManager::getInstance(res)) != 0)
521         {
522                 eDebug("no resource manager");
523                 return err;
524         }
525         if ((err = res->getChannelList(db)) != 0)
526         {
527                 eDebug("no channel list");
528                 return err;
529         }
530         
531                 /* we are sure to have a ..DVB reference as the info() call was forwarded here according to it's ID. */
532         if ((err = db->getService((eServiceReferenceDVB&)ref, service)) != 0)
533         {
534                 eDebug("getService failed!");
535                 return err;
536         }
537
538         return 0;
539 }
540
541 eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): 
542         m_reference(ref), m_dvb_service(service), m_is_paused(0)
543 {
544         m_is_pvr = !ref.path.empty();
545         
546         m_timeshift_enabled = m_timeshift_active = 0;
547         m_skipmode = 0;
548         
549         CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
550         CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
551         CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
552
553         m_cuesheet_changed = 0;
554         m_cutlist_enabled = 1;
555 }
556
557 eDVBServicePlay::~eDVBServicePlay()
558 {
559 }
560
561 void eDVBServicePlay::gotNewEvent()
562 {
563 #if 0
564                 // debug only
565         ePtr<eServiceEvent> m_event_now, m_event_next;
566         getEvent(m_event_now, 0);
567         getEvent(m_event_next, 1);
568
569         if (m_event_now)
570                 eDebug("now running: %s (%d seconds :)", m_event_now->m_event_name.c_str(), m_event_now->m_duration);
571         if (m_event_next)
572                 eDebug("next running: %s (%d seconds :)", m_event_next->m_event_name.c_str(), m_event_next->m_duration);
573 #endif
574         m_event((iPlayableService*)this, evUpdatedEventInfo);
575 }
576
577 void eDVBServicePlay::serviceEvent(int event)
578 {
579         switch (event)
580         {
581         case eDVBServicePMTHandler::eventTuned:
582         {
583                 ePtr<iDVBDemux> m_demux;
584                 if (!m_service_handler.getDataDemux(m_demux))
585                 {
586                         eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_reference;
587                         int sid = ref.getParentServiceID().get();
588                         if (!sid)
589                                 sid = ref.getServiceID().get();
590                         if ( ref.getParentTransportStreamID().get() &&
591                                 ref.getParentTransportStreamID() != ref.getTransportStreamID() )
592                                 m_event_handler.startOther(m_demux, sid);
593                         else
594                                 m_event_handler.start(m_demux, sid);
595                 }
596                 break;
597         }
598         case eDVBServicePMTHandler::eventTuneFailed:
599         {
600                 eDebug("DVB service failed to tune");
601                 m_event((iPlayableService*)this, evTuneFailed);
602                 break;
603         }
604         case eDVBServicePMTHandler::eventNewProgramInfo:
605         {
606                 eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active);
607                 if (m_timeshift_enabled)
608                         updateTimeshiftPids();
609                 if (!m_timeshift_active)
610                         updateDecoder();
611                 if (m_first_program_info && m_is_pvr)
612                 {
613                         m_first_program_info = 0;
614                         seekTo(0);
615                 }
616                 m_event((iPlayableService*)this, evUpdatedInfo);
617                 break;
618         }
619         case eDVBServicePMTHandler::eventEOF:
620                 m_event((iPlayableService*)this, evEOF);
621                 break;
622         case eDVBServicePMTHandler::eventSOF:
623                 m_event((iPlayableService*)this, evSOF);
624                 break;
625         }
626 }
627
628 void eDVBServicePlay::serviceEventTimeshift(int event)
629 {
630         switch (event)
631         {
632         case eDVBServicePMTHandler::eventNewProgramInfo:
633                 if (m_timeshift_active)
634                         updateDecoder();
635                 break;
636         case eDVBServicePMTHandler::eventEOF:
637                 switchToLive();
638                 break;
639         }
640 }
641
642 RESULT eDVBServicePlay::start()
643 {
644         int r;
645                 /* in pvr mode, we only want to use one demux. in tv mode, we're using 
646                    two (one for decoding, one for data source), as we must be prepared
647                    to start recording from the data demux. */
648         m_cue = new eCueSheet();
649
650         m_first_program_info = 1;
651         eServiceReferenceDVB &service = (eServiceReferenceDVB&)m_reference;
652         r = m_service_handler.tune(service, m_is_pvr, m_cue);
653         
654                 /* inject EIT if there is a stored one */
655         if (m_is_pvr)
656         {
657                 std::string filename = service.path;
658                 filename.erase(filename.length()-2, 2);
659                 filename+="eit";
660                 int fd = ::open( filename.c_str(), O_RDONLY );
661                 if ( fd > -1 )
662                 {
663                         __u8 buf[4096];
664                         int rd = ::read(fd, buf, 4096);
665                         ::close(fd);
666                         if ( rd > 12 /*EIT_LOOP_SIZE*/ )
667                         {
668                                 Event ev(buf);
669                                 ePtr<eServiceEvent> event = new eServiceEvent;
670                                 ePtr<eServiceEvent> empty;
671                                 event->parseFrom(&ev, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get());
672                                 m_event_handler.inject(event, 0);
673                                 m_event_handler.inject(empty, 1);
674                                 eDebug("injected");
675                         }
676                 }
677         }
678         
679         if (m_is_pvr)
680                 loadCuesheet();
681
682         m_event(this, evStart);
683         m_event((iPlayableService*)this, evSeekableStatusChanged);
684         return 0;
685 }
686
687 RESULT eDVBServicePlay::stop()
688 {
689         stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
690
691         m_service_handler_timeshift.free();
692         m_service_handler.free();
693         
694         if (m_is_pvr && m_cuesheet_changed)
695                 saveCuesheet();
696         
697         return 0;
698 }
699
700 RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
701 {
702         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
703         return 0;
704 }
705
706 RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
707 {
708                 /* note: we check for timeshift to be enabled,
709                    not neccessary active. if you pause when timeshift
710                    is not active, you should activate it when unpausing */
711         if ((!m_is_pvr) && (!m_timeshift_enabled))
712         {
713                 ptr = 0;
714                 return -1;
715         }
716
717         ptr = this;
718         return 0;
719 }
720
721 RESULT eDVBServicePlay::setSlowMotion(int ratio)
722 {
723         if (m_decoder)
724                 return m_decoder->setSlowMotion(ratio);
725         else
726                 return -1;
727 }
728
729 RESULT eDVBServicePlay::setFastForward(int ratio)
730 {
731         int skipmode, ffratio;
732         
733         if (ratio > 8)
734         {
735                 skipmode = ratio;
736                 ffratio = 1;
737         } else if (ratio > 0)
738         {
739                 skipmode = 0;
740                 ffratio = ratio;
741         } else if (!ratio)
742         {
743                 skipmode = 0;
744                 ffratio = 0;
745         } else // if (ratio < 0)
746         {
747                 skipmode = ratio;
748                 ffratio = 1;
749         }
750
751         if (m_skipmode != skipmode)
752         {
753                 eDebug("setting cue skipmode to %d", skipmode);
754                 if (m_cue)
755                         m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
756         }
757         
758         m_skipmode = skipmode;
759         
760         if (!m_decoder)
761                 return -1;
762
763         return m_decoder->setFastForward(ffratio);
764 }
765     
766 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
767 {
768         if (m_is_pvr || m_timeshift_enabled)
769         {
770                 ptr = this;
771                 return 0;
772         }
773         
774         ptr = 0;
775         return -1;
776 }
777
778         /* TODO: when timeshift is enabled but not active, this doesn't work. */
779 RESULT eDVBServicePlay::getLength(pts_t &len)
780 {
781         ePtr<iDVBPVRChannel> pvr_channel;
782         
783         if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
784                 return -1;
785         
786         return pvr_channel->getLength(len);
787 }
788
789 RESULT eDVBServicePlay::pause()
790 {
791         if (!m_is_paused && m_decoder)
792         {
793                 m_is_paused = 1;
794                 return m_decoder->freeze(0);
795         } else
796                 return -1;
797 }
798
799 RESULT eDVBServicePlay::unpause()
800 {
801         if (m_is_paused && m_decoder)
802         {
803                 m_is_paused = 0;
804                 return m_decoder->unfreeze();
805         } else
806                 return -1;
807 }
808
809 RESULT eDVBServicePlay::seekTo(pts_t to)
810 {
811         eDebug("eDVBServicePlay::seekTo: jump %lld", to);
812         
813         if (!m_decode_demux)
814                 return -1;
815
816         ePtr<iDVBPVRChannel> pvr_channel;
817         
818         if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
819                 return -1;
820         
821         if (!m_cue)
822                 return -1;
823         
824         m_cue->seekTo(0, to);
825         return 0;
826 }
827
828 RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
829 {
830         eDebug("eDVBServicePlay::seekRelative: jump %d, %lld", direction, to);
831         
832         if (!m_decode_demux)
833                 return -1;
834
835         ePtr<iDVBPVRChannel> pvr_channel;
836         
837         if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
838                 return -1;
839         
840         int mode = 1;
841         
842                         /* HACK until we have skip-AP api */
843         if ((to > 0) && (to < 100))
844                 mode = 2;
845         
846         to *= direction;
847         
848         if (!m_cue)
849                 return 0;
850         
851         m_cue->seekTo(mode, to);
852         return 0;
853 }
854
855 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
856 {
857         ePtr<iDVBPVRChannel> pvr_channel;
858         
859         if (!m_decode_demux)
860                 return -1;
861         
862         if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
863                 return -1;
864         
865         int r = 0;
866
867                 /* if there is a decoder, use audio or video PTS */
868         if (m_decoder)
869         {
870                 r = m_decoder->getPTS(0, pos);
871                 if (r)
872                         return r;
873         }
874         
875                 /* fixup */
876         return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
877 }
878
879 RESULT eDVBServicePlay::setTrickmode(int trick)
880 {
881         if (m_decoder)
882                 m_decoder->setTrickmode(trick);
883         return 0;
884 }
885
886 RESULT eDVBServicePlay::isCurrentlySeekable()
887 {
888         return m_is_pvr || m_timeshift_active;
889 }
890
891 RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
892 {
893         ptr = this;
894         return 0;
895 }
896
897 RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
898 {
899         ptr = this;
900         return 0;
901 }
902
903 RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
904 {
905         ptr = this;
906         return 0;
907 }
908
909 RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
910 {
911         ptr = this;
912         return 0;
913 }
914
915 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
916 {
917         ptr = 0;
918         if (m_timeshift_enabled || !m_is_pvr)
919         {
920                 if (!m_timeshift_enabled)
921                 {
922                                 /* we need enough diskspace */
923                         struct statfs fs;
924                         if (statfs(TSPATH "/.", &fs) < 0)
925                         {
926                                 eDebug("statfs failed!");
927                                 return -2;
928                         }
929                 
930                         if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
931                         {
932                                 eDebug("not enough diskspace for timeshift! (less than 1GB)");
933                                 return -3;
934                         }
935                 }
936                 ptr = this;
937                 return 0;
938         }
939         return -1;
940 }
941
942 RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
943 {
944         if (m_is_pvr)
945         {
946                 ptr = this;
947                 return 0;
948         }
949         ptr = 0;
950         return -1;
951 }
952
953 RESULT eDVBServicePlay::getName(std::string &name)
954 {
955         if (m_is_pvr)
956         {
957                 ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
958                 return i->getName(m_reference, name);
959         }
960         if (m_dvb_service)
961         {
962                 m_dvb_service->getName(m_reference, name);
963                 if (name.empty())
964                         name = "(...)";
965         }
966         else if (!m_reference.name.empty())
967                 eStaticServiceDVBInformation().getName(m_reference, name);
968         else
969                 name = "DVB service";
970         return 0;
971 }
972
973 RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
974 {
975         return m_event_handler.getEvent(evt, nownext);
976 }
977
978 int eDVBServicePlay::getInfo(int w)
979 {
980         eDVBServicePMTHandler::program program;
981
982         if (m_service_handler.getProgramInfo(program))
983                 return -1;
984         
985         switch (w)
986         {
987         case sAspect:
988                 if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
989                 {
990                         ePtr<eServiceEvent> evt;
991                         if (!m_event_handler.getEvent(evt, 0))
992                         {
993                                 ePtr<eComponentData> data;
994                                 if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
995                                 {
996                                         if ( data->getStreamContent() == 1 )
997                                         {
998                                                 switch(data->getComponentType())
999                                                 {
1000                                                         // SD
1001                                                         case 1: // 4:3 SD PAL
1002                                                         case 2:
1003                                                         case 3: // 16:9 SD PAL
1004                                                         case 4: // > 16:9 PAL
1005                                                         case 5: // 4:3 SD NTSC
1006                                                         case 6: 
1007                                                         case 7: // 16:9 SD NTSC
1008                                                         case 8: // > 16:9 NTSC
1009
1010                                                         // HD
1011                                                         case 9: // 4:3 HD PAL
1012                                                         case 0xA:
1013                                                         case 0xB: // 16:9 HD PAL
1014                                                         case 0xC: // > 16:9 HD PAL
1015                                                         case 0xD: // 4:3 HD NTSC
1016                                                         case 0xE:
1017                                                         case 0xF: // 16:9 HD NTSC
1018                                                         case 0x10: // > 16:9 HD PAL
1019                                                                 return data->getComponentType();
1020                                                 }
1021                                         }
1022                                 }
1023                         }
1024                 }
1025                 return -1;
1026         case sIsCrypted: return program.isCrypted;
1027         case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
1028         case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
1029         case sPCRPID: return program.pcrPid;
1030         case sPMTPID: return program.pmtPid;
1031         case sTXTPID: return program.textPid;
1032         case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
1033         case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
1034         case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
1035         case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
1036         case sProvider: if (!m_dvb_service) return -1; return -2;
1037         default:
1038                 return -1;
1039         }
1040 }
1041
1042 std::string eDVBServicePlay::getInfoString(int w)
1043 {       
1044         switch (w)
1045         {
1046         case sProvider:
1047                 if (!m_dvb_service) return "";
1048                 return m_dvb_service->m_provider_name;
1049         default:
1050                 return "";
1051         }
1052 }
1053
1054 int eDVBServicePlay::getNumberOfTracks()
1055 {
1056         eDVBServicePMTHandler::program program;
1057         if (m_service_handler.getProgramInfo(program))
1058                 return 0;
1059         return program.audioStreams.size();
1060 }
1061
1062 RESULT eDVBServicePlay::selectTrack(unsigned int i)
1063 {
1064         int ret = selectAudioStream(i);
1065
1066         if (m_decoder->start())
1067                 return -5;
1068
1069         return ret;
1070 }
1071
1072 RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1073 {
1074         eDVBServicePMTHandler::program program;
1075
1076         if (m_service_handler.getProgramInfo(program))
1077                 return -1;
1078         
1079         if (i >= program.audioStreams.size())
1080                 return -2;
1081         
1082         if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
1083                 info.m_description = "MPEG";
1084         else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
1085                 info.m_description = "AC3";
1086         else  if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
1087                 info.m_description = "DTS";
1088         else
1089                 info.m_description = "???";
1090
1091         if (program.audioStreams[i].component_tag != -1)
1092         {
1093                 ePtr<eServiceEvent> evt;
1094                 if (!m_event_handler.getEvent(evt, 0))
1095                 {
1096                         ePtr<eComponentData> data;
1097                         if (!evt->getComponentData(data, program.audioStreams[i].component_tag))
1098                                 info.m_language = data->getText();
1099                 }
1100         }
1101
1102         if (info.m_language.empty())
1103                 info.m_language = program.audioStreams[i].language_code;
1104         
1105         return 0;
1106 }
1107
1108 int eDVBServicePlay::selectAudioStream(int i)
1109 {
1110         eDVBServicePMTHandler::program program;
1111
1112         if (m_service_handler.getProgramInfo(program))
1113                 return -1;
1114         
1115         if ((unsigned int)i >= program.audioStreams.size())
1116                 return -2;
1117         
1118         if (!m_decoder)
1119                 return -3;
1120         
1121         if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
1122                 return -4;
1123
1124         if (m_dvb_service && !m_is_pvr)
1125         {
1126                 if (program.audioStreams[i].type == eDVBAudio::aMPEG)
1127                 {
1128                         m_dvb_service->setCachePID(eDVBService::cAPID, program.audioStreams[i].pid);
1129                         m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
1130                 }       else
1131                 {
1132                         m_dvb_service->setCachePID(eDVBService::cAPID, -1);
1133                         m_dvb_service->setCachePID(eDVBService::cAC3PID, program.audioStreams[i].pid);
1134                 }
1135         }
1136
1137         m_current_audio_stream = i;
1138
1139         return 0;
1140 }
1141
1142 int eDVBServicePlay::getFrontendInfo(int w)
1143 {
1144         if (m_is_pvr)
1145                 return 0;
1146         eUsePtr<iDVBChannel> channel;
1147         if(m_service_handler.getChannel(channel))
1148                 return 0;
1149         ePtr<iDVBFrontend> fe;
1150         if(channel->getFrontend(fe))
1151                 return 0;
1152         return fe->readFrontendData(w);
1153 }
1154
1155 int eDVBServicePlay::getNumberOfSubservices()
1156 {
1157         ePtr<eServiceEvent> evt;
1158         if (!m_event_handler.getEvent(evt, 0))
1159                 return evt->getNumOfLinkageServices();
1160         return 0;
1161 }
1162
1163 RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
1164 {
1165         ePtr<eServiceEvent> evt;
1166         if (!m_event_handler.getEvent(evt, 0))
1167         {
1168                 if (!evt->getLinkageService(sub, m_reference, n))
1169                         return 0;
1170         }
1171         sub.type=eServiceReference::idInvalid;
1172         return -1;
1173 }
1174
1175 RESULT eDVBServicePlay::startTimeshift()
1176 {
1177         ePtr<iDVBDemux> demux;
1178         
1179         eDebug("Start timeshift!");
1180         
1181         if (m_timeshift_enabled)
1182                 return -1;
1183         
1184                 /* start recording with the data demux. */
1185         if (m_service_handler.getDataDemux(demux))
1186                 return -2;
1187
1188         demux->createTSRecorder(m_record);
1189         if (!m_record)
1190                 return -3;
1191
1192         char templ[]=TSPATH "/timeshift.XXXXXX";
1193         m_timeshift_fd = mkstemp(templ);
1194         m_timeshift_file = templ;
1195         
1196         eDebug("recording to %s", templ);
1197         
1198         if (m_timeshift_fd < 0)
1199         {
1200                 m_record = 0;
1201                 return -4;
1202         }
1203                 
1204         m_record->setTargetFD(m_timeshift_fd);
1205
1206         m_timeshift_enabled = 1;
1207         
1208         updateTimeshiftPids();
1209         m_record->start();
1210
1211         return 0;
1212 }
1213
1214 RESULT eDVBServicePlay::stopTimeshift()
1215 {
1216         if (!m_timeshift_enabled)
1217                 return -1;
1218         
1219         switchToLive();
1220         
1221         m_timeshift_enabled = 0;
1222         
1223         m_record->stop();
1224         m_record = 0;
1225         
1226         close(m_timeshift_fd);
1227         eDebug("remove timeshift file");
1228         remove(m_timeshift_file.c_str());
1229         
1230         return 0;
1231 }
1232
1233 int eDVBServicePlay::isTimeshiftActive()
1234 {
1235         return m_timeshift_enabled && m_timeshift_active;
1236 }
1237
1238 RESULT eDVBServicePlay::activateTimeshift()
1239 {
1240         if (!m_timeshift_enabled)
1241                 return -1;
1242         
1243         if (!m_timeshift_active)
1244         {
1245                 switchToTimeshift();
1246                 return 0;
1247         }
1248         
1249         return -2;
1250 }
1251
1252 PyObject *eDVBServicePlay::getCutList()
1253 {
1254         PyObject *list = PyList_New(0);
1255         
1256         for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1257         {
1258                 PyObject *tuple = PyTuple_New(2);
1259                 PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
1260                 PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
1261                 PyList_Append(list, tuple);
1262                 Py_DECREF(tuple);
1263         }
1264         
1265         return list;
1266 }
1267
1268 void eDVBServicePlay::setCutList(PyObject *list)
1269 {
1270         if (!PyList_Check(list))
1271                 return;
1272         int size = PyList_Size(list);
1273         int i;
1274         
1275         m_cue_entries.clear();
1276         
1277         for (i=0; i<size; ++i)
1278         {
1279                 PyObject *tuple = PyList_GetItem(list, i);
1280                 if (!PyTuple_Check(tuple))
1281                 {
1282                         eDebug("non-tuple in cutlist");
1283                         continue;
1284                 }
1285                 if (PyTuple_Size(tuple) != 2)
1286                 {
1287                         eDebug("cutlist entries need to be a 2-tuple");
1288                         continue;
1289                 }
1290                 PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
1291                 if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
1292                 {
1293                         eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
1294                         continue;
1295                 }
1296                 pts_t pts = PyLong_AsLongLong(ppts);
1297                 int type = PyInt_AsLong(ptype);
1298                 m_cue_entries.insert(cueEntry(pts, type));
1299                 eDebug("adding %08llx, %d", pts, type);
1300         }
1301         m_cuesheet_changed = 1;
1302         
1303         cutlistToCuesheet();
1304         m_event((iPlayableService*)this, evCuesheetChanged);
1305 }
1306
1307 void eDVBServicePlay::setCutListEnable(int enable)
1308 {
1309         m_cutlist_enabled = enable;
1310         cutlistToCuesheet();
1311 }
1312
1313 void eDVBServicePlay::updateTimeshiftPids()
1314 {
1315         if (!m_record)
1316                 return;
1317         
1318         eDVBServicePMTHandler::program program;
1319         if (m_service_handler.getProgramInfo(program))
1320                 return;
1321         else
1322         {
1323                 std::set<int> pids_to_record;
1324                 pids_to_record.insert(0); // PAT
1325                 if (program.pmtPid != -1)
1326                         pids_to_record.insert(program.pmtPid); // PMT
1327
1328                 if (program.textPid != -1)
1329                         pids_to_record.insert(program.textPid); // Videotext
1330
1331                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1332                         i(program.videoStreams.begin()); 
1333                         i != program.videoStreams.end(); ++i)
1334                         pids_to_record.insert(i->pid);
1335
1336                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1337                         i(program.audioStreams.begin()); 
1338                         i != program.audioStreams.end(); ++i)
1339                                 pids_to_record.insert(i->pid);
1340
1341                 std::set<int> new_pids, obsolete_pids;
1342                 
1343                 std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
1344                                 m_pids_active.begin(), m_pids_active.end(),
1345                                 std::inserter(new_pids, new_pids.begin()));
1346                 
1347                 std::set_difference(
1348                                 m_pids_active.begin(), m_pids_active.end(),
1349                                 pids_to_record.begin(), pids_to_record.end(), 
1350                                 std::inserter(new_pids, new_pids.begin())
1351                                 );
1352
1353                 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
1354                         m_record->addPID(*i);
1355
1356                 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
1357                         m_record->removePID(*i);
1358         }
1359 }
1360
1361 void eDVBServicePlay::switchToLive()
1362 {
1363         if (!m_timeshift_active)
1364                 return;
1365         
1366         m_decoder = 0;
1367         m_decode_demux = 0;
1368                 /* free the timeshift service handler, we need the resources */
1369         m_service_handler_timeshift.free();
1370         m_timeshift_active = 0;
1371         
1372         m_event((iPlayableService*)this, evSeekableStatusChanged);
1373         
1374         updateDecoder();
1375 }
1376
1377 void eDVBServicePlay::switchToTimeshift()
1378 {
1379         if (m_timeshift_active)
1380                 return;
1381         
1382         m_decode_demux = 0;
1383         m_decoder = 0;
1384         
1385         m_timeshift_active = 1;
1386
1387         m_event((iPlayableService*)this, evSeekableStatusChanged);
1388         
1389         eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
1390         r.path = m_timeshift_file;
1391         
1392         m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
1393 }
1394
1395 void eDVBServicePlay::updateDecoder()
1396 {
1397         int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
1398         eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
1399
1400         eDVBServicePMTHandler::program program;
1401         if (h.getProgramInfo(program))
1402                 eDebug("getting program info failed.");
1403         else
1404         {
1405                 eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
1406                 if (!program.videoStreams.empty())
1407                 {
1408                         eDebugNoNewLine(" (");
1409                         for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1410                                 i(program.videoStreams.begin()); 
1411                                 i != program.videoStreams.end(); ++i)
1412                         {
1413                                 if (vpid == -1)
1414                                         vpid = i->pid;
1415                                 if (i != program.videoStreams.begin())
1416                                         eDebugNoNewLine(", ");
1417                                 eDebugNoNewLine("%04x", i->pid);
1418                         }
1419                         eDebugNoNewLine(")");
1420                 }
1421                 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
1422                 if (!program.audioStreams.empty())
1423                 {
1424                         eDebugNoNewLine(" (");
1425                         for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1426                                 i(program.audioStreams.begin()); 
1427                                 i != program.audioStreams.end(); ++i)
1428                         {
1429                                 if (apid == -1)
1430                                 {
1431                                         apid = i->pid;
1432                                         apidtype = i->type;
1433                                 }
1434                                 if (i != program.audioStreams.begin())
1435                                         eDebugNoNewLine(", ");
1436                                 eDebugNoNewLine("%04x", i->pid);
1437                         }
1438                         eDebugNoNewLine(")");
1439                 }
1440                 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
1441                 pcrpid = program.pcrPid;
1442                 eDebug(", and the text pid is %04x", program.textPid);
1443                 tpid = program.textPid;
1444         }
1445
1446         if (!m_decoder)
1447         {
1448                 h.getDecodeDemux(m_decode_demux);
1449                 if (m_decode_demux)
1450                         m_decode_demux->getMPEGDecoder(m_decoder);
1451                 if (m_cue)
1452                         m_cue->setDecodingDemux(m_decode_demux, m_decoder);
1453         }
1454
1455         if (m_decoder)
1456         {
1457                 m_decoder->setVideoPID(vpid);
1458                 m_current_audio_stream = 0;
1459                 m_decoder->setAudioPID(apid, apidtype);
1460                 if (!(m_is_pvr || m_timeshift_active))
1461                         m_decoder->setSyncPCR(pcrpid);
1462                 else
1463                         m_decoder->setSyncPCR(-1);
1464                 m_decoder->setTextPID(tpid);
1465                 m_decoder->start();
1466 // how we can do this better?
1467 // update cache pid when the user changed the audio track or video track
1468 // TODO handling of difference audio types.. default audio types..
1469                                 
1470                 /* don't worry about non-existing services, nor pvr services */
1471                 if (m_dvb_service && !m_is_pvr)
1472                 {
1473                         if (apidtype == eDVBAudio::aMPEG)
1474                         {
1475                                 m_dvb_service->setCachePID(eDVBService::cAPID, apid);
1476                                 m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
1477                         }
1478                         else
1479                         {
1480                                 m_dvb_service->setCachePID(eDVBService::cAPID, -1);
1481                                 m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
1482                         }
1483                         m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
1484                         m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
1485                         m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
1486                 }
1487         }
1488 }
1489
1490 void eDVBServicePlay::loadCuesheet()
1491 {
1492         std::string filename = m_reference.path + ".cuts";
1493         
1494         m_cue_entries.clear();
1495
1496         FILE *f = fopen(filename.c_str(), "rb");
1497
1498         if (f)
1499         {
1500                 eDebug("loading cuts..");
1501                 while (1)
1502                 {
1503                         unsigned long long where;
1504                         unsigned int what;
1505                         
1506                         if (!fread(&where, sizeof(where), 1, f))
1507                                 break;
1508                         if (!fread(&what, sizeof(what), 1, f))
1509                                 break;
1510                         
1511 #if BYTE_ORDER == LITTLE_ENDIAN
1512                         where = bswap_64(where);
1513 #endif
1514                         what = ntohl(what);
1515                         
1516                         if (what > 2)
1517                                 break;
1518                         
1519                         m_cue_entries.insert(cueEntry(where, what));
1520                 }
1521                 fclose(f);
1522                 eDebug("%d entries", m_cue_entries.size());
1523         } else
1524                 eDebug("cutfile not found!");
1525         
1526         m_cuesheet_changed = 0;
1527         cutlistToCuesheet();
1528         m_event((iPlayableService*)this, evCuesheetChanged);
1529 }
1530
1531 void eDVBServicePlay::saveCuesheet()
1532 {
1533         std::string filename = m_reference.path + ".cuts";
1534         
1535         FILE *f = fopen(filename.c_str(), "wb");
1536
1537         if (f)
1538         {
1539                 unsigned long long where;
1540                 int what;
1541
1542                 for (std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1543                 {
1544 #if BYTE_ORDER == BIG_ENDIAN
1545                         where = i->where;
1546 #else
1547                         where = bswap_64(i->where);
1548 #endif
1549                         what = htonl(i->what);
1550                         fwrite(&where, sizeof(where), 1, f);
1551                         fwrite(&what, sizeof(what), 1, f);
1552                         
1553                 }
1554                 fclose(f);
1555         }
1556         
1557         m_cuesheet_changed = 0;
1558 }
1559
1560 void eDVBServicePlay::cutlistToCuesheet()
1561 {
1562         if (!m_cue)
1563         {
1564                 eDebug("no cue sheet");
1565                 return;
1566         }       
1567         m_cue->clear();
1568         
1569         if (!m_cutlist_enabled)
1570         {
1571                 m_cue->commitSpans();
1572                 eDebug("cutlists where disabled");
1573                 return;
1574         }
1575
1576         pts_t in = 0, out = 0, length = 0;
1577         
1578         getLength(length);
1579                 
1580         std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
1581         
1582         while (1)
1583         {
1584                 if (i == m_cue_entries.end())
1585                         out = length;
1586                 else {
1587                         if (i->what == 0) /* in */
1588                         {
1589                                 in = i++->where;
1590                                 continue;
1591                         } else if (i->what == 1) /* out */
1592                                 out = i++->where;
1593                         else /* mark */
1594                         {
1595                                 i++;
1596                                 continue;
1597                         }
1598                 }
1599                 
1600                 if (in != out)
1601                         m_cue->addSourceSpan(in, out);
1602                 
1603                 in = length;
1604                 
1605                 if (i == m_cue_entries.end())
1606                         break;
1607         }
1608         m_cue->commitSpans();
1609 }
1610
1611 DEFINE_REF(eDVBServicePlay)
1612
1613 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");