Support fast channel change.
authorhschang <chang@dev3>
Mon, 16 May 2016 07:51:49 +0000 (16:51 +0900)
committerhschang <chang@dev3>
Mon, 16 May 2016 07:51:49 +0000 (16:51 +0900)
35 files changed:
Navigation.py
configure.ac
lib/dvb/Makefile.am
lib/dvb/decoder.cpp
lib/dvb/decoder.h
lib/dvb/dvb.cpp
lib/dvb/fcc.cpp [new file with mode: 0644]
lib/dvb/fcc.h [new file with mode: 0644]
lib/dvb/fccdecoder.cpp [new file with mode: 0644]
lib/dvb/fccdecoder.h [new file with mode: 0644]
lib/dvb/idvb.h
lib/dvb/pmt.cpp
lib/dvb/pmt.h
lib/nav/core.cpp
lib/nav/core.h
lib/python/Components/ServiceList.py
lib/python/Plugins/Plugin.py
lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile.am [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/FastChannelChange/__init__.py [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile.am [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/FastChannelChange/meta/plugin_fastchannelchange.xml [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/Makefile.am
lib/python/Screens/Satconfig.py
lib/python/Screens/ServiceScan.py
lib/python/enigma_python.i
lib/service/Makefile.am
lib/service/iservice.h
lib/service/listboxservice.cpp
lib/service/listboxservice.h
lib/service/servicedvb.cpp
lib/service/servicedvb.h
lib/service/servicedvbfcc.cpp [new file with mode: 0644]
lib/service/servicedvbfcc.h [new file with mode: 0644]
lib/service/servicedvbrecord.cpp

index 1043f80..451b6da 100644 (file)
@@ -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()
+
index 134dc50..7c52d00 100644 (file)
@@ -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
index b185237..f4216d9 100644 (file)
@@ -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
index 8a4a82d..e3d97af 100644 (file)
@@ -34,6 +34,8 @@
 #include <sys/stat.h>
 #include <errno.h>
 
+#include <lib/dvb/fccdecoder.h>
+
        /* 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;
+}
+
index f0f8b2f..0636516 100644 (file)
@@ -40,6 +40,7 @@ class eDVBVideo: public iObject, public Object
 private:
        ePtr<eDVBDemux> 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<eDVBPCR> m_pcr;
        ePtr<eDVBTText> 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
index 0a371f0..72d4687 100755 (executable)
@@ -7,6 +7,7 @@
 #include <lib/dvb/specs.h>
 
 #include <lib/dvb/fbc.h>
+#include <lib/dvb/fcc.h>
 
 #include <errno.h>
 #include <sys/types.h>
@@ -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<int*> fcc_decremented_fe_usecounts;
+       std::map<eDVBChannelID, int> fcc_chids;
+       int apply_to_ignore = 0;
+       if (!eFCCServiceManager::getFCCChannelID(fcc_chids))
+       {
+               for (std::map<eDVBChannelID, int>::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<active_channel>::iterator ii(active_channels.begin()); ii != active_channels.end(); ++ii)
+                       {
+                               eSmartPtrList<eDVBRegisteredFrontend> &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<iDVBFrontend> fe;
+                                               if (!ii->m_channel->getFrontend(fe))
+                                               {
+                                                       for (eSmartPtrList<eDVBRegisteredFrontend>::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<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
        {
                eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
@@ -999,7 +1050,10 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                        // another on eUsePtr<iDVBChannel> 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<iDVBFrontend> 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<int*>::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 (file)
index 0000000..614340b
--- /dev/null
@@ -0,0 +1,335 @@
+#include <lib/dvb/fcc.h>
+#include <lib/nav/core.h>
+#include <lib/base/nconfig.h>
+#include <lib/base/eerror.h>
+#include <lib/python/python.h>
+
+//#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<eDVBChannelID, int> &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<iPlayableService> &service)
+{
+       std::map< ePtr<iPlayableService>, 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<eConnection> 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<ePtr<iPlayableService>, 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<ePtr<iPlayableService>, 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<ePtr<iPlayableService>, 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<ePtr<iPlayableService>, 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<iPlayableService> &service)
+{
+       ePtr<iPlayableService> new_service = 0;
+
+       printFCCServices();
+
+       int get_fcc_decoding = 0;
+
+       /* stop previous decoding service */
+       std::map< ePtr<iPlayableService>, 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<eConnection> 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<iPlayableService> service)
+{
+       ePtr<iFrontendInformation> ptr;
+       service->frontendInfo(ptr);
+       return ptr->getFrontendInfo(iDVBFrontend_ENUMS::locked);
+}
+
+PyObject *eFCCServiceManager::getFCCServiceList()
+{
+       ePyObject dest = PyDict_New();
+       if (dest)
+       {
+               std::map< ePtr<iPlayableService>, 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<iPlayableService>, 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<eDVBChannelID, int> &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 (file)
index 0000000..aacd9d8
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef __dvb_fcc_h
+#define __dvb_fcc_h
+
+#include <lib/dvb/idvb.h>
+#include <lib/base/object.h>
+#include <lib/service/iservice.h>
+#include <connection.h>
+
+class eNavigation;
+
+class FCCServiceChannels
+{
+private:
+       std::map<eDVBChannelID, int> m_fcc_chids;
+
+public:
+       void addFCCService(const eServiceReference &service);
+       void removeFCCService(const eServiceReference &service);
+       int getFCCChannelID(std::map<eDVBChannelID, int> &fcc_chids);
+};
+
+typedef struct _tagFccElem
+{
+       eServiceReference m_service_reference;
+       ePtr<eConnection> 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<ePtr<iPlayableService>, FCCServiceElem, std::less<iPlayableService*> > m_FCCServices;
+       FCCServiceChannels m_fccServiceChannels;
+
+       bool m_fcc_enable;
+
+       void FCCEvent(iPlayableService* service, int event);
+public:
+       PSignal1<void, int> 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<iPlayableService> &SWIG_OUTPUT);
+       RESULT stopFCCService(const eServiceReference &sref);
+       RESULT stopFCCService();
+       RESULT cleanupFCCService();
+       RESULT tryFCCService(const eServiceReference &service, ePtr<iPlayableService> &ptr);
+       int isLocked(ePtr<iPlayableService> service);
+       PyObject *getFCCServiceList();
+       void printFCCServices();
+       static int getFCCChannelID(std::map<eDVBChannelID, int> &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 (file)
index 0000000..c408c1a
--- /dev/null
@@ -0,0 +1,75 @@
+#include <lib/dvb/fccdecoder.h>
+#include <lib/base/eerror.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+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 (file)
index 0000000..c7b57c5
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __dvb_fcc_decoder_h
+#define __dvb_fcc_decoder_h
+
+#include <vector>
+
+class eFCCDecoder
+{
+       std::vector<int> 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 */
index 2c11ff8..d0d1043 100644 (file)
@@ -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;
 
index b3e8aeb..5132b38 100644 (file)
@@ -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<ProgramMapSection*>::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<eConnection> eDVBCAService::m_chanAddedConn;
index b15c95b..b2fc68f 100644 (file)
@@ -158,6 +158,9 @@ class eDVBServicePMTHandler: public Object
        uint8_t m_decode_demux_num;
        ePtr<eTimer> 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<iTsSource> &, 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;
index ad77766..2e54d87 100644 (file)
@@ -1,6 +1,7 @@
 #include <lib/nav/core.h>
 #include <lib/base/eerror.h>
 #include <lib/python/python.h>
+#include <lib/dvb/fcc.h>
 
 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()
index 9f7be88..a4906c1 100644 (file)
@@ -6,6 +6,7 @@
 #include <connection.h>
 #include <map>
 #include <set>
+#include <lib/dvb/fcc.h>
 
 class eNavigation: public iObject, public Object
 {
@@ -22,6 +23,10 @@ class eNavigation: public iObject, public Object
 
        Signal2<void,ePtr<iRecordableService>,int> m_record_event;
        void recordEvent(iRecordableService* service, int event);
+
+       friend class eFCCServiceManager;
+       ePtr<eFCCServiceManager> m_fccmgr;
+
 public:
        
        RESULT playService(const eServiceReference &service);
index 3930c07..6972d91 100644 (file)
@@ -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()
 
index 2137d0e..d19ec26 100755 (executable)
@@ -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 (file)
index 0000000..ae91ed5
--- /dev/null
@@ -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 (file)
index 0000000..139597f
--- /dev/null
@@ -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 (file)
index 0000000..4e3b9e9
--- /dev/null
@@ -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 (file)
index 0000000..cf4571a
--- /dev/null
@@ -0,0 +1,16 @@
+<default>
+         <prerequisites>
+                    <tag type="System" />
+         </prerequisites>
+          <info>
+                    <author>hschang</author>
+                    <name>FastChannelChange</name>
+                    <packagename>enigma2-plugin-systemplugins-fastchannelchange</packagename>
+                    <shortdescription>Fast Channel Change of your VU+</shortdescription>
+                    <description>Fast Channel Change of your VU+</description>
+          </info>
+
+         <files type="package"> <!-- without version, without .ipk -->
+               <file type="package" name="enigma2-plugin-systemplugins-fastchannelchange" />
+       </files>
+</default>
diff --git a/lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py b/lib/python/Plugins/SystemPlugins/FastChannelChange/plugin.py
new file mode 100644 (file)
index 0000000..7c3b2cc
--- /dev/null
@@ -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 =  """
+               <screen position="center,center" size="590,320" >
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="90,15" size="140,40" alphatest="on" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="360,15" size="140,40" alphatest="on" />
+                       <widget source="key_red" render="Label" position="90,15" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
+                       <widget source="key_green" render="Label" position="360,15" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="config" zPosition="2" position="15,80" size="560,140" scrollbarMode="showOnDemand" transparent="1" />
+                       <widget source="description" render="Label" position="30,240" size="530,60" font="Regular;24" halign="center" valign="center" />
+               </screen>
+               """
+
+       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
+
index 3a16f5f..950b50e 100755 (executable)
@@ -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
index 48b580c..3b75e46 100644 (file)
@@ -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:
index efa218b..d333230 100644 (file)
@@ -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)
index 6a25592..158c986 100755 (executable)
@@ -100,6 +100,7 @@ is usually caused by not marking PSignals as immutable.
 #include <lib/dvb_ci/dvbci_ui.h>
 #include <lib/python/python.h>
 #include <lib/gdi/picload.h>
+#include <lib/dvb/fcc.h>
 %}
 
 %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 <lib/base/message.h>
 %include <lib/base/etpm.h>
 %include <lib/base/nconfig.h>
@@ -221,6 +223,7 @@ typedef long time_t;
 %include <lib/dvb/db.h>
 %include <lib/python/python.h>
 %include <lib/gdi/picload.h>
+%include <lib/dvb/fcc.h>
 /**************  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. */
index 9f956b6..2df1dd9 100644 (file)
@@ -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
index 7099d7d..f0be087 100644 (file)
@@ -841,6 +841,8 @@ public:
                evStopped,
                evHBBTVInfo,
 
+               evFccFailed,
+
                evUser = 0x100
        };
 };
@@ -896,7 +898,8 @@ public:
                evNewProgramInfo,
                evRecordFailed,
                evRecordWriteError,
-               evNewEventInfo
+               evNewEventInfo,
+               evTuneStart,
        };
        enum {
                NoError=0,
index cd65f35..7944503 100644 (file)
@@ -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);
index ab5bd20..ee28c49 100644 (file)
@@ -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();
index 3f580fe..c3abb81 100755 (executable)
@@ -19,6 +19,8 @@
 #include <lib/base/nconfig.h> // access to python config
 #include <lib/base/httpstream.h>
 
+#include <lib/service/servicedvbfcc.h>
+
                /* for subtitles */
 #include <lib/gui/esubtitle.h>
 
@@ -28,6 +30,8 @@
 #include <byteswap.h>
 #include <netinet/in.h>
 
+#include <lib/dvb/fcc.h>
+
 #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<iPlayableService> &ptr)
 {
        ePtr<eDVBService> 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<eDVBService> &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);
 
index 6971545..7ded16a 100644 (file)
@@ -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<int> 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 (file)
index 0000000..610ccc4
--- /dev/null
@@ -0,0 +1,446 @@
+#include <lib/service/servicedvbfcc.h>
+#include <lib/components/file_eraser.h>
+#include <lib/dvb/decoder.h>
+#include <lib/base/nconfig.h>
+
+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<int>::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<int>::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<eDVBServicePMTHandler::videoStream>::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<eDVBServicePMTHandler::audioStream>::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<eDVBResourceManager> res_mgr;
+                               if (!eDVBResourceManager::getInstance(res_mgr))
+                               {
+                                       ePtr<iDVBChannelList> db;
+                                       if (!res_mgr->getChannelList(db))
+                                       {
+                                               ePtr<eDVBService> 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 (file)
index 0000000..54a0dca
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __servicedvbfcc_h
+#define __servicedvbfcc_h
+
+#include <lib/service/servicedvb.h>
+#include <list>
+
+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<int> m_fcc_events;
+       int m_pmtVersion;
+};
+
+#endif /* __servicedvbfcc_h */
\ No newline at end of file
index dc1b22e..4af0320 100644 (file)
@@ -255,6 +255,10 @@ int eDVBServiceRecord::doPrepare()
                                source = ePtr<iTsSource>(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;