1 #include <lib/base/eerror.h>
2 #include <lib/base/object.h>
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 #include <lib/base/nconfig.h> // access to python config
9 #include <lib/dvb/dvb.h>
10 #include <lib/dvb/db.h>
11 #include <lib/dvb/decoder.h>
13 #include <lib/components/file_eraser.h>
14 #include <lib/service/servicedvbrecord.h>
15 #include <lib/service/event.h>
16 #include <lib/dvb/metaparser.h>
17 #include <lib/dvb/tstools.h>
18 #include <lib/python/python.h>
21 #include <lib/gui/esubtitle.h>
26 #include <netinet/in.h>
28 #include <dvbsi++/event_information_section.h>
30 #define INTERNAL_TELETEXT
33 #error no byte order defined!
36 #define TSPATH "/media/hdd"
38 class eStaticServiceDVBInformation: public iStaticServiceInformation
40 DECLARE_REF(eStaticServiceDVBInformation);
42 RESULT getName(const eServiceReference &ref, std::string &name);
43 int getLength(const eServiceReference &ref);
46 DEFINE_REF(eStaticServiceDVBInformation);
48 RESULT eStaticServiceDVBInformation::getName(const eServiceReference &ref, std::string &name)
50 eServiceReferenceDVB &service = (eServiceReferenceDVB&)ref;
51 if ( !ref.name.empty() )
53 if (service.getParentTransportStreamID().get()) // linkage subservice
55 ePtr<iServiceHandler> service_center;
56 if (!eServiceCenter::getInstance(service_center))
58 eServiceReferenceDVB parent = service;
59 parent.setTransportStreamID( service.getParentTransportStreamID() );
60 parent.setServiceID( service.getParentServiceID() );
61 parent.setParentTransportStreamID(eTransportStreamID(0));
62 parent.setParentServiceID(eServiceID(0));
64 ePtr<iStaticServiceInformation> service_info;
65 if (!service_center->info(parent, service_info))
67 if (!service_info->getName(parent, name))
69 // just show short name
70 unsigned int pos = name.find("\xc2\x86");
71 if ( pos != std::string::npos )
73 pos = name.find("\xc2\x87");
74 if ( pos != std::string::npos )
90 int eStaticServiceDVBInformation::getLength(const eServiceReference &ref)
95 class eStaticServiceDVBBouquetInformation: public iStaticServiceInformation
97 DECLARE_REF(eStaticServiceDVBBouquetInformation);
99 RESULT getName(const eServiceReference &ref, std::string &name);
100 int getLength(const eServiceReference &ref);
103 DEFINE_REF(eStaticServiceDVBBouquetInformation);
105 RESULT eStaticServiceDVBBouquetInformation::getName(const eServiceReference &ref, std::string &name)
107 ePtr<iDVBChannelList> db;
108 ePtr<eDVBResourceManager> res;
111 if ((err = eDVBResourceManager::getInstance(res)) != 0)
113 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no resource manager!");
116 if ((err = res->getChannelList(db)) != 0)
118 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no channel list!");
123 if ((err = db->getBouquet(ref, bouquet)) != 0)
125 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. getBouquet failed!");
129 if ( bouquet && bouquet->m_bouquet_name.length() )
131 name = bouquet->m_bouquet_name;
138 int eStaticServiceDVBBouquetInformation::getLength(const eServiceReference &ref)
143 class eStaticServiceDVBPVRInformation: public iStaticServiceInformation
145 DECLARE_REF(eStaticServiceDVBPVRInformation);
146 eServiceReference m_ref;
147 eDVBMetaParser m_parser;
149 eStaticServiceDVBPVRInformation(const eServiceReference &ref);
150 RESULT getName(const eServiceReference &ref, std::string &name);
151 int getLength(const eServiceReference &ref);
153 int getInfo(const eServiceReference &ref, int w);
154 std::string getInfoString(const eServiceReference &ref,int w);
157 DEFINE_REF(eStaticServiceDVBPVRInformation);
159 eStaticServiceDVBPVRInformation::eStaticServiceDVBPVRInformation(const eServiceReference &ref)
162 m_parser.parseFile(ref.path);
165 RESULT eStaticServiceDVBPVRInformation::getName(const eServiceReference &ref, std::string &name)
167 ASSERT(ref == m_ref);
168 name = m_parser.m_name.size() ? m_parser.m_name : ref.path;
172 int eStaticServiceDVBPVRInformation::getLength(const eServiceReference &ref)
174 ASSERT(ref == m_ref);
178 if (tstools.openFile(ref.path.c_str()))
182 if (tstools.calcLen(len))
188 int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w)
192 case iServiceInformation::sDescription:
193 return iServiceInformation::resIsString;
194 case iServiceInformation::sServiceref:
195 return iServiceInformation::resIsString;
196 case iServiceInformation::sTimeCreate:
197 if (m_parser.m_time_create)
198 return m_parser.m_time_create;
200 return iServiceInformation::resNA;
202 return iServiceInformation::resNA;
206 std::string eStaticServiceDVBPVRInformation::getInfoString(const eServiceReference &ref,int w)
210 case iServiceInformation::sDescription:
211 return m_parser.m_description;
212 case iServiceInformation::sServiceref:
213 return m_parser.m_ref.toString();
219 class eDVBPVRServiceOfflineOperations: public iServiceOfflineOperations
221 DECLARE_REF(eDVBPVRServiceOfflineOperations);
222 eServiceReferenceDVB m_ref;
224 eDVBPVRServiceOfflineOperations(const eServiceReference &ref);
226 RESULT deleteFromDisk(int simulate);
227 RESULT getListOfFilenames(std::list<std::string> &);
230 DEFINE_REF(eDVBPVRServiceOfflineOperations);
232 eDVBPVRServiceOfflineOperations::eDVBPVRServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReferenceDVB&)ref)
236 RESULT eDVBPVRServiceOfflineOperations::deleteFromDisk(int simulate)
242 std::list<std::string> res;
243 if (getListOfFilenames(res))
246 eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
248 eDebug("FATAL !! can't get background file eraser");
250 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
252 eDebug("Removing %s...", i->c_str());
254 eraser->erase(i->c_str());
256 ::unlink(i->c_str());
263 RESULT eDVBPVRServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
266 res.push_back(m_ref.path);
268 // handling for old splitted recordings (enigma 1)
273 snprintf(buf, 255, "%s.%03d", m_ref.path.c_str(), slice++);
275 if (stat(buf, &s) < 0)
280 res.push_back(m_ref.path + ".meta");
281 res.push_back(m_ref.path + ".ap");
282 res.push_back(m_ref.path + ".cuts");
283 std::string tmp = m_ref.path;
284 tmp.erase(m_ref.path.length()-3);
285 res.push_back(tmp + ".eit");
289 DEFINE_REF(eServiceFactoryDVB)
291 eServiceFactoryDVB::eServiceFactoryDVB()
293 ePtr<eServiceCenter> sc;
295 eServiceCenter::getPrivInstance(sc);
297 sc->addServiceFactory(eServiceFactoryDVB::id, this);
300 eServiceFactoryDVB::~eServiceFactoryDVB()
302 ePtr<eServiceCenter> sc;
304 eServiceCenter::getPrivInstance(sc);
306 sc->removeServiceFactory(eServiceFactoryDVB::id);
309 DEFINE_REF(eDVBServiceList);
311 eDVBServiceList::eDVBServiceList(const eServiceReference &parent): m_parent(parent)
315 eDVBServiceList::~eDVBServiceList()
319 RESULT eDVBServiceList::startQuery()
321 ePtr<iDVBChannelList> db;
322 ePtr<eDVBResourceManager> res;
325 if ((err = eDVBResourceManager::getInstance(res)) != 0)
327 eDebug("no resource manager");
330 if ((err = res->getChannelList(db)) != 0)
332 eDebug("no channel list");
336 ePtr<eDVBChannelQuery> q;
338 if (!m_parent.path.empty())
340 eDVBChannelQuery::compile(q, m_parent.path);
343 eDebug("compile query failed");
348 if ((err = db->startQuery(m_query, q, m_parent)) != 0)
350 eDebug("startQuery failed");
357 RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
359 eServiceReferenceDVB ref;
364 while (!m_query->getNextResult(ref))
368 list.sort(iListableServiceCompare(this));
373 // The first argument of this function is a format string to specify the order and
374 // the content of the returned list
375 // useable format options are
376 // R = Service Reference (as swig object .. this is very slow)
377 // S = Service Reference (as python string object .. same as ref.toString())
378 // N = Service Name (as python string object)
379 // when exactly one return value per service is selected in the format string,
380 // then each value is directly a list entry
381 // when more than one value is returned per service, then the list is a list of
383 // unknown format string chars are returned as python None values !
384 PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
387 std::list<eServiceReference> tmplist;
390 if (!format || !(retcount=strlen(format)))
391 format = "R"; // just return service reference swig object ...
393 if (!getContent(tmplist, sorted))
395 int services=tmplist.size();
396 ePtr<iStaticServiceInformation> sptr;
397 eServiceCenterPtr service_center;
399 if (strchr(format, 'N'))
400 eServiceCenter::getPrivInstance(service_center);
402 ret = PyList_New(services);
403 std::list<eServiceReference>::iterator it(tmplist.begin());
405 for (int cnt=0; cnt < services; ++cnt)
407 eServiceReference &ref=*it++;
408 PyObject *tuple = retcount > 1 ? PyTuple_New(retcount) : 0;
409 for (int i=0; i < retcount; ++i)
414 case 'R': // service reference (swig)object
415 tmp = New_eServiceReference(ref);
417 case 'S': // service reference string
418 tmp = PyString_FromString(ref.toString().c_str());
420 case 'N': // service name
423 service_center->info(ref, sptr);
427 sptr->getName(ref, name);
429 tmp = PyString_FromString(name.c_str());
433 tmp = PyString_FromString("<n/a>");
446 PyTuple_SET_ITEM(tuple, i, tmp);
448 PyList_SET_ITEM(ret, cnt, tmp);
452 PyList_SET_ITEM(ret, cnt, tuple);
455 return ret ? ret : PyList_New(0);
458 RESULT eDVBServiceList::getNext(eServiceReference &ref)
463 return m_query->getNextResult((eServiceReferenceDVB&)ref);
466 int eDVBServiceList::compareLessEqual(const eServiceReference &a, const eServiceReference &b)
468 return m_query->compareLessEqual((const eServiceReferenceDVB&)a, (const eServiceReferenceDVB&)b);
471 RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
473 if (m_parent.flags & eServiceReference::flagDirectory) // bouquet
475 ePtr<iDVBChannelList> db;
476 ePtr<eDVBResourceManager> resm;
478 if (eDVBResourceManager::getInstance(resm) || resm->getChannelList(db))
481 if (db->getBouquet(m_parent, m_bouquet) != 0)
492 RESULT eDVBServiceList::addService(eServiceReference &ref)
496 return m_bouquet->addService(ref);
499 RESULT eDVBServiceList::removeService(eServiceReference &ref)
503 return m_bouquet->removeService(ref);
506 RESULT eDVBServiceList::moveService(eServiceReference &ref, int pos)
510 return m_bouquet->moveService(ref, pos);
513 RESULT eDVBServiceList::flushChanges()
517 return m_bouquet->flushChanges();
520 RESULT eDVBServiceList::setListName(const std::string &name)
524 return m_bouquet->setListName(name);
527 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
529 ePtr<eDVBService> service;
530 int r = lookupService(service, ref);
533 // check resources...
534 ptr = new eDVBServicePlay(ref, service);
538 RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
540 if (ref.path.empty())
542 ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
551 RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
553 ePtr<eDVBServiceList> list = new eDVBServiceList(ref);
554 if (list->startQuery())
564 RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
566 /* is a listable service? */
567 if ((ref.flags & eServiceReference::flagDirectory) == eServiceReference::flagDirectory) // bouquet
569 if ( !ref.name.empty() ) // satellites or providers list
570 ptr = new eStaticServiceDVBInformation;
571 else // a dvb bouquet
572 ptr = new eStaticServiceDVBBouquetInformation;
574 else if (!ref.path.empty()) /* do we have a PVR service? */
575 ptr = new eStaticServiceDVBPVRInformation(ref);
576 else // normal dvb service
578 ePtr<eDVBService> service;
579 if (lookupService(service, ref)) // no eDVBService avail for this reference ( Linkage Services... )
580 ptr = new eStaticServiceDVBInformation;
582 /* eDVBService has the iStaticServiceInformation interface, so we pass it here. */
588 RESULT eServiceFactoryDVB::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
590 if (ref.path.empty())
596 ptr = new eDVBPVRServiceOfflineOperations(ref);
601 RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServiceReference &ref)
603 // TODO: handle the listing itself
604 // if (ref.... == -1) .. return "... bouquets ...";
605 // could be also done in another serviceFactory (with seperate ID) to seperate actual services and lists
607 ePtr<iDVBChannelList> db;
608 ePtr<eDVBResourceManager> res;
611 if ((err = eDVBResourceManager::getInstance(res)) != 0)
613 eDebug("no resource manager");
616 if ((err = res->getChannelList(db)) != 0)
618 eDebug("no channel list");
622 /* we are sure to have a ..DVB reference as the info() call was forwarded here according to it's ID. */
623 if ((err = db->getService((eServiceReferenceDVB&)ref, service)) != 0)
625 eDebug("getService failed!");
632 eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service):
633 m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
636 m_is_pvr = !m_reference.path.empty();
638 m_timeshift_enabled = m_timeshift_active = 0;
641 CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
642 CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
643 CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
645 m_cuesheet_changed = 0;
646 m_cutlist_enabled = 1;
648 m_subtitle_widget = 0;
650 CONNECT(m_subtitle_sync_timer.timeout, eDVBServicePlay::checkSubtitleTiming);
653 eDVBServicePlay::~eDVBServicePlay()
655 delete m_subtitle_widget;
658 void eDVBServicePlay::gotNewEvent()
662 ePtr<eServiceEvent> m_event_now, m_event_next;
663 getEvent(m_event_now, 0);
664 getEvent(m_event_next, 1);
667 eDebug("now running: %s (%d seconds :)", m_event_now->m_event_name.c_str(), m_event_now->m_duration);
669 eDebug("next running: %s (%d seconds :)", m_event_next->m_event_name.c_str(), m_event_next->m_duration);
671 m_event((iPlayableService*)this, evUpdatedEventInfo);
674 void eDVBServicePlay::serviceEvent(int event)
678 case eDVBServicePMTHandler::eventTuned:
680 ePtr<iDVBDemux> m_demux;
681 if (!m_service_handler.getDataDemux(m_demux))
683 eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_reference;
684 int sid = ref.getParentServiceID().get();
686 sid = ref.getServiceID().get();
687 if ( ref.getParentTransportStreamID().get() &&
688 ref.getParentTransportStreamID() != ref.getTransportStreamID() )
689 m_event_handler.startOther(m_demux, sid);
691 m_event_handler.start(m_demux, sid);
695 case eDVBServicePMTHandler::eventTuneFailed:
697 eDebug("DVB service failed to tune");
698 m_event((iPlayableService*)this, evTuneFailed);
701 case eDVBServicePMTHandler::eventNewProgramInfo:
703 eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active);
704 if (m_timeshift_enabled)
705 updateTimeshiftPids();
706 if (!m_timeshift_active)
708 if (m_first_program_info && m_is_pvr)
710 m_first_program_info = 0;
713 m_event((iPlayableService*)this, evUpdatedInfo);
716 case eDVBServicePMTHandler::eventEOF:
717 m_event((iPlayableService*)this, evEOF);
719 case eDVBServicePMTHandler::eventSOF:
720 m_event((iPlayableService*)this, evSOF);
725 void eDVBServicePlay::serviceEventTimeshift(int event)
729 case eDVBServicePMTHandler::eventNewProgramInfo:
730 if (m_timeshift_active)
733 case eDVBServicePMTHandler::eventSOF:
734 m_event((iPlayableService*)this, evSOF);
736 case eDVBServicePMTHandler::eventEOF:
742 RESULT eDVBServicePlay::start()
745 /* in pvr mode, we only want to use one demux. in tv mode, we're using
746 two (one for decoding, one for data source), as we must be prepared
747 to start recording from the data demux. */
749 m_cue = new eCueSheet();
751 m_first_program_info = 1;
752 eServiceReferenceDVB &service = (eServiceReferenceDVB&)m_reference;
753 r = m_service_handler.tune(service, m_is_pvr, m_cue);
755 // get back correct service reference (after parsing of recording meta files)
756 m_service_handler.getServiceReference(service);
758 /* inject EIT if there is a stored one */
761 std::string filename = service.path;
762 filename.erase(filename.length()-2, 2);
764 int fd = ::open( filename.c_str(), O_RDONLY );
768 int rd = ::read(fd, buf, 4096);
770 if ( rd > 12 /*EIT_LOOP_SIZE*/ )
773 ePtr<eServiceEvent> event = new eServiceEvent;
774 ePtr<eServiceEvent> empty;
775 event->parseFrom(&ev, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get());
776 m_event_handler.inject(event, 0);
777 m_event_handler.inject(empty, 1);
786 m_event(this, evStart);
787 m_event((iPlayableService*)this, evSeekableStatusChanged);
791 RESULT eDVBServicePlay::stop()
793 stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
795 m_service_handler_timeshift.free();
796 m_service_handler.free();
798 if (m_is_pvr && m_cuesheet_changed)
804 RESULT eDVBServicePlay::setTarget(int target)
806 m_is_primary = !target;
810 RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
812 connection = new eConnection((iPlayableService*)this, m_event.connect(event));
816 RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
818 /* note: we check for timeshift to be enabled,
819 not neccessary active. if you pause when timeshift
820 is not active, you should activate it when unpausing */
821 if ((!m_is_pvr) && (!m_timeshift_enabled))
831 RESULT eDVBServicePlay::setSlowMotion(int ratio)
834 return m_decoder->setSlowMotion(ratio);
839 RESULT eDVBServicePlay::setFastForward(int ratio)
841 int skipmode, ffratio;
847 } else if (ratio > 0)
855 } else // if (ratio < 0)
861 if (m_skipmode != skipmode)
863 eDebug("setting cue skipmode to %d", skipmode);
865 m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
868 m_skipmode = skipmode;
873 return m_decoder->setFastForward(ffratio);
876 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
878 if (m_is_pvr || m_timeshift_enabled)
888 /* TODO: when timeshift is enabled but not active, this doesn't work. */
889 RESULT eDVBServicePlay::getLength(pts_t &len)
891 ePtr<iDVBPVRChannel> pvr_channel;
893 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
896 return pvr_channel->getLength(len);
899 RESULT eDVBServicePlay::pause()
901 if (!m_is_paused && m_decoder)
904 return m_decoder->freeze(0);
909 RESULT eDVBServicePlay::unpause()
911 if (m_is_paused && m_decoder)
914 return m_decoder->unfreeze();
919 RESULT eDVBServicePlay::seekTo(pts_t to)
921 eDebug("eDVBServicePlay::seekTo: jump %lld", to);
926 ePtr<iDVBPVRChannel> pvr_channel;
928 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
934 m_cue->seekTo(0, to);
938 RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
940 eDebug("eDVBServicePlay::seekRelative: jump %d, %lld", direction, to);
945 ePtr<iDVBPVRChannel> pvr_channel;
947 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
952 /* HACK until we have skip-AP api */
953 if ((to > 0) && (to < 100))
961 m_cue->seekTo(mode, to);
965 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
967 ePtr<iDVBPVRChannel> pvr_channel;
972 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
977 /* if there is a decoder, use audio or video PTS */
980 r = m_decoder->getPTS(0, pos);
986 return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
989 RESULT eDVBServicePlay::setTrickmode(int trick)
992 m_decoder->setTrickmode(trick);
996 RESULT eDVBServicePlay::isCurrentlySeekable()
998 return m_is_pvr || m_timeshift_active;
1001 RESULT eDVBServicePlay::frontendInfo(ePtr<iFrontendInformation> &ptr)
1007 RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
1013 RESULT eDVBServicePlay::audioChannel(ePtr<iAudioChannelSelection> &ptr)
1019 RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
1025 RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
1031 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
1034 if (m_have_video_pid && // HACK !!! FIXMEE !! temporary no timeshift on radio services !!
1035 (m_timeshift_enabled || !m_is_pvr))
1037 if (!m_timeshift_enabled)
1039 /* we need enough diskspace */
1041 if (statfs(TSPATH "/.", &fs) < 0)
1043 eDebug("statfs failed!");
1047 if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
1049 eDebug("not enough diskspace for timeshift! (less than 1GB)");
1059 RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
1070 RESULT eDVBServicePlay::subtitle(ePtr<iSubtitleOutput> &ptr)
1076 RESULT eDVBServicePlay::audioDelay(ePtr<iAudioDelay> &ptr)
1082 RESULT eDVBServicePlay::getName(std::string &name)
1086 ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
1087 return i->getName(m_reference, name);
1091 m_dvb_service->getName(m_reference, name);
1095 else if (!m_reference.name.empty())
1096 eStaticServiceDVBInformation().getName(m_reference, name);
1098 name = "DVB service";
1102 RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
1104 return m_event_handler.getEvent(evt, nownext);
1107 int eDVBServicePlay::getInfo(int w)
1109 eDVBServicePMTHandler::program program;
1112 return resIsPyObject;
1114 if (m_service_handler.getProgramInfo(program))
1120 if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
1122 ePtr<eServiceEvent> evt;
1123 if (!m_event_handler.getEvent(evt, 0))
1125 ePtr<eComponentData> data;
1126 if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
1128 if ( data->getStreamContent() == 1 )
1130 switch(data->getComponentType())
1133 case 1: // 4:3 SD PAL
1135 case 3: // 16:9 SD PAL
1136 case 4: // > 16:9 PAL
1137 case 5: // 4:3 SD NTSC
1139 case 7: // 16:9 SD NTSC
1140 case 8: // > 16:9 NTSC
1143 case 9: // 4:3 HD PAL
1145 case 0xB: // 16:9 HD PAL
1146 case 0xC: // > 16:9 HD PAL
1147 case 0xD: // 4:3 HD NTSC
1149 case 0xF: // 16:9 HD NTSC
1150 case 0x10: // > 16:9 HD PAL
1151 return data->getComponentType();
1158 case sIsCrypted: return program.isCrypted();
1159 case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
1160 case sVideoType: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].type;
1161 case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
1162 case sPCRPID: return program.pcrPid;
1163 case sPMTPID: return program.pmtPid;
1164 case sTXTPID: return program.textPid;
1165 case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
1166 case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
1167 case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
1168 case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
1169 case sProvider: if (!m_dvb_service) return -1; return -2;
1175 std::string eDVBServicePlay::getInfoString(int w)
1180 if (!m_dvb_service) return "";
1181 return m_dvb_service->m_provider_name;
1185 return iServiceInformation::getInfoString(w);
1188 PyObject *eDVBServicePlay::getInfoObject(int w)
1193 return m_service_handler.getCaIds();
1197 return iServiceInformation::getInfoObject(w);
1200 int eDVBServicePlay::getNumberOfTracks()
1202 eDVBServicePMTHandler::program program;
1203 if (m_service_handler.getProgramInfo(program))
1205 return program.audioStreams.size();
1208 RESULT eDVBServicePlay::selectTrack(unsigned int i)
1210 int ret = selectAudioStream(i);
1212 if (m_decoder->start())
1218 RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1220 eDVBServicePMTHandler::program program;
1222 if (m_service_handler.getProgramInfo(program))
1225 if (i >= program.audioStreams.size())
1228 if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
1229 info.m_description = "MPEG";
1230 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
1231 info.m_description = "AC3";
1232 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAAC)
1233 info.m_description = "AAC";
1234 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
1235 info.m_description = "DTS";
1237 info.m_description = "???";
1239 if (program.audioStreams[i].component_tag != -1)
1241 ePtr<eServiceEvent> evt;
1242 if (!m_event_handler.getEvent(evt, 0))
1244 ePtr<eComponentData> data;
1245 if (!evt->getComponentData(data, program.audioStreams[i].component_tag))
1246 info.m_language = data->getText();
1250 if (info.m_language.empty())
1251 info.m_language = program.audioStreams[i].language_code;
1256 int eDVBServicePlay::selectAudioStream(int i)
1258 eDVBServicePMTHandler::program program;
1260 if (m_service_handler.getProgramInfo(program))
1263 if ((unsigned int)i >= program.audioStreams.size())
1269 if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
1272 if (m_dvb_service && !m_is_pvr)
1274 if (program.audioStreams[i].type == eDVBAudio::aMPEG)
1276 m_dvb_service->setCacheEntry(eDVBService::cAPID, program.audioStreams[i].pid);
1277 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
1280 m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
1281 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, program.audioStreams[i].pid);
1285 m_current_audio_stream = i;
1290 int eDVBServicePlay::getCurrentChannel()
1292 return m_decoder ? m_decoder->getAudioChannel() : STEREO;
1295 RESULT eDVBServicePlay::selectChannel(int i)
1297 if (i < LEFT || i > RIGHT || i == STEREO)
1300 m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
1302 m_decoder->setAudioChannel(i);
1306 int eDVBServiceBase::getFrontendInfo(int w)
1308 eUsePtr<iDVBChannel> channel;
1309 if(m_service_handler.getChannel(channel))
1311 ePtr<iDVBFrontend> fe;
1312 if(channel->getFrontend(fe))
1314 return fe->readFrontendData(w);
1317 PyObject *eDVBServiceBase::getFrontendData(bool original)
1321 eUsePtr<iDVBChannel> channel;
1322 if(!m_service_handler.getChannel(channel))
1324 ePtr<iDVBFrontend> fe;
1325 if(!channel->getFrontend(fe))
1327 ret = fe->readTransponderData(original);
1330 ePtr<iDVBFrontendParameters> feparm;
1331 channel->getCurrentFrontendParameters(feparm);
1334 eDVBFrontendParametersSatellite osat;
1335 if (!feparm->getDVBS(osat))
1337 void PutToDict(PyObject *, const char*, long);
1338 void PutToDict(PyObject *, const char*, const char*);
1339 PutToDict(ret, "orbital_position", osat.orbital_position);
1340 const char *tmp = "UNKNOWN";
1341 switch(osat.polarisation)
1343 case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
1344 case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
1345 case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR_LEFT"; break;
1346 case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR_RIGHT"; break;
1349 PutToDict(ret, "polarization", tmp);
1363 int eDVBServicePlay::getNumberOfSubservices()
1365 ePtr<eServiceEvent> evt;
1366 if (!m_event_handler.getEvent(evt, 0))
1367 return evt->getNumOfLinkageServices();
1371 RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
1373 ePtr<eServiceEvent> evt;
1374 if (!m_event_handler.getEvent(evt, 0))
1376 if (!evt->getLinkageService(sub, m_reference, n))
1379 sub.type=eServiceReference::idInvalid;
1383 RESULT eDVBServicePlay::startTimeshift()
1385 ePtr<iDVBDemux> demux;
1387 eDebug("Start timeshift!");
1389 if (m_timeshift_enabled)
1392 /* start recording with the data demux. */
1393 if (m_service_handler.getDataDemux(demux))
1396 demux->createTSRecorder(m_record);
1400 char templ[]=TSPATH "/timeshift.XXXXXX";
1401 m_timeshift_fd = mkstemp(templ);
1402 m_timeshift_file = templ;
1404 eDebug("recording to %s", templ);
1406 if (m_timeshift_fd < 0)
1412 m_record->setTargetFD(m_timeshift_fd);
1414 m_timeshift_enabled = 1;
1416 updateTimeshiftPids();
1422 RESULT eDVBServicePlay::stopTimeshift()
1424 if (!m_timeshift_enabled)
1429 m_timeshift_enabled = 0;
1434 close(m_timeshift_fd);
1435 eDebug("remove timeshift file");
1436 remove(m_timeshift_file.c_str());
1441 int eDVBServicePlay::isTimeshiftActive()
1443 return m_timeshift_enabled && m_timeshift_active;
1446 RESULT eDVBServicePlay::activateTimeshift()
1448 if (!m_timeshift_enabled)
1451 if (!m_timeshift_active)
1453 switchToTimeshift();
1460 PyObject *eDVBServicePlay::getCutList()
1462 PyObject *list = PyList_New(0);
1464 for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1466 PyObject *tuple = PyTuple_New(2);
1467 PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
1468 PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
1469 PyList_Append(list, tuple);
1476 void eDVBServicePlay::setCutList(PyObject *list)
1478 if (!PyList_Check(list))
1480 int size = PyList_Size(list);
1483 m_cue_entries.clear();
1485 for (i=0; i<size; ++i)
1487 PyObject *tuple = PyList_GetItem(list, i);
1488 if (!PyTuple_Check(tuple))
1490 eDebug("non-tuple in cutlist");
1493 if (PyTuple_Size(tuple) != 2)
1495 eDebug("cutlist entries need to be a 2-tuple");
1498 PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
1499 if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
1501 eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
1504 pts_t pts = PyLong_AsLongLong(ppts);
1505 int type = PyInt_AsLong(ptype);
1506 m_cue_entries.insert(cueEntry(pts, type));
1507 eDebug("adding %08llx, %d", pts, type);
1509 m_cuesheet_changed = 1;
1511 cutlistToCuesheet();
1512 m_event((iPlayableService*)this, evCuesheetChanged);
1515 void eDVBServicePlay::setCutListEnable(int enable)
1517 m_cutlist_enabled = enable;
1518 cutlistToCuesheet();
1521 void eDVBServicePlay::updateTimeshiftPids()
1526 eDVBServicePMTHandler::program program;
1527 if (m_service_handler.getProgramInfo(program))
1531 std::set<int> pids_to_record;
1532 pids_to_record.insert(0); // PAT
1533 if (program.pmtPid != -1)
1534 pids_to_record.insert(program.pmtPid); // PMT
1536 if (program.textPid != -1)
1537 pids_to_record.insert(program.textPid); // Videotext
1539 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1540 i(program.videoStreams.begin());
1541 i != program.videoStreams.end(); ++i)
1542 pids_to_record.insert(i->pid);
1544 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1545 i(program.audioStreams.begin());
1546 i != program.audioStreams.end(); ++i)
1547 pids_to_record.insert(i->pid);
1549 std::set<int> new_pids, obsolete_pids;
1551 std::set_difference(pids_to_record.begin(), pids_to_record.end(),
1552 m_pids_active.begin(), m_pids_active.end(),
1553 std::inserter(new_pids, new_pids.begin()));
1555 std::set_difference(
1556 m_pids_active.begin(), m_pids_active.end(),
1557 pids_to_record.begin(), pids_to_record.end(),
1558 std::inserter(new_pids, new_pids.begin())
1561 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
1562 m_record->addPID(*i);
1564 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
1565 m_record->removePID(*i);
1569 void eDVBServicePlay::switchToLive()
1571 if (!m_timeshift_active)
1577 m_teletext_parser = 0;
1578 m_new_subtitle_page_connection = 0;
1580 /* free the timeshift service handler, we need the resources */
1581 m_service_handler_timeshift.free();
1582 m_timeshift_active = 0;
1584 m_event((iPlayableService*)this, evSeekableStatusChanged);
1589 void eDVBServicePlay::switchToTimeshift()
1591 if (m_timeshift_active)
1596 m_teletext_parser = 0;
1597 m_new_subtitle_page_connection = 0;
1599 m_timeshift_active = 1;
1601 m_event((iPlayableService*)this, evSeekableStatusChanged);
1603 eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
1604 r.path = m_timeshift_file;
1606 m_cue = new eCueSheet();
1607 m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
1608 updateDecoder(); /* mainly to switch off PCR */
1611 void eDVBServicePlay::updateDecoder()
1613 int vpid = -1, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1;
1615 eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
1617 bool defaultac3=false;
1618 std::string default_ac3;
1620 if (!ePythonConfigQuery::getConfigValue("config.av.defaultac3", default_ac3))
1621 defaultac3 = default_ac3 == "enable";
1623 eDVBServicePMTHandler::program program;
1624 if (h.getProgramInfo(program))
1625 eDebug("getting program info failed.");
1628 eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
1629 if (!program.videoStreams.empty())
1631 eDebugNoNewLine(" (");
1632 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1633 i(program.videoStreams.begin());
1634 i != program.videoStreams.end(); ++i)
1641 if (i != program.videoStreams.begin())
1642 eDebugNoNewLine(", ");
1643 eDebugNoNewLine("%04x", i->pid);
1645 eDebugNoNewLine(")");
1647 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
1648 if (!program.audioStreams.empty())
1650 eDebugNoNewLine(" (");
1651 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1652 i(program.audioStreams.begin());
1653 i != program.audioStreams.end(); ++i)
1655 if (apid == -1 || (apidtype == eDVBAudio::aMPEG && defaultac3))
1657 if ( apid == -1 || (i->type != eDVBAudio::aMPEG) )
1663 if (i != program.audioStreams.begin())
1664 eDebugNoNewLine(", ");
1665 eDebugNoNewLine("%04x", i->pid);
1667 eDebugNoNewLine(")");
1669 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
1670 pcrpid = program.pcrPid;
1671 eDebug(", and the text pid is %04x", program.textPid);
1672 tpid = program.textPid;
1677 h.getDecodeDemux(m_decode_demux);
1679 m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
1681 m_cue->setDecodingDemux(m_decode_demux, m_decoder);
1682 #ifdef INTERNAL_TELETEXT
1683 m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
1684 m_teletext_parser->connectNewPage(slot(*this, &eDVBServicePlay::newSubtitlePage), m_new_subtitle_page_connection);
1692 achannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL);
1693 ac3_delay = m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
1694 pcm_delay = m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
1696 else // subservice or recording
1698 eServiceReferenceDVB parent = ((eServiceReferenceDVB&)m_reference).getParentServiceReference();
1699 if (!parent && !m_reference.path.empty()) // is recording
1701 parent = (eServiceReferenceDVB&)m_reference;
1706 ePtr<eDVBResourceManager> res_mgr;
1707 if (!eDVBResourceManager::getInstance(res_mgr))
1709 ePtr<iDVBChannelList> db;
1710 if (!res_mgr->getChannelList(db))
1712 ePtr<eDVBService> origService;
1713 if (!db->getService(parent, origService))
1715 ac3_delay = origService->getCacheEntry(eDVBService::cAC3DELAY);
1716 pcm_delay = origService->getCacheEntry(eDVBService::cPCMDELAY);
1723 m_decoder->setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
1724 m_decoder->setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
1726 m_decoder->setVideoPID(vpid, vpidtype);
1727 m_current_audio_stream = 0;
1728 m_decoder->setAudioPID(apid, apidtype);
1729 if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
1730 m_decoder->setSyncPCR(pcrpid);
1732 m_decoder->setSyncPCR(-1);
1734 m_decoder->setTextPID(tpid);
1736 if (m_teletext_parser)
1737 m_teletext_parser->start(tpid);
1740 m_decoder->setTrickmode(1);
1744 if (vpid > 0 && vpid < 0x2000)
1748 std::string radio_pic;
1749 if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic))
1750 m_decoder->setRadioPic(radio_pic);
1753 m_decoder->setAudioChannel(achannel);
1755 // how we can do this better?
1756 // update cache pid when the user changed the audio track or video track
1757 // TODO handling of difference audio types.. default audio types..
1759 /* don't worry about non-existing services, nor pvr services */
1760 if (m_dvb_service && !m_is_pvr)
1762 if (apidtype == eDVBAudio::aMPEG)
1764 m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
1765 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
1769 m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
1770 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
1772 m_dvb_service->setCacheEntry(eDVBService::cVPID, vpid);
1773 m_dvb_service->setCacheEntry(eDVBService::cVTYPE, vpidtype == eDVBVideo::MPEG2 ? -1 : vpidtype);
1774 m_dvb_service->setCacheEntry(eDVBService::cPCRPID, pcrpid);
1775 m_dvb_service->setCacheEntry(eDVBService::cTPID, tpid);
1778 m_have_video_pid = (vpid > 0 && vpid < 0x2000);
1781 void eDVBServicePlay::loadCuesheet()
1783 std::string filename = m_reference.path + ".cuts";
1785 m_cue_entries.clear();
1787 FILE *f = fopen(filename.c_str(), "rb");
1791 eDebug("loading cuts..");
1794 unsigned long long where;
1797 if (!fread(&where, sizeof(where), 1, f))
1799 if (!fread(&what, sizeof(what), 1, f))
1802 #if BYTE_ORDER == LITTLE_ENDIAN
1803 where = bswap_64(where);
1810 m_cue_entries.insert(cueEntry(where, what));
1813 eDebug("%d entries", m_cue_entries.size());
1815 eDebug("cutfile not found!");
1817 m_cuesheet_changed = 0;
1818 cutlistToCuesheet();
1819 m_event((iPlayableService*)this, evCuesheetChanged);
1822 void eDVBServicePlay::saveCuesheet()
1824 std::string filename = m_reference.path + ".cuts";
1826 FILE *f = fopen(filename.c_str(), "wb");
1830 unsigned long long where;
1833 for (std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1835 #if BYTE_ORDER == BIG_ENDIAN
1838 where = bswap_64(i->where);
1840 what = htonl(i->what);
1841 fwrite(&where, sizeof(where), 1, f);
1842 fwrite(&what, sizeof(what), 1, f);
1848 m_cuesheet_changed = 0;
1851 void eDVBServicePlay::cutlistToCuesheet()
1855 eDebug("no cue sheet");
1860 if (!m_cutlist_enabled)
1862 m_cue->commitSpans();
1863 eDebug("cutlists where disabled");
1867 pts_t in = 0, out = 0, length = 0;
1871 std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
1875 if (i == m_cue_entries.end())
1878 if (i->what == 0) /* in */
1882 } else if (i->what == 1) /* out */
1892 m_cue->addSourceSpan(in, out);
1896 if (i == m_cue_entries.end())
1899 m_cue->commitSpans();
1902 RESULT eDVBServicePlay::enableSubtitles(eWidget *parent, PyObject *entry)
1904 if (m_subtitle_widget)
1905 disableSubtitles(parent);
1907 if (!m_teletext_parser)
1910 if (!PyInt_Check(entry))
1913 m_subtitle_widget = new eSubtitleWidget(parent);
1914 m_subtitle_widget->resize(parent->size()); /* full size */
1916 int page = PyInt_AsLong(entry);
1918 m_teletext_parser->setPage(page);
1923 RESULT eDVBServicePlay::disableSubtitles(eWidget *parent)
1925 delete m_subtitle_widget;
1926 m_subtitle_widget = 0;
1930 PyObject *eDVBServicePlay::getSubtitleList()
1932 if (!m_teletext_parser)
1938 PyObject *l = PyList_New(0);
1940 for (std::set<int>::iterator i(m_teletext_parser->m_found_subtitle_pages.begin()); i != m_teletext_parser->m_found_subtitle_pages.end(); ++i)
1942 PyObject *tuple = PyTuple_New(2);
1944 sprintf(desc, "Page %x", *i);
1945 PyTuple_SetItem(tuple, 0, PyString_FromString(desc));
1946 PyTuple_SetItem(tuple, 1, PyInt_FromLong(*i));
1947 PyList_Append(l, tuple);
1953 void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
1955 if (m_subtitle_widget)
1957 m_subtitle_pages.push_back(page);
1959 checkSubtitleTiming();
1963 void eDVBServicePlay::checkSubtitleTiming()
1967 if (m_subtitle_pages.empty())
1970 eDVBTeletextSubtitlePage p = m_subtitle_pages.front();
1975 m_decoder->getPTS(0, pos);
1977 int diff = p.m_pts - pos;
1980 eDebug("[late (%d ms)]", -diff / 90);
1985 eDebug("[invalid]");
1991 m_subtitle_widget->setPage(p);
1992 m_subtitle_pages.pop_front();
1995 m_subtitle_sync_timer.start(diff / 90, 1);
2001 int eDVBServicePlay::getAC3Delay()
2004 return m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
2006 return m_decoder->getAC3Delay();
2011 int eDVBServicePlay::getPCMDelay()
2014 return m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
2016 return m_decoder->getPCMDelay();
2021 void eDVBServicePlay::setAC3Delay(int delay)
2024 m_dvb_service->setCacheEntry(eDVBService::cAC3DELAY, delay ? delay : -1);
2026 m_decoder->setAC3Delay(delay);
2029 void eDVBServicePlay::setPCMDelay(int delay)
2032 m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
2034 m_decoder->setPCMDelay(delay);
2037 DEFINE_REF(eDVBServicePlay)
2039 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");