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