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 #define INTERNAL_TELETEXT
31 #error no byte order defined!
34 #define TSPATH "/media/hdd"
36 class eStaticServiceDVBInformation: public iStaticServiceInformation
38 DECLARE_REF(eStaticServiceDVBInformation);
40 RESULT getName(const eServiceReference &ref, std::string &name);
41 int getLength(const eServiceReference &ref);
44 DEFINE_REF(eStaticServiceDVBInformation);
46 RESULT eStaticServiceDVBInformation::getName(const eServiceReference &ref, std::string &name)
48 eServiceReferenceDVB &service = (eServiceReferenceDVB&)ref;
49 if ( !ref.name.empty() )
51 if (service.getParentTransportStreamID().get()) // linkage subservice
53 ePtr<iServiceHandler> service_center;
54 if (!eServiceCenter::getInstance(service_center))
56 eServiceReferenceDVB parent = service;
57 parent.setTransportStreamID( service.getParentTransportStreamID() );
58 parent.setServiceID( service.getParentServiceID() );
59 parent.setParentTransportStreamID(eTransportStreamID(0));
60 parent.setParentServiceID(eServiceID(0));
62 ePtr<iStaticServiceInformation> service_info;
63 if (!service_center->info(parent, service_info))
65 if (!service_info->getName(parent, name))
67 // just show short name
68 unsigned int pos = name.find("\xc2\x86");
69 if ( pos != std::string::npos )
71 pos = name.find("\xc2\x87");
72 if ( pos != std::string::npos )
88 int eStaticServiceDVBInformation::getLength(const eServiceReference &ref)
93 class eStaticServiceDVBBouquetInformation: public iStaticServiceInformation
95 DECLARE_REF(eStaticServiceDVBBouquetInformation);
97 RESULT getName(const eServiceReference &ref, std::string &name);
98 int getLength(const eServiceReference &ref);
101 DEFINE_REF(eStaticServiceDVBBouquetInformation);
103 RESULT eStaticServiceDVBBouquetInformation::getName(const eServiceReference &ref, std::string &name)
105 ePtr<iDVBChannelList> db;
106 ePtr<eDVBResourceManager> res;
109 if ((err = eDVBResourceManager::getInstance(res)) != 0)
111 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no resource manager!");
114 if ((err = res->getChannelList(db)) != 0)
116 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. no channel list!");
121 if ((err = db->getBouquet(ref, bouquet)) != 0)
123 eDebug("eStaticServiceDVBBouquetInformation::getName failed.. getBouquet failed!");
127 if ( bouquet && bouquet->m_bouquet_name.length() )
129 name = bouquet->m_bouquet_name;
136 int eStaticServiceDVBBouquetInformation::getLength(const eServiceReference &ref)
141 class eStaticServiceDVBPVRInformation: public iStaticServiceInformation
143 DECLARE_REF(eStaticServiceDVBPVRInformation);
144 eServiceReference m_ref;
145 eDVBMetaParser m_parser;
147 eStaticServiceDVBPVRInformation(const eServiceReference &ref);
148 RESULT getName(const eServiceReference &ref, std::string &name);
149 int getLength(const eServiceReference &ref);
150 RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time);
152 int getInfo(const eServiceReference &ref, int w);
153 std::string getInfoString(const eServiceReference &ref,int w);
156 DEFINE_REF(eStaticServiceDVBPVRInformation);
158 eStaticServiceDVBPVRInformation::eStaticServiceDVBPVRInformation(const eServiceReference &ref)
161 m_parser.parseFile(ref.path);
164 RESULT eStaticServiceDVBPVRInformation::getName(const eServiceReference &ref, std::string &name)
166 ASSERT(ref == m_ref);
167 name = m_parser.m_name.size() ? m_parser.m_name : ref.path;
171 int eStaticServiceDVBPVRInformation::getLength(const eServiceReference &ref)
173 ASSERT(ref == m_ref);
177 if (tstools.openFile(ref.path.c_str()))
181 if (tstools.calcLen(len))
187 int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w)
191 case iServiceInformation::sDescription:
192 return iServiceInformation::resIsString;
193 case iServiceInformation::sServiceref:
194 return iServiceInformation::resIsString;
195 case iServiceInformation::sTimeCreate:
196 if (m_parser.m_time_create)
197 return m_parser.m_time_create;
199 return iServiceInformation::resNA;
201 return iServiceInformation::resNA;
205 std::string eStaticServiceDVBPVRInformation::getInfoString(const eServiceReference &ref,int w)
209 case iServiceInformation::sDescription:
210 return m_parser.m_description;
211 case iServiceInformation::sServiceref:
212 return m_parser.m_ref.toString();
218 RESULT eStaticServiceDVBPVRInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
220 if (!ref.path.empty())
222 ePtr<eServiceEvent> event = new eServiceEvent;
223 std::string filename = ref.path;
224 filename.erase(filename.length()-2, 2);
226 if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
236 class eDVBPVRServiceOfflineOperations: public iServiceOfflineOperations
238 DECLARE_REF(eDVBPVRServiceOfflineOperations);
239 eServiceReferenceDVB m_ref;
241 eDVBPVRServiceOfflineOperations(const eServiceReference &ref);
243 RESULT deleteFromDisk(int simulate);
244 RESULT getListOfFilenames(std::list<std::string> &);
247 DEFINE_REF(eDVBPVRServiceOfflineOperations);
249 eDVBPVRServiceOfflineOperations::eDVBPVRServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReferenceDVB&)ref)
253 RESULT eDVBPVRServiceOfflineOperations::deleteFromDisk(int simulate)
259 std::list<std::string> res;
260 if (getListOfFilenames(res))
263 eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
265 eDebug("FATAL !! can't get background file eraser");
267 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
269 eDebug("Removing %s...", i->c_str());
271 eraser->erase(i->c_str());
273 ::unlink(i->c_str());
280 RESULT eDVBPVRServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
283 res.push_back(m_ref.path);
285 // handling for old splitted recordings (enigma 1)
290 snprintf(buf, 255, "%s.%03d", m_ref.path.c_str(), slice++);
292 if (stat(buf, &s) < 0)
297 res.push_back(m_ref.path + ".meta");
298 res.push_back(m_ref.path + ".ap");
299 res.push_back(m_ref.path + ".cuts");
300 std::string tmp = m_ref.path;
301 tmp.erase(m_ref.path.length()-3);
302 res.push_back(tmp + ".eit");
306 DEFINE_REF(eServiceFactoryDVB)
308 eServiceFactoryDVB::eServiceFactoryDVB()
310 ePtr<eServiceCenter> sc;
312 eServiceCenter::getPrivInstance(sc);
314 sc->addServiceFactory(eServiceFactoryDVB::id, this);
317 eServiceFactoryDVB::~eServiceFactoryDVB()
319 ePtr<eServiceCenter> sc;
321 eServiceCenter::getPrivInstance(sc);
323 sc->removeServiceFactory(eServiceFactoryDVB::id);
326 DEFINE_REF(eDVBServiceList);
328 eDVBServiceList::eDVBServiceList(const eServiceReference &parent): m_parent(parent)
332 eDVBServiceList::~eDVBServiceList()
336 RESULT eDVBServiceList::startQuery()
338 ePtr<iDVBChannelList> db;
339 ePtr<eDVBResourceManager> res;
342 if ((err = eDVBResourceManager::getInstance(res)) != 0)
344 eDebug("no resource manager");
347 if ((err = res->getChannelList(db)) != 0)
349 eDebug("no channel list");
353 ePtr<eDVBChannelQuery> q;
355 if (!m_parent.path.empty())
357 eDVBChannelQuery::compile(q, m_parent.path);
360 eDebug("compile query failed");
365 if ((err = db->startQuery(m_query, q, m_parent)) != 0)
367 eDebug("startQuery failed");
374 RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
376 eServiceReferenceDVB ref;
381 while (!m_query->getNextResult(ref))
385 list.sort(iListableServiceCompare(this));
390 // The first argument of this function is a format string to specify the order and
391 // the content of the returned list
392 // useable format options are
393 // R = Service Reference (as swig object .. this is very slow)
394 // S = Service Reference (as python string object .. same as ref.toString())
395 // N = Service Name (as python string object)
396 // when exactly one return value per service is selected in the format string,
397 // then each value is directly a list entry
398 // when more than one value is returned per service, then the list is a list of
400 // unknown format string chars are returned as python None values !
401 PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
404 std::list<eServiceReference> tmplist;
407 if (!format || !(retcount=strlen(format)))
408 format = "R"; // just return service reference swig object ...
410 if (!getContent(tmplist, sorted))
412 int services=tmplist.size();
413 ePtr<iStaticServiceInformation> sptr;
414 eServiceCenterPtr service_center;
416 if (strchr(format, 'N'))
417 eServiceCenter::getPrivInstance(service_center);
419 ret = PyList_New(services);
420 std::list<eServiceReference>::iterator it(tmplist.begin());
422 for (int cnt=0; cnt < services; ++cnt)
424 eServiceReference &ref=*it++;
425 PyObject *tuple = retcount > 1 ? PyTuple_New(retcount) : 0;
426 for (int i=0; i < retcount; ++i)
431 case 'R': // service reference (swig)object
432 tmp = New_eServiceReference(ref);
434 case 'S': // service reference string
435 tmp = PyString_FromString(ref.toString().c_str());
437 case 'N': // service name
440 service_center->info(ref, sptr);
444 sptr->getName(ref, name);
446 tmp = PyString_FromString(name.c_str());
450 tmp = PyString_FromString("<n/a>");
463 PyTuple_SET_ITEM(tuple, i, tmp);
465 PyList_SET_ITEM(ret, cnt, tmp);
469 PyList_SET_ITEM(ret, cnt, tuple);
472 return ret ? ret : PyList_New(0);
475 RESULT eDVBServiceList::getNext(eServiceReference &ref)
480 return m_query->getNextResult((eServiceReferenceDVB&)ref);
483 int eDVBServiceList::compareLessEqual(const eServiceReference &a, const eServiceReference &b)
485 return m_query->compareLessEqual((const eServiceReferenceDVB&)a, (const eServiceReferenceDVB&)b);
488 RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
490 if (m_parent.flags & eServiceReference::flagDirectory) // bouquet
492 ePtr<iDVBChannelList> db;
493 ePtr<eDVBResourceManager> resm;
495 if (eDVBResourceManager::getInstance(resm) || resm->getChannelList(db))
498 if (db->getBouquet(m_parent, m_bouquet) != 0)
509 RESULT eDVBServiceList::addService(eServiceReference &ref)
513 return m_bouquet->addService(ref);
516 RESULT eDVBServiceList::removeService(eServiceReference &ref)
520 return m_bouquet->removeService(ref);
523 RESULT eDVBServiceList::moveService(eServiceReference &ref, int pos)
527 return m_bouquet->moveService(ref, pos);
530 RESULT eDVBServiceList::flushChanges()
534 return m_bouquet->flushChanges();
537 RESULT eDVBServiceList::setListName(const std::string &name)
541 return m_bouquet->setListName(name);
544 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
546 ePtr<eDVBService> service;
547 int r = lookupService(service, ref);
550 // check resources...
551 ptr = new eDVBServicePlay(ref, service);
555 RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
557 if (ref.path.empty())
559 ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
568 RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
570 ePtr<eDVBServiceList> list = new eDVBServiceList(ref);
571 if (list->startQuery())
581 RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
583 /* is a listable service? */
584 if ((ref.flags & eServiceReference::flagDirectory) == eServiceReference::flagDirectory) // bouquet
586 if ( !ref.name.empty() ) // satellites or providers list
587 ptr = new eStaticServiceDVBInformation;
588 else // a dvb bouquet
589 ptr = new eStaticServiceDVBBouquetInformation;
591 else if (!ref.path.empty()) /* do we have a PVR service? */
592 ptr = new eStaticServiceDVBPVRInformation(ref);
593 else // normal dvb service
595 ePtr<eDVBService> service;
596 if (lookupService(service, ref)) // no eDVBService avail for this reference ( Linkage Services... )
597 ptr = new eStaticServiceDVBInformation;
599 /* eDVBService has the iStaticServiceInformation interface, so we pass it here. */
605 RESULT eServiceFactoryDVB::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
607 if (ref.path.empty())
613 ptr = new eDVBPVRServiceOfflineOperations(ref);
618 RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServiceReference &ref)
620 // TODO: handle the listing itself
621 // if (ref.... == -1) .. return "... bouquets ...";
622 // could be also done in another serviceFactory (with seperate ID) to seperate actual services and lists
624 ePtr<iDVBChannelList> db;
625 ePtr<eDVBResourceManager> res;
628 if ((err = eDVBResourceManager::getInstance(res)) != 0)
630 eDebug("no resource manager");
633 if ((err = res->getChannelList(db)) != 0)
635 eDebug("no channel list");
639 /* we are sure to have a ..DVB reference as the info() call was forwarded here according to it's ID. */
640 if ((err = db->getService((eServiceReferenceDVB&)ref, service)) != 0)
642 eDebug("getService failed!");
649 eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service):
650 m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
653 m_is_pvr = !m_reference.path.empty();
655 m_timeshift_enabled = m_timeshift_active = 0;
658 CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
659 CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
660 CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
662 m_cuesheet_changed = 0;
663 m_cutlist_enabled = 1;
665 m_subtitle_widget = 0;
667 CONNECT(m_subtitle_sync_timer.timeout, eDVBServicePlay::checkSubtitleTiming);
670 eDVBServicePlay::~eDVBServicePlay()
672 delete m_subtitle_widget;
675 void eDVBServicePlay::gotNewEvent()
679 ePtr<eServiceEvent> m_event_now, m_event_next;
680 getEvent(m_event_now, 0);
681 getEvent(m_event_next, 1);
684 eDebug("now running: %s (%d seconds :)", m_event_now->m_event_name.c_str(), m_event_now->m_duration);
686 eDebug("next running: %s (%d seconds :)", m_event_next->m_event_name.c_str(), m_event_next->m_duration);
688 m_event((iPlayableService*)this, evUpdatedEventInfo);
691 void eDVBServicePlay::serviceEvent(int event)
695 case eDVBServicePMTHandler::eventTuned:
697 ePtr<iDVBDemux> m_demux;
698 if (!m_service_handler.getDataDemux(m_demux))
700 eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_reference;
701 int sid = ref.getParentServiceID().get();
703 sid = ref.getServiceID().get();
704 if ( ref.getParentTransportStreamID().get() &&
705 ref.getParentTransportStreamID() != ref.getTransportStreamID() )
706 m_event_handler.startOther(m_demux, sid);
708 m_event_handler.start(m_demux, sid);
712 case eDVBServicePMTHandler::eventTuneFailed:
714 eDebug("DVB service failed to tune");
715 m_event((iPlayableService*)this, evTuneFailed);
718 case eDVBServicePMTHandler::eventNewProgramInfo:
720 eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active);
721 if (m_timeshift_enabled)
722 updateTimeshiftPids();
723 if (!m_timeshift_active)
725 if (m_first_program_info && m_is_pvr)
727 m_first_program_info = 0;
730 m_event((iPlayableService*)this, evUpdatedInfo);
733 case eDVBServicePMTHandler::eventEOF:
734 m_event((iPlayableService*)this, evEOF);
736 case eDVBServicePMTHandler::eventSOF:
737 m_event((iPlayableService*)this, evSOF);
742 void eDVBServicePlay::serviceEventTimeshift(int event)
746 case eDVBServicePMTHandler::eventNewProgramInfo:
747 if (m_timeshift_active)
750 case eDVBServicePMTHandler::eventSOF:
751 m_event((iPlayableService*)this, evSOF);
753 case eDVBServicePMTHandler::eventEOF:
759 RESULT eDVBServicePlay::start()
762 /* in pvr mode, we only want to use one demux. in tv mode, we're using
763 two (one for decoding, one for data source), as we must be prepared
764 to start recording from the data demux. */
766 m_cue = new eCueSheet();
768 m_first_program_info = 1;
769 eServiceReferenceDVB &service = (eServiceReferenceDVB&)m_reference;
770 r = m_service_handler.tune(service, m_is_pvr, m_cue);
772 /* inject EIT if there is a stored one */
775 std::string filename = service.path;
776 filename.erase(filename.length()-2, 2);
778 ePtr<eServiceEvent> event = new eServiceEvent;
779 if (!event->parseFrom(filename, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get()))
781 ePtr<eServiceEvent> empty;
782 m_event_handler.inject(event, 0);
783 m_event_handler.inject(empty, 1);
790 m_event(this, evStart);
791 m_event((iPlayableService*)this, evSeekableStatusChanged);
795 RESULT eDVBServicePlay::stop()
797 stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
799 m_service_handler_timeshift.free();
800 m_service_handler.free();
802 if (m_is_pvr && m_cuesheet_changed)
808 RESULT eDVBServicePlay::setTarget(int target)
810 m_is_primary = !target;
814 RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
816 connection = new eConnection((iPlayableService*)this, m_event.connect(event));
820 RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
822 /* note: we check for timeshift to be enabled,
823 not neccessary active. if you pause when timeshift
824 is not active, you should activate it when unpausing */
825 if ((!m_is_pvr) && (!m_timeshift_enabled))
835 RESULT eDVBServicePlay::setSlowMotion(int ratio)
838 return m_decoder->setSlowMotion(ratio);
843 RESULT eDVBServicePlay::setFastForward(int ratio)
845 int skipmode, ffratio;
851 } else if (ratio > 0)
859 } else // if (ratio < 0)
865 if (m_skipmode != skipmode)
867 eDebug("setting cue skipmode to %d", skipmode);
869 m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
872 m_skipmode = skipmode;
877 return m_decoder->setFastForward(ffratio);
880 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
882 if (m_is_pvr || m_timeshift_enabled)
892 /* TODO: when timeshift is enabled but not active, this doesn't work. */
893 RESULT eDVBServicePlay::getLength(pts_t &len)
895 ePtr<iDVBPVRChannel> pvr_channel;
897 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
900 return pvr_channel->getLength(len);
903 RESULT eDVBServicePlay::pause()
905 if (!m_is_paused && m_decoder)
908 return m_decoder->freeze(0);
913 RESULT eDVBServicePlay::unpause()
915 if (m_is_paused && m_decoder)
918 return m_decoder->unfreeze();
923 RESULT eDVBServicePlay::seekTo(pts_t to)
925 eDebug("eDVBServicePlay::seekTo: jump %lld", to);
930 ePtr<iDVBPVRChannel> pvr_channel;
932 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
938 m_cue->seekTo(0, to);
942 RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
944 eDebug("eDVBServicePlay::seekRelative: jump %d, %lld", direction, to);
949 ePtr<iDVBPVRChannel> pvr_channel;
951 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
956 /* HACK until we have skip-AP api */
957 if ((to > 0) && (to < 100))
965 m_cue->seekTo(mode, to);
969 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
971 ePtr<iDVBPVRChannel> pvr_channel;
976 if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
981 /* if there is a decoder, use audio or video PTS */
984 r = m_decoder->getPTS(0, pos);
990 return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
993 RESULT eDVBServicePlay::setTrickmode(int trick)
996 m_decoder->setTrickmode(trick);
1000 RESULT eDVBServicePlay::isCurrentlySeekable()
1002 return m_is_pvr || m_timeshift_active;
1005 RESULT eDVBServicePlay::frontendInfo(ePtr<iFrontendInformation> &ptr)
1011 RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
1017 RESULT eDVBServicePlay::audioChannel(ePtr<iAudioChannelSelection> &ptr)
1023 RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
1029 RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
1035 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
1038 if (m_have_video_pid && // HACK !!! FIXMEE !! temporary no timeshift on radio services !!
1039 (m_timeshift_enabled || !m_is_pvr))
1041 if (!m_timeshift_enabled)
1043 /* we need enough diskspace */
1045 if (statfs(TSPATH "/.", &fs) < 0)
1047 eDebug("statfs failed!");
1051 if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
1053 eDebug("not enough diskspace for timeshift! (less than 1GB)");
1063 RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
1074 RESULT eDVBServicePlay::subtitle(ePtr<iSubtitleOutput> &ptr)
1080 RESULT eDVBServicePlay::audioDelay(ePtr<iAudioDelay> &ptr)
1086 RESULT eDVBServicePlay::getName(std::string &name)
1090 ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
1091 return i->getName(m_reference, name);
1095 m_dvb_service->getName(m_reference, name);
1099 else if (!m_reference.name.empty())
1100 eStaticServiceDVBInformation().getName(m_reference, name);
1102 name = "DVB service";
1106 RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
1108 return m_event_handler.getEvent(evt, nownext);
1111 int eDVBServicePlay::getInfo(int w)
1113 eDVBServicePMTHandler::program program;
1116 return resIsPyObject;
1118 if (m_service_handler.getProgramInfo(program))
1124 if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
1126 ePtr<eServiceEvent> evt;
1127 if (!m_event_handler.getEvent(evt, 0))
1129 ePtr<eComponentData> data;
1130 if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
1132 if ( data->getStreamContent() == 1 )
1134 switch(data->getComponentType())
1137 case 1: // 4:3 SD PAL
1139 case 3: // 16:9 SD PAL
1140 case 4: // > 16:9 PAL
1141 case 5: // 4:3 SD NTSC
1143 case 7: // 16:9 SD NTSC
1144 case 8: // > 16:9 NTSC
1147 case 9: // 4:3 HD PAL
1149 case 0xB: // 16:9 HD PAL
1150 case 0xC: // > 16:9 HD PAL
1151 case 0xD: // 4:3 HD NTSC
1153 case 0xF: // 16:9 HD NTSC
1154 case 0x10: // > 16:9 HD PAL
1155 return data->getComponentType();
1162 case sIsCrypted: return program.isCrypted();
1163 case sVideoPID: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
1164 case sVideoType: if (program.videoStreams.empty()) return -1; return program.videoStreams[0].type;
1165 case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
1166 case sPCRPID: return program.pcrPid;
1167 case sPMTPID: return program.pmtPid;
1168 case sTXTPID: return program.textPid;
1169 case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
1170 case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
1171 case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
1172 case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
1173 case sProvider: if (!m_dvb_service) return -1; return -2;
1179 std::string eDVBServicePlay::getInfoString(int w)
1184 if (!m_dvb_service) return "";
1185 return m_dvb_service->m_provider_name;
1189 return iServiceInformation::getInfoString(w);
1192 PyObject *eDVBServicePlay::getInfoObject(int w)
1197 return m_service_handler.getCaIds();
1201 return iServiceInformation::getInfoObject(w);
1204 int eDVBServicePlay::getNumberOfTracks()
1206 eDVBServicePMTHandler::program program;
1207 if (m_service_handler.getProgramInfo(program))
1209 return program.audioStreams.size();
1212 RESULT eDVBServicePlay::selectTrack(unsigned int i)
1214 int ret = selectAudioStream(i);
1216 if (m_decoder->start())
1222 RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1224 eDVBServicePMTHandler::program program;
1226 if (m_service_handler.getProgramInfo(program))
1229 if (i >= program.audioStreams.size())
1232 if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
1233 info.m_description = "MPEG";
1234 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
1235 info.m_description = "AC3";
1236 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAAC)
1237 info.m_description = "AAC";
1238 else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
1239 info.m_description = "DTS";
1241 info.m_description = "???";
1243 if (program.audioStreams[i].component_tag != -1)
1245 ePtr<eServiceEvent> evt;
1246 if (!m_event_handler.getEvent(evt, 0))
1248 ePtr<eComponentData> data;
1249 if (!evt->getComponentData(data, program.audioStreams[i].component_tag))
1250 info.m_language = data->getText();
1254 if (info.m_language.empty())
1255 info.m_language = program.audioStreams[i].language_code;
1260 int eDVBServicePlay::selectAudioStream(int i)
1262 eDVBServicePMTHandler::program program;
1264 if (m_service_handler.getProgramInfo(program))
1267 if ((unsigned int)i >= program.audioStreams.size())
1273 if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
1276 if (m_dvb_service && !m_is_pvr)
1278 if (program.audioStreams[i].type == eDVBAudio::aMPEG)
1280 m_dvb_service->setCacheEntry(eDVBService::cAPID, program.audioStreams[i].pid);
1281 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
1284 m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
1285 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, program.audioStreams[i].pid);
1289 m_current_audio_stream = i;
1294 int eDVBServicePlay::getCurrentChannel()
1296 return m_decoder ? m_decoder->getAudioChannel() : STEREO;
1299 RESULT eDVBServicePlay::selectChannel(int i)
1301 if (i < LEFT || i > RIGHT || i == STEREO)
1304 m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
1306 m_decoder->setAudioChannel(i);
1310 int eDVBServiceBase::getFrontendInfo(int w)
1312 eUsePtr<iDVBChannel> channel;
1313 if(m_service_handler.getChannel(channel))
1315 ePtr<iDVBFrontend> fe;
1316 if(channel->getFrontend(fe))
1318 return fe->readFrontendData(w);
1321 PyObject *eDVBServiceBase::getFrontendData(bool original)
1325 eUsePtr<iDVBChannel> channel;
1326 if(!m_service_handler.getChannel(channel))
1328 ePtr<iDVBFrontend> fe;
1329 if(!channel->getFrontend(fe))
1331 ret = fe->readTransponderData(original);
1334 ePtr<iDVBFrontendParameters> feparm;
1335 channel->getCurrentFrontendParameters(feparm);
1338 eDVBFrontendParametersSatellite osat;
1339 if (!feparm->getDVBS(osat))
1341 void PutToDict(PyObject *, const char*, long);
1342 void PutToDict(PyObject *, const char*, const char*);
1343 PutToDict(ret, "orbital_position", osat.orbital_position);
1344 const char *tmp = "UNKNOWN";
1345 switch(osat.polarisation)
1347 case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
1348 case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
1349 case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR_LEFT"; break;
1350 case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR_RIGHT"; break;
1353 PutToDict(ret, "polarization", tmp);
1367 int eDVBServicePlay::getNumberOfSubservices()
1369 ePtr<eServiceEvent> evt;
1370 if (!m_event_handler.getEvent(evt, 0))
1371 return evt->getNumOfLinkageServices();
1375 RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
1377 ePtr<eServiceEvent> evt;
1378 if (!m_event_handler.getEvent(evt, 0))
1380 if (!evt->getLinkageService(sub, m_reference, n))
1383 sub.type=eServiceReference::idInvalid;
1387 RESULT eDVBServicePlay::startTimeshift()
1389 ePtr<iDVBDemux> demux;
1391 eDebug("Start timeshift!");
1393 if (m_timeshift_enabled)
1396 /* start recording with the data demux. */
1397 if (m_service_handler.getDataDemux(demux))
1400 demux->createTSRecorder(m_record);
1404 char templ[]=TSPATH "/timeshift.XXXXXX";
1405 m_timeshift_fd = mkstemp(templ);
1406 m_timeshift_file = templ;
1408 eDebug("recording to %s", templ);
1410 if (m_timeshift_fd < 0)
1416 m_record->setTargetFD(m_timeshift_fd);
1418 m_timeshift_enabled = 1;
1420 updateTimeshiftPids();
1426 RESULT eDVBServicePlay::stopTimeshift()
1428 if (!m_timeshift_enabled)
1433 m_timeshift_enabled = 0;
1438 close(m_timeshift_fd);
1439 eDebug("remove timeshift file");
1440 remove(m_timeshift_file.c_str());
1445 int eDVBServicePlay::isTimeshiftActive()
1447 return m_timeshift_enabled && m_timeshift_active;
1450 RESULT eDVBServicePlay::activateTimeshift()
1452 if (!m_timeshift_enabled)
1455 if (!m_timeshift_active)
1457 switchToTimeshift();
1464 PyObject *eDVBServicePlay::getCutList()
1466 PyObject *list = PyList_New(0);
1468 for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1470 PyObject *tuple = PyTuple_New(2);
1471 PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
1472 PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
1473 PyList_Append(list, tuple);
1480 void eDVBServicePlay::setCutList(PyObject *list)
1482 if (!PyList_Check(list))
1484 int size = PyList_Size(list);
1487 m_cue_entries.clear();
1489 for (i=0; i<size; ++i)
1491 PyObject *tuple = PyList_GetItem(list, i);
1492 if (!PyTuple_Check(tuple))
1494 eDebug("non-tuple in cutlist");
1497 if (PyTuple_Size(tuple) != 2)
1499 eDebug("cutlist entries need to be a 2-tuple");
1502 PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
1503 if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
1505 eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
1508 pts_t pts = PyLong_AsLongLong(ppts);
1509 int type = PyInt_AsLong(ptype);
1510 m_cue_entries.insert(cueEntry(pts, type));
1511 eDebug("adding %08llx, %d", pts, type);
1513 m_cuesheet_changed = 1;
1515 cutlistToCuesheet();
1516 m_event((iPlayableService*)this, evCuesheetChanged);
1519 void eDVBServicePlay::setCutListEnable(int enable)
1521 m_cutlist_enabled = enable;
1522 cutlistToCuesheet();
1525 void eDVBServicePlay::updateTimeshiftPids()
1530 eDVBServicePMTHandler::program program;
1531 if (m_service_handler.getProgramInfo(program))
1535 std::set<int> pids_to_record;
1536 pids_to_record.insert(0); // PAT
1537 if (program.pmtPid != -1)
1538 pids_to_record.insert(program.pmtPid); // PMT
1540 if (program.textPid != -1)
1541 pids_to_record.insert(program.textPid); // Videotext
1543 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1544 i(program.videoStreams.begin());
1545 i != program.videoStreams.end(); ++i)
1546 pids_to_record.insert(i->pid);
1548 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1549 i(program.audioStreams.begin());
1550 i != program.audioStreams.end(); ++i)
1551 pids_to_record.insert(i->pid);
1553 std::set<int> new_pids, obsolete_pids;
1555 std::set_difference(pids_to_record.begin(), pids_to_record.end(),
1556 m_pids_active.begin(), m_pids_active.end(),
1557 std::inserter(new_pids, new_pids.begin()));
1559 std::set_difference(
1560 m_pids_active.begin(), m_pids_active.end(),
1561 pids_to_record.begin(), pids_to_record.end(),
1562 std::inserter(new_pids, new_pids.begin())
1565 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
1566 m_record->addPID(*i);
1568 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
1569 m_record->removePID(*i);
1573 void eDVBServicePlay::switchToLive()
1575 if (!m_timeshift_active)
1581 m_teletext_parser = 0;
1582 m_new_subtitle_page_connection = 0;
1584 /* free the timeshift service handler, we need the resources */
1585 m_service_handler_timeshift.free();
1586 m_timeshift_active = 0;
1588 m_event((iPlayableService*)this, evSeekableStatusChanged);
1593 void eDVBServicePlay::switchToTimeshift()
1595 if (m_timeshift_active)
1600 m_teletext_parser = 0;
1601 m_new_subtitle_page_connection = 0;
1603 m_timeshift_active = 1;
1605 m_event((iPlayableService*)this, evSeekableStatusChanged);
1607 eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
1608 r.path = m_timeshift_file;
1610 m_cue = new eCueSheet();
1611 m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
1612 updateDecoder(); /* mainly to switch off PCR */
1615 void eDVBServicePlay::updateDecoder()
1617 int vpid = -1, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1;
1619 eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
1621 bool defaultac3=false;
1622 std::string default_ac3;
1624 if (!ePythonConfigQuery::getConfigValue("config.av.defaultac3", default_ac3))
1625 defaultac3 = default_ac3 == "enable";
1627 eDVBServicePMTHandler::program program;
1628 if (h.getProgramInfo(program))
1629 eDebug("getting program info failed.");
1632 eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
1633 if (!program.videoStreams.empty())
1635 eDebugNoNewLine(" (");
1636 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
1637 i(program.videoStreams.begin());
1638 i != program.videoStreams.end(); ++i)
1645 if (i != program.videoStreams.begin())
1646 eDebugNoNewLine(", ");
1647 eDebugNoNewLine("%04x", i->pid);
1649 eDebugNoNewLine(")");
1651 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
1652 if (!program.audioStreams.empty())
1654 eDebugNoNewLine(" (");
1655 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
1656 i(program.audioStreams.begin());
1657 i != program.audioStreams.end(); ++i)
1659 if (apid == -1 || (apidtype == eDVBAudio::aMPEG && defaultac3))
1661 if ( apid == -1 || (i->type != eDVBAudio::aMPEG) )
1667 if (i != program.audioStreams.begin())
1668 eDebugNoNewLine(", ");
1669 eDebugNoNewLine("%04x", i->pid);
1671 eDebugNoNewLine(")");
1673 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
1674 pcrpid = program.pcrPid;
1675 eDebug(", and the text pid is %04x", program.textPid);
1676 tpid = program.textPid;
1681 h.getDecodeDemux(m_decode_demux);
1683 m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
1685 m_cue->setDecodingDemux(m_decode_demux, m_decoder);
1686 #ifdef INTERNAL_TELETEXT
1687 m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
1688 m_teletext_parser->connectNewPage(slot(*this, &eDVBServicePlay::newSubtitlePage), m_new_subtitle_page_connection);
1696 achannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL);
1697 ac3_delay = m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
1698 pcm_delay = m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
1700 else // subservice or recording
1702 eServiceReferenceDVB ref;
1703 m_service_handler.getServiceReference(ref);
1704 eServiceReferenceDVB parent = ref.getParentServiceReference();
1709 ePtr<eDVBResourceManager> res_mgr;
1710 if (!eDVBResourceManager::getInstance(res_mgr))
1712 ePtr<iDVBChannelList> db;
1713 if (!res_mgr->getChannelList(db))
1715 ePtr<eDVBService> origService;
1716 if (!db->getService(parent, origService))
1718 ac3_delay = origService->getCacheEntry(eDVBService::cAC3DELAY);
1719 pcm_delay = origService->getCacheEntry(eDVBService::cPCMDELAY);
1725 m_decoder->setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
1726 m_decoder->setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
1728 m_decoder->setVideoPID(vpid, vpidtype);
1729 m_current_audio_stream = 0;
1730 m_decoder->setAudioPID(apid, apidtype);
1731 if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
1732 m_decoder->setSyncPCR(pcrpid);
1734 m_decoder->setSyncPCR(-1);
1736 m_decoder->setTextPID(tpid);
1738 if (m_teletext_parser)
1739 m_teletext_parser->start(tpid);
1742 m_decoder->setTrickmode(1);
1746 if (vpid > 0 && vpid < 0x2000)
1750 std::string radio_pic;
1751 if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic))
1752 m_decoder->setRadioPic(radio_pic);
1755 m_decoder->setAudioChannel(achannel);
1757 // how we can do this better?
1758 // update cache pid when the user changed the audio track or video track
1759 // TODO handling of difference audio types.. default audio types..
1761 /* don't worry about non-existing services, nor pvr services */
1762 if (m_dvb_service && !m_is_pvr)
1764 if (apidtype == eDVBAudio::aMPEG)
1766 m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
1767 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
1771 m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
1772 m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
1774 m_dvb_service->setCacheEntry(eDVBService::cVPID, vpid);
1775 m_dvb_service->setCacheEntry(eDVBService::cVTYPE, vpidtype == eDVBVideo::MPEG2 ? -1 : vpidtype);
1776 m_dvb_service->setCacheEntry(eDVBService::cPCRPID, pcrpid);
1777 m_dvb_service->setCacheEntry(eDVBService::cTPID, tpid);
1780 m_have_video_pid = (vpid > 0 && vpid < 0x2000);
1783 void eDVBServicePlay::loadCuesheet()
1785 std::string filename = m_reference.path + ".cuts";
1787 m_cue_entries.clear();
1789 FILE *f = fopen(filename.c_str(), "rb");
1793 eDebug("loading cuts..");
1796 unsigned long long where;
1799 if (!fread(&where, sizeof(where), 1, f))
1801 if (!fread(&what, sizeof(what), 1, f))
1804 #if BYTE_ORDER == LITTLE_ENDIAN
1805 where = bswap_64(where);
1812 m_cue_entries.insert(cueEntry(where, what));
1815 eDebug("%d entries", m_cue_entries.size());
1817 eDebug("cutfile not found!");
1819 m_cuesheet_changed = 0;
1820 cutlistToCuesheet();
1821 m_event((iPlayableService*)this, evCuesheetChanged);
1824 void eDVBServicePlay::saveCuesheet()
1826 std::string filename = m_reference.path + ".cuts";
1828 FILE *f = fopen(filename.c_str(), "wb");
1832 unsigned long long where;
1835 for (std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
1837 #if BYTE_ORDER == BIG_ENDIAN
1840 where = bswap_64(i->where);
1842 what = htonl(i->what);
1843 fwrite(&where, sizeof(where), 1, f);
1844 fwrite(&what, sizeof(what), 1, f);
1850 m_cuesheet_changed = 0;
1853 void eDVBServicePlay::cutlistToCuesheet()
1857 eDebug("no cue sheet");
1862 if (!m_cutlist_enabled)
1864 m_cue->commitSpans();
1865 eDebug("cutlists where disabled");
1869 pts_t in = 0, out = 0, length = 0;
1873 std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
1877 if (i == m_cue_entries.end())
1880 if (i->what == 0) /* in */
1884 } else if (i->what == 1) /* out */
1894 m_cue->addSourceSpan(in, out);
1898 if (i == m_cue_entries.end())
1901 m_cue->commitSpans();
1904 RESULT eDVBServicePlay::enableSubtitles(eWidget *parent, PyObject *entry)
1906 if (m_subtitle_widget)
1907 disableSubtitles(parent);
1909 if (!m_teletext_parser)
1912 if (!PyInt_Check(entry))
1915 m_subtitle_widget = new eSubtitleWidget(parent);
1916 m_subtitle_widget->resize(parent->size()); /* full size */
1918 int page = PyInt_AsLong(entry);
1920 m_teletext_parser->setPage(page);
1925 RESULT eDVBServicePlay::disableSubtitles(eWidget *parent)
1927 delete m_subtitle_widget;
1928 m_subtitle_widget = 0;
1932 PyObject *eDVBServicePlay::getSubtitleList()
1934 if (!m_teletext_parser)
1940 PyObject *l = PyList_New(0);
1942 for (std::set<int>::iterator i(m_teletext_parser->m_found_subtitle_pages.begin()); i != m_teletext_parser->m_found_subtitle_pages.end(); ++i)
1944 PyObject *tuple = PyTuple_New(2);
1946 sprintf(desc, "Page %x", *i);
1947 PyTuple_SetItem(tuple, 0, PyString_FromString(desc));
1948 PyTuple_SetItem(tuple, 1, PyInt_FromLong(*i));
1949 PyList_Append(l, tuple);
1955 void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
1957 if (m_subtitle_widget)
1959 m_subtitle_pages.push_back(page);
1961 checkSubtitleTiming();
1965 void eDVBServicePlay::checkSubtitleTiming()
1969 if (m_subtitle_pages.empty())
1972 eDVBTeletextSubtitlePage p = m_subtitle_pages.front();
1977 m_decoder->getPTS(0, pos);
1979 int diff = p.m_pts - pos;
1982 eDebug("[late (%d ms)]", -diff / 90);
1987 eDebug("[invalid]");
1993 m_subtitle_widget->setPage(p);
1994 m_subtitle_pages.pop_front();
1997 m_subtitle_sync_timer.start(diff / 90, 1);
2003 int eDVBServicePlay::getAC3Delay()
2006 return m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
2008 return m_decoder->getAC3Delay();
2013 int eDVBServicePlay::getPCMDelay()
2016 return m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
2018 return m_decoder->getPCMDelay();
2023 void eDVBServicePlay::setAC3Delay(int delay)
2026 m_dvb_service->setCacheEntry(eDVBService::cAC3DELAY, delay ? delay : -1);
2028 m_decoder->setAC3Delay(delay);
2031 void eDVBServicePlay::setPCMDelay(int delay)
2034 m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
2036 m_decoder->setPCMDelay(delay);
2039 DEFINE_REF(eDVBServicePlay)
2041 eAutoInitPtr<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");