From: hschang Date: Mon, 16 May 2016 07:51:49 +0000 (+0900) Subject: Support fast channel change. X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=commitdiff_plain;h=27e06ba9c8b055c20ea8c70a405deb0e4019edc5 Support fast channel change. --- diff --git a/Navigation.py b/Navigation.py index 1043f80..451b6da 100644 --- a/Navigation.py +++ b/Navigation.py @@ -113,6 +113,7 @@ class Navigation: print "stopService" if self.pnav: self.pnav.stopService() + self.currentlyPlayingServiceReference = None def pause(self, p): return self.pnav and self.pnav.pause(p) @@ -124,3 +125,4 @@ class Navigation: def stopUserServices(self): self.stopService() + diff --git a/configure.ac b/configure.ac index 134dc50..7c52d00 100644 --- a/configure.ac +++ b/configure.ac @@ -306,6 +306,8 @@ lib/python/Plugins/SystemPlugins/BoxModeConfig/Makefile lib/python/Plugins/SystemPlugins/BoxModeConfig/meta/Makefile lib/python/Plugins/SystemPlugins/Solo4kMiscControl/Makefile lib/python/Plugins/SystemPlugins/Solo4kMiscControl/meta/Makefile +lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile +lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile lib/python/Tools/Makefile lib/service/Makefile lib/components/Makefile diff --git a/lib/dvb/Makefile.am b/lib/dvb/Makefile.am index b185237..f4216d9 100644 --- a/lib/dvb/Makefile.am +++ b/lib/dvb/Makefile.am @@ -31,7 +31,9 @@ libenigma_dvb_a_SOURCES = \ teletext.cpp \ tstools.cpp \ volume.cpp \ - fbc.cpp + fbc.cpp \ + fcc.cpp \ + fccdecoder.cpp dvbincludedir = $(pkgincludedir)/lib/dvb dvbinclude_HEADERS = \ @@ -63,4 +65,6 @@ dvbinclude_HEADERS = \ teletext.h \ tstools.h \ volume.h \ - fbc.h + fbc.h \ + fcc.h \ + fccdecoder.h diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp index 8a4a82d..e3d97af 100644 --- a/lib/dvb/decoder.cpp +++ b/lib/dvb/decoder.cpp @@ -34,6 +34,8 @@ #include #include +#include + /* these are quite new... */ #ifndef AUDIO_GET_PTS #define AUDIO_GET_PTS _IOR('o', 19, __u64) @@ -206,9 +208,9 @@ int eDVBAudio::startPid(int pid, int type) case aDTSHD: bypass = 0x10; break; - case aDDP: - bypass = 0x22; - break; + case aDDP: + bypass = 0x22; + break; } @@ -304,8 +306,8 @@ eDVBAudio::~eDVBAudio() DEFINE_REF(eDVBVideo); -eDVBVideo::eDVBVideo(eDVBDemux *demux, int dev) - : m_demux(demux), m_dev(dev), +eDVBVideo::eDVBVideo(eDVBDemux *demux, int dev, bool fcc_enable) + : m_demux(demux), m_dev(dev), m_fcc_enable(fcc_enable), m_width(-1), m_height(-1), m_framerate(-1), m_aspect(-1), m_progressive(-1) { char filename[128]; @@ -408,6 +410,9 @@ int eDVBVideo::startPid(int pid, int type) { int streamtype = VIDEO_STREAMTYPE_MPEG2; + if (m_fcc_enable) + return 0; + if ((m_fd < 0) || (m_fd_demux < 0)) return -1; dmx_pes_filter_params pes; @@ -474,6 +479,9 @@ int eDVBVideo::startPid(int pid, int type) void eDVBVideo::stop() { + if (m_fcc_enable) + return; + #if HAVE_DVB_API_VERSION > 2 eDebugNoNewLine("DEMUX_STOP - video - "); if (::ioctl(m_fd_demux, DMX_STOP) < 0) @@ -996,7 +1004,7 @@ int eTSMPEGDecoder::setState() { if ((m_vpid >= 0) && (m_vpid < 0x1FFF)) { - m_video = new eDVBVideo(m_demux, m_decoder); + m_video = new eDVBVideo(m_demux, m_decoder, m_fcc_enable); m_video->connectEvent(slot(*this, &eTSMPEGDecoder::video_event), m_video_event_conn); if (m_video->startPid(m_vpid, m_vtype)) res = -1; @@ -1102,7 +1110,8 @@ RESULT eTSMPEGDecoder::setAC3Delay(int delay) eTSMPEGDecoder::eTSMPEGDecoder(eDVBDemux *demux, int decoder) : m_demux(demux), m_vpid(-1), m_vtype(-1), m_apid(-1), m_atype(-1), m_pcrpid(-1), m_textpid(-1), - m_changed(0), m_decoder(decoder), m_video_clip_fd(-1), m_showSinglePicTimer(eTimer::create(eApp)) + m_changed(0), m_decoder(decoder), m_video_clip_fd(-1), m_showSinglePicTimer(eTimer::create(eApp)), + m_fcc_fd(-1), m_fcc_enable(false), m_fcc_state(fcc_state_stop), m_fcc_feid(-1), m_fcc_vpid(-1), m_fcc_vtype(-1), m_fcc_pcrpid(-1) { demux->connectEvent(slot(*this, &eTSMPEGDecoder::demux_event), m_demux_event_conn); CONNECT(m_showSinglePicTimer->timeout, eTSMPEGDecoder::finishShowSinglePic); @@ -1115,6 +1124,8 @@ eTSMPEGDecoder::~eTSMPEGDecoder() m_vpid = m_apid = m_pcrpid = m_textpid = pidNone; m_changed = -1; setState(); + fccStop(); + fccFreeFD(); } RESULT eTSMPEGDecoder::setVideoPID(int vpid, int type) @@ -1421,3 +1432,242 @@ int eTSMPEGDecoder::getVideoAspect() return m_video->getAspect(); return -1; } + +#define FCC_SET_VPID 100 +#define FCC_SET_APID 101 +#define FCC_SET_PCRPID 102 +#define FCC_SET_VCODEC 103 +#define FCC_SET_ACODEC 104 +#define FCC_SET_FRONTEND_ID 105 +#define FCC_START 106 +#define FCC_STOP 107 +#define FCC_DECODER_START 108 +#define FCC_DECODER_STOP 109 + +RESULT eTSMPEGDecoder::prepareFCC(int fe_id, int vpid, int vtype, int pcrpid) +{ + //eDebug("[eTSMPEGDecoder::prepareFCC] vp : %d, vt : %d, pp : %d, fe : %d", vpid, vtype, pcrpid, fe_id); + + if ((fccGetFD() == -1) || (fccSetPids(fe_id, vpid, vtype, pcrpid) < 0) || (fccStart() < 0)) + { + fccFreeFD(); + return -1; + } + + m_fcc_enable = true; + + return 0; +} + +RESULT eTSMPEGDecoder::fccDecoderStart() +{ + if (m_fcc_fd == -1) + return -1; + + if (m_fcc_state != fcc_state_ready) + { + eDebug("FCC decoder is already in decoding state."); + return 0; + } + + if (ioctl(m_fcc_fd, FCC_DECODER_START) < 0) + { + eDebug("ioctl FCC_DECODER_START failed! (%m)"); + return -1; + } + + m_fcc_state = fcc_state_decoding; + + eDebug("[eTSMPEGDecoder] FCC_DECODER_START OK!"); + return 0; +} + +RESULT eTSMPEGDecoder::fccDecoderStop() +{ + if (m_fcc_fd == -1) + return -1; + + if (m_fcc_state != fcc_state_decoding) + { + eDebug("FCC decoder is not in decoding state."); + } + else if (ioctl(m_fcc_fd, FCC_DECODER_STOP) < 0) + { + eDebug("ioctl FCC_DECODER_STOP failed! (%m)"); + return -1; + } + + m_fcc_state = fcc_state_ready; + + /* stop pcr, video, audio, text */ + finishShowSinglePic(); + + m_vpid = m_apid = m_pcrpid = m_textpid = pidNone; + m_changed = -1; + setState(); + + eDebug("[eTSMPEGDecoder] FCC_DECODER_STOP OK!"); + return 0; +} + +RESULT eTSMPEGDecoder::fccUpdatePids(int fe_id, int vpid, int vtype, int pcrpid) +{ + //eDebug("[eTSMPEGDecoder] vp : %d, vt : %d, pp : %d, fe : %d", vpid, vtype, pcrpid, fe_id); + + if ((fe_id != m_fcc_feid) || (vpid != m_fcc_vpid) || (vtype != m_fcc_vtype) || (pcrpid != m_fcc_pcrpid)) + { + int cur_fcc_state = m_fcc_state; + fccStop(); + if (prepareFCC(fe_id, vpid, vtype, pcrpid)) + { + eDebug("[eTSMPEGDecoder] prepare FCC failed!"); + return -1; + } + } + return 0; +} + +RESULT eTSMPEGDecoder::fccStart() +{ + if (m_fcc_fd == -1) + return -1; + + if (m_fcc_state != fcc_state_stop) + { + eDebug("[eTSMPEGDecoder] FCC is already started!"); + return 0; + } + else if (ioctl(m_fcc_fd, FCC_START) < 0) + { + eDebug("ioctl FCC_START failed! (%m)"); + return -1; + } + + eDebug("[eTSMPEGDecoder] FCC_START OK!"); + + m_fcc_state = fcc_state_ready; + return 0; +} + +RESULT eTSMPEGDecoder::fccStop() +{ + if (m_fcc_fd == -1) + return -1; + + if (m_fcc_state == fcc_state_stop) + { + eDebug("[eTSMPEGDecoder] FCC is already stopped!"); + return 0; + } + + else if (m_fcc_state == fcc_state_decoding) + { + fccDecoderStop(); + } + + if (ioctl(m_fcc_fd, FCC_STOP) < 0) + { + eDebug("ioctl FCC_STOP failed! (%m)"); + return -1; + } + + m_fcc_state = fcc_state_stop; + + eDebug("[eTSMPEGDecoder] FCC_STOP OK!"); + return 0; +} + +RESULT eTSMPEGDecoder::fccSetPids(int fe_id, int vpid, int vtype, int pcrpid) +{ + int streamtype = VIDEO_STREAMTYPE_MPEG2; + + if (m_fcc_fd == -1) + return -1; + + if (ioctl(m_fcc_fd, FCC_SET_FRONTEND_ID, fe_id) < 0) + { + eDebug("[eTSMPEGDecoder] FCC_SET_FRONTEND_ID failed! (%m)"); + return -1; + } + + else if(ioctl(m_fcc_fd, FCC_SET_PCRPID, pcrpid) < 0) + { + eDebug("[eTSMPEGDecoder] FCC_SET_PCRPID failed! (%m)"); + return -1; + } + + else if (ioctl(m_fcc_fd, FCC_SET_VPID, vpid) < 0) + { + eDebug("[eTSMPEGDecoder] FCC_SET_VPID failed! (%m)"); + return -1; + } + + switch(vtype) + { + default: + case eDVBVideo::MPEG2: + break; + case eDVBVideo::MPEG4_H264: + streamtype = VIDEO_STREAMTYPE_MPEG4_H264; + break; + case eDVBVideo::MPEG1: + streamtype = VIDEO_STREAMTYPE_MPEG1; + break; + case eDVBVideo::MPEG4_Part2: + streamtype = VIDEO_STREAMTYPE_MPEG4_Part2; + break; + case eDVBVideo::VC1: + streamtype = VIDEO_STREAMTYPE_VC1; + break; + case eDVBVideo::VC1_SM: + streamtype = VIDEO_STREAMTYPE_VC1_SM; + break; + case eDVBVideo::H265_HEVC: + streamtype = VIDEO_STREAMTYPE_H265_HEVC; + break; + } + + if(ioctl(m_fcc_fd, FCC_SET_VCODEC, streamtype) < 0) + { + eDebug("[eTSMPEGDecoder] FCC_SET_VCODEC failed! (%m)"); + return -1; + } + + m_fcc_feid = fe_id; + m_fcc_vpid = vpid; + m_fcc_vtype = vtype; + m_fcc_pcrpid = pcrpid; + + //eDebug("[eTSMPEGDecoder] SET PIDS OK!"); + return 0; +} + +RESULT eTSMPEGDecoder::fccGetFD() +{ + if (m_fcc_fd == -1) + { + eFCCDecoder* fcc = eFCCDecoder::getInstance(); + if (fcc != NULL) + { + m_fcc_fd = fcc->allocateFcc(); + } + } + + return m_fcc_fd; +} + +RESULT eTSMPEGDecoder::fccFreeFD() +{ + if (m_fcc_fd != -1) + { + eFCCDecoder* fcc = eFCCDecoder::getInstance(); + if (fcc != NULL) + { + fcc->freeFcc(m_fcc_fd); + m_fcc_fd = -1; + } + } + + return 0; +} + diff --git a/lib/dvb/decoder.h b/lib/dvb/decoder.h index f0f8b2f..0636516 100644 --- a/lib/dvb/decoder.h +++ b/lib/dvb/decoder.h @@ -40,6 +40,7 @@ class eDVBVideo: public iObject, public Object private: ePtr m_demux; int m_fd, m_fd_demux, m_dev; + bool m_fcc_enable; #if HAVE_DVB_API_VERSION < 3 m_fd_video; #endif @@ -50,7 +51,7 @@ private: int m_width, m_height, m_framerate, m_aspect, m_progressive; public: enum { MPEG2, MPEG4_H264, MPEG1, MPEG4_Part2, VC1, VC1_SM, H265_HEVC }; - eDVBVideo(eDVBDemux *demux, int dev); + eDVBVideo(eDVBDemux *demux, int dev, bool fcc_enable=false); void stop(); #if HAVE_DVB_API_VERSION < 3 int setPid(int pid); @@ -120,6 +121,16 @@ private: ePtr m_pcr; ePtr m_text; int m_vpid, m_vtype, m_apid, m_atype, m_pcrpid, m_textpid; + + int m_fcc_fd; + bool m_fcc_enable; + int m_fcc_state; + + int m_fcc_feid; + int m_fcc_vpid; + int m_fcc_vtype; + int m_fcc_pcrpid; + enum { changeVideo = 1, @@ -167,6 +178,7 @@ public: - trickmode, highspeed reverse: data source fast forwards / reverses, decoder just displays frames as fast as it can - slow motion: decoder displays frames multiple times */ + enum { stateStop, statePause, @@ -195,6 +207,23 @@ public: int getVideoAspect(); static RESULT setHwPCMDelay(int delay); static RESULT setHwAC3Delay(int delay); + + enum + { + fcc_state_stop, + fcc_state_ready, + fcc_state_decoding + }; + + RESULT prepareFCC(int fe_id, int vpid, int vtype, int pcrpid); + RESULT fccStart(); + RESULT fccStop(); + RESULT fccDecoderStart(); + RESULT fccDecoderStop(); + RESULT fccUpdatePids(int fe_id, int vpid, int vtype, int pcrpid); + RESULT fccSetPids(int fe_id, int vpid, int vtype, int pcrpid); + RESULT fccGetFD(); + RESULT fccFreeFD(); }; #endif diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 0a371f0..72d4687 100755 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -988,6 +989,56 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons int *decremented_cached_channel_fe_usecount=NULL, *decremented_fe_usecount=NULL; + /* check FCC channels */ + std::vector fcc_decremented_fe_usecounts; + std::map fcc_chids; + int apply_to_ignore = 0; + if (!eFCCServiceManager::getFCCChannelID(fcc_chids)) + { + for (std::map::iterator i(fcc_chids.begin()); i != fcc_chids.end(); ++i) + { + //eDebug("[eDVBResourceManager::canAllocateChannel] FCC NS : %08x, TSID : %04x, ONID : %04x", i->first.dvbnamespace.get(), i->first.transport_stream_id.get(), i->first.original_network_id.get()); + if (ignore == i->first) + { + apply_to_ignore = i->second; + continue; + } + for (std::list::iterator ii(active_channels.begin()); ii != active_channels.end(); ++ii) + { + eSmartPtrList &frontends = simulate ? m_simulate_frontend : m_frontend; + if (ii->m_channel_id == i->first) + { + eDVBChannel *channel = (eDVBChannel*) &(*ii->m_channel); + + int check_usecount = channel == &(*m_cached_channel) ? 1 : 0; + check_usecount += i->second * 2; // one is used in eDVBServicePMTHandler and another is used in eDVBScan. + //eDebug("[eDVBResourceManager::canAllocateChannel] channel->getUseCount() : %d , check_usecount : %d (cached : %d)", channel->getUseCount(), check_usecount, channel == &(*m_cached_channel)); + if (channel->getUseCount() == check_usecount) + { + ePtr fe; + if (!ii->m_channel->getFrontend(fe)) + { + for (eSmartPtrList::iterator iii(frontends.begin()); iii != frontends.end(); ++iii) + { + if ( &(*fe) == &(*iii->m_frontend) ) + { + //eDebug("[eDVBResourceManager::canAllocateChannel] fcc : decrease fcc fe use_count! feid : %d (%d -> %d)", iii->m_frontend->getSlotID(), iii->m_inuse, iii->m_inuse-1); + --iii->m_inuse; + int *tmp_decremented_fe_usecount = &iii->m_inuse; + fcc_decremented_fe_usecounts.push_back(tmp_decremented_fe_usecount); + if (channel == &(*m_cached_channel)) + decremented_cached_channel_fe_usecount = tmp_decremented_fe_usecount; + break; + } + } + } + } + break; + } + } + } + } + for (std::list::iterator i(active_channels.begin()); i != active_channels.end(); ++i) { eSmartPtrList &frontends = simulate ? m_simulate_frontend : m_frontend; @@ -999,7 +1050,10 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons // another on eUsePtr is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan) // so we must check here if usecount is 3 (when the channel is equal to the cached channel) // or 2 when the cached channel is not equal to the compared channel - if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2) // channel only used once.. + int check_usecount = channel == &(*m_cached_channel) ? 1 : 0; + check_usecount += (apply_to_ignore+1) * 2; // one is used in eDVBServicePMTHandler and another is used in eDVBScan. + //eDebug("[eDVBResourceManager::canAllocateChannel] channel->getUseCount() : %d , check_usecount : %d (cached : %d)", channel->getUseCount(), check_usecount, channel == &(*m_cached_channel)); + if (channel->getUseCount() == check_usecount) // channel only used once..(except fcc) { ePtr fe; if (!i->m_channel->getFrontend(fe)) @@ -1008,6 +1062,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons { if ( &(*fe) == &(*ii->m_frontend) ) { + //eDebug("[eDVBResourceManager::canAllocateChannel] ignore : decrease fcc fe use_count! feid : %d (%d -> %d)", ii->m_frontend->getSlotID(), ii->m_inuse, ii->m_inuse-1); --ii->m_inuse; decremented_fe_usecount = &ii->m_inuse; if (channel == &(*m_cached_channel)) @@ -1070,6 +1125,14 @@ error: ++(*decremented_fe_usecount); if (decremented_cached_channel_fe_usecount) ++(*decremented_cached_channel_fe_usecount); + if (fcc_decremented_fe_usecounts.size()) + { + for (std::vector::iterator i(fcc_decremented_fe_usecounts.begin()); i != fcc_decremented_fe_usecounts.end(); ++i) + { + //eDebug("[eDVBResourceManager::canAllocateChannel] fcc : increase fcc fe use_count!"); + ++(**i); + } + } return ret; } diff --git a/lib/dvb/fcc.cpp b/lib/dvb/fcc.cpp new file mode 100644 index 0000000..614340b --- /dev/null +++ b/lib/dvb/fcc.cpp @@ -0,0 +1,335 @@ +#include +#include +#include +#include +#include + +//#define FCC_DEBUG + +void FCCServiceChannels::addFCCService(const eServiceReference &service) +{ + eDVBChannelID fcc_chid; + + ((const eServiceReferenceDVB&)service).getChannelID(fcc_chid); + + if (m_fcc_chids.find(fcc_chid) != m_fcc_chids.end()) + m_fcc_chids[fcc_chid] += 1; + else + m_fcc_chids[fcc_chid] = 1; +} + +void FCCServiceChannels::removeFCCService(const eServiceReference &service) +{ + eDVBChannelID fcc_chid; + ((const eServiceReferenceDVB&)service).getChannelID(fcc_chid); + + if (m_fcc_chids.find(fcc_chid) != m_fcc_chids.end()) + { + m_fcc_chids[fcc_chid] -= 1; + + if (m_fcc_chids[fcc_chid] == 0) + m_fcc_chids.erase(fcc_chid); + } +} + +int FCCServiceChannels::getFCCChannelID(std::map &fcc_chids) +{ + if (!m_fcc_chids.size()) return -1; + + fcc_chids = m_fcc_chids; + return 0; +} + +eFCCServiceManager *eFCCServiceManager::m_instance = (eFCCServiceManager*)0; + +eFCCServiceManager* eFCCServiceManager::getInstance() +{ + return m_instance; +} + +eFCCServiceManager::eFCCServiceManager(eNavigation *navptr) + :m_core(navptr), m_fcc_enable(false) +{ + if (!m_instance) + { + m_instance = this; + } +} + +eFCCServiceManager::~eFCCServiceManager() +{ + if (m_instance == this) + { + m_instance = 0; + } +} + +RESULT eFCCServiceManager::playFCCService(const eServiceReference &ref, ePtr &service) +{ + std::map< ePtr, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (;it != m_FCCServices.end();++it) + { + ASSERT (ref != it->second.m_service_reference); + } + + ASSERT(m_core->m_servicehandler); + RESULT res = m_core->m_servicehandler->play(ref, service); + if (res) + service = 0; + else + { + ePtr conn; + service->connectEvent(slot(*this, &eFCCServiceManager::FCCEvent), conn); + + FCCServiceElem elem = {ref, conn, fcc_state_preparing}; + m_FCCServices[service] = elem; + + res = service->start(); + } + + printFCCServices(); + + return res; +} + +void eFCCServiceManager::FCCEvent(iPlayableService* service, int event) +{ + std::map, FCCServiceElem >::iterator it = m_FCCServices.find(service); + if (it == m_FCCServices.end()) + { + eDebug("[eFCCServiceManager] event for non registered FCC service"); + return; + } + + switch (event) + { + case iPlayableService::evStart: + { + m_fccServiceChannels.addFCCService(it->second.m_service_reference); + break; + } + case iPlayableService::evStopped: + { + m_fccServiceChannels.removeFCCService(it->second.m_service_reference); + break; + } + case iPlayableService::evTuneFailed: + case iPlayableService::evFccFailed: + { + eDebug("[eFCCServiceManager][%s] set service to state failed.", it->second.m_service_reference.toString().c_str()); + it->second.m_state = fcc_state_failed; + break; + } + } + m_fcc_event(event); +} + +RESULT eFCCServiceManager::cleanupFCCService() +{ + if (m_FCCServices.size()) + { + std::map, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (;it != m_FCCServices.end();++it) + { + eDebug("[eFCCServiceManager] stop FCC service sref : %s", it->second.m_service_reference.toString().c_str()); + it->first->stop(); + } + + m_FCCServices.clear(); + } + return 0; +} + +RESULT eFCCServiceManager::stopFCCService(const eServiceReference &sref) +{ + if (m_FCCServices.size()) + { + std::map, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (; it != m_FCCServices.end();) + { + if (it->second.m_service_reference == sref) + { + eDebug("[eFCCServiceManager] stop FCC service sref : %s", it->second.m_service_reference.toString().c_str()); + it->first->stop(); + m_FCCServices.erase(it++); + } + else + { + ++it; + } + } + printFCCServices(); + } + return 0; +} + +RESULT eFCCServiceManager::stopFCCService() +{ + if (m_FCCServices.size()) + { + std::map, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (; it != m_FCCServices.end();) + { + if (it->second.m_state == fcc_state_failed) + { + eDebug("[eFCCServiceManager] stop FCC service sref : %s", it->second.m_service_reference.toString().c_str()); + it->first->stop(); + m_FCCServices.erase(it++); + } + else + { + ++it; + } + } + + printFCCServices(); + } + return 0; +} + +RESULT eFCCServiceManager::tryFCCService(const eServiceReference &sref, ePtr &service) +{ + ePtr new_service = 0; + + printFCCServices(); + + int get_fcc_decoding = 0; + + /* stop previous decoding service */ + std::map< ePtr, FCCServiceElem >::iterator it; + for (it = m_FCCServices.begin();it != m_FCCServices.end();++it) + { + if (it->second.m_state == fcc_state_decoding) + { + ASSERT(get_fcc_decoding == 0); + get_fcc_decoding = 1; + + /* send end event */ + m_core->m_event(iPlayableService::evEnd); + + /* kill service and event */ + m_core->m_service_event_conn = 0; + m_core->m_runningService = 0; + + /* connect to fcc event */ + ePtr conn; + it->first->connectEvent(slot(*this, &eFCCServiceManager::FCCEvent), conn); + it->second.m_service_event_conn = conn; + it->second.m_state = fcc_state_preparing; + + /* switch to FCC prepare state */ + it->first->start(); + + /* update FCCServiceChannels */ + m_fccServiceChannels.addFCCService(it->second.m_service_reference); + } + } + + /* search new service */ + for (it = m_FCCServices.begin();it != m_FCCServices.end();++it) + { + if (it->second.m_service_reference == sref) + { + eDebug("[eFCCServiceManager] use FCC service sref : %s", it->second.m_service_reference.toString().c_str()); + it->second.m_service_event_conn = 0; /* disconnect FCC event */ + it->second.m_state = fcc_state_decoding; + new_service = it->first; + m_fccServiceChannels.removeFCCService(it->second.m_service_reference); + break; + } + } + + if (new_service) + { + service = new_service; + } + + else /* If new service is not found in FCC service list, cleanup all FCC prepared services and get new FCC service. */ + { + cleanupFCCService(); + m_core->stopService(); + if (eFCCServiceManager::checkAvailable(sref)) + { + ASSERT(m_core->m_servicehandler); + m_core->m_servicehandler->play(sref, service); + + if (service) + { + FCCServiceElem elem = {sref, 0, fcc_state_decoding}; + m_FCCServices[service] = elem; + service->start(); // do FCC preparing + } + } + else + { + return -1; + } + } + + printFCCServices(); + + return 0; +} + +int eFCCServiceManager::isLocked(ePtr service) +{ + ePtr ptr; + service->frontendInfo(ptr); + return ptr->getFrontendInfo(iDVBFrontend_ENUMS::locked); +} + +PyObject *eFCCServiceManager::getFCCServiceList() +{ + ePyObject dest = PyDict_New(); + if (dest) + { + std::map< ePtr, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (;it != m_FCCServices.end();++it) + { + ePyObject tplist = PyList_New(0); + PyList_Append(tplist, PyInt_FromLong((long)it->second.m_state)); + PyList_Append(tplist, PyInt_FromLong((long)isLocked(it->first))); + PyDict_SetItemString(dest, it->second.m_service_reference.toString().c_str(), tplist); + Py_DECREF(tplist); + } + } + + else + Py_RETURN_NONE; + return dest; +} + +void eFCCServiceManager::printFCCServices() +{ +#ifdef FCC_DEBUG + eDebug(" [eFCCServiceManager::printFCCServices][*] total size : %d", m_FCCServices.size()); + + std::map< ePtr, FCCServiceElem >::iterator it = m_FCCServices.begin(); + for (;it != m_FCCServices.end();++it) + { + eDebug(" [eFCCServiceManager::printFCCServices][*] sref : %s, state : %d, tune : %d", it->second.m_service_reference.toString().c_str(), it->second.m_state, isLocked(it->first)); + } +#else + ; +#endif +} + +int eFCCServiceManager::getFCCChannelID(std::map &fcc_chids) +{ + eFCCServiceManager *fcc_mng = eFCCServiceManager::getInstance(); + if (!fcc_mng) return -1; + return fcc_mng->m_fccServiceChannels.getFCCChannelID(fcc_chids); +} + +bool eFCCServiceManager::checkAvailable(const eServiceReference &ref) +{ + int serviceType = ref.getData(0); + eFCCServiceManager *fcc_mng = eFCCServiceManager::getInstance(); + + if (ref.path.empty() && (serviceType != 2) && (serviceType != 10) && fcc_mng) // no PVR, streaming, radio channel.. + return fcc_mng->isEnable(); + return false; +} + +DEFINE_REF(eFCCServiceManager); + diff --git a/lib/dvb/fcc.h b/lib/dvb/fcc.h new file mode 100644 index 0000000..aacd9d8 --- /dev/null +++ b/lib/dvb/fcc.h @@ -0,0 +1,67 @@ +#ifndef __dvb_fcc_h +#define __dvb_fcc_h + +#include +#include +#include +#include + +class eNavigation; + +class FCCServiceChannels +{ +private: + std::map m_fcc_chids; + +public: + void addFCCService(const eServiceReference &service); + void removeFCCService(const eServiceReference &service); + int getFCCChannelID(std::map &fcc_chids); +}; + +typedef struct _tagFccElem +{ + eServiceReference m_service_reference; + ePtr m_service_event_conn; + int m_state; +}FCCServiceElem; + +class eFCCServiceManager: public iObject, public Object +{ + DECLARE_REF(eFCCServiceManager); +private: + eNavigation *m_core; + static eFCCServiceManager* m_instance; + std::map, FCCServiceElem, std::less > m_FCCServices; + FCCServiceChannels m_fccServiceChannels; + + bool m_fcc_enable; + + void FCCEvent(iPlayableService* service, int event); +public: + PSignal1 m_fcc_event; + static eFCCServiceManager* getInstance(); + eFCCServiceManager(eNavigation *navptr); + ~eFCCServiceManager(); + + enum + { + fcc_state_preparing, + fcc_state_decoding, + fcc_state_failed + }; + SWIG_VOID(RESULT) playFCCService(const eServiceReference &ref, ePtr &SWIG_OUTPUT); + RESULT stopFCCService(const eServiceReference &sref); + RESULT stopFCCService(); + RESULT cleanupFCCService(); + RESULT tryFCCService(const eServiceReference &service, ePtr &ptr); + int isLocked(ePtr service); + PyObject *getFCCServiceList(); + void printFCCServices(); + static int getFCCChannelID(std::map &fcc_chids); + static bool checkAvailable(const eServiceReference &ref); + void setFCCEnable(int enable) { m_fcc_enable = (enable != 0); } + bool isEnable() { return m_fcc_enable; } +}; + +#endif /* __dvb_fcc_h */ diff --git a/lib/dvb/fccdecoder.cpp b/lib/dvb/fccdecoder.cpp new file mode 100644 index 0000000..c408c1a --- /dev/null +++ b/lib/dvb/fccdecoder.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include +#include +#include +#include + +bool eFCCDecoder::isDestroyed = false; +eFCCDecoder::eFCCDecoder() +{ + int index = 0; + + eDebug("[eFCCDecoder] scanning for FCC device files.."); + while(1) + { + struct stat s; + char filename[128]; + sprintf(filename, "/dev/fcc%d", index); + if (stat(filename, &s)) + break; + + eDebug("[eFCCDecoder] %s found..", filename); + m_fccs.push_back(-1); + index++; + } +} + +eFCCDecoder::~eFCCDecoder() +{ + isDestroyed = true; +} + +int eFCCDecoder::allocateFcc() +{ + int fccFd = -1; + for(unsigned int i = 0; i < m_fccs.size(); i++) + { + if (m_fccs[i]== -1) + { + char filename[128]; + sprintf(filename, "/dev/fcc%d", i); + + fccFd = ::open(filename, O_RDWR); + if (fccFd < 0) + eDebug("[eFCCDecoder] open %s failed!", filename); + + else + eDebug("[eFCCDecoder] alloc %s", filename); + + m_fccs[i] = fccFd; + break; + } + } + + return fccFd; +} + +void eFCCDecoder::freeFcc(int fccFd) +{ + if (fccFd < 0) + return; + + for(unsigned int i = 0; i < m_fccs.size(); i++) + { + if (m_fccs[i]== fccFd) + { + m_fccs[i] = -1; + eDebug("[eFCCDecoder] close /dev/fcc%d", i); + ::close(fccFd); + break; + } + } +} + diff --git a/lib/dvb/fccdecoder.h b/lib/dvb/fccdecoder.h new file mode 100644 index 0000000..c7b57c5 --- /dev/null +++ b/lib/dvb/fccdecoder.h @@ -0,0 +1,28 @@ +#ifndef __dvb_fcc_decoder_h +#define __dvb_fcc_decoder_h + +#include + +class eFCCDecoder +{ + std::vector m_fccs; + static eFCCDecoder *instance; + static bool isDestroyed; + +public: + eFCCDecoder(); + ~eFCCDecoder(); + int allocateFcc(); + void freeFcc(int fccFd); + + static eFCCDecoder* getInstance() + { + if (isDestroyed) + return NULL; + + static eFCCDecoder instance; + return &instance; + } +}; + +#endif /* __dvb_fcc_decoder_h */ diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h index 2c11ff8..d0d1043 100644 --- a/lib/dvb/idvb.h +++ b/lib/dvb/idvb.h @@ -636,6 +636,7 @@ public: virtual RESULT getCADemuxID(uint8_t &id)=0; virtual RESULT flush()=0; virtual int openDVR(int flags)=0; + virtual int getSource()=0; }; #if HAVE_DVB_API_VERSION < 3 && !defined(VIDEO_EVENT_SIZE_CHANGED) @@ -690,6 +691,14 @@ public: /** Display any complete data as fast as possible */ virtual RESULT setTrickmode()=0; + + virtual RESULT prepareFCC(int fe_id, int vpid, int vtype, int pcrpid)=0; + + virtual RESULT fccDecoderStart()=0; + + virtual RESULT fccDecoderStop()=0; + + virtual RESULT fccUpdatePids(int fe_id, int vpid, int vtype, int pcrpid)=0; virtual RESULT getPTS(int what, pts_t &pts) = 0; diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index b3e8aeb..5132b38 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -33,6 +33,8 @@ eDVBServicePMTHandler::eDVBServicePMTHandler() m_pmt_pid = -1; m_dsmcc_pid = -1; m_isstreamclient = false; + m_ca_disabled = false; + m_pmt_ready = false; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); @@ -115,6 +117,7 @@ void eDVBServicePMTHandler::PMTready(int error) serviceEvent(eventNoPMT); else { + m_pmt_ready = true; m_have_cached_program = false; serviceEvent(eventNewProgramInfo); mDemuxId = m_decode_demux_num; @@ -132,9 +135,11 @@ void eDVBServicePMTHandler::PMTready(int error) else demuxes[1]=demuxes[0]; eDVBCAService::register_service(m_reference, demuxes, m_ca_servicePtr); - eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); + if (!m_ca_disabled) + eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); } - eDVBCIInterfaces::getInstance()->gotPMT(this); + if (!m_ca_disabled) + eDVBCIInterfaces::getInstance()->gotPMT(this); } if (m_ca_servicePtr) { @@ -504,6 +509,8 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) program.pmtPid = -1; program.textPid = -1; program.aitPid = -1; + program.isCached = false; + program.pmtVersion = -1; int first_ac3 = -1; program.defaultAudioStream = 0; @@ -532,6 +539,7 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) eDVBTableSpec table_spec; ptr->getSpec(table_spec); program.pmtPid = table_spec.pid < 0x1fff ? table_spec.pid : -1; + program.pmtVersion = table_spec.version; std::vector::const_iterator i; for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { @@ -955,6 +963,9 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) int cached_pcrpid = m_service->getCacheEntry(eDVBService::cPCRPID), vpidtype = m_service->getCacheEntry(eDVBService::cVTYPE), cnt=0; + + program.isCached = true; + if ( vpidtype == -1 ) vpidtype = videoStream::vtMPEG2; if ( cached_vpid != -1 ) @@ -1139,7 +1150,7 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem if (!m_resourceManager->getChannelList(db)) db->getService((eServiceReferenceDVB&)m_reference, m_service); - if (!res && !simulate) + if (!res && !simulate && !m_ca_disabled) eDVBCIInterfaces::getInstance()->addPMTHandler(this); } else if (!simulate) // no simulation of playback services { @@ -1266,6 +1277,28 @@ void eDVBServicePMTHandler::free() m_demux = 0; } +void eDVBServicePMTHandler::addCaHandler() +{ + m_ca_disabled = false; + if (m_channel) + { + eDVBCIInterfaces::getInstance()->addPMTHandler(this); + if (m_pmt_ready) + { + eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); + eDVBCIInterfaces::getInstance()->gotPMT(this); + } + } +} + +void eDVBServicePMTHandler::removeCaHandler() +{ + m_ca_disabled = true; + if (m_channel) + eDVBCIInterfaces::getInstance()->removePMTHandler(this); +} + + CAServiceMap eDVBCAService::exist; ChannelMap eDVBCAService::exist_channels; ePtr eDVBCAService::m_chanAddedConn; diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index b15c95b..b2fc68f 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -158,6 +158,9 @@ class eDVBServicePMTHandler: public Object uint8_t m_decode_demux_num; ePtr m_no_pat_entry_delay; uint8_t mDemuxId; + + bool m_pmt_ready; + bool m_ca_disabled; public: eDVBServicePMTHandler(); ~eDVBServicePMTHandler(); @@ -254,6 +257,8 @@ public: int pmtPid; int textPid; int aitPid; + int pmtVersion; + bool isCached; bool isCrypted() { return !caids.empty(); } PyObject *createPythonObject(); }; @@ -274,6 +279,7 @@ public: void getHBBTVUrl(std::string &ret) { ret = m_HBBTVUrl; } void getDemuxID(int &id) { id = mDemuxId; } + void setCaDisable(bool disable) { m_ca_disabled = disable; } /* deprecated interface */ int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); @@ -282,6 +288,8 @@ public: int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0, bool isstreamclient=false); void free(); + void addCaHandler(); + void removeCaHandler(); private: bool m_have_cached_program; program m_cached_program; diff --git a/lib/nav/core.cpp b/lib/nav/core.cpp index ad77766..2e54d87 100644 --- a/lib/nav/core.cpp +++ b/lib/nav/core.cpp @@ -1,6 +1,7 @@ #include #include #include +#include void eNavigation::serviceEvent(iPlayableService* service, int event) { @@ -24,10 +25,15 @@ void eNavigation::recordEvent(iRecordableService* service, int event) RESULT eNavigation::playService(const eServiceReference &service) { - stopService(); - - ASSERT(m_servicehandler); - RESULT res = m_servicehandler->play(service, m_runningService); + RESULT res = -1; + + if (m_fccmgr->tryFCCService(service, m_runningService) == -1) + { + stopService(); + ASSERT(m_servicehandler); + res = m_servicehandler->play(service, m_runningService); + } + if (m_runningService) { m_runningService->connectEvent(slot(*this, &eNavigation::serviceEvent), m_service_event_conn); @@ -69,6 +75,8 @@ RESULT eNavigation::stopService(void) /* kill service. */ m_service_event_conn = 0; + + m_fccmgr->cleanupFCCService(); return 0; } @@ -150,6 +158,7 @@ eNavigation::eNavigation(iServiceHandler *serviceHandler) { ASSERT(serviceHandler); m_servicehandler = serviceHandler; + m_fccmgr = new eFCCServiceManager(this); } eNavigation::~eNavigation() diff --git a/lib/nav/core.h b/lib/nav/core.h index 9f7be88..a4906c1 100644 --- a/lib/nav/core.h +++ b/lib/nav/core.h @@ -6,6 +6,7 @@ #include #include #include +#include class eNavigation: public iObject, public Object { @@ -22,6 +23,10 @@ class eNavigation: public iObject, public Object Signal2,int> m_record_event; void recordEvent(iRecordableService* service, int event); + + friend class eFCCServiceManager; + ePtr m_fccmgr; + public: RESULT playService(const eServiceReference &service); diff --git a/lib/python/Components/ServiceList.py b/lib/python/Components/ServiceList.py index 3930c07..6972d91 100644 --- a/lib/python/Components/ServiceList.py +++ b/lib/python/Components/ServiceList.py @@ -123,6 +123,9 @@ class ServiceList(HTMLComponent, GUIComponent): self.l.getNext(r) return r + def getList(self): + return self.l.getList() + def atBegin(self): return self.instance.atBegin() diff --git a/lib/python/Plugins/Plugin.py b/lib/python/Plugins/Plugin.py index 2137d0e..d19ec26 100755 --- a/lib/python/Plugins/Plugin.py +++ b/lib/python/Plugins/Plugin.py @@ -60,11 +60,7 @@ class PluginDescriptor: # should be provided to name and describe the new menu entry. WHERE_SOFTWAREMANAGER = 14 - WHERE_SATCONFIGCHANGED = 15 - - WHERE_SERVICESCAN = 16 - - WHERE_EXTENSIONSINGLE = 17 + WHERE_EXTENSIONSINGLE = 15 def __init__(self, name = "Plugin", where = [ ], description = "", icon = None, fnc = None, wakeupfnc = None, needsRestart = None, internal = False, weight = 0): diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile.am b/lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile.am new file mode 100644 index 0000000..ae91ed5 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile.am @@ -0,0 +1,7 @@ +installdir = $(pkglibdir)/python/Plugins/SystemPlugins/FastChannelChange + +SUBDIRS = meta + +install_PYTHON = \ + __init__.py \ + plugin.py diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/__init__.py b/lib/python/Plugins/SystemPlugins/FastChannelChange/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FastChannelChange/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile.am new file mode 100644 index 0000000..4e3b9e9 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile.am @@ -0,0 +1,3 @@ +installdir = $(datadir)/meta + +dist_install_DATA = plugin_fastchannelchange.xml diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/plugin_fastchannelchange.xml b/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/plugin_fastchannelchange.xml new file mode 100644 index 0000000..cf4571a --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FastChannelChange/meta/plugin_fastchannelchange.xml @@ -0,0 +1,16 @@ + + + + + + hschang + FastChannelChange + enigma2-plugin-systemplugins-fastchannelchange + Fast Channel Change of your VU+ + Fast Channel Change of your VU+ + + + + + + diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py b/lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py new file mode 100644 index 0000000..7c3b2cc --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py @@ -0,0 +1,589 @@ + +from Plugins.Plugin import PluginDescriptor +import NavigationInstance + +from Screens.Screen import Screen +from Screens.InfoBar import InfoBar +from Screens.MessageBox import MessageBox + +from Components.NimManager import nimmanager +from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigSelection +from Components.ConfigList import ConfigListScreen +from Components.ActionMap import ActionMap +from Components.Sources.StaticText import StaticText +from Components.ServiceEventTracker import ServiceEventTracker + +from enigma import iPlayableService, iServiceInformation, eEnv, eTimer, eServiceReference, iRecordableService + +import os +import glob + +from enigma import eFCCServiceManager + +g_max_fcc = len(glob.glob('/dev/fcc?')) +g_default_fcc = (g_max_fcc) > 5 and 5 or g_max_fcc + +config.plugins.fccsetup = ConfigSubsection() +config.plugins.fccsetup.activate = ConfigYesNo(default = False) +config.plugins.fccsetup.maxfcc = ConfigSelection(default = str(g_default_fcc), choices = list((str(n), str(n)) for n in range(2, g_max_fcc+1))) +config.plugins.fccsetup.zapupdown = ConfigYesNo(default = True) +config.plugins.fccsetup.history = ConfigYesNo(default = False) +config.plugins.fccsetup.priority = ConfigSelection(default = "zapupdown", choices = { "zapupdown" : _("Zap Up/Down"), "historynextback" : _("History Prev/Next") }) +config.plugins.fccsetup.disableforrec = ConfigYesNo(default = True) + +FccInstance = None + +def FCCChanged(): + if FccInstance: + FccInstance.FCCSetupChanged() + +def checkSupportFCC(): + global g_max_fcc + return bool(g_max_fcc) + +class FCCSupport: + def __init__(self, session): + self.session = session + + self.fccmgr = eFCCServiceManager.getInstance(); + + self.fccList = [] + + self.createListTimer = eTimer() + self.createListTimer.callback.append(self.FCCCreateList) + + self.getSrefTimer = eTimer() + self.getSrefTimer.callback.append(self.FCCGetCurSref) + + self.eventList = [] + self.fccEventTimer = eTimer() + self.fccEventTimer.callback.append(self.FCCApplyEvent) + + self.fccForceStartTimer = eTimer() + self.fccForceStartTimer.callback.append(self.FCCForceStartForREC) + + self.fccResetTimer = eTimer() + self.fccResetTimer.callback.append(self.FCCResetTimerForREC) + + self.activating = False + + self.fccSetupActivate = checkSupportFCC() and config.plugins.fccsetup.activate.value + self.maxFCC = int(config.plugins.fccsetup.maxfcc.value) + self.zapdownEnable = config.plugins.fccsetup.zapupdown.value + self.historyEnable = config.plugins.fccsetup.history.value + self.priority = config.plugins.fccsetup.priority.value + self.disableforrec = config.plugins.fccsetup.disableforrec.value + self.fccmgr.setFCCEnable(int(self.fccSetupActivate)) + + self.setProcFCC(self.fccSetupActivate) + self.fccTimeoutTimer = eTimer() + self.fccTimeoutTimer.callback.append(self.FCCTimeout) + self.fccTimeoutEventCode = 0x102 + self.fccTimeoutWait = None + + self.fccmgr.m_fcc_event.get().append(self.FCCGetEvent) + + self.getRecordings() + + self.__event_tracker = None + self.onClose = [] + self.changeEventTracker() + + def setProcFCC(self, value): + procPath = "/proc/stb/frontend/fbc/fcc" + if os.access(procPath, os.W_OK): + fd = open(procPath,'w') + fd.write(value and "enable" or "disable") + fd.close() + else: + print "[FCCSupport] write fail! : ", procPath + + def gotRecordEvent(self, service, event): + if self.disableforrec: + if (not self.recordings) and (event == iRecordableService.evTuneStart): + self.getRecordings() + if self.recordings: + self.FCCForceStopForREC() + + elif event == iRecordableService.evEnd: + self.getRecordings() + if not self.recordings: + self.FCCForceStartForREC() + else: + if event == iRecordableService.evTuneStart: + self.fccResetTimer.stop() + self.FCCForceStopForREC() + self.fccForceStartTimer.start(2000, True) + + elif event == iRecordableService.evEnd: + self.fccForceStartTimer.stop() + self.fccResetTimer.start(2000, True) + + def FCCForceStopForREC(self): + self.enableEventTracker(False) + self.FCCDisableServices() + self.FCCStopAllServices() + + def FCCForceStartForREC(self): + self.enableEventTracker(True) + self.FCCForceStart() + + def FCCResetTimerForREC(self): + self.FCCForceStopForREC() + self.FCCForceStartForREC() + + def FCCSetupChanged(self): + fcc_changed = False + + newFccSetupActivate = checkSupportFCC() and config.plugins.fccsetup.activate.value + if self.fccSetupActivate != newFccSetupActivate: + self.fccSetupActivate = newFccSetupActivate + self.setProcFCC(self.fccSetupActivate) + fcc_changed = True + + if int(config.plugins.fccsetup.maxfcc.value) != self.maxFCC: + self.maxFCC = int(config.plugins.fccsetup.maxfcc.value) + fcc_changed = True + + if self.zapdownEnable != config.plugins.fccsetup.zapupdown.value: + self.zapdownEnable = config.plugins.fccsetup.zapupdown.value + fcc_changed = True + + if self.historyEnable != config.plugins.fccsetup.history.value: + self.historyEnable = config.plugins.fccsetup.history.value + fcc_changed = True + + if self.priority != config.plugins.fccsetup.priority.value: + self.priority = config.plugins.fccsetup.priority.value + fcc_changed = True + + if self.disableforrec != config.plugins.fccsetup.disableforrec.value: + self.disableforrec = config.plugins.fccsetup.disableforrec.value + fcc_changed = True + + self.getRecordings() + self.changeEventTracker() + + if (not self.fccSetupActivate) or (self.disableforrec and self.recordings): + self.FCCDisableServices() + + if fcc_changed: + self.fccmgr.setFCCEnable(int(self.fccSetupActivate)) + curPlaying = self.session.nav.getCurrentlyPlayingServiceReference() + if curPlaying: + self.session.nav.stopService() + self.session.nav.playService(curPlaying) + + # get current recording state + def getRecordings(self): + self.recordings = bool(self.session.nav.getRecordings()) + + def addRecordEventCallback(self, enable=True): + if enable: + if self.gotRecordEvent not in self.session.nav.record_event: + self.session.nav.record_event.append(self.gotRecordEvent) + else: + if self.gotRecordEvent in self.session.nav.record_event: + self.session.nav.record_event.remove(self.gotRecordEvent) + + def changeEventTracker(self): + if self.fccSetupActivate: + self.addRecordEventCallback(True) + if self.disableforrec and self.recordings: + self.enableEventTracker(False) + else: + self.enableEventTracker(True) + else: + self.addRecordEventCallback(False) + self.enableEventTracker(False) + + def enableEventTracker(self, activate): + if activate: + if not self.__event_tracker: + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evStart: self.getEvStart, + iPlayableService.evEnd: self.getEvEnd, + iPlayableService.evTunedIn: self.getEvTunedIn, + iPlayableService.evTuneFailed: self.getEvTuneFailed + }) + + elif self.__event_tracker: + # run ServiceEventTracker.__del_event() + for x in self.onClose: + x() + + self.onClose = [] + self.__event_tracker = None + + def getEvStart(self): + self.createListTimer.start(0,True) + + def getEvEnd(self): + self.FCCDisableServices() + + def getEvTunedIn(self): + self.FCCTryStart() + + def getEvTuneFailed(self): + self.FCCTryStart() + + def isPlayableFCC(self, sref): + playable = True + if isinstance(sref, str): + sref = eServiceReference(sref) + + if sref.getPath(): # is PVR? or streaming? + playable = False + + if int(sref.getData(0)) in (2, 10): # is RADIO? + playable = False + + return playable + + def getZapUpDownList(self): + fccZapUpDownList = [] + serviceList = InfoBar.instance.servicelist.servicelist.getList() + curServiceRef = InfoBar.instance.servicelist.servicelist.getCurrent().toString() + + serviceRefList = [] + for idx in range(len(serviceList)): + sref = serviceList[idx].toString() + if (sref.split(':')[1] == '0') and self.isPlayableFCC(sref) : # remove marker + serviceRefList.append(sref) + + if curServiceRef in serviceRefList: + serviceRefListSize = len(serviceRefList) + curServiceIndex = serviceRefList.index(curServiceRef) + + for x in range(self.maxFCC-1): + if x > (serviceRefListSize-2): # if not ((x+1) <= (serviceRefListSize-1)) + break + + idx = (x / 2) + 1 + if x % 2: + idx *= -1 # idx : [ 1, -1, 2, -2, 3, -3, 4, -4 ....] + idx = (curServiceIndex+idx) % serviceRefListSize # calc wraparound + try: + fccZapUpDownList.append(serviceRefList[idx]) + except: + print "[FCCCreateList] append error, idx : %d" % idx + break + + return fccZapUpDownList + + def getHistoryPrevNextList(self): + historyList = [] + history = InfoBar.instance.servicelist.history[:] + history_pos = InfoBar.instance.servicelist.history_pos + history_len = len(history) + + if history_len > 1 and history_pos > 0: + historyPrev = history[history_pos-1][:][-1].toString() + if self.isPlayableFCC(historyPrev): + historyList.append(historyPrev) + + if history_len > 1 and history_pos < (history_len-1): + historyNext = history[history_pos+1][:][-1].toString() + if self.isPlayableFCC(historyNext): + historyList.append(historyNext) + + return historyList + + def FCCCreateList(self): + if (not self.fccSetupActivate) or (self.disableforrec and self.recordings): + return + + if InfoBar.instance: + self.fccList = [] + fccZapUpDownList = [] + historyList = [] + + if self.zapdownEnable: + fccZapUpDownList = self.getZapUpDownList() + + if self.historyEnable: + historyList = self.getHistoryPrevNextList() + + if self.priority == "zapupdown": + fccZapDownLen = len(fccZapUpDownList) + if fccZapDownLen: + size = fccZapDownLen > 2 and 2 or fccZapDownLen + self.fccList = fccZapUpDownList[:size] + fccZapUpDownList = fccZapUpDownList[size:] + + self.addFCCList(historyList) + self.addFCCList(fccZapUpDownList) + else: + self.addFCCList(historyList) + self.addFCCList(fccZapUpDownList) + + self.FCCReconfigureFccList() + + def addFCCList(self, newlist): + fccListMaxLen = self.maxFCC-1 + for sref in newlist: + if len(self.fccList) >= fccListMaxLen: + break + + if sref not in self.fccList: + self.fccList.append(sref) + + def FCCReconfigureFccList(self): + stopFCCList = [] + currentFCCList = self.fccmgr.getFCCServiceList() + + for (sref, value) in currentFCCList.items(): + state = value[0] + + if state == 2: # fcc_state_failed + stopFCCList.append(sref) + + elif sref in self.fccList: # check conflict FCC channel (decoder/prepare) + self.fccList.remove(sref) + + elif state == 0: # fcc_state_preparing + stopFCCList.append(sref) + + for sref in stopFCCList: + self.fccmgr.stopFCCService(eServiceReference(sref)) + + def FCCTryStart(self): + self.getSrefTimer.start(0, True) + + def FCCGetCurSref(self): + if (not self.fccSetupActivate) or (self.disableforrec and self.recordings): + return + + if self.createListTimer.isActive(): + self.createListTimer.stop() + self.FCCCreateList() + + curSref = self.session.nav.getCurrentlyPlayingServiceReference() + + if curSref and self.isPlayableFCC(curSref): + self.FCCStart() + else: + print "[FCCSupport][FCCGetCurSref] get current serviceReference failed!!" + + def FCCStart(self): + self.activating = True + self.FCCGetEvent(iPlayableService.evTunedIn) + + def FCCGetEvent(self, event): + if self.activating and event in (iPlayableService.evTunedIn, iPlayableService.evTuneFailed, iPlayableService.evFccFailed, self.fccTimeoutEventCode): + self.eventList.append(event) + self.fccEventTimer.start(0, True) + + def FCCApplyEvent(self): + if not self.activating: + return + + while self.eventList: + event = self.eventList.pop(0) + + self.FCCTimeoutTimerStop() + + if event in (iPlayableService.evTuneFailed, iPlayableService.evFccFailed): + self.fccmgr.stopFCCService() # stop FCC Services in failed state + + if not self.FCCCheckAndTimerStart() and len(self.fccList): + sref = self.fccList.pop(0) + if self.isPlayableFCC(sref): # remove PVR, streaming, radio channels + self.fccmgr.playFCCService(eServiceReference(sref)) + self.FCCTimeoutTimerStart(sref) + + def FCCStopAllServices(self): + self.FCCTimeoutTimerStop() + fccServiceList = self.fccmgr.getFCCServiceList() + for (sref, value) in fccServiceList.items(): + state = value[0] + if state != 1 : # 1 : fcc_state_decoding + self.fccmgr.stopFCCService(eServiceReference(sref)) + + def FCCDisableServices(self): + self.FCCTimeoutTimerStop() + self.getSrefTimer.stop() + self.activating = False + self.fccList = [] + + self.fccEventTimer.stop() + self.fccmgr.stopFCCService() + self.eventList = [] + + def FCCForceStart(self): + self.getEvStart() + self.getEvTunedIn() + + def FCCCheckNoLocked(self): + for (sref, value) in self.fccmgr.getFCCServiceList().items(): + state = value[0] + locked = value[1] + if state != 1 and locked == 0: # no fcc decoding and no locked + return sref + return None + + def FCCTimeout(self): + sref = self.FCCCheckNoLocked() + if sref and sref == self.fccTimeoutWait: + self.fccmgr.stopFCCService(eServiceReference(sref)) + self.FCCGetEvent(self.fccTimeoutEventCode) + + def FCCCheckAndTimerStart(self): + sref = self.FCCCheckNoLocked() + if sref: + self.FCCTimeoutTimerStart(sref) + return True + return False + + def FCCTimeoutTimerStart(self, sref): + self.fccTimeoutWait = sref + self.fccTimeoutTimer.start(5000, True) + + def FCCTimeoutTimerStop(self): + self.fccTimeoutWait = None + self.fccTimeoutTimer.stop() + +class FCCSetup(Screen, ConfigListScreen): + skin = """ + + + + + + + + + """ + + def __init__(self,session): + Screen.__init__(self,session) + self.title = _("Fast Channel Change Setup") + self.session = session + self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ], + { + "ok": self.keySave, + "cancel": self.keyCancel, + "red": self.keyCancel, + "green": self.keySave, + }, -2) + self.list = [] + ConfigListScreen.__init__(self, self.list,session = self.session) + self["key_red"] = StaticText(_("Cancel")) + self["key_green"] = StaticText(_("Save")) + + self.isSupport = checkSupportFCC() + + if self.isSupport: + self["description"] = StaticText("") + self.createConfig() + self.createSetup() + else: + self["description"] = StaticText(_("Box or driver is not support FCC.")) + + def createConfig(self): + self.enableEntry = getConfigListEntry(_("FCC enabled"), config.plugins.fccsetup.activate) + self.fccmaxEntry = getConfigListEntry(_("Max Channels"), config.plugins.fccsetup.maxfcc) + self.zapupdownEntry = getConfigListEntry(_("Zap Up/Down"), config.plugins.fccsetup.zapupdown) + self.historyEntry = getConfigListEntry(_("History Prev/Next"), config.plugins.fccsetup.history) + self.priorityEntry = getConfigListEntry(_("priority"), config.plugins.fccsetup.priority) + self.recEntry = getConfigListEntry(_("Disable FCC during recordings"), config.plugins.fccsetup.disableforrec) + + def createSetup(self): + self.list = [] + self.list.append( self.enableEntry ) + if self.enableEntry[1].value: + self.list.append( self.fccmaxEntry ) + self.list.append( self.zapupdownEntry ) + self.list.append( self.historyEntry ) + if self.zapupdownEntry[1].value and self.historyEntry[1].value: + self.list.append( self.priorityEntry ) + self.list.append(self.recEntry) + + self["config"].list = self.list + self["config"].l.setList(self.list) + + def keyLeft(self): + ConfigListScreen.keyLeft(self) + self.setupChanged() + + def keyRight(self): + ConfigListScreen.keyRight(self) + self.setupChanged() + + def setupChanged(self): + currentEntry = self["config"].getCurrent() + if currentEntry in (self.zapupdownEntry, self.historyEntry, self.enableEntry): + if not (self.zapupdownEntry[1].value or self.historyEntry[1].value): + if currentEntry == self.historyEntry: + self.zapupdownEntry[1].value = True + else: + self.historyEntry[1].value = True + elif self.zapupdownEntry[1].value and self.historyEntry[1].value: + if int(self.fccmaxEntry[1].value) < 5: + if g_max_fcc < 5: + self.fccmaxEntry[1].value = str(g_max_fcc) + else: + self.fccmaxEntry[1].value = str(5) + + self.createSetup() + + def keySave(self): + if not self.isSupport: + self.keyCancel() + return + + ConfigListScreen.keySave(self) + FCCChanged() + +def getExtensionName(): + if config.plugins.fccsetup.activate.value: + return _("Disable FastChannelChange") + + return _("Enable FastChannelChange") + +def ToggleUpdate(): + if config.plugins.fccsetup.activate.value: + config.plugins.fccsetup.activate.value = False + else: + config.plugins.fccsetup.activate.value = True + config.plugins.fccsetup.activate.save() + FCCChanged() + +def FCCSupportInit(reason, **kwargs): + if kwargs.has_key("session"): + global FccInstance + FccInstance = FCCSupport(kwargs["session"]) + +def addExtentions(infobarExtensions): + infobarExtensions.addExtension((getExtensionName, ToggleUpdate, lambda: True), None) + +def main(session, **kwargs): + session.open(FCCSetup) + +def Plugins(**kwargs): + list = [] + + global g_max_fcc + if g_max_fcc: + list.append( + PluginDescriptor(name="FCCSupport", + description="Fast Channel Change Support", + where = [PluginDescriptor.WHERE_SESSIONSTART], + fnc = FCCSupportInit)) + + list.append( + PluginDescriptor(name="FCCExtensionMenu", + description="Fast Channel Change Extension Menu", + where = [PluginDescriptor.WHERE_EXTENSIONSINGLE], + fnc = addExtentions)) + + list.append( + PluginDescriptor(name=_("FCCSetup"), + description=_("Fast Channel Change Setup"), + where = [PluginDescriptor.WHERE_PLUGINMENU], + needsRestart = False, + fnc = main)) + + return list + diff --git a/lib/python/Plugins/SystemPlugins/Makefile.am b/lib/python/Plugins/SystemPlugins/Makefile.am index 3a16f5f..950b50e 100755 --- a/lib/python/Plugins/SystemPlugins/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/Makefile.am @@ -8,7 +8,7 @@ SUBDIRS = SoftwareManager FrontprocessorUpgrade PositionerSetup Satfinder \ Blindscan RemoteControlCode UI3DSetup UIPositionSetup HDMICEC LEDBrightnessSetup \ FirmwareUpgrade CrashReport 3GModemManager WirelessAccessPoint ZappingModeSelection \ DeviceManager TransCodingSetup WOLSetup NetDrive AudioEffect AnimationSetup \ - BoxModeConfig Solo4kMiscControl + BoxModeConfig Solo4kMiscControl FastChannelChange install_PYTHON = \ __init__.py diff --git a/lib/python/Screens/Satconfig.py b/lib/python/Screens/Satconfig.py index 48b580c..3b75e46 100644 --- a/lib/python/Screens/Satconfig.py +++ b/lib/python/Screens/Satconfig.py @@ -544,9 +544,6 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): self.nimConfig.connectedTo.setChoices(choices) for x in self["config"].list: x[1].save() - - for p in plugins.getPlugins(PluginDescriptor.WHERE_SATCONFIGCHANGED): - p() def cancelConfirm(self, result): if not result: diff --git a/lib/python/Screens/ServiceScan.py b/lib/python/Screens/ServiceScan.py index efa218b..d333230 100644 --- a/lib/python/Screens/ServiceScan.py +++ b/lib/python/Screens/ServiceScan.py @@ -62,11 +62,6 @@ class ServiceScan(Screen): }) self.onFirstExecBegin.append(self.doServiceScan) - self.onClose.append(self.doPluginCB) - - def doPluginCB(self): - for p in plugins.getPlugins(PluginDescriptor.WHERE_SERVICESCAN): - p() def doServiceScan(self): self["scan"] = CScan(self["scan_progress"], self["scan_state"], self["servicelist"], self["pass"], self.scanList, self["network"], self["transponder"], self["FrontendInfo"], self.session.summary) diff --git a/lib/python/enigma_python.i b/lib/python/enigma_python.i index 6a25592..158c986 100755 --- a/lib/python/enigma_python.i +++ b/lib/python/enigma_python.i @@ -100,6 +100,7 @@ is usually caused by not marking PSignals as immutable. #include #include #include +#include %} %feature("ref") iObject "$this->AddRef(); /* eDebug(\"AddRef (%s:%d)!\", __FILE__, __LINE__); */ " @@ -163,6 +164,7 @@ typedef long time_t; %immutable eHdmiCEC::messageReceivedKey; %immutable ePythonMessagePump::recv_msg; %immutable eDVBLocalTimeHandler::m_timeUpdated; +%immutable eFCCServiceManager::m_fcc_event; %include %include %include @@ -221,6 +223,7 @@ typedef long time_t; %include %include %include +%include /************** eptr **************/ /************** signals **************/ @@ -344,6 +347,15 @@ int getLinkedSlotID(int fe) } %} +void setFCCEnable(int); +%{ +void setFCCEnable(int enable) +{ + eFCCServiceManager *fcc_mng = eFCCServiceManager::getInstance(); + if (fcc_mng) setFCCEnable(enable); +} +%} + /************** temp *****************/ /* need a better place for this, i agree. */ diff --git a/lib/service/Makefile.am b/lib/service/Makefile.am index 9f956b6..2df1dd9 100644 --- a/lib/service/Makefile.am +++ b/lib/service/Makefile.am @@ -15,6 +15,7 @@ libenigma_service_a_SOURCES = \ service.cpp \ servicedvb.cpp \ servicedvbrecord.cpp \ + servicedvbfcc.cpp \ servicefs.cpp \ servicemp3.cpp \ servicem2ts.cpp @@ -27,6 +28,7 @@ serviceinclude_HEADERS = \ service.h \ servicedvb.h \ servicedvbrecord.h \ + servicedvbfcc.h \ servicefs.h \ servicemp3.h \ servicem2ts.h diff --git a/lib/service/iservice.h b/lib/service/iservice.h index 7099d7d..f0be087 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -841,6 +841,8 @@ public: evStopped, evHBBTVInfo, + evFccFailed, + evUser = 0x100 }; }; @@ -896,7 +898,8 @@ public: evNewProgramInfo, evRecordFailed, evRecordWriteError, - evNewEventInfo + evNewEventInfo, + evTuneStart, }; enum { NoError=0, diff --git a/lib/service/listboxservice.cpp b/lib/service/listboxservice.cpp index cd65f35..7944503 100644 --- a/lib/service/listboxservice.cpp +++ b/lib/service/listboxservice.cpp @@ -130,6 +130,17 @@ void eListboxServiceContent::getNext(eServiceReference &ref) ref = eServiceReference(); } +PyObject *eListboxServiceContent::getList() +{ + ePyObject result = PyList_New(m_list.size()); + int pos=0; + for (list::iterator it(m_list.begin()); it != m_list.end(); ++it) + { + PyList_SET_ITEM(result, pos++, NEW_eServiceReference(*it)); + } + return result; +} + int eListboxServiceContent::getNextBeginningWithChar(char c) { // printf("Char: %c\n", c); diff --git a/lib/service/listboxservice.h b/lib/service/listboxservice.h index ab5bd20..ee28c49 100644 --- a/lib/service/listboxservice.h +++ b/lib/service/listboxservice.h @@ -21,6 +21,7 @@ public: void getCurrent(eServiceReference &ref); void getPrev(eServiceReference &ref); void getNext(eServiceReference &ref); + PyObject *getList(); int getNextBeginningWithChar(char c); int getPrevMarkerPos(); diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 3f580fe..c3abb81 100755 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -19,6 +19,8 @@ #include // access to python config #include +#include + /* for subtitles */ #include @@ -28,6 +30,8 @@ #include #include +#include + #ifndef BYTE_ORDER #error no byte order defined! #endif @@ -814,11 +818,15 @@ RESULT eDVBServiceList::setListName(const std::string &name) RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr &ptr) { ePtr service; + int r = lookupService(service, ref); if (r) service = 0; // check resources... - ptr = new eDVBServicePlay(ref, service); + if (eFCCServiceManager::checkAvailable(ref)) + ptr = new eDVBServiceFCCPlay(ref, service); + else + ptr = new eDVBServicePlay(ref, service); return 0; } @@ -933,17 +941,19 @@ RESULT eServiceFactoryDVB::lookupService(ePtr &service, const eServ return 0; } -eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): +eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service, bool connect_event): m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0) { m_is_primary = 1; m_is_stream = m_reference.path.substr(0, 7) == "http://"; m_is_pvr = (!m_reference.path.empty() && !m_is_stream); - + m_timeshift_enabled = m_timeshift_active = 0, m_timeshift_changed = 0; m_skipmode = m_fastforward = m_slowmotion = 0; - - CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent); + + if (connect_event) + CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent); + CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift); CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent); diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h index 6971545..7ded16a 100644 --- a/lib/service/servicedvb.h +++ b/lib/service/servicedvb.h @@ -202,7 +202,7 @@ protected: int m_current_audio_pid; int m_current_video_pid_type; - eDVBServicePlay(const eServiceReference &ref, eDVBService *service); + eDVBServicePlay(const eServiceReference &ref, eDVBService *service, bool connect_event=true); /* events */ void gotNewEvent(); @@ -230,7 +230,7 @@ protected: std::set m_pids_active; void updateTimeshiftPids(); - void switchToLive(); + virtual void switchToLive(); void resetTimeshift(int start); void switchToTimeshift(); diff --git a/lib/service/servicedvbfcc.cpp b/lib/service/servicedvbfcc.cpp new file mode 100644 index 0000000..610ccc4 --- /dev/null +++ b/lib/service/servicedvbfcc.cpp @@ -0,0 +1,446 @@ +#include +#include +#include +#include + +eDVBServiceFCCPlay::eDVBServiceFCCPlay(const eServiceReference &ref, eDVBService *service) + :eDVBServicePlay(ref, service, false), m_fcc_flag(0), m_fcc_mode(fcc_mode_preparing), m_fcc_mustplay(false), + m_pmtVersion(-1) +{ + CONNECT(m_service_handler.serviceEvent, eDVBServiceFCCPlay::serviceEvent); +} + +eDVBServiceFCCPlay::~eDVBServiceFCCPlay() +{ +} + +void eDVBServiceFCCPlay::serviceEvent(int event) +{ + if (!m_is_primary) // PIP mode + { + eDVBServicePlay::serviceEvent(event); + return; + } + + m_tune_state = event; + + switch (event) + { + case eDVBServicePMTHandler::eventTuned: + { + eDVBServicePlay::serviceEvent(event); + pushbackFCCEvents(evTunedIn); + break; + } + case eDVBServicePMTHandler::eventNoResources: + case eDVBServicePMTHandler::eventNoPAT: + case eDVBServicePMTHandler::eventNoPATEntry: + case eDVBServicePMTHandler::eventNoPMT: + case eDVBServicePMTHandler::eventTuneFailed: + case eDVBServicePMTHandler::eventMisconfiguration: + { + eDVBServicePlay::serviceEvent(event); + pushbackFCCEvents(evTuneFailed); + break; + } + case eDVBServicePMTHandler::eventNewProgramInfo: + { + eDebug("eventNewProgramInfo %d %d", m_timeshift_enabled, m_timeshift_active); + if (m_timeshift_enabled) + updateTimeshiftPids(); + + if (!m_timeshift_active) + processNewProgramInfo(); + + if (!m_timeshift_active) + { + m_event((iPlayableService*)this, evUpdatedInfo); + pushbackFCCEvents(evUpdatedInfo); + } + break; + } + case eDVBServicePMTHandler::eventPreStart: + case eDVBServicePMTHandler::eventEOF: + case eDVBServicePMTHandler::eventSOF: + { + eDVBServicePlay::serviceEvent(event); + break; + } + case eDVBServicePMTHandler::eventHBBTVInfo: + { + eDVBServicePlay::serviceEvent(event); + pushbackFCCEvents(evHBBTVInfo); + break; + } + } +} + +RESULT eDVBServiceFCCPlay::start() +{ + if (!m_is_primary) // PIP mode + { + eDVBServicePlay::start(); + return 0; + } + + if (m_fcc_flag & fcc_start) // already started + { + changeFCCMode(); + } + else + { + m_fcc_flag |= fcc_start; + pushbackFCCEvents(evStart); + + /* disable CA Interfaces on fcc_mode_preparing */ + m_service_handler.setCaDisable(true); + eDVBServicePlay::start(); + } + return 0; +} + +void eDVBServiceFCCPlay::pushbackFCCEvents(int event) +{ + if (event == evTuneFailed) + m_fcc_flag |= fcc_tune_failed; + m_fcc_events.push_back(event); +} + +void eDVBServiceFCCPlay::popFCCEvents() +{ + m_fcc_events.unique(); // remove duplicate evUpdatedInfo + for (std::list::iterator it = m_fcc_events.begin(); it != m_fcc_events.end(); ++it) + { + if (*it == evUpdatedInfo) + { + updateFCCDecoder(); + break; + } + } + + /* add CaHandler */ + m_service_handler.addCaHandler(); + + /* send events */ + for (std::list::iterator it = m_fcc_events.begin(); it != m_fcc_events.end(); ++it) + { + int event = *it; +// eDebug("[eDVBServiceFCCPlay::popFCCEvents][%s] send event : %s", m_reference.toString().c_str(), eventDesc[event]); + m_event((iPlayableService*)this, event); + } +} + +void eDVBServiceFCCPlay::changeFCCMode() +{ + if (m_fcc_mode == fcc_mode_decoding) + { + eDebug("[eDVBServiceFCCPlay::changeFCCMode][%s] disable FCC decoding.", m_reference.toString().c_str()); + m_fcc_mode = fcc_mode_preparing; + + /* remove CaHandler */ + m_service_handler.removeCaHandler(); + + if (m_fcc_flag & fcc_tune_failed) + m_event((iPlayableService*)this, evTuneFailed); + + else if (m_fcc_flag & fcc_failed) + m_event((iPlayableService*)this, evFccFailed); + + FCCDecoderStop(); + } + else + { + eDebug("[eDVBServiceFCCPlay::changeFCCMode][%s] enable FCC decoding.", m_reference.toString().c_str()); + m_fcc_mode = fcc_mode_decoding; + popFCCEvents(); + } +} + +void eDVBServiceFCCPlay::processNewProgramInfo(bool toLive) +{ + updateFCCDecoder(toLive); + + if (m_fcc_flag & fcc_failed) + { + m_event((iPlayableService*)this, evFccFailed); + } +} + +void eDVBServiceFCCPlay::updateFCCDecoder(bool sendSeekableStateChanged) +{ + eDebug("[eDVBServiceFCCPlay::updateFCCDecoder][%s]", m_reference.toString().c_str()); + int vpid = -1, vpidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1; + bool isProgramInfoCached = false; + bool pmtVersionChanged = false; + + eDVBServicePMTHandler &h = m_service_handler; + + eDVBServicePMTHandler::program program; + if (h.getProgramInfo(program)) + eDebug("getting program info failed."); + else + { + eDebugNoNewLine("have %zd video stream(s)", program.videoStreams.size()); + if (!program.videoStreams.empty()) + { + eDebugNoNewLine(" ("); + for (std::vector::const_iterator + i(program.videoStreams.begin()); + i != program.videoStreams.end(); ++i) + { + if (vpid == -1) + { + vpid = i->pid; + vpidtype = i->type; + } + if (i != program.videoStreams.begin()) + eDebugNoNewLine(", "); + eDebugNoNewLine("%04x", i->pid); + } + eDebugNoNewLine(")"); + } + eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size()); + if (!program.audioStreams.empty()) + { + eDebugNoNewLine(" ("); + for (std::vector::const_iterator + i(program.audioStreams.begin()); + i != program.audioStreams.end(); ++i) + { + if (i != program.audioStreams.begin()) + eDebugNoNewLine(", "); + eDebugNoNewLine("%04x", i->pid); + } + eDebugNoNewLine(")"); + } + eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid); + pcrpid = program.pcrPid; + eDebugNoNewLine(", and the text pid is %04x", program.textPid); + tpid = program.textPid; + eDebug(" %s", program.isCached ? "(Cached)":""); + isProgramInfoCached = program.isCached; + if (m_pmtVersion != program.pmtVersion) + { + if (m_pmtVersion != -1) + pmtVersionChanged = true; + m_pmtVersion = program.pmtVersion; + //eDebug("[eDVBServiceFCCPlay::updateFCCDecoder] pmt version : %d", m_pmtVersion); + } + } + + if (!m_decoder) + { + h.getDecodeDemux(m_decode_demux); + if (m_decode_demux) + { + m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary); + if (m_decoder) + m_decoder->connectVideoEvent(slot(*this, &eDVBServiceFCCPlay::video_event), m_video_event_connection); + } + m_fcc_mustplay = true; + } + + if (m_decoder) + { + if (!((m_fcc_flag & fcc_ready)||(m_fcc_flag & fcc_novideo))) + { + if (vpid == -1) + { + if (!isProgramInfoCached) + m_fcc_flag |= fcc_novideo; + } + else if ((vpidtype == -1) || (pcrpid== -1)) + { + if (!isProgramInfoCached) + m_fcc_flag |= fcc_failed; + } + else if (!m_decoder->prepareFCC(m_decode_demux->getSource(), vpid, vpidtype, pcrpid)) + m_fcc_flag |= fcc_ready; + else + m_fcc_flag |= fcc_failed; + } + else if (pmtVersionChanged) + { + m_decoder->fccUpdatePids(m_decode_demux->getSource(), vpid, vpidtype, pcrpid); + m_fcc_flag &=~fcc_decoding; + } + } + + if (m_fcc_mode != fcc_mode_decoding) + return; + + /* fcc_mode_decoding */ + if (!(m_fcc_flag & fcc_ready) && !(m_fcc_flag & fcc_novideo)) + { + eDebug("[eDVBServiceFCCPlay::updateFCCDecoder] fcc is not ready."); + return; + } + + if (m_decode_demux) + { + if (m_is_primary) + { + m_teletext_parser = new eDVBTeletextParser(m_decode_demux); + m_teletext_parser->connectNewPage(slot(*this, &eDVBServiceFCCPlay::newSubtitlePage), m_new_subtitle_page_connection); + m_subtitle_parser = new eDVBSubtitleParser(m_decode_demux); + m_subtitle_parser->connectNewPage(slot(*this, &eDVBServiceFCCPlay::newDVBSubtitlePage), m_new_dvb_subtitle_page_connection); + if (m_timeshift_changed) + { + ePyObject subs = getCachedSubtitle(); + if (subs != Py_None) + { + int type = PyInt_AsLong(PyTuple_GET_ITEM(subs, 0)), + pid = PyInt_AsLong(PyTuple_GET_ITEM(subs, 1)), + comp_page = PyInt_AsLong(PyTuple_GET_ITEM(subs, 2)), // ttx page + anc_page = PyInt_AsLong(PyTuple_GET_ITEM(subs, 3)); // ttx magazine + if (type == 0) // dvb + m_subtitle_parser->start(pid, comp_page, anc_page); + else if (type == 1) // ttx + m_teletext_parser->setPageAndMagazine(comp_page, anc_page); + } + Py_DECREF(subs); + } + } + } + + m_timeshift_changed = 0; + + if (m_decoder) + { + bool wasSeekable = m_decoder->getVideoProgressive() != -1; + + if (m_dvb_service) + { + achannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL); + ac3_delay = m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY); + pcm_delay = m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY); + } + else // subservice + { + eServiceReferenceDVB ref; + m_service_handler.getServiceReference(ref); + eServiceReferenceDVB parent = ref.getParentServiceReference(); + if (!parent) + parent = ref; + if (parent) + { + ePtr res_mgr; + if (!eDVBResourceManager::getInstance(res_mgr)) + { + ePtr db; + if (!res_mgr->getChannelList(db)) + { + ePtr origService; + if (!db->getService(parent, origService)) + { + ac3_delay = origService->getCacheEntry(eDVBService::cAC3DELAY); + pcm_delay = origService->getCacheEntry(eDVBService::cPCMDELAY); + } + } + } + } + } + + setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay); + setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay); + + m_decoder->setVideoPID(vpid, vpidtype); + selectAudioStream(); + + if (!(m_is_pvr || m_is_stream || m_timeshift_active)) + m_decoder->setSyncPCR(pcrpid); + else + m_decoder->setSyncPCR(-1); + + if (m_is_primary) + { + m_decoder->setTextPID(tpid); + m_teletext_parser->start(program.textPid); + } + + if (vpid > 0 && vpid < 0x2000) + ; + else + { + std::string radio_pic; + if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic)) + m_decoder->setRadioPic(radio_pic); + } + + /* fcc stop and decoder start */ + if (!(m_fcc_flag & fcc_novideo)) + { + if (m_fcc_flag & fcc_decoding) + ; + else if(!m_decoder->fccDecoderStart()) + m_fcc_flag |= fcc_decoding; + } + + if (m_fcc_mustplay) + { + m_fcc_mustplay = false; + m_decoder->play(); + } + else + { + m_decoder->set(); + } + + m_decoder->setAudioChannel(achannel); + + /* don't worry about non-existing services, nor pvr services */ + if (m_dvb_service) + { + /* (audio pid will be set in selectAudioTrack */ + m_dvb_service->setCacheEntry(eDVBService::cVPID, vpid); + m_dvb_service->setCacheEntry(eDVBService::cVTYPE, vpidtype == eDVBVideo::MPEG2 ? -1 : vpidtype); + m_dvb_service->setCacheEntry(eDVBService::cPCRPID, pcrpid); + m_dvb_service->setCacheEntry(eDVBService::cTPID, tpid); + } + if (!sendSeekableStateChanged && (m_decoder->getVideoProgressive() != -1) != wasSeekable) + sendSeekableStateChanged = true; + } + m_have_video_pid = (vpid > 0 && vpid < 0x2000); + + if (sendSeekableStateChanged) + m_event((iPlayableService*)this, evSeekableStatusChanged); +} + +void eDVBServiceFCCPlay::FCCDecoderStop() +{ + eDebug("[eDVBServiceFCCPlay::FCCDecoderStop][%s]", m_reference.toString().c_str()); + + if ((m_fcc_flag & fcc_ready) && m_decoder) + { + m_teletext_parser = 0; + m_new_subtitle_page_connection = 0; + m_subtitle_parser = 0; + m_new_dvb_subtitle_page_connection = 0; + + m_decoder->fccDecoderStop(); + m_fcc_flag &=~fcc_decoding; + } +} + +void eDVBServiceFCCPlay::switchToLive() +{ + if (!m_timeshift_active) + return; + + eDebug("eDVBServiceFCCPlay::SwitchToLive"); + + resetTimeshift(0); + + m_is_paused = m_skipmode = m_fastforward = m_slowmotion = 0; /* not supported in live mode */ + + /* free the timeshift service handler, we need the resources */ + m_service_handler_timeshift.free(); + + //updateDecoder(true); + m_fcc_flag &=~fcc_ready; + m_fcc_flag &=~fcc_decoding; + processNewProgramInfo(true); +} + +DEFINE_REF(eDVBServiceFCCPlay) + diff --git a/lib/service/servicedvbfcc.h b/lib/service/servicedvbfcc.h new file mode 100644 index 0000000..54a0dca --- /dev/null +++ b/lib/service/servicedvbfcc.h @@ -0,0 +1,47 @@ +#ifndef __servicedvbfcc_h +#define __servicedvbfcc_h + +#include +#include + +class eDVBServiceFCCPlay: public eDVBServicePlay +{ + DECLARE_REF(eDVBServiceFCCPlay); +public: + eDVBServiceFCCPlay(const eServiceReference &ref, eDVBService *service); + virtual ~eDVBServiceFCCPlay(); + void serviceEvent(int event); + RESULT start(); +protected: + void pushbackFCCEvents(int event); + void popFCCEvents(); + void changeFCCMode(); + void processNewProgramInfo(bool toLive=false); + void updateFCCDecoder(bool sendSeekableStateChanged=false); + void FCCDecoderStop(); + void switchToLive(); + + bool m_fcc_enable; + + enum { + fcc_start = 1, + fcc_tune_failed = 2, + fcc_failed = 4, + fcc_ready = 8, + fcc_decoding = 16, + fcc_novideo = 32, + }; + int m_fcc_flag; + + enum { + fcc_mode_preparing, + fcc_mode_decoding + }; + int m_fcc_mode; + + bool m_fcc_mustplay; + std::list m_fcc_events; + int m_pmtVersion; +}; + +#endif /* __servicedvbfcc_h */ \ No newline at end of file diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index dc1b22e..4af0320 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -255,6 +255,10 @@ int eDVBServiceRecord::doPrepare() source = ePtr(f); } } + else + { + m_event((iRecordableService*)this, evTuneStart); + } return m_service_handler.tuneExt(m_ref, 0, source, m_ref.path.c_str(), 0, m_simulate, 0, isstreamclient); } return 0;