allow disabling cutlists
[vuplus_dvbapp] / 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         to *= direction;
841         
842         if (!m_cue)
843                 return 0;
844         
845         m_cue->seekTo(1, to);
846         return 0;
847 }
848
849 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
850 {
851         ePtr<iDVBPVRChannel> pvr_channel;
852         
853         if (!m_decode_demux)
854                 return -1;
855         
856         if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
857                 return -1;
858         
859         int r = 0;
860
861                 /* if there is a decoder, use audio or video PTS */
862         if (m_decoder)
863         {
864                 r = m_decoder->getPTS(0, pos);
865                 if (r)
866                         return r;
867         }
868         
869                 /* fixup */
870         return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
871 }
872
873 RESULT eDVBServicePlay::setTrickmode(int trick)
874 {
875         if (m_decoder)
876                 m_decoder->setTrickmode(trick);
877         return 0;
878 }
879
880 RESULT eDVBServicePlay::isCurrentlySeekable()
881 {
882         return m_is_pvr || m_timeshift_active;
883 }
884
885 RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
886 {
887         ptr = this;
888         return 0;
889 }
890
891 RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
892 {
893         ptr = this;
894         return 0;
895 }
896
897 RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
898 {
899         ptr = this;
900         return 0;
901 }
902
903 RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
904 {
905         ptr = this;
906         return 0;
907 }
908
909 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
910 {
911         ptr = 0;
912         if (m_timeshift_enabled || !m_is_pvr)
913         {
914                 if (!m_timeshift_enabled)
915                 {
916                                 /* we need enough diskspace */
917                         struct statfs fs;
918                         if (statfs(TSPATH "/.", &fs) < 0)
919                         {
920                                 eDebug("statfs failed!");
921                                 return -2;
922                         }
923                 
924                         if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
925                         {
926                                 eDebug("not enough diskspace for timeshift! (less than 1GB)");
927                                 return -3;
928                         }
929                 }
930                 ptr = this;
931                 return 0;
932         }
933         return -1;
934 }
935
936 RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
937 {
938         if (m_is_pvr)
939         {
940                 ptr = this;
941                 return 0;
942         }
943         ptr = 0;
944         return -1;
945 }
946
947 RESULT eDVBServicePlay::getName(std::string &name)
948 {
949         if (m_is_pvr)
950         {
951                 ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
952                 return i->getName(m_reference, name);
953         }
954         if (m_dvb_service)
955         {
956                 m_dvb_service->getName(m_reference, name);
957                 if (name.empty())
958                         name = "(...)";
959         }
960         else if (!m_reference.name.empty())
961                 eStaticServiceDVBInformation().getName(m_reference, name);
962         else
963                 name = "DVB service";
964         return 0;
965 }
966
967 RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
968 {
969         return m_event_handler.getEvent(evt, nownext);
970 }
971
972 int eDVBServicePlay::getInfo(int w)
973 {
974         eDVBServicePMTHandler::program program;
975
976         if (m_service_handler.getProgramInfo(program))
977                 return -1;
978         
979         switch (w)
980         {
981         case sAspect:
982                 if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
983                 {
984                         ePtr<eServiceEvent> evt;
985                         if (!m_event_handler.getEvent(evt, 0))
986                         {
987                                 ePtr<eComponentData> data;
988                                 if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
989                                 {
990                                         if ( data->getStreamContent() == 1 )
991                                         {
992                                                 switch(data->getComponentType())
993                                                 {
994                                                         // SD
995                                                         case 1: // 4:3 SD PAL
996                                                         case 2:
997                                                         case 3: // 16:9 SD PAL
998                                                         case 4: // > 16:9 PAL
999                                                         case 5: // 4:3 SD NTSC
1000                                                         case 6: 
1001                                                         case 7: // 16:9 SD NTSC
1002                                                         case 8: // > 16:9 NTSC
1003
1004                                                         // HD
1005                                                         case 9: // 4:3 HD PAL
1006                                                         case 0xA:
1007                                                         case 0xB: // 16:9 HD PAL
1008                                                         case 0xC: // > 16:9 HD PAL
1009                                                         case 0xD: // 4:3 HD NTSC
1010                                                         case 0xE:
1011                                                         case 0xF: // 16:9 HD NTSC
1012                                                         case 0x10: // > 16:9 HD PAL
1013                                                                 return data->getComponentType();
1014                                                 }
1015                                         }
1016                                 }
1017                         }
1018                 }
1019                 return -1;
1020         case sIsCrypted: return program.isCrypted;
1021         case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
1022         case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
1023         case sPCRPID: return program.pcrPid;
1024         case sPMTPID: return program.pmtPid;
1025         case sTXTPID: return program.textPid;
1026         case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
1027         case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
1028         case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
1029         case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
1030         case sProvider: if (!m_dvb_service) return -1; return -2;
1031         default:
1032                 return -1;
1033         }
1034 }
1035
1036 std::string eDVBServicePlay::getInfoString(int w)
1037 {       
1038         switch (w)
1039         {
1040         case sProvider:
1041                 if (!m_dvb_service) return "";
1042                 return m_dvb_service->m_provider_name;
1043         default:
1044                 return "";
1045         }
1046 }
1047
1048 int eDVBServicePlay::getNumberOfTracks()
1049 {
1050         eDVBServicePMTHandler::program program;
1051         if (m_service_handler.getProgramInfo(program))
1052                 return 0;
1053         return program.audioStreams.size();
1054 }
1055
1056 RESULT eDVBServicePlay::selectTrack(unsigned int i)
1057 {
1058         int ret = selectAudioStream(i);
1059
1060         if (m_decoder->start())
1061                 return -5;
1062
1063         return ret;
1064 }
1065
1066 RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1067 {
1068         eDVBServicePMTHandler::program program;
1069
1070         if (m_service_handler.getProgramInfo(program))
1071                 return -1;
1072         
1073         if (i >= program.audioStreams.size())
1074                 return -2;
1075         
1076         if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
1077                 info.m_description = "MPEG";
1078         else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
1079                 info.m_description = "AC3";
1080         else  if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
1081                 info.m_description = "DTS";
1082         else
1083                 info.m_description = "???";
1084
1085         if (program.audioStreams[i].component_tag != -1)
1086         {
1087                 ePtr<eServiceEvent> evt;
1088                 if (!m_event_handler.getEvent(evt, 0))
1089                 {
1090                         ePtr<eComponentData> data;
1091                         if (!evt->getComponentData(data, program.audioStreams[i].component_tag))
1092                                 info.m_language = data->getText();
1093                 }
1094         }
1095
1096         if (info.m_language.empty())
1097                 info.m_language = program.audioStreams[i].language_code;
1098         
1099         return 0;
1100 }
1101
1102 int eDVBServicePlay::selectAudioStream(int i)
1103 {
1104         eDVBServicePMTHandler::program program;
1105
1106         if (m_service_handler.getProgramInfo(program))
1107                 return -1;
1108         
1109         if ((unsigned int)i >= program.audioStreams.size())
1110                 return -2;
1111         
1112         if (!m_decoder)
1113                 return -3;
1114         
1115         if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
1116                 return -4;
1117
1118         if (m_dvb_service && !m_is_pvr)
1119         {
1120                 if (program.audioStreams[i].type == eDVBAudio::aMPEG)
1121                 {
1122                         m_dvb_service->setCachePID(eDVBService::cAPID, program.audioStreams[i].pid);
1123                         m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
1124                 }       else
1125                 {
1126                         m_dvb_service->setCachePID(eDVBService::cAPID, -1);
1127                         m_dvb_service->setCachePID(eDVBService::cAC3PID, program.audioStreams[i].pid);
1128                 }
1129         }
1130
1131         m_current_audio_stream = i;
1132
1133         return 0;
1134 }
1135
1136 int eDVBServicePlay::getFrontendInfo(int w)
1137 {
1138         if (m_is_pvr)
1139                 return 0;
1140         eUsePtr<iDVBChannel> channel;
1141         if(m_service_handler.getChannel(channel))
1142                 return 0;
1143         ePtr<iDVBFrontend> fe;
1144         if(channel->getFrontend(fe))
1145                 return 0;
1146         return fe->readFrontendData(w);
1147 }
1148
1149 int eDVBServicePlay::getNumberOfSubservices()
1150 {
1151         ePtr<eServiceEvent> evt;
1152         if (!m_event_handler.getEvent(evt, 0))
1153                 return evt->getNumOfLinkageServices();
1154         return 0;
1155 }
1156
1157 RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
1158 {
1159         ePtr<eServiceEvent> evt;
1160         if (!m_event_handler.getEvent(evt, 0))
1161         {
1162                 if (!evt->getLinkageService(sub, m_reference, n))
1163                         return 0;
1164         }
1165         sub.type=eServiceReference::idInvalid;
1166         return -1;
1167 }
1168
1169 RESULT eDVBServicePlay::startTimeshift()
1170 {
1171         ePtr<iDVBDemux> demux;
1172         
1173         eDebug("Start timeshift!");
1174         
1175         if (m_timeshift_enabled)
1176                 return -1;
1177         
1178                 /* start recording with the data demux. */
1179         if (m_service_handler.getDataDemux(demux))
1180                 return -2;
1181
1182         demux->createTSRecorder(m_record);
1183         if (!m_record)
1184                 return -3;
1185
1186         char templ[]=TSPATH "/timeshift.XXXXXX";
1187         m_timeshift_fd = mkstemp(templ);
1188         m_timeshift_file = templ;
1189         
1190         eDebug("recording to %s", templ);
1191         
1192         if (m_timeshift_fd < 0)
1193         {
1194                 m_record = 0;
1195                 return -4;
1196         }
1197                 
1198         m_record->setTargetFD(m_timeshift_fd);
1199
1200         m_timeshift_enabled = 1;
1201         
1202         updateTimeshiftPids();
1203         m_record->start();
1204
1205         return 0;
1206 }
1207
1208 RESULT eDVBServicePlay::stopTimeshift()
1209 {
1210         if (!m_timeshift_enabled)
1211                 return -1;
1212         
1213         switchToLive();
1214         
1215         m_timeshift_enabled = 0;
1216         
1217         m_record->stop();
1218         m_record = 0;
1219         
1220         close(m_timeshift_fd);
1221         eDebug("remove timeshift file");
1222         remove(m_timeshift_file.c_str());
1223         
1224         return 0;
1225 }
1226
1227 int eDVBServicePlay::isTimeshiftActive()
1228 {
1229         return m_timeshift_enabled && m_timeshift_active;
1230 }
1231
1232 RESULT eDVBServicePlay::activateTimeshift()
1233 {
1234         if (!m_timeshift_enabled)
1235                 return -1;
1236         
1237         if (!m_timeshift_active)
1238         {
1239                 switchToTimeshift();
1240                 return 0;
1241         }
1242         
1243         return -2;
1244 }
1245
1246 PyObject *eDVBServicePlay::getCutList()
1247 {
1248         PyObject *list = PyList_New(0);
1249         
1250         for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1251         {
1252                 PyObject *tuple = PyTuple_New(2);
1253                 PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
1254                 PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
1255                 PyList_Append(list, tuple);
1256                 Py_DECREF(tuple);
1257         }
1258         
1259         return list;
1260 }
1261
1262 void eDVBServicePlay::setCutList(PyObject *list)
1263 {
1264         if (!PyList_Check(list))
1265                 return;
1266         int size = PyList_Size(list);
1267         int i;
1268         
1269         m_cue_entries.clear();
1270         
1271         for (i=0; i<size; ++i)
1272         {
1273                 PyObject *tuple = PyList_GetItem(list, i);
1274                 if (!PyTuple_Check(tuple))
1275                 {
1276                         eDebug("non-tuple in cutlist");
1277                         continue;
1278                 }
1279                 if (PyTuple_Size(tuple) != 2)
1280                 {
1281                         eDebug("cutlist entries need to be a 2-tuple");
1282                         continue;
1283                 }
1284                 PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
1285                 if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
1286                 {
1287                         eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
1288                         continue;
1289                 }
1290                 pts_t pts = PyLong_AsLongLong(ppts);
1291                 int type = PyInt_AsLong(ptype);
1292                 m_cue_entries.insert(cueEntry(pts, type));
1293                 eDebug("adding %08llx, %d", pts, type);
1294         }
1295         m_cuesheet_changed = 1;
1296         
1297         cutlistToCuesheet();
1298         m_event((iPlayableService*)this, evCuesheetChanged);
1299 }
1300
1301 void eDVBServicePlay::setCutListEnable(int enable)
1302 {
1303         m_cutlist_enabled = enable;
1304         cutlistToCuesheet();
1305 }
1306
1307 void eDVBServicePlay::updateTimeshiftPids()
1308 {
1309         if (!m_record)
1310                 return;
1311         
1312         eDVBServicePMTHandler::program program;
1313         if (m_service_handler.getProgramInfo(program))
1314                 return;
1315         else
1316         {
1317                 std::set<int> pids_to_record;
1318                 pids_to_record.insert(0); // PAT
1319                 if (program.pmtPid != -1)
1320                         pids_to_record.insert(program.pmtPid); // PMT
1321
1322                 if (program.textPid != -1)
1323                         pids_to_record.insert(program.textPid); // Videotext
1324
1325                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1326                         i(program.videoStreams.begin()); 
1327                         i != program.videoStreams.end(); ++i)
1328                         pids_to_record.insert(i->pid);
1329
1330                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1331                         i(program.audioStreams.begin()); 
1332                         i != program.audioStreams.end(); ++i)
1333                                 pids_to_record.insert(i->pid);
1334
1335                 std::set<int> new_pids, obsolete_pids;
1336                 
1337                 std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
1338                                 m_pids_active.begin(), m_pids_active.end(),
1339                                 std::inserter(new_pids, new_pids.begin()));
1340                 
1341                 std::set_difference(
1342                                 m_pids_active.begin(), m_pids_active.end(),
1343                                 pids_to_record.begin(), pids_to_record.end(), 
1344                                 std::inserter(new_pids, new_pids.begin())
1345                                 );
1346
1347                 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
1348                         m_record->addPID(*i);
1349
1350                 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
1351                         m_record->removePID(*i);
1352         }
1353 }
1354
1355 void eDVBServicePlay::switchToLive()
1356 {
1357         if (!m_timeshift_active)
1358                 return;
1359         
1360         m_decoder = 0;
1361         m_decode_demux = 0;
1362                 /* free the timeshift service handler, we need the resources */
1363         m_service_handler_timeshift.free();
1364         m_timeshift_active = 0;
1365         
1366         m_event((iPlayableService*)this, evSeekableStatusChanged);
1367         
1368         updateDecoder();
1369 }
1370
1371 void eDVBServicePlay::switchToTimeshift()
1372 {
1373         if (m_timeshift_active)
1374                 return;
1375         
1376         m_decode_demux = 0;
1377         m_decoder = 0;
1378         
1379         m_timeshift_active = 1;
1380
1381         m_event((iPlayableService*)this, evSeekableStatusChanged);
1382         
1383         eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
1384         r.path = m_timeshift_file;
1385         
1386         m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
1387 }
1388
1389 void eDVBServicePlay::updateDecoder()
1390 {
1391         int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
1392         eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
1393
1394         eDVBServicePMTHandler::program program;
1395         if (h.getProgramInfo(program))
1396                 eDebug("getting program info failed.");
1397         else
1398         {
1399                 eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
1400                 if (!program.videoStreams.empty())
1401                 {
1402                         eDebugNoNewLine(" (");
1403                         for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1404                                 i(program.videoStreams.begin()); 
1405                                 i != program.videoStreams.end(); ++i)
1406                         {
1407                                 if (vpid == -1)
1408                                         vpid = i->pid;
1409                                 if (i != program.videoStreams.begin())
1410                                         eDebugNoNewLine(", ");
1411                                 eDebugNoNewLine("%04x", i->pid);
1412                         }
1413                         eDebugNoNewLine(")");
1414                 }
1415                 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
1416                 if (!program.audioStreams.empty())
1417                 {
1418                         eDebugNoNewLine(" (");
1419                         for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1420                                 i(program.audioStreams.begin()); 
1421                                 i != program.audioStreams.end(); ++i)
1422                         {
1423                                 if (apid == -1)
1424                                 {
1425                                         apid = i->pid;
1426                                         apidtype = i->type;
1427                                 }
1428                                 if (i != program.audioStreams.begin())
1429                                         eDebugNoNewLine(", ");
1430                                 eDebugNoNewLine("%04x", i->pid);
1431                         }
1432                         eDebugNoNewLine(")");
1433                 }
1434                 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
1435                 pcrpid = program.pcrPid;
1436                 eDebug(", and the text pid is %04x", program.textPid);
1437                 tpid = program.textPid;
1438         }
1439
1440         if (!m_decoder)
1441         {
1442                 h.getDecodeDemux(m_decode_demux);
1443                 if (m_decode_demux)
1444                         m_decode_demux->getMPEGDecoder(m_decoder);
1445                 if (m_cue)
1446                         m_cue->setDecodingDemux(m_decode_demux, m_decoder);
1447         }
1448
1449         if (m_decoder)
1450         {
1451                 m_decoder->setVideoPID(vpid);
1452                 m_current_audio_stream = 0;
1453                 m_decoder->setAudioPID(apid, apidtype);
1454                 if (!(m_is_pvr || m_timeshift_active))
1455                         m_decoder->setSyncPCR(pcrpid);
1456                 else
1457                         m_decoder->setSyncPCR(-1);
1458                 m_decoder->setTextPID(tpid);
1459                 m_decoder->start();
1460 // how we can do this better?
1461 // update cache pid when the user changed the audio track or video track
1462 // TODO handling of difference audio types.. default audio types..
1463                                 
1464                 /* don't worry about non-existing services, nor pvr services */
1465                 if (m_dvb_service && !m_is_pvr)
1466                 {
1467                         if (apidtype == eDVBAudio::aMPEG)
1468                         {
1469                                 m_dvb_service->setCachePID(eDVBService::cAPID, apid);
1470                                 m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
1471                         }
1472                         else
1473                         {
1474                                 m_dvb_service->setCachePID(eDVBService::cAPID, -1);
1475                                 m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
1476                         }
1477                         m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
1478                         m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
1479                         m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
1480                 }
1481         }
1482 }
1483
1484 void eDVBServicePlay::loadCuesheet()
1485 {
1486         std::string filename = m_reference.path + ".cuts";
1487         
1488         m_cue_entries.clear();
1489
1490         FILE *f = fopen(filename.c_str(), "rb");
1491
1492         if (f)
1493         {
1494                 eDebug("loading cuts..");
1495                 while (1)
1496                 {
1497                         unsigned long long where;
1498                         unsigned int what;
1499                         
1500                         if (!fread(&where, sizeof(where), 1, f))
1501                                 break;
1502                         if (!fread(&what, sizeof(what), 1, f))
1503                                 break;
1504                         
1505 #if BYTE_ORDER == LITTLE_ENDIAN
1506                         where = bswap_64(where);
1507 #endif
1508                         what = ntohl(what);
1509                         
1510                         if (what > 2)
1511                                 break;
1512                         
1513                         m_cue_entries.insert(cueEntry(where, what));
1514                 }
1515                 fclose(f);
1516                 eDebug("%d entries", m_cue_entries.size());
1517         } else
1518                 eDebug("cutfile not found!");
1519         
1520         m_cuesheet_changed = 0;
1521         cutlistToCuesheet();
1522         m_event((iPlayableService*)this, evCuesheetChanged);
1523 }
1524
1525 void eDVBServicePlay::saveCuesheet()
1526 {
1527         std::string filename = m_reference.path + ".cuts";
1528         
1529         FILE *f = fopen(filename.c_str(), "wb");
1530
1531         if (f)
1532         {
1533                 unsigned long long where;
1534                 int what;
1535
1536                 for (std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1537                 {
1538 #if BYTE_ORDER == BIG_ENDIAN
1539                         where = i->where;
1540 #else
1541                         where = bswap_64(i->where);
1542 #endif
1543                         what = htonl(i->what);
1544                         fwrite(&where, sizeof(where), 1, f);
1545                         fwrite(&what, sizeof(what), 1, f);
1546                         
1547                 }
1548                 fclose(f);
1549         }
1550         
1551         m_cuesheet_changed = 0;
1552 }
1553
1554 void eDVBServicePlay::cutlistToCuesheet()
1555 {
1556         if (!m_cue)
1557         {
1558                 eDebug("no cue sheet");
1559                 return;
1560         }       
1561         m_cue->clear();
1562         
1563         if (!m_cutlist_enabled)
1564         {
1565                 m_cue->commitSpans();
1566                 eDebug("cutlists where disabled");
1567                 return;
1568         }
1569
1570         pts_t in = 0, out = 0, length = 0;
1571         
1572         getLength(length);
1573                 
1574         std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
1575         
1576         while (1)
1577         {
1578                 if (i == m_cue_entries.end())
1579                         out = length;
1580                 else {
1581                         if (i->what == 0) /* in */
1582                         {
1583                                 in = i++->where;
1584                                 continue;
1585                         } else if (i->what == 1) /* out */
1586                                 out = i++->where;
1587                         else /* mark */
1588                         {
1589                                 i++;
1590                                 continue;
1591                         }
1592                 }
1593                 
1594                 if (in != out)
1595                         m_cue->addSourceSpan(in, out);
1596                 
1597                 in = length;
1598                 
1599                 if (i == m_cue_entries.end())
1600                         break;
1601         }
1602         m_cue->commitSpans();
1603 }
1604
1605 DEFINE_REF(eDVBServicePlay)
1606
1607 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");