+eServiceFactoryDVB::eServiceFactoryDVB()
+{
+ ePtr<eServiceCenter> sc;
+
+ eServiceCenter::getPrivInstance(sc);
+ if (sc)
+ sc->addServiceFactory(eServiceFactoryDVB::id, this);
+
+ m_StaticServiceDVBInfo = new eStaticServiceDVBInformation;
+ m_StaticServiceDVBBouquetInfo = new eStaticServiceDVBBouquetInformation;
+}
+
+eServiceFactoryDVB::~eServiceFactoryDVB()
+{
+ ePtr<eServiceCenter> sc;
+
+ eServiceCenter::getPrivInstance(sc);
+ if (sc)
+ sc->removeServiceFactory(eServiceFactoryDVB::id);
+}
+
+DEFINE_REF(eDVBServiceList);
+
+eDVBServiceList::eDVBServiceList(const eServiceReference &parent): m_parent(parent)
+{
+}
+
+eDVBServiceList::~eDVBServiceList()
+{
+}
+
+RESULT eDVBServiceList::startQuery()
+{
+ ePtr<iDVBChannelList> db;
+ ePtr<eDVBResourceManager> res;
+
+ int err;
+ if ((err = eDVBResourceManager::getInstance(res)) != 0)
+ {
+ eDebug("no resource manager");
+ return err;
+ }
+ if ((err = res->getChannelList(db)) != 0)
+ {
+ eDebug("no channel list");
+ return err;
+ }
+
+ ePtr<eDVBChannelQuery> q;
+
+ if (!m_parent.path.empty())
+ {
+ eDVBChannelQuery::compile(q, m_parent.path);
+ if (!q)
+ {
+ eDebug("compile query failed");
+ return err;
+ }
+ }
+
+ if ((err = db->startQuery(m_query, q, m_parent)) != 0)
+ {
+ eDebug("startQuery failed");
+ return err;
+ }
+
+ return 0;
+}
+
+RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
+{
+ eServiceReferenceDVB ref;
+
+ if (!m_query)
+ return -1;
+
+ while (!m_query->getNextResult(ref))
+ list.push_back(ref);
+
+ if (sorted)
+ list.sort(iListableServiceCompare(this));
+
+ return 0;
+}
+
+// The first argument of this function is a format string to specify the order and
+// the content of the returned list
+// useable format options are
+// R = Service Reference (as swig object .. this is very slow)
+// S = Service Reference (as python string object .. same as ref.toString())
+// C = Service Reference (as python string object .. same as ref.toCompareString())
+// N = Service Name (as python string object)
+// n = Short Service Name (short name brakets used) (as python string object)
+// when exactly one return value per service is selected in the format string,
+// then each value is directly a list entry
+// when more than one value is returned per service, then the list is a list of
+// python tuples
+// unknown format string chars are returned as python None values !
+PyObject *eDVBServiceList::getContent(const char* format, bool sorted)
+{
+ ePyObject ret;
+ std::list<eServiceReference> tmplist;
+ int retcount=1;
+
+ if (!format || !(retcount=strlen(format)))
+ format = "R"; // just return service reference swig object ...
+
+ if (!getContent(tmplist, sorted))
+ {
+ int services=tmplist.size();
+ ePtr<iStaticServiceInformation> sptr;
+ eServiceCenterPtr service_center;
+
+ if (strchr(format, 'N') || strchr(format, 'n'))
+ eServiceCenter::getPrivInstance(service_center);
+
+ ret = PyList_New(services);
+ std::list<eServiceReference>::iterator it(tmplist.begin());
+
+ for (int cnt=0; cnt < services; ++cnt)
+ {
+ eServiceReference &ref=*it++;
+ ePyObject tuple = retcount > 1 ? PyTuple_New(retcount) : ePyObject();
+ for (int i=0; i < retcount; ++i)
+ {
+ ePyObject tmp;
+ switch(format[i])
+ {
+ case 'R': // service reference (swig)object
+ tmp = NEW_eServiceReference(ref);
+ break;
+ case 'C': // service reference compare string
+ tmp = PyString_FromString(ref.toCompareString().c_str());
+ break;
+ case 'S': // service reference string
+ tmp = PyString_FromString(ref.toString().c_str());
+ break;
+ case 'N': // service name
+ if (service_center)
+ {
+ service_center->info(ref, sptr);
+ if (sptr)
+ {
+ std::string name;
+ sptr->getName(ref, name);
+
+ // filter short name brakets
+ unsigned int pos;
+ while((pos = name.find("\xc2\x86")) != std::string::npos)
+ name.erase(pos,2);
+ while((pos = name.find("\xc2\x87")) != std::string::npos)
+ name.erase(pos,2);
+
+ if (name.length())
+ tmp = PyString_FromString(name.c_str());
+ }
+ }
+ if (!tmp)
+ tmp = PyString_FromString("<n/a>");
+ break;
+ case 'n': // short service name
+ if (service_center)
+ {
+ service_center->info(ref, sptr);
+ if (sptr)
+ {
+ std::string name;
+ sptr->getName(ref, name);
+ name = buildShortName(name);
+ if (name.length())
+ tmp = PyString_FromString(name.c_str());
+ }
+ }
+ if (!tmp)
+ tmp = PyString_FromString("<n/a>");
+ break;
+ default:
+ if (tuple)
+ {
+ tmp = Py_None;
+ Py_INCREF(Py_None);
+ }
+ break;
+ }
+ if (tmp)
+ {
+ if (tuple)
+ PyTuple_SET_ITEM(tuple, i, tmp);
+ else
+ PyList_SET_ITEM(ret, cnt, tmp);
+ }
+ }
+ if (tuple)
+ PyList_SET_ITEM(ret, cnt, tuple);
+ }
+ }
+ return ret ? (PyObject*)ret : (PyObject*)PyList_New(0);
+}
+
+RESULT eDVBServiceList::getNext(eServiceReference &ref)
+{
+ if (!m_query)
+ return -1;
+
+ return m_query->getNextResult((eServiceReferenceDVB&)ref);
+}
+
+RESULT eDVBServiceList::startEdit(ePtr<iMutableServiceList> &res)
+{
+ if (m_parent.flags & eServiceReference::canDescent) // bouquet
+ {
+ ePtr<iDVBChannelList> db;
+ ePtr<eDVBResourceManager> resm;
+
+ if (eDVBResourceManager::getInstance(resm) || resm->getChannelList(db))
+ return -1;
+
+ if (db->getBouquet(m_parent, m_bouquet) != 0)
+ return -1;
+
+ res = this;
+
+ return 0;
+ }
+ res = 0;
+ return -1;
+}
+
+RESULT eDVBServiceList::addService(eServiceReference &ref, eServiceReference before)
+{
+ if (!m_bouquet)
+ return -1;
+ return m_bouquet->addService(ref, before);
+}
+
+RESULT eDVBServiceList::removeService(eServiceReference &ref)
+{
+ if (!m_bouquet)
+ return -1;
+ return m_bouquet->removeService(ref);
+}
+
+RESULT eDVBServiceList::moveService(eServiceReference &ref, int pos)
+{
+ if (!m_bouquet)
+ return -1;
+ return m_bouquet->moveService(ref, pos);
+}
+
+RESULT eDVBServiceList::flushChanges()
+{
+ if (!m_bouquet)
+ return -1;
+ return m_bouquet->flushChanges();
+}
+
+RESULT eDVBServiceList::setListName(const std::string &name)
+{
+ if (!m_bouquet)
+ return -1;
+ return m_bouquet->setListName(name);
+}
+
+RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
+{
+ ePtr<eDVBService> service;
+ int r = lookupService(service, ref);
+ if (r)
+ service = 0;
+ // check resources...
+ ptr = new eDVBServicePlay(ref, service);
+ return 0;
+}
+
+RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
+{
+ if (ref.path.empty())
+ {
+ ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
+ return 0;
+ } else
+ {
+ ptr = 0;
+ return -1;
+ }
+}
+
+RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
+{
+ ePtr<eDVBServiceList> list = new eDVBServiceList(ref);
+ if (list->startQuery())
+ {
+ ptr = 0;
+ return -1;
+ }
+
+ ptr = list;
+ return 0;
+}
+
+RESULT eServiceFactoryDVB::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
+{
+ /* is a listable service? */
+ if (ref.flags & eServiceReference::canDescent) // bouquet
+ {
+ if ( !ref.name.empty() ) // satellites or providers list
+ ptr = m_StaticServiceDVBInfo;
+ else // a dvb bouquet
+ ptr = m_StaticServiceDVBBouquetInfo;
+ }
+ else if (!ref.path.empty()) /* do we have a PVR service? */
+ ptr = new eStaticServiceDVBPVRInformation(ref);
+ else // normal dvb service
+ {
+ ePtr<eDVBService> service;
+ if (lookupService(service, ref)) // no eDVBService avail for this reference ( Linkage Services... )
+ ptr = m_StaticServiceDVBInfo;
+ else
+ /* eDVBService has the iStaticServiceInformation interface, so we pass it here. */
+ ptr = service;
+ }
+ return 0;
+}
+
+RESULT eServiceFactoryDVB::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
+{
+ if (ref.path.empty())
+ {
+ ptr = 0;
+ return -1;
+ } else
+ {
+ ptr = new eDVBPVRServiceOfflineOperations(ref);
+ return 0;
+ }
+}
+
+RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServiceReference &ref)
+{
+ // TODO: handle the listing itself
+ // if (ref.... == -1) .. return "... bouquets ...";
+ // could be also done in another serviceFactory (with seperate ID) to seperate actual services and lists
+ // TODO: cache
+ ePtr<iDVBChannelList> db;
+ ePtr<eDVBResourceManager> res;
+
+ int err;
+ if ((err = eDVBResourceManager::getInstance(res)) != 0)
+ {
+ eDebug("no resource manager");
+ return err;
+ }
+ if ((err = res->getChannelList(db)) != 0)
+ {
+ eDebug("no channel list");
+ return err;
+ }
+
+ /* we are sure to have a ..DVB reference as the info() call was forwarded here according to it's ID. */
+ if ((err = db->getService((eServiceReferenceDVB&)ref, service)) != 0)
+ {
+ eDebug("getService failed!");
+ return err;
+ }
+
+ return 0;
+}
+
+eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service):
+ m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
+{
+ memset(&m_videoEventData, 0, sizeof(struct iTSMPEGDecoder::videoEvent));
+ m_is_primary = 1;
+ m_is_pvr = !m_reference.path.empty();
+
+ m_timeshift_enabled = m_timeshift_active = 0;
+ m_skipmode = 0;
+
+ CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
+ CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
+ CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
+
+ m_cuesheet_changed = 0;
+ m_cutlist_enabled = 1;
+
+ m_subtitle_widget = 0;
+
+ m_tune_state = -1;
+
+ CONNECT(m_subtitle_sync_timer.timeout, eDVBServicePlay::checkSubtitleTiming);
+}
+
+eDVBServicePlay::~eDVBServicePlay()
+{
+ delete m_subtitle_widget;
+}
+
+void eDVBServicePlay::gotNewEvent()
+{
+#if 0
+ // debug only
+ ePtr<eServiceEvent> m_event_now, m_event_next;
+ getEvent(m_event_now, 0);
+ getEvent(m_event_next, 1);
+
+ if (m_event_now)
+ eDebug("now running: %s (%d seconds :)", m_event_now->m_event_name.c_str(), m_event_now->m_duration);
+ if (m_event_next)
+ eDebug("next running: %s (%d seconds :)", m_event_next->m_event_name.c_str(), m_event_next->m_duration);
+#endif
+ m_event((iPlayableService*)this, evUpdatedEventInfo);
+}
+
+void eDVBServicePlay::serviceEvent(int event)
+{
+ m_tune_state = event;
+
+ switch (event)
+ {
+ case eDVBServicePMTHandler::eventTuned:
+ {
+ ePtr<iDVBDemux> m_demux;
+ if (!m_service_handler.getDataDemux(m_demux))
+ {
+ eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_reference;
+ int sid = ref.getParentServiceID().get();
+ if (!sid)
+ sid = ref.getServiceID().get();
+ if ( ref.getParentTransportStreamID().get() &&
+ ref.getParentTransportStreamID() != ref.getTransportStreamID() )
+ m_event_handler.startOther(m_demux, sid);
+ else
+ m_event_handler.start(m_demux, sid);
+ }
+ break;
+ }
+ case eDVBServicePMTHandler::eventNoResources:
+ case eDVBServicePMTHandler::eventNoPAT:
+ case eDVBServicePMTHandler::eventNoPATEntry:
+ case eDVBServicePMTHandler::eventNoPMT:
+ case eDVBServicePMTHandler::eventTuneFailed:
+ {
+ eDebug("DVB service failed to tune - error %d", event);
+ m_event((iPlayableService*)this, evTuneFailed);
+ break;
+ }
+ case eDVBServicePMTHandler::eventNewProgramInfo:
+ {
+ eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active);
+ if (m_timeshift_enabled)
+ updateTimeshiftPids();
+ if (!m_timeshift_active)
+ updateDecoder();
+ if (m_first_program_info && m_is_pvr)
+ {
+ m_first_program_info = 0;
+ seekTo(0);
+ }
+ m_event((iPlayableService*)this, evUpdatedInfo);
+ break;
+ }
+ case eDVBServicePMTHandler::eventEOF:
+ m_event((iPlayableService*)this, evEOF);
+ break;
+ case eDVBServicePMTHandler::eventSOF:
+ m_event((iPlayableService*)this, evSOF);
+ break;
+ }
+}
+
+void eDVBServicePlay::serviceEventTimeshift(int event)
+{
+ switch (event)
+ {
+ case eDVBServicePMTHandler::eventNewProgramInfo:
+ if (m_timeshift_active)
+ updateDecoder();
+ break;
+ case eDVBServicePMTHandler::eventSOF:
+ m_event((iPlayableService*)this, evSOF);
+ break;
+ case eDVBServicePMTHandler::eventEOF:
+ if ((!m_is_paused) && (m_skipmode >= 0))
+ switchToLive();
+ break;
+ }
+}
+
+RESULT eDVBServicePlay::start()
+{
+ int r;
+ /* in pvr mode, we only want to use one demux. in tv mode, we're using
+ two (one for decoding, one for data source), as we must be prepared
+ to start recording from the data demux. */
+ if (m_is_pvr)
+ m_cue = new eCueSheet();
+ else
+ m_event(this, evStart);
+
+ m_first_program_info = 1;
+ eServiceReferenceDVB &service = (eServiceReferenceDVB&)m_reference;
+ r = m_service_handler.tune(service, m_is_pvr, m_cue);
+
+ /* inject EIT if there is a stored one */
+ if (m_is_pvr)
+ {
+ std::string filename = service.path;
+ filename.erase(filename.length()-2, 2);
+ filename+="eit";
+ ePtr<eServiceEvent> event = new eServiceEvent;
+ if (!event->parseFrom(filename, (service.getTransportStreamID().get()<<16)|service.getOriginalNetworkID().get()))
+ {
+ ePtr<eServiceEvent> empty;
+ m_event_handler.inject(event, 0);
+ m_event_handler.inject(empty, 1);
+ }
+ }
+
+ if (m_is_pvr)
+ {
+ loadCuesheet();
+ m_event(this, evStart);
+ }
+ return 0;
+}
+
+RESULT eDVBServicePlay::stop()
+{
+ /* add bookmark for last play position */
+ if (m_is_pvr)
+ {
+ pts_t play_position, length;
+ if (!getPlayPosition(play_position))
+ {
+ /* remove last position */
+ for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end();)
+ {
+ if (i->what == 3) /* current play position */
+ {
+ m_cue_entries.erase(i);
+ i = m_cue_entries.begin();
+ continue;
+ } else
+ ++i;
+ }
+
+ if (getLength(length))
+ length = 0;
+
+ if (length)
+ {
+ int perc = play_position * 100LL / length;
+
+ /* only store last play position when between 5% and 95% */
+ if ((5 < perc) && (perc < 95))
+ m_cue_entries.insert(cueEntry(play_position, 3)); /* last play position */
+ }
+ m_cuesheet_changed = 1;
+ }
+ }
+
+ stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
+
+ m_service_handler_timeshift.free();
+ m_service_handler.free();
+
+ if (m_is_pvr && m_cuesheet_changed)
+ {
+ struct stat s;
+ /* save cuesheet only when main file is accessible. */
+ if (!::stat(m_reference.path.c_str(), &s))
+ saveCuesheet();
+ }
+ m_event((iPlayableService*)this, evStopped);
+ return 0;
+}
+
+RESULT eDVBServicePlay::setTarget(int target)
+{
+ m_is_primary = !target;
+ return 0;
+}
+
+RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
+{
+ connection = new eConnection((iPlayableService*)this, m_event.connect(event));
+ return 0;
+}
+
+RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
+{
+ /* note: we check for timeshift to be enabled,
+ not neccessary active. if you pause when timeshift
+ is not active, you should activate it when unpausing */
+ if ((!m_is_pvr) && (!m_timeshift_enabled))
+ {
+ ptr = 0;
+ return -1;
+ }
+
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::setSlowMotion(int ratio)
+{
+ if (m_decoder)
+ return m_decoder->setSlowMotion(ratio);
+ else
+ return -1;
+}
+
+RESULT eDVBServicePlay::setFastForward(int ratio)
+{
+ int skipmode, ffratio;
+
+ if (ratio > 8)
+ {
+ skipmode = ratio;
+ ffratio = 1;
+ } else if (ratio > 0)
+ {
+ skipmode = 0;
+ ffratio = ratio;
+ } else if (!ratio)
+ {
+ skipmode = 0;
+ ffratio = 0;
+ } else // if (ratio < 0)
+ {
+ skipmode = ratio;
+ ffratio = 1;
+ }
+
+ if (m_skipmode != skipmode)
+ {
+ eDebug("setting cue skipmode to %d", skipmode);
+ if (m_cue)
+ m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
+ }
+
+ m_skipmode = skipmode;
+
+ if (!m_decoder)
+ return -1;
+
+ return m_decoder->setFastForward(ffratio);
+}
+
+RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
+{
+ if (m_is_pvr || m_timeshift_enabled)
+ {
+ ptr = this;
+ return 0;
+ }
+
+ ptr = 0;
+ return -1;
+}
+
+ /* TODO: when timeshift is enabled but not active, this doesn't work. */
+RESULT eDVBServicePlay::getLength(pts_t &len)
+{
+ ePtr<iDVBPVRChannel> pvr_channel;
+
+ if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
+ return -1;
+
+ return pvr_channel->getLength(len);
+}
+
+RESULT eDVBServicePlay::pause()
+{
+ if (!m_is_paused && m_decoder)
+ {
+ m_is_paused = 1;
+ return m_decoder->freeze(0);
+ } else
+ return -1;
+}
+
+RESULT eDVBServicePlay::unpause()
+{
+ if (m_is_paused && m_decoder)
+ {
+ m_is_paused = 0;
+ return m_decoder->unfreeze();
+ } else
+ return -1;
+}
+
+RESULT eDVBServicePlay::seekTo(pts_t to)
+{
+ eDebug("eDVBServicePlay::seekTo: jump %lld", to);
+
+ if (!m_decode_demux)
+ return -1;
+
+ ePtr<iDVBPVRChannel> pvr_channel;
+
+ if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
+ return -1;
+
+ if (!m_cue)
+ return -1;
+
+ m_cue->seekTo(0, to);
+ return 0;
+}
+
+RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
+{
+ eDebug("eDVBServicePlay::seekRelative: jump %d, %lld", direction, to);
+
+ if (!m_decode_demux)
+ return -1;
+
+ ePtr<iDVBPVRChannel> pvr_channel;
+
+ if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
+ return -1;
+
+ int mode = 1;
+
+ /* HACK until we have skip-AP api */
+ if ((to > 0) && (to < 100))
+ mode = 2;
+
+ to *= direction;
+
+ if (!m_cue)
+ return 0;
+
+ m_cue->seekTo(mode, to);
+ return 0;
+}
+
+RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
+{
+ ePtr<iDVBPVRChannel> pvr_channel;
+
+ if (!m_decode_demux)
+ return -1;
+
+ if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
+ return -1;
+
+ int r = 0;
+
+ /* if there is a decoder, use audio or video PTS */
+ if (m_decoder)
+ {
+ r = m_decoder->getPTS(0, pos);
+ if (r)
+ return r;
+ }
+
+ /* fixup */
+ return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0);
+}
+
+RESULT eDVBServicePlay::setTrickmode(int trick)
+{
+ if (m_decoder)
+ m_decoder->setTrickmode(trick);
+ return 0;
+}
+
+RESULT eDVBServicePlay::isCurrentlySeekable()
+{
+ return m_is_pvr || m_timeshift_active;
+}
+
+RESULT eDVBServicePlay::frontendInfo(ePtr<iFrontendInformation> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::info(ePtr<iServiceInformation> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::audioChannel(ePtr<iAudioChannelSelection> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::audioTracks(ePtr<iAudioTrackSelection> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
+{
+ ptr = 0;
+ if (m_have_video_pid && // HACK !!! FIXMEE !! temporary no timeshift on radio services !!
+ (m_timeshift_enabled || !m_is_pvr))
+ {
+ if (!m_timeshift_enabled)
+ {
+ /* we need enough diskspace */
+ struct statfs fs;
+ if (statfs(TSPATH "/.", &fs) < 0)
+ {
+ eDebug("statfs failed!");
+ return -2;
+ }
+
+ if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
+ {
+ eDebug("not enough diskspace for timeshift! (less than 1GB)");
+ return -3;
+ }
+ }
+ ptr = this;
+ return 0;
+ }
+ return -1;
+}
+
+RESULT eDVBServicePlay::cueSheet(ePtr<iCueSheet> &ptr)
+{
+ if (m_is_pvr)
+ {
+ ptr = this;
+ return 0;
+ }
+ ptr = 0;
+ return -1;
+}
+
+RESULT eDVBServicePlay::subtitle(ePtr<iSubtitleOutput> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::audioDelay(ePtr<iAudioDelay> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::rdsDecoder(ePtr<iRdsDecoder> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+RESULT eDVBServicePlay::getName(std::string &name)
+{
+ if (m_is_pvr)
+ {
+ ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
+ return i->getName(m_reference, name);
+ }
+ if (m_dvb_service)
+ {
+ m_dvb_service->getName(m_reference, name);
+ if (name.empty())
+ name = "(...)";
+ }
+ else if (!m_reference.name.empty())
+ eStaticServiceDVBInformation().getName(m_reference, name);
+ else
+ name = "DVB service";
+ return 0;
+}
+
+RESULT eDVBServicePlay::getEvent(ePtr<eServiceEvent> &evt, int nownext)
+{
+ return m_event_handler.getEvent(evt, nownext);
+}
+
+int eDVBServicePlay::getInfo(int w)
+{
+ eDVBServicePMTHandler::program program;
+
+ if (w == sCAIDs)
+ return resIsPyObject;
+
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ int no_program_info = 0;
+
+ if (h.getProgramInfo(program))
+ no_program_info = 1;
+
+ switch (w)
+ {
+#if HAVE_DVB_API_VERSION >= 3
+ case sVideoHeight:
+ if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+ return m_videoEventData.height;
+ return -1;
+ case sVideoWidth:
+ if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+ return m_videoEventData.width;
+ return -1;
+#else
+#warning "FIXMEE implement sVideoHeight, sVideoWidth for old DVB API"
+#endif
+ case sAspect:
+#if HAVE_DVB_API_VERSION >= 3
+ if (m_videoEventData.type != iTSMPEGDecoder::videoEvent::eventUnknown)
+ return m_videoEventData.aspect == VIDEO_FORMAT_4_3 ? 1 : 3;
+ else
+#else
+#warning "FIXMEE implement sAspect for old DVB API"
+#endif
+ if (no_program_info)
+ return -1;
+ else if (!program.videoStreams.empty() && program.videoStreams[0].component_tag != -1)
+ {
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ {
+ ePtr<eComponentData> data;
+ if (!evt->getComponentData(data, program.videoStreams[0].component_tag))
+ {
+ if ( data->getStreamContent() == 1 )
+ {
+ switch(data->getComponentType())
+ {
+ // SD
+ case 1: // 4:3 SD PAL
+ case 2:
+ case 3: // 16:9 SD PAL
+ case 4: // > 16:9 PAL
+ case 5: // 4:3 SD NTSC
+ case 6:
+ case 7: // 16:9 SD NTSC
+ case 8: // > 16:9 NTSC
+
+ // HD
+ case 9: // 4:3 HD PAL
+ case 0xA:
+ case 0xB: // 16:9 HD PAL
+ case 0xC: // > 16:9 HD PAL
+ case 0xD: // 4:3 HD NTSC
+ case 0xE:
+ case 0xF: // 16:9 HD NTSC
+ case 0x10: // > 16:9 HD PAL
+ return data->getComponentType();
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ case sIsCrypted: if (no_program_info) return -1; return program.isCrypted();
+ case sVideoPID: if (no_program_info) return -1; if (program.videoStreams.empty()) return -1; return program.videoStreams[0].pid;
+ case sVideoType: if (no_program_info) return -1; if (program.videoStreams.empty()) return -1; return program.videoStreams[0].type;
+ case sAudioPID: if (no_program_info) return -1; if (program.audioStreams.empty()) return -1; return program.audioStreams[0].pid;
+ case sPCRPID: if (no_program_info) return -1; return program.pcrPid;
+ case sPMTPID: if (no_program_info) return -1; return program.pmtPid;
+ case sTXTPID: if (no_program_info) return -1; return program.textPid;
+ case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
+ case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
+ case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
+ case sNamespace: return ((const eServiceReferenceDVB&)m_reference).getDVBNamespace().get();
+ case sProvider: if (!m_dvb_service) return -1; return -2;
+ case sServiceref: return resIsString;
+ case sDVBState: return m_tune_state;
+ default:
+ return -1;
+ }
+}
+
+std::string eDVBServicePlay::getInfoString(int w)
+{
+ switch (w)
+ {
+ case sProvider:
+ if (!m_dvb_service) return "";
+ return m_dvb_service->m_provider_name;
+ case sServiceref:
+ return m_reference.toString();
+ default:
+ break;
+ }
+ return iServiceInformation::getInfoString(w);
+}
+
+PyObject *eDVBServicePlay::getInfoObject(int w)
+{
+ switch (w)
+ {
+ case sCAIDs:
+ return m_service_handler.getCaIds();
+ case sTransponderData:
+ return eStaticServiceDVBInformation().getInfoObject(m_reference, w);
+ default:
+ break;
+ }
+ return iServiceInformation::getInfoObject(w);
+}
+
+int eDVBServicePlay::getNumberOfTracks()
+{
+ eDVBServicePMTHandler::program program;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+ if (h.getProgramInfo(program))
+ return 0;
+ return program.audioStreams.size();
+}
+
+RESULT eDVBServicePlay::selectTrack(unsigned int i)
+{
+ int ret = selectAudioStream(i);
+
+ if (m_decoder->start())
+ return -5;
+
+ return ret;
+}
+
+RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
+{
+ eDVBServicePMTHandler::program program;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ if (h.getProgramInfo(program))
+ return -1;
+
+ if (i >= program.audioStreams.size())
+ return -2;
+
+ if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
+ info.m_description = "MPEG";
+ else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
+ info.m_description = "AC3";
+ else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAAC)
+ info.m_description = "AAC";
+ else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
+ info.m_description = "DTS";
+ else
+ info.m_description = "???";
+
+ if (program.audioStreams[i].component_tag != -1)
+ {
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ {
+ ePtr<eComponentData> data;
+ if (!evt->getComponentData(data, program.audioStreams[i].component_tag))
+ info.m_language = data->getText();
+ }
+ }
+
+ if (info.m_language.empty())
+ info.m_language = program.audioStreams[i].language_code;
+
+ return 0;
+}
+
+int eDVBServicePlay::selectAudioStream(int i)
+{
+ eDVBServicePMTHandler::program program;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ if (h.getProgramInfo(program))
+ return -1;
+
+ if ((unsigned int)i >= program.audioStreams.size())
+ return -2;
+
+ if (!m_decoder)
+ return -3;
+
+ if (m_decoder->setAudioPID(program.audioStreams[i].pid, program.audioStreams[i].type))
+ return -4;
+
+ if (m_rds_decoder)
+ m_rds_decoder->start(program.audioStreams[i].pid);
+
+ if (m_dvb_service && !m_is_pvr)
+ {
+ if (program.audioStreams[i].type == eDVBAudio::aMPEG)
+ {
+ m_dvb_service->setCacheEntry(eDVBService::cAPID, program.audioStreams[i].pid);
+ m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
+ }
+ else
+ {
+ m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
+ m_dvb_service->setCacheEntry(eDVBService::cAC3PID, program.audioStreams[i].pid);
+ }
+ }
+
+ h.resetCachedProgram();
+
+ return 0;
+}
+
+int eDVBServicePlay::getCurrentChannel()
+{
+ return m_decoder ? m_decoder->getAudioChannel() : STEREO;
+}
+
+RESULT eDVBServicePlay::selectChannel(int i)
+{
+ if (i < LEFT || i > RIGHT || i == STEREO)
+ i = -1; // Stereo
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
+ if (m_decoder)
+ m_decoder->setAudioChannel(i);
+ return 0;
+}
+
+std::string eDVBServicePlay::getText(int x)
+{
+ if (m_rds_decoder)
+ switch(x)
+ {
+ case RadioText:
+ return convertLatin1UTF8(m_rds_decoder->getRadioText());
+ case RtpText:
+ return convertLatin1UTF8(m_rds_decoder->getRtpText());
+ }
+ return "";
+}
+
+void eDVBServicePlay::rdsDecoderEvent(int what)
+{
+ switch(what)
+ {
+ case eDVBRdsDecoder::RadioTextChanged:
+ m_event((iPlayableService*)this, evUpdatedRadioText);
+ break;
+ case eDVBRdsDecoder::RtpTextChanged:
+ m_event((iPlayableService*)this, evUpdatedRtpText);
+ break;
+ case eDVBRdsDecoder::RassInteractivePicMaskChanged:
+ m_event((iPlayableService*)this, evUpdatedRassInteractivePicMask);
+ break;
+ case eDVBRdsDecoder::RecvRassSlidePic:
+ m_event((iPlayableService*)this, evUpdatedRassSlidePic);
+ break;
+ }
+}
+
+void eDVBServicePlay::showRassSlidePicture()
+{
+ if (m_rds_decoder)
+ {
+ if (m_decoder)
+ {
+ std::string rass_slide_pic = m_rds_decoder->getRassSlideshowPicture();
+ if (rass_slide_pic.length())
+ m_decoder->showSinglePic(rass_slide_pic.c_str());
+ else
+ eDebug("empty filename for rass slide picture received!!");
+ }
+ else
+ eDebug("no MPEG Decoder to show iframes avail");
+ }
+ else
+ eDebug("showRassSlidePicture called.. but not decoder");
+}
+
+void eDVBServicePlay::showRassInteractivePic(int page, int subpage)
+{
+ if (m_rds_decoder)
+ {
+ if (m_decoder)
+ {
+ std::string rass_interactive_pic = m_rds_decoder->getRassPicture(page, subpage);
+ if (rass_interactive_pic.length())
+ m_decoder->showSinglePic(rass_interactive_pic.c_str());
+ else
+ eDebug("empty filename for rass interactive picture %d/%d received!!", page, subpage);
+ }
+ else
+ eDebug("no MPEG Decoder to show iframes avail");
+ }
+ else
+ eDebug("showRassInteractivePic called.. but not decoder");
+}
+
+ePyObject eDVBServicePlay::getRassInteractiveMask()
+{
+ if (m_rds_decoder)
+ return m_rds_decoder->getRassPictureMask();
+ Py_RETURN_NONE;
+}
+
+int eDVBServiceBase::getFrontendInfo(int w)
+{
+ eUsePtr<iDVBChannel> channel;
+ if(m_service_handler.getChannel(channel))
+ return 0;
+ ePtr<iDVBFrontend> fe;
+ if(channel->getFrontend(fe))
+ return 0;
+ return fe->readFrontendData(w);
+}
+
+PyObject *eDVBServiceBase::getFrontendData()
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ fe->getFrontendData(ret);
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getFrontendStatus()
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ fe->getFrontendStatus(ret);
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getTransponderData(bool original)
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ {
+ fe->getTransponderData(ret, original);
+ ePtr<iDVBFrontendParameters> feparm;
+ channel->getCurrentFrontendParameters(feparm);
+ if (feparm)
+ {
+ eDVBFrontendParametersSatellite osat;
+ if (!feparm->getDVBS(osat))
+ {
+ void PutToDict(ePyObject &, const char*, long);
+ void PutToDict(ePyObject &, const char*, const char*);
+ PutToDict(ret, "orbital_position", osat.orbital_position);
+ const char *tmp = "UNKNOWN";
+ switch(osat.polarisation)
+ {
+ case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR_LEFT"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR_RIGHT"; break;
+ default:break;
+ }
+ PutToDict(ret, "polarization", tmp);
+ }
+ }
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getAll(bool original)
+{
+ ePyObject ret = getTransponderData(original);
+ if (ret != Py_None)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ {
+ fe->getFrontendData(ret);
+ fe->getFrontendStatus(ret);
+ }
+ }
+ }
+ return ret;
+}
+
+int eDVBServicePlay::getNumberOfSubservices()
+{
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ return evt->getNumOfLinkageServices();
+ return 0;
+}
+
+RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
+{
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ {
+ if (!evt->getLinkageService(sub, m_reference, n))
+ return 0;
+ }
+ sub.type=eServiceReference::idInvalid;
+ return -1;
+}
+
+RESULT eDVBServicePlay::startTimeshift()
+{
+ ePtr<iDVBDemux> demux;
+
+ eDebug("Start timeshift!");
+
+ if (m_timeshift_enabled)
+ return -1;
+
+ /* start recording with the data demux. */
+ if (m_service_handler.getDataDemux(demux))
+ return -2;
+
+ demux->createTSRecorder(m_record);
+ if (!m_record)
+ return -3;
+
+ char templ[]=TSPATH "/timeshift.XXXXXX";
+ m_timeshift_fd = mkstemp(templ);
+ m_timeshift_file = templ;
+
+ eDebug("recording to %s", templ);
+
+ if (m_timeshift_fd < 0)
+ {
+ m_record = 0;
+ return -4;
+ }
+
+ m_record->setTargetFD(m_timeshift_fd);
+
+ m_timeshift_enabled = 1;
+
+ updateTimeshiftPids();
+ m_record->start();
+
+ return 0;
+}
+
+RESULT eDVBServicePlay::stopTimeshift()
+{
+ if (!m_timeshift_enabled)
+ return -1;
+
+ switchToLive();
+
+ m_timeshift_enabled = 0;
+
+ m_record->stop();
+ m_record = 0;
+
+ close(m_timeshift_fd);
+ eDebug("remove timeshift file");
+ eBackgroundFileEraser::getInstance()->erase(m_timeshift_file.c_str());
+
+ return 0;
+}
+
+int eDVBServicePlay::isTimeshiftActive()
+{
+ return m_timeshift_enabled && m_timeshift_active;
+}
+
+RESULT eDVBServicePlay::activateTimeshift()
+{
+ if (!m_timeshift_enabled)
+ return -1;
+
+ if (!m_timeshift_active)
+ {
+ switchToTimeshift();
+ return 0;
+ }
+
+ return -2;
+}
+
+PyObject *eDVBServicePlay::getCutList()
+{
+ ePyObject list = PyList_New(0);
+
+ for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
+ {
+ ePyObject tuple = PyTuple_New(2);
+ PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where));
+ PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what));
+ PyList_Append(list, tuple);
+ Py_DECREF(tuple);
+ }
+
+ return list;
+}
+
+void eDVBServicePlay::setCutList(ePyObject list)
+{
+ if (!PyList_Check(list))
+ return;
+ int size = PyList_Size(list);
+ int i;
+
+ m_cue_entries.clear();
+
+ for (i=0; i<size; ++i)
+ {
+ ePyObject tuple = PyList_GET_ITEM(list, i);
+ if (!PyTuple_Check(tuple))
+ {
+ eDebug("non-tuple in cutlist");
+ continue;
+ }
+ if (PyTuple_Size(tuple) != 2)
+ {
+ eDebug("cutlist entries need to be a 2-tuple");
+ continue;
+ }
+ ePyObject ppts = PyTuple_GET_ITEM(tuple, 0), ptype = PyTuple_GET_ITEM(tuple, 1);
+ if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
+ {
+ eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
+ continue;
+ }
+ pts_t pts = PyLong_AsLongLong(ppts);
+ int type = PyInt_AsLong(ptype);
+ m_cue_entries.insert(cueEntry(pts, type));
+ eDebug("adding %08llx, %d", pts, type);
+ }
+ m_cuesheet_changed = 1;
+
+ cutlistToCuesheet();
+ m_event((iPlayableService*)this, evCuesheetChanged);
+}
+
+void eDVBServicePlay::setCutListEnable(int enable)