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