Support scrambled playback.
authorhschang <chang@dev3>
Wed, 14 Jun 2017 09:37:56 +0000 (18:37 +0900)
committerhschang <chang@dev3>
Wed, 14 Jun 2017 09:37:56 +0000 (18:37 +0900)
33 files changed:
RecordTimer.py
configure.ac
data/setup.xml
lib/base/httpstream.cpp
lib/base/httpstream.h
lib/base/itssource.h
lib/base/rawfile.cpp
lib/base/rawfile.h
lib/dvb/dvb.cpp
lib/dvb/dvb.h
lib/dvb/metaparser.cpp
lib/dvb/metaparser.h
lib/dvb/pmt.cpp
lib/dvb/pmt.h
lib/dvb/pvrparse.cpp
lib/dvb_ci/dvbci.cpp
lib/dvb_ci/dvbci.h
lib/python/Components/RecordingConfig.py
lib/python/Components/SystemInfo.py
lib/python/Plugins/SystemPlugins/Makefile.am
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile.am [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/PvrDescrambleConvert.py [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/__init__.py [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile.am [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/plugin_pvrdescrambleconvert.xml [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/plugin.py [new file with mode: 0644]
lib/service/iservice.h
lib/service/servicedvb.cpp
lib/service/servicedvbrecord.cpp
lib/service/servicedvbrecord.h
lib/service/servicehdmi.cpp
lib/service/servicehdmi.h
lib/service/servicem2ts.cpp

index d3ccd75..32fb923 100755 (executable)
@@ -90,7 +90,7 @@ class RecordTimerEntry(timer.TimerEntry, object):
                        Notifications.AddNotification(Screens.Standby.TryQuitMainloop, 1, onSessionOpenCallback=RecordTimerEntry.stopTryQuitMainloop, default_yes = default_yes)
 #################################################################
 
-       def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None):
+       def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None, descramble = True, record_ecm = False, filename = None):
                timer.TimerEntry.__init__(self, int(begin), int(end))
 
                if checkOldTimers == True:
@@ -122,14 +122,24 @@ class RecordTimerEntry(timer.TimerEntry, object):
                self.autoincreasetime = 3600 * 24 # 1 day
                self.tags = tags or []
 
+               self.descramble = descramble
+               self.record_ecm = record_ecm
+
                self.log_entries = []
                self.resetState()
-       
+
+               self.Filename = filename
+               self.pvrConvert = False
+
        def log(self, code, msg):
                self.log_entries.append((int(time()), code, msg))
                print "[TIMER]", msg
 
        def calculateFilename(self):
+               if self.Filename:
+                       self.log(0, "Filename calculated as: '%s'" % self.Filename)
+                       return
+
                service_name = self.service_ref.getServiceName()
                begin_date = strftime("%Y%m%d %H%M", localtime(self.begin))
                begin_shortdate = strftime("%Y%m%d", localtime(self.begin))
@@ -196,7 +206,7 @@ class RecordTimerEntry(timer.TimerEntry, object):
                                if event_id is None:
                                        event_id = -1
 
-                       prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id, self.name.replace("\n", ""), self.description.replace("\n", ""), ' '.join(self.tags))
+                       prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id, self.name.replace("\n", ""), self.description.replace("\n", ""), ' '.join(self.tags), bool(self.descramble), bool(self.record_ecm))
                        if prep_res:
                                if prep_res == -255:
                                        self.log(4, "failed to write meta information")
@@ -376,6 +386,9 @@ class RecordTimerEntry(timer.TimerEntry, object):
                        # that in our state, with also keeping the possibility to re-try.
                        # TODO: this has to be done.
                elif event == iRecordableService.evStart:
+                       if self.pvrConvert:
+                               return
+
                        text = _("A record has been started:\n%s") % self.name
                        if self.dirnameHadToFallback:
                                text = '\n'.join((text, _("Please note that the previously selected media could not be accessed and therefore the default directory is being used instead.")))
index 00a4bd2..36488af 100644 (file)
@@ -310,6 +310,8 @@ lib/python/Plugins/SystemPlugins/FastChannelChange/Makefile
 lib/python/Plugins/SystemPlugins/FastChannelChange/meta/Makefile
 lib/python/Plugins/SystemPlugins/Ultimo4kMiscControl/Makefile
 lib/python/Plugins/SystemPlugins/Ultimo4kMiscControl/meta/Makefile
+lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile
+lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile
 lib/python/Tools/Makefile
 lib/service/Makefile
 lib/components/Makefile
index 5ce6b62..9613440 100755 (executable)
@@ -61,6 +61,8 @@
                        <item level="2" text="Preferred tuner">config.usage.frontend_priority</item>
                        <item level="2" text="Limited character set for recording filenames">config.recording.ascii_filenames</item>
                        <item level="2" text="Composition of the recording filenames">config.recording.filename_composition</item>
+                       <item level="2" text="Always include ECM in recordings" requires="ScrambledPlayback" description="Always include ECM messages in recordings. This overrides the individual timer settings globally. It allows recordings to be always decrypted afterwards (sometimes called offline decoding), if supported by your receiver. Default: off.">config.recording.always_ecm</item>
+                       <item level="2" text="Never decrypt while recording" requires="ScrambledPlayback" description="Never decrypt the content while recording. This overrides the individual timer settings globally. If enabled, recordings are stored in crypted presentation and must be decrypted afterwards (sometimes called offline decoding). Default: off.">config.recording.never_decrypt</item>
                </setup>
                <setup key="subtitlesetup" title="Subtitle settings">
                        <item level="2" text="Subtitle font color" description="Configure the color of the subtitles.">config.subtitles.subtitle_fontcolor</item>
index 8f55425..ee4ffe4 100644 (file)
@@ -352,3 +352,9 @@ off_t eHttpStream::length()
 {
        return (off_t)-1;
 }
+
+off_t eHttpStream::offset()
+{
+       return 0;
+}
+
index 228d2b0..84ea5e9 100644 (file)
@@ -32,6 +32,7 @@ class eHttpStream: public iTsSource, public eSocketBase, public Object, public e
        off_t lseek(off_t offset, int whence);
        ssize_t read(off_t offset, void *buf, size_t count);
        off_t length();
+       off_t offset();
        int valid();
        bool isStream() { return true; }
 public:
index adaeb4c..735574c 100644 (file)
@@ -14,6 +14,7 @@ public:
 
        virtual off_t length()=0;
        virtual int valid()=0;
+       virtual off_t offset() = 0;
        virtual bool isStream() { return false; }
 };
 
index 3a09e07..e32b262 100644 (file)
@@ -251,3 +251,8 @@ off_t eRawFile::length()
 {
        return m_totallength;
 }
+
+off_t eRawFile::offset()
+{
+       return m_last_offset;
+}
index 7b736a3..02bf2e3 100644 (file)
@@ -19,6 +19,7 @@ public:
        off_t lseek(off_t offset, int whence);
        ssize_t read(off_t offset, void *buf, size_t count);
        off_t length();
+       off_t offset();
        int valid();
 private:
        int m_fd;     /* for uncached */
index 756c685..bc79306 100644 (file)
@@ -2035,7 +2035,8 @@ RESULT eDVBChannel::playSource(ePtr<iTsSource> &source, const char *streaminfo_f
                return -ENOENT;
        }
 
-       m_tstools.setSource(source, streaminfo_file);
+       m_source = source;
+       m_tstools.setSource(m_source, streaminfo_file);
 
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
@@ -2072,12 +2073,12 @@ RESULT eDVBChannel::playSource(ePtr<iTsSource> &source, const char *streaminfo_f
        m_pvr_thread = new eDVBChannelFilePush();
        m_pvr_thread->enablePVRCommit(1);
        /* If the source specifies a length, it's a file. If not, it's a stream */
-       m_pvr_thread->setStreamMode(source->isStream());
+       m_pvr_thread->setStreamMode(m_source->isStream());
        m_pvr_thread->setScatterGather(this);
 
        m_event(this, evtPreStart);
 
-       m_pvr_thread->start(source, m_pvr_fd_dst);
+       m_pvr_thread->start(m_source, m_pvr_fd_dst);
        CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
 
        m_state = state_ok;
@@ -2099,8 +2100,9 @@ void eDVBChannel::stopSource()
                ::close(m_pvr_fd_dst);
                m_pvr_fd_dst = -1;
        }
-       ePtr<iTsSource> d;
-       m_tstools.setSource(d);
+
+       m_source = NULL;
+       m_tstools.setSource(m_source);
 }
 
 void eDVBChannel::stopFile()
@@ -2141,8 +2143,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
        } else
                now = pos; /* fixup supplied */
 
-       off_t off = 0; /* TODO: fixme */
-       r = m_tstools.fixupPTS(off, now);
+       r = m_tstools.fixupPTS(m_source ? m_source->offset() : 0, now);
        if (r)
        {
                eDebug("fixup PTS failed");
index c0acc4f..ff9ea6f 100644 (file)
@@ -287,6 +287,7 @@ private:
        Signal1<void,iDVBChannel*> m_stateChanged;
        Signal2<void,iDVBChannel*,int> m_event;
        int m_state;
+       ePtr<iTsSource> m_source;
 
                        /* for channel list */
        ePtr<eDVBResourceManager> m_mgr;
index 5eff4d9..19007fc 100644 (file)
@@ -8,6 +8,7 @@ eDVBMetaParser::eDVBMetaParser()
        m_data_ok = 0;
        m_length = 0;
        m_filesize = 0;
+       m_scrambled = 0;
 }
 
 int eDVBMetaParser::parseFile(const std::string &basename)
@@ -94,6 +95,9 @@ int eDVBMetaParser::parseMeta(const std::string &tsname)
                case 7:
                        m_service_data = line;
                        break;
+               case 8:
+                       m_scrambled = atoi(line);
+                       break;
                default:
                        break;
                }
@@ -149,8 +153,8 @@ int eDVBMetaParser::parseRecordings(const std::string &filename)
                        m_time_create = 0;
                        m_length = 0;
                        m_filesize = fileSize(filename);
-                                               
                        m_data_ok = 1;
+                       m_scrambled = 0;
                        fclose(f);
                        updateMeta(filename.c_str());
                        return 0;
@@ -172,7 +176,7 @@ int eDVBMetaParser::updateMeta(const std::string &tsname)
        FILE *f = fopen(filename.c_str(), "w");
        if (!f)
                return -ENOENT;
-       fprintf(f, "%s\n%s\n%s\n%d\n%s\n%d\n%lld\n%s\n", ref.toString().c_str(), m_name.c_str(), m_description.c_str(), m_time_create, m_tags.c_str(), m_length, m_filesize, m_service_data.c_str() );
+       fprintf(f, "%s\n%s\n%s\n%d\n%s\n%d\n%lld\n%s\n%d\n", ref.toString().c_str(), m_name.c_str(), m_description.c_str(), m_time_create, m_tags.c_str(), m_length, m_filesize, m_service_data.c_str(), m_scrambled);
        fclose(f);
        return 0;
 }
index 3368294..a171290 100644 (file)
@@ -15,7 +15,7 @@ public:
        long long fileSize(const std::string &basename);
 
        eServiceReferenceDVB m_ref;
-       int m_data_ok, m_time_create, m_length;
+       int m_data_ok, m_time_create, m_length, m_scrambled;
        std::string m_name, m_description, m_tags, m_service_data;
        long long m_filesize;
 };
index e6c1d2c..d5a7800 100644 (file)
@@ -32,7 +32,8 @@ eDVBServicePMTHandler::eDVBServicePMTHandler()
        m_use_decode_demux = 0;
        m_pmt_pid = -1;
        m_dsmcc_pid = -1;
-       m_isstreamclient = false;
+       m_service_type = livetv;
+       m_descramble = true;
        m_ca_disabled = false;
        m_pmt_ready = false;
        eDVBResourceManager::getInstance(m_resourceManager);
@@ -114,6 +115,19 @@ void eDVBServicePMTHandler::channelEvent(iDVBChannel *channel, int event)
        }
 }
 
+void eDVBServicePMTHandler::registerCAService()
+{
+       int demuxes[2] = {0,0};
+       uint8_t demuxid;
+       m_demux->getCADemuxID(demuxid);
+       demuxes[0]=demuxid;
+       if (m_decode_demux_num != 0xFF)
+               demuxes[1]=m_decode_demux_num;
+       else
+               demuxes[1]=demuxes[0];
+       eDVBCAService::register_service(m_reference, demuxes, m_ca_servicePtr);
+}
+
 void eDVBServicePMTHandler::PMTready(int error)
 {
        if (error)
@@ -123,26 +137,24 @@ void eDVBServicePMTHandler::PMTready(int error)
                m_pmt_ready = true;
                m_have_cached_program = false;
                serviceEvent(eventNewProgramInfo);
+
                mDemuxId = m_decode_demux_num;
-               if (!m_pvr_channel) // don't send campmt to camd.socket for playbacked services
+               if (!m_pvr_channel)
                {
                        eEPGCache::getInstance()->PMTready(this);
-                       if(!m_ca_servicePtr && !m_isstreamclient)
+               }
+
+               if (m_descramble)
+               {
+                       if(!m_ca_servicePtr)
                        {
-                               int demuxes[2] = {0,0};
-                               uint8_t tmp;
-                               m_demux->getCADemuxID(tmp);
-                               demuxes[0]=tmp;
-                               if (m_decode_demux_num != 0xFF)
-                                       demuxes[1]=m_decode_demux_num;
-                               else
-                                       demuxes[1]=demuxes[0];
-                               eDVBCAService::register_service(m_reference, demuxes, m_ca_servicePtr);
-                               if (!m_ca_disabled)
-                                       eDVBCIInterfaces::getInstance()->recheckPMTHandlers();
+                               registerCAService();
                        }
                        if (!m_ca_disabled)
+                       {
+                               eDVBCIInterfaces::getInstance()->recheckPMTHandlers();
                                eDVBCIInterfaces::getInstance()->gotPMT(this);
+                       }
                }
                if (m_ca_servicePtr)
                {
@@ -152,6 +164,9 @@ void eDVBServicePMTHandler::PMTready(int error)
                        else
                                eDebug("eDVBServicePMTHandler cannot call buildCAPMT");
                }
+
+               if (m_service_type == pvrDescramble)
+                       serviceEvent(eventStartPvrDescramble);
        }
 }
 
@@ -1123,19 +1138,20 @@ void eDVBServicePMTHandler::SDTScanEvent(int event)
        }
 }
 
-int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue, bool simulate, eDVBService *service)
+int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue, bool simulate, eDVBService *service, serviceType type, bool descramble)
 {
        ePtr<iTsSource> s;
-       return tuneExt(ref, use_decode_demux, s, NULL, cue, simulate, service);
+       return tuneExt(ref, use_decode_demux, s, NULL, cue, simulate, service, type, descramble);
 }
 
-int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service, bool isstreamclient)
+int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service, serviceType type, bool descramble)
 {
        RESULT res=0;
        m_reference = ref;
        m_use_decode_demux = use_decode_demux;
        m_no_pat_entry_delay->stop();
-       m_isstreamclient = isstreamclient;
+       m_service_type = type;
+       m_descramble = descramble;
 
                /* use given service as backup. This is used for timeshift where we want to clone the live stream using the cache, but in fact have a PVR channel */
        m_service = service;
@@ -1153,7 +1169,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 && !m_ca_disabled)
+               if (!res && !simulate && m_descramble && !m_ca_disabled)
                        eDVBCIInterfaces::getInstance()->addPMTHandler(this);
        } else if (!simulate) // no simulation of playback services
        {
@@ -1180,10 +1196,16 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem
                eDebug("alloc PVR");
                        /* allocate PVR */
                eDVBChannelID chid;
-               if (m_isstreamclient) ref.getChannelID(chid);
+               if (m_service_type == streamclient) ref.getChannelID(chid);
                res = m_resourceManager->allocatePVRChannel(chid, m_pvr_channel);
                if (res)
+               {
                        eDebug("allocatePVRChannel failed!\n");
+               }
+               else if (descramble)
+               {
+                       eDVBCIInterfaces::getInstance()->addPMTHandler(this);
+               }
                m_channel = m_pvr_channel;
        }
 
@@ -1221,15 +1243,7 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem
                        m_pvr_channel->setCueSheet(cue);
 
                        if (m_pvr_channel->getDemux(m_pvr_demux_tmp, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode))
-                       {
-                               if (m_isstreamclient)
-                               {
-                                       eDebug("Allocating %s-decoding a demux for http channel failed.", m_use_decode_demux ? "" : "non-");
-                                       return -2;
-                               }
-                               else
-                                       eDebug("Allocating %s-decoding a demux for PVR channel failed.", m_use_decode_demux ? "" : "non-");
-                       }
+                               eDebug("[eDVBServicePMTHandler] Allocating %s-decoding a demux for PVR channel failed.", m_use_decode_demux ? "" : "non-");
                        else if (source)
                                m_pvr_channel->playSource(source, streaminfo_file);
                        else
@@ -1301,6 +1315,10 @@ void eDVBServicePMTHandler::removeCaHandler()
                eDVBCIInterfaces::getInstance()->removePMTHandler(this);
 }
 
+bool eDVBServicePMTHandler::isCiConnected()
+{
+       eDVBCIInterfaces::getInstance()->isCiConnected(this);
+}
 
 CAServiceMap eDVBCAService::exist;
 ChannelMap eDVBCAService::exist_channels;
index eb4f593..f26e453 100644 (file)
@@ -140,6 +140,7 @@ class eDVBServicePMTHandler: public Object
        eAUTable<eTable<ApplicationInformationSection> > m_AIT;
        eAUTable<eTable<OCSection> > m_OC;
 
+       void registerCAService();
        void PMTready(int error);
        void PATready(int error);
        
@@ -190,6 +191,7 @@ public:
                
                eventMisconfiguration, // a channel was not found in any list, or no frontend was found which could provide this channel
                eventNoDiskSpace,  // no disk space available
+               eventStartPvrDescramble,   // start PVR Descramble Convert
        };
 #ifndef SWIG
        Signal1<void,int> serviceEvent;
@@ -282,19 +284,38 @@ public:
        void getDemuxID(int &id) { id = mDemuxId; }
        void setCaDisable(bool disable) { m_ca_disabled = disable; }
 
+       enum serviceType
+       {
+               livetv = 0,
+               recording = 1,
+               scrambled_recording = 2,
+               playback = 3,
+               timeshift_recording = 4,
+               scrambled_timeshift_recording = 5,
+               timeshift_playback = 6,
+               streamserver = 7,
+               scrambled_streamserver = 8,
+               streamclient = 9,
+               offline = 10,
+               pvrDescramble = 11,
+       };
+
        /* deprecated interface */
-       int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0);
+       int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0, serviceType type = livetv, bool descramble = true);
 
        /* new interface */
-       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);
+       int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0, serviceType type = livetv, bool descramble=true);
 
        void free();
        void addCaHandler();
        void removeCaHandler();
+       bool isCiConnected();
+       bool isPmtReady() { return m_pmt_ready; }
 private:
        bool m_have_cached_program;
        program m_cached_program;
-       bool m_isstreamclient;
+       bool m_descramble;
+       serviceType m_service_type;
 #endif
 };
 
index 173a774..5b7e790 100755 (executable)
@@ -535,6 +535,12 @@ int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset)
        if (!(pkt[3] & 0x10)) /* no payload? */
                return 0;
 
+       if (pkt[3] & 0xc0)
+       {
+               /* scrambled stream, we cannot parse pts */
+               return 0;
+       }
+
        if (pkt[3] & 0x20) // adaptation field present?
                pkt += pkt[4] + 4 + 1;  /* skip adaptation field and header */
        else
index 60a64dd..e7618d0 100644 (file)
@@ -226,7 +226,11 @@ void eDVBCIInterfaces::ciRemoved(eDVBCISlot *slot)
                        slot->linked_next->setSource(slot->current_source);
                else // last CI in chain
                {
-                       setInputSource(slot->current_tuner, slot->current_source);
+                       if(slot->current_tuner == 99)
+                               slot->setSource(PVR_NONE);
+                       else
+                               setInputSource(slot->current_tuner, slot->current_source);
+
                        slot->removeVtunerPid();
                }
                slot->linked_next = 0;
@@ -244,6 +248,7 @@ static bool canDescrambleMultipleServices(int slotid)
        snprintf(configStr, 255, "config.ci.%d.canDescrambleMultipleServices", slotid);
        std::string str;
        ePythonConfigQuery::getConfigValue(configStr, str);
+       eDebugCI("canDescrambleMultipleServices %s", str.c_str());      
        if ( str == "auto" )
        {
                std::string appname = eDVBCI_UI::getInstance()->getAppName(slotid);
@@ -494,6 +499,8 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                                                eDVBFrontend *fe = (eDVBFrontend*) &(*frontend);
                                                                tunernum = fe->getSlotID();
                                                        }
+                                                       else // no frontend, PVR
+                                                               tunernum = 99;
                                                }
                                                ASSERT(tunernum != -1);
                                                data_source tuner_source = TUNER_A;
@@ -502,12 +509,15 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                                        case 0 ... 22:
                                                                tuner_source = (data_source)tunernum;
                                                                break;
+                                                       case 99: tuner_source = PVR;
+                                                               break;
                                                        default:
                                                                eDebug("try to get source for tuner %d!!\n", tunernum);
                                                                break;
                                                }
                                                ci_it->current_tuner = tunernum;
-                                               setInputSource(tunernum, ci_source);
+                                               if (tunernum != 99)
+                                                       setInputSource(tunernum, ci_source);
                                                ci_it->setSource(tuner_source);
                                        }
                                        else
@@ -597,7 +607,11 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
                                        slot->linked_next->setSource(slot->current_source);
                                else
                                {
-                                       setInputSource(slot->current_tuner, slot->current_source);
+                                       if(slot->current_tuner == 99)
+                                               slot->setSource(PVR_NONE);
+                                       else
+                                               setInputSource(slot->current_tuner, slot->current_source);
+
                                        slot->removeVtunerPid();
                                }
 
@@ -640,6 +654,19 @@ void eDVBCIInterfaces::gotPMT(eDVBServicePMTHandler *pmthandler)
                        tmp = tmp->linked_next;
                }
        }
+
+}
+
+bool eDVBCIInterfaces::isCiConnected(eDVBServicePMTHandler *pmthandler)
+{
+       bool ret = false;
+       PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(), m_pmt_handlers.end(), pmthandler);
+       if (it != m_pmt_handlers.end() && it->cislot)
+       {
+               ret = true;
+       }
+
+       return ret;
 }
 
 int eDVBCIInterfaces::getMMIState(int slotid)
@@ -652,12 +679,11 @@ int eDVBCIInterfaces::getMMIState(int slotid)
        return slot->getMMIState();
 }
 
-static const char *tuner_source[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "CI0", "CI1", "CI2", "CI3"};
+static const char *g_tuner_source[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "CI0", "CI1", "CI2", "CI3"};
 
 int eDVBCIInterfaces::setInputSource(int tuner_no, data_source source)
 {
-//     eDebug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-//     eDebug("eDVBCIInterfaces::setInputSource(%d %d)", tuner_no, (int)source);
+       eDebugCI("[eDVBCIInterfaces] setInputSource(%d, %d)", tuner_no, (int)source);
        if (getNumOfSlots() > 1) // FIXME .. we force DM8000 when more than one CI Slot is avail
        {
                char buf[64];
@@ -669,13 +695,10 @@ int eDVBCIInterfaces::setInputSource(int tuner_no, data_source source)
                        return 0;
                }
 
-               if (tuner_no > 3)
-                       eDebug("setInputSource(%d, %d) failed... dm8000 just have four inputs", tuner_no, (int)source);
-
                switch(source)
                {
-                       case TUNER_A ... CI_D:
-                               fprintf(input, tuner_source[(int)source]);
+           case TUNER_A ... CI_D:
+               fprintf(input, g_tuner_source[(int)source]);
                                break;
                        default:
                                eDebug("setInputSource for input %d failed!!!\n", (int)source);
@@ -1268,6 +1291,7 @@ void eDVBCISlot::removeService(uint16_t program_number)
 int eDVBCISlot::setSource(data_source source)
 {
        current_source = source;
+       eDebugCI("[eDVBCISlot] setSource %d", (int)source);
        if (eDVBCIInterfaces::getInstance()->getNumOfSlots() > 1) // FIXME .. we force DM8000 when more than one CI Slot is avail
        {
                char buf[64];
@@ -1276,7 +1300,13 @@ int eDVBCISlot::setSource(data_source source)
                switch(source)
                {
                        case TUNER_A ... CI_D:
-                               fprintf(ci, tuner_source[(int)source]);
+                               fprintf(ci, g_tuner_source[(int)source]);
+                               break;
+                       case PVR:
+                               fprintf(ci, "PVR");
+                               break;
+                       case PVR_NONE:
+                               fprintf(ci, "PVR_NONE");
                                break;
                        default:
                                eDebug("CI Slot %d: setSource %d failed!!!\n", getSlotID(), (int)source);
@@ -1299,7 +1329,7 @@ int eDVBCISlot::setSource(data_source source)
                        fprintf(ci, "%s", source==TUNER_A ? "A" : "B");  // configure CI data source (TunerA, TunerB)
                fclose(ci);
        }
-       eDebug("CI Slot %d setSource(%d)", getSlotID(), (int)source);
+       eDebugCI("[eDVBCISlot] CI Slot %d setSource(%d)", getSlotID(), (int)source);
        return 0;
 }
 
index 4a49d7c..4262623 100644 (file)
@@ -36,7 +36,7 @@ struct queueData
 
 enum data_source
 {
-       TUNER_A=0, TUNER_B, TUNER_C, TUNER_D, TUNER_E, TUNER_F, TUNER_G, TUNER_H, TUNER_I, TUNER_J, TUNER_K, TUNER_L, TUNER_M, TUNER_N, TUNER_O, TUNER_P, TUNER_Q, TUNER_R, TUNER_S, TUNER_T, TUNER_U, TUNER_V, CI_A, CI_B, CI_C, CI_D
+       TUNER_A=0, TUNER_B, TUNER_C, TUNER_D, TUNER_E, TUNER_F, TUNER_G, TUNER_H, TUNER_I, TUNER_J, TUNER_K, TUNER_L, TUNER_M, TUNER_N, TUNER_O, TUNER_P, TUNER_Q, TUNER_R, TUNER_S, TUNER_T, TUNER_U, TUNER_V, CI_A, CI_B, CI_C, CI_D, PVR, PVR_NONE
 };
 
 typedef std::pair<std::string, uint32_t> providerPair;
@@ -141,6 +141,7 @@ public:
        void removePMTHandler(eDVBServicePMTHandler *pmthandler);
        void recheckPMTHandlers();
        void gotPMT(eDVBServicePMTHandler *pmthandler);
+       bool isCiConnected(eDVBServicePMTHandler *pmthandler);
        void ciRemoved(eDVBCISlot *slot);
        int getSlotState(int slot);
 
index 40dfb2c..6979de6 100755 (executable)
@@ -11,4 +11,6 @@ def InitRecordingConfig():
        config.recording.filename_composition = ConfigSelection(default = "standard", choices = [
                ("standard", _("standard")),
                ("short", _("Short filenames")),
-               ("long", _("Long filenames")) ] )
\ No newline at end of file
+               ("long", _("Long filenames")) ] )
+       config.recording.always_ecm = ConfigYesNo(default = False)
+       config.recording.never_decrypt = ConfigYesNo(default = False)
\ No newline at end of file
index 67f2e75..6fcab1d 100644 (file)
@@ -31,4 +31,5 @@ SystemInfo["FrontpanelDisplayGrayscale"] = fileExists("/dev/dbox/oled0")
 SystemInfo["DeepstandbySupport"] = HardwareInfo().get_device_name() != "dm800"
 SystemInfo["HdmiInSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["WOWLSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
+SystemInfo["ScrambledPlayback"] = HardwareInfo().get_vu_device_name() in ("solo4k", "ultimo4k")
 
index a5c65be..4cf41a6 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 FastChannelChange Ultimo4kMiscControl
+       BoxModeConfig Solo4kMiscControl FastChannelChange Ultimo4kMiscControl PvrDescrambleConvert
 
 install_PYTHON =       \
        __init__.py
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile.am b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile.am
new file mode 100644 (file)
index 0000000..be3f0fb
--- /dev/null
@@ -0,0 +1,9 @@
+installdir = $(pkglibdir)/python/Plugins/SystemPlugins/PvrDescrambleConvert
+
+SUBDIRS = meta
+
+install_PYTHON =  \
+       __init__.py \
+       plugin.py \
+       PvrDescrambleConvert.py
+
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/PvrDescrambleConvert.py b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/PvrDescrambleConvert.py
new file mode 100644 (file)
index 0000000..56e0220
--- /dev/null
@@ -0,0 +1,370 @@
+from Components.config import config, ConfigSubsection, ConfigSelection
+from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
+
+from enigma import eTimer, eServiceReference, eServiceCenter, iServiceInformation, iRecordableService
+from RecordTimer import RecordTimerEntry, RecordTimer
+from Tools.Directories import fileExists
+from ServiceReference import ServiceReference
+
+from Tools import Notifications
+from Screens.MessageBox import MessageBox
+
+import os
+import glob
+import time
+
+config.plugins.pvrdesconvertsetup = ConfigSubsection()
+config.plugins.pvrdesconvertsetup.activate = ConfigSelection(default = "disable", choices = [ ("enable", _("Enable")), ("disable", _("Disable"))] )
+
+# lib/dvb/pmt.h
+SERVICETYPE_PVR_DESCRAMBLE = 11
+
+def fileExist(fileName, flag = os.W_OK):
+       return os.access(fileName, flag)
+
+def sync():
+       if hasattr(os, 'sync'):
+               os.sync()
+       else:
+               import ctypes
+               libc = ctypes.CDLL("libc.so.6")
+               libc.sync()
+
+# iStaticServiceInformation
+class StubInfo:
+       def getName(self, sref):
+               return os.path.split(sref.getPath())[1]
+       def getLength(self, sref):
+               return -1
+       def getEvent(self, sref, *args):
+               return None
+       def isPlayable(self):
+               return True
+       def getInfo(self, sref, w):
+               if w == iServiceInformation.sTimeCreate:
+                       return os.stat(sref.getPath()).st_ctime
+               if w == iServiceInformation.sFileSize:
+                       return os.stat(sref.getPath()).st_size
+               if w == iServiceInformation.sDescription:
+                       return sref.getPath()
+               return 0
+       def getInfoString(self, sref, w):
+               return ''
+stubInfo = StubInfo()
+
+class PVRDescrambleConvertInfos:
+       def __init__(self):
+               self.navigation = None
+
+       def getNavigation(self):
+               if not self.navigation:
+                       import NavigationInstance
+                       if NavigationInstance:
+                               self.navigation = NavigationInstance.instance
+
+               return self.navigation
+
+       def getRecordings(self):
+               recordings = []
+               nav = self.getNavigation()
+               if nav:
+                       recordings = nav.getRecordings()
+                       print "getRecordings : ", recordings
+
+               return recordings
+
+       def getInstandby(self):
+               from Screens.Standby import inStandby
+               return inStandby
+
+       def getCurrentMoviePath(self):
+               if not fileExists(config.movielist.last_videodir.value):
+                       config.movielist.last_videodir.value = defaultMoviePath()
+                       config.movielist.last_videodir.save()
+
+               curMovieRef = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + config.movielist.last_videodir.value)
+               return curMovieRef
+
+class PVRDescrambleConvert(PVRDescrambleConvertInfos):
+       def __init__(self):
+               PVRDescrambleConvertInfos.__init__(self)
+               config.misc.standbyCounter.addNotifier(self.enterStandby, initial_call = False)
+
+               self.convertTimer = eTimer()
+               self.convertTimer.callback.append(self.startConvert)
+
+               self.stopConvertTimer = eTimer()
+               self.stopConvertTimer.callback.append(self.stopConvert)
+
+               self.converting = None
+               self.convertFilename = None
+               self.currentPvr = None
+
+               self.pvrLists = []
+
+               self.oldService = None
+
+       def enterStandby(self, configElement):
+               if config.plugins.pvrdesconvertsetup.activate.value == "enable":
+                       instandby = self.getInstandby()
+                       instandby.onClose.append(self.leaveStandby)
+
+                       self.loadScrambledPvrList()
+
+                       # register record callback
+                       self.appendRecordEventCB()
+
+                       self.startConvertTimer()
+
+       def leaveStandby(self):
+               self.removeRecordEventCB()
+               self.convertTimer.stop()
+               self.stopConvert()
+
+       def startConvertTimer(self):
+               self.convertTimer.start(3000, True)
+
+       def startStopConvertTimer(self):
+               self.stopConvertTimer.start(500, True)
+
+       def appendRecordEventCB(self):
+               nav = self.getNavigation()
+               if nav:
+                       if self.gotRecordEvent not in nav.record_event:
+                               nav.record_event.append(self.gotRecordEvent)
+
+       def removeRecordEventCB(self):
+               nav = self.getNavigation()
+               if nav:
+                       if self.gotRecordEvent in nav.record_event:
+                               nav.record_event.remove(self.gotRecordEvent)
+
+       def gotRecordEvent(self, service, event):
+               if service.getServiceType() == SERVICETYPE_PVR_DESCRAMBLE:
+                       if event == iRecordableService.evEnd:
+                               if self.getInstandby():
+                                       self.startConvertTimer()
+                       elif event == iRecordableService.evPvrEof:
+                               self.stopConvert(convertFinished = True)
+                       elif event == iRecordableService.evRecordFailed:
+                               self.startStopConvertTimer()
+               else:
+                       if event in (iRecordableService.evPvrTuneStart, iRecordableService.evTuneStart):
+                               if self.currentPvr:
+                                       self.pvrLists.insert(0, self.currentPvr)
+                                       self.currentPvr = None
+                                       self.startStopConvertTimer()
+                       elif event == iRecordableService.evEnd:
+                               if self.getInstandby():
+                                       self.startConvertTimer()
+
+       def loadScrambledPvrList(self):
+               self.pvrLists = []
+
+               serviceHandler = eServiceCenter.getInstance()
+               curMovieRef = self.getCurrentMoviePath()
+               movieRefList = serviceHandler.list(curMovieRef)
+
+               if movieRefList is None:
+                       print "Load pvr list failed!"
+                       return
+
+               while 1:
+                       sref = movieRefList.getNext()
+                       if not sref.valid():
+                               break
+
+                       if config.ParentalControl.servicepinactive.value and config.ParentalControl.storeservicepin.value != "never":
+                               from Components.ParentalControl import parentalControl
+                               if not parentalControl.sessionPinCached and parentalControl.isProtected(sref):
+                                       continue
+
+                       if sref.flags & eServiceReference.mustDescent:
+                               continue
+
+                       if not sref.getPath():
+                               return
+
+                       info = serviceHandler.info(sref)
+
+                       real_sref = "1:0:0:0:0:0:0:0:0:0:"
+                       if info is not None:
+                               real_sref = info.getInfoString(sref, iServiceInformation.sServiceref)
+
+                       if info is None:
+                               info = stubInfo
+
+                       begin = info.getInfo(sref, iServiceInformation.sTimeCreate)
+
+                       # convert separe-separated list of tags into a set
+                       name = info.getName(sref)
+                       scrambled = info.getInfo(sref, iServiceInformation.sIsScrambled)
+                       length = info.getLength(sref)
+
+                       if scrambled == 1:
+                               #print "====" * 30
+                               #print "[loadScrambledPvrList] sref.toString() : ", sref.toString()
+                               #print "[loadScrambledPvrList] sref.getPath() : ", sref.getPath()
+                               #print "[loadScrambledPvrList] name : ", name
+                               #print "[loadScrambledPvrList] begin : ", begin
+                               #print "[loadScrambledPvrList] length : ", length
+                               #print "[loadScrambledPvrList] scrambled : ", scrambled
+                               #print ""
+                               rec = (begin, sref, name, length, real_sref)
+                               if rec not in self.pvrLists:
+                                       self.pvrLists.append( rec )
+
+               self.pvrLists.sort()
+
+       def checkBeforeStartConvert(self):
+               return self.pvrLists and (not bool(self.getRecordings())) and (not self.converting) and self.getInstandby()
+
+       def startConvert(self):
+               if not self.checkBeforeStartConvert():
+                       return
+
+               self.currentPvr = self.pvrLists.pop(0)
+               if self.currentPvr is None:
+                       return
+
+               (_begin, sref, name, length, real_ref) = self.currentPvr
+
+               m_path = sref.getPath()
+               sref = eServiceReference(real_ref + m_path)
+
+               begin = int(time.time())
+               end = begin + 3600      # dummy
+               #end = begin + int(length) + 2
+               description = ""
+               eventid = None
+
+               if isinstance(sref, eServiceReference):
+                       sref = ServiceReference(sref)
+
+               if m_path.endswith('.ts'):
+                       m_path = m_path[:-3]
+
+               filename = m_path + "_pvrdesc"
+
+               recording = RecordTimerEntry(sref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath(), filename=filename)
+               recording.dontSave = True
+               recording.autoincrease = True
+               recording.setAutoincreaseEnd()
+               recording.pvrConvert = True # do not handle evStart event
+
+               nav = self.getNavigation()
+               simulTimerList = nav.RecordTimer.record(recording)
+               if simulTimerList is None:      # no conflict
+                       recordings = self.getRecordings()
+                       if len(recordings) == 1:
+                               self.converting = recording
+                               self.convertFilename = (sref.getPath(), filename + ".ts")
+                       else:
+                               print "[PVRDescrambleConvert] error, wrong recordings info."
+               else:
+                       self.currentPvr = None
+                       self.startConvertTimer()
+
+                       if len(simulTimerList) > 1: # with other recording
+                               print "[PVRDescrambleConvert] conflicts !"
+                       else:
+                               print "[PVRDescrambleConvert] Couldn't record due to invalid service %s" % sref
+                       recording.autoincrease = False
+
+               print "[PVRDescrambleConvert] startConvert, self.converting : ", self.converting
+
+       def removeStr(self, fileName, s):
+               if fileName.find(s) == -1:
+                       return fileName
+
+               sp = fileName.split(s)
+
+               return sp[0] + sp[1]
+
+       def renameDelPvr(self, pvrName, subName):
+               targetName = pvrName + subName
+               outName = self.removeStr(pvrName, ".ts") + "_del" + ".ts" + subName
+
+               if fileExist(targetName):
+                       #print "RENAME %s -> %s" % (targetName, outName)
+                       os.rename(targetName, outName)
+                       return outName
+
+               return None
+
+       def renameConvertPvr(self, pvrName, subName):
+               targetName = pvrName + subName
+               outName = self.removeStr(pvrName, "_pvrdesc") + subName
+
+               if fileExist(targetName):
+                       #print "RENAME %s -> %s" % (targetName, outName)
+                       os.rename(targetName, outName)
+                       return outName
+
+               return None
+
+       def renamePvr(self, pvr_ori, pvr_convert):
+               pvr_ori_del = self.renameDelPvr(pvr_ori, "")
+               if not pvr_ori_del:
+                       return None
+
+               self.renameDelPvr(pvr_ori, ".meta")
+               self.renameDelPvr(pvr_ori, ".ap")
+               self.renameDelPvr(pvr_ori, ".sc")
+               self.renameDelPvr(pvr_ori, ".cuts")
+
+               pvr_convert_fixed = self.renameConvertPvr(pvr_convert, "")
+               if not pvr_convert_fixed:
+                       return None
+
+               self.renameConvertPvr(pvr_convert, ".meta")
+               self.renameConvertPvr(pvr_convert, ".ap")
+               self.renameConvertPvr(pvr_convert, ".sc")
+               self.renameConvertPvr(pvr_convert, ".cuts")
+
+               if os.path.exists(pvr_convert[:-3] + '.eit'):
+                       os.remove(pvr_convert[:-3] + '.eit')
+
+               return pvr_ori_del
+
+       def stopConvert(self, convertFinished = False):
+               name = "Unknown"
+               if self.currentPvr:
+                       (_begin, sref, name, length, real_ref) = self.currentPvr
+                       self.currentPvr = None
+
+               if self.converting:
+                       nav = self.getNavigation()
+                       nav.RecordTimer.removeEntry(self.converting)
+                       convertFilename = self.convertFilename
+                       self.converting = None
+                       self.convertFilename = None
+
+                       if convertFilename:
+                               (pvr_ori, pvr_convert) = convertFilename
+                               if convertFinished:
+                                       # check size
+                                       if fileExist(pvr_convert, os.F_OK) and os.stat(pvr_convert).st_size:
+                                               pvr_ori_del = self.renamePvr(pvr_ori, pvr_convert)
+                                               if pvr_ori_del:
+                                                       self.deletePvr(pvr_ori_del)
+                                               self.addNotification(_("A PVR descramble converting is finished.\n%s") % name)
+                                       else:
+                                               self.deletePvr(pvr_convert)
+                               else:
+                                       self.deletePvr(pvr_convert)
+
+               sync()
+
+       def deletePvr(self, filename):
+               serviceHandler = eServiceCenter.getInstance()
+               ref = eServiceReference(1, 0, filename)
+               offline = serviceHandler.offlineOperations(ref)
+               if offline.deleteFromDisk(0):
+                       print "[PVRDescrambleConvert] delete failed : ", filename
+
+       def addNotification(self, text):
+               Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=5)
+
+pvr_descramble_convert = PVRDescrambleConvert()
+
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/__init__.py b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/__init__.py
new file mode 100644 (file)
index 0000000..e9be9ab
--- /dev/null
@@ -0,0 +1 @@
+#nothing
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile.am
new file mode 100644 (file)
index 0000000..7f9af15
--- /dev/null
@@ -0,0 +1,4 @@
+installdir = $(datadir)/meta
+
+dist_install_DATA = plugin_pvrdescrambleconvert.xml
+
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/plugin_pvrdescrambleconvert.xml b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/plugin_pvrdescrambleconvert.xml
new file mode 100644 (file)
index 0000000..3b4f426
--- /dev/null
@@ -0,0 +1,16 @@
+<default>
+         <prerequisites>
+                    <tag type="System" />
+         </prerequisites>
+          <info>
+                    <author>hschang(hschang@dev3)</author>
+                    <name>PVR Descramble Convert</name>
+                    <packagename>enigma2-plugin-systemplugins-pvrdescrambleconvert</packagename>
+                    <shortdescription>PVR descramble in standby.</shortdescription>
+                    <description>PVR descramble in standby.</description>
+          </info>
+
+         <files type="package"> <!-- without version, without .ipk -->
+               <file type="package" name="enigma2-plugin-systemplugins-pvrdescrambleconvert" />
+       </files>
+</default>
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/plugin.py b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/plugin.py
new file mode 100644 (file)
index 0000000..8548b0a
--- /dev/null
@@ -0,0 +1,63 @@
+from Plugins.Plugin import PluginDescriptor
+from Screens.Screen import Screen
+from Components.ConfigList import ConfigListScreen
+from Components.ActionMap import ActionMap
+from Components.Sources.StaticText import StaticText
+from Components.config import config, getConfigListEntry
+from PvrDescrambleConvert import pvr_descramble_convert
+
+class PvrDescrambleConvertSetup(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 = _("Pvr Descramble Convert 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["description"] = StaticText("")
+               self.createConfig()
+               self.createSetup()
+
+       def createConfig(self):
+               self.enableEntry = getConfigListEntry(_("Enable PVR Descramble in standby"), config.plugins.pvrdesconvertsetup.activate)
+
+       def createSetup(self):
+               self.list = []
+               self.list.append( self.enableEntry )
+               self["config"].list = self.list
+               self["config"].l.setList(self.list)
+
+def main(session, **kwargs):
+       session.open(PvrDescrambleConvertSetup)
+
+def Plugins(**kwargs):
+       list = []
+       list.append(
+               PluginDescriptor(name=_("PVR Descramble Convert Setup"),
+               description=_("PVR descramble in standby"),
+               where = [PluginDescriptor.WHERE_PLUGINMENU],
+               needsRestart = False,
+               fnc = main))
+
+       return list
+
index f0be087..33ff66b 100644 (file)
@@ -361,6 +361,7 @@ public:
 
                sHBBTVUrl,
                sLiveStreamDemuxId,
+               sIsScrambled,
 
                sUser = 0x100
        };
@@ -900,6 +901,8 @@ public:
                evRecordWriteError,
                evNewEventInfo,
                evTuneStart,
+               evPvrTuneStart,
+               evPvrEof,
        };
        enum {
                NoError=0,
@@ -910,6 +913,7 @@ public:
                errTuneFailed=-255,
                errMisconfiguration = -256,
                errNoResources = -257,
+               errNoCiConnected = -258
        };
 };
 
@@ -925,13 +929,14 @@ public:
        virtual RESULT connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)=0;
 #endif
        virtual SWIG_VOID(RESULT) getError(int &SWIG_OUTPUT)=0;
-       virtual RESULT prepare(const char *filename, time_t begTime=-1, time_t endTime=-1, int eit_event_id=-1, const char *name=0, const char *descr=0, const char *tags=0)=0;
+       virtual RESULT prepare(const char *filename, time_t begTime=-1, time_t endTime=-1, int eit_event_id=-1, const char *name=0, const char *descr=0, const char *tags=0, bool descramble = true, bool recordecm = false)=0;
        virtual RESULT prepareStreaming()=0;
        virtual RESULT start(bool simulate=false)=0;
        virtual RESULT stop()=0;
        virtual SWIG_VOID(RESULT) frontendInfo(ePtr<iFrontendInformation> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) stream(ePtr<iStreamableService> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) subServices(ePtr<iSubserviceList> &SWIG_OUTPUT)=0;
+       virtual SWIG_VOID(RESULT) getServiceType(int &SWIG_OUTPUT)=0;
 };
 SWIG_TEMPLATE_TYPEDEF(ePtr<iRecordableService>, iRecordableServicePtr);
 
index acc998a..1494cc5 100755 (executable)
@@ -376,6 +376,8 @@ int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w
                        return m_parser.m_time_create;
                else
                        return iServiceInformation::resNA;
+       case iServiceInformation::sIsScrambled:
+               return m_parser.m_scrambled;
        default:
                return iServiceInformation::resNA;
        }
@@ -832,21 +834,9 @@ RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableServ
 
 RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
 {
-       if (ref.path.empty())
-       {
-               ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
-               return 0;
-       } else
-       {
-               bool isstream = ref.path.substr(0, 7) == "http://";
-               if(isstream)
-               {
-                       ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref, isstream);
-                       return 0;
-               }
-               ptr = 0;
-               return -1;
-       }
+       bool isstream = ref.path.substr(0, 7) == "http://";
+       ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref, isstream);
+       return 0;
 }
 
 RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
@@ -1134,7 +1124,7 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                        if (m_skipmode < 0)
                                m_cue->seekTo(0, -1000);
                        ePtr<iTsSource> source = createTsSource(r);
-                       m_service_handler_timeshift.tuneExt(r, 1, source, r.path.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+                       m_service_handler_timeshift.tuneExt(r, 1, source, r.path.c_str(), m_cue, 0, m_dvb_service, false); /* use the decoder demux for everything */
 
                        m_event((iPlayableService*)this, evUser+1);
                }
@@ -1164,7 +1154,9 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                                resetTimeshift(1);
 
                                ePtr<iTsSource> source = createTsSource(r);
-                               m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file_next.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+                               int use_decode_demux = 1;
+                               eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::timeshift_playback;
+                               m_service_handler_timeshift.tuneExt(r, use_decode_demux, source, m_timeshift_file_next.c_str(), m_cue, 0, m_dvb_service, type, false); /* use the decoder demux for everything */
 
                                m_event((iPlayableService*)this, evUser+1);
                        }
@@ -1180,26 +1172,44 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
 RESULT eDVBServicePlay::start()
 {
        eServiceReferenceDVB service = (eServiceReferenceDVB&)m_reference;
+       bool scrambled = true;
+       eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::livetv;
+
+       int use_decode_demux = 0;
 
                /* in pvr mode, we only want to use one demux. in tv mode, we're using 
                   two (one for decoding, one for data source), as we must be prepared
                   to start recording from the data demux. */
        if (m_is_pvr)
        {
+               use_decode_demux = 1;
                eDVBMetaParser meta;
                if (!meta.parseFile(m_reference.path))
                {
                        service = meta.m_ref;
                        service.path = m_reference.path;
+                       scrambled = meta.m_scrambled;
+               }
+               else
+               {
+                       /* when there is no meta file we need to handle ts/m2ts as descrambled */
+                       scrambled = false;
                }
                m_cue = new eCueSheet();
+               type = eDVBServicePMTHandler::playback;
        }
        else
                m_event(this, evStart);
 
+       if (m_is_stream)
+       {
+               scrambled = true;
+               type = eDVBServicePMTHandler::streamclient;
+       }
+
        m_first_program_info = 1;
        ePtr<iTsSource> source = createTsSource(service);
-       m_service_handler.tuneExt(service, m_is_pvr, source, service.path.c_str(), m_cue, false, m_dvb_service, m_is_stream);
+       m_service_handler.tuneExt(service, use_decode_demux, source, service.path.c_str(), m_cue, false, m_dvb_service, type, scrambled);
 
        if (m_is_pvr)
        {
@@ -2516,7 +2526,9 @@ void eDVBServicePlay::switchToTimeshift()
        m_cue->seekTo(0, -1000);
 
        ePtr<iTsSource> source = createTsSource(r);
-       m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+       int use_decode_demux = 1;
+       eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::timeshift_playback;
+       m_service_handler_timeshift.tuneExt(r, use_decode_demux, source, m_timeshift_file.c_str(), m_cue, 0, m_dvb_service, type, false); /* use the decoder demux for everything */
 
        eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
        pause();
index 4af0320..6ce66ae 100644 (file)
@@ -3,6 +3,8 @@
 #include <lib/dvb/epgcache.h>
 #include <lib/dvb/metaparser.h>
 #include <lib/base/httpstream.h>
+#include <lib/base/nconfig.h>
+
 #include <fcntl.h>
 
        /* for cutlist */
@@ -22,12 +24,16 @@ eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref, bool isstr
        CONNECT(m_event_handler.m_eit_changed, eDVBServiceRecord::gotNewEvent);
        m_state = stateIdle;
        m_want_record = 0;
+       m_record_ecm = false;
+       m_descramble = true;
+       m_pvr_descramble = false;
        m_tuned = 0;
        m_target_fd = -1;
        m_error = 0;
        m_streaming = 0;
        m_simulate = false;
        m_last_event_id = -1;
+       m_serviceType = eDVBServicePMTHandler::recording;
 }
 
 void eDVBServiceRecord::serviceEvent(int event)
@@ -71,7 +77,12 @@ void eDVBServiceRecord::serviceEvent(int event)
                if (m_state == stateIdle)
                        doPrepare();
                else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
-                       doRecord();
+               {
+                       if (m_pvr_descramble)
+                               updateDecoder();
+                       else
+                               doRecord();
+               }
                m_event((iRecordableService*)this, evNewProgramInfo);
                break;
        }
@@ -83,14 +94,34 @@ void eDVBServiceRecord::serviceEvent(int event)
                m_error = errNoResources;
                m_event((iRecordableService*)this, evTuneFailed);
                break;
+       case eDVBServicePMTHandler::eventEOF:
+               m_event((iRecordableService*)this, evPvrEof);
+               break;
+       case eDVBServicePMTHandler::eventStartPvrDescramble:
+               if (m_want_record)
+               {
+                       doRecord();
+               }
+               break;
        }
 }
 
-RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags)
+RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags, bool descramble, bool recordecm)
 {
+       bool config_recording_always_ecm = ePythonConfigQuery::getConfigBoolValue("config.recording.always_ecm", false);
+       bool config_recording_never_decrypt = ePythonConfigQuery::getConfigBoolValue("config.recording.never_decrypt", false);
        m_filename = filename;
        m_streaming = 0;
-       
+       m_descramble = config_recording_never_decrypt ? false : descramble;
+       m_record_ecm = config_recording_always_ecm ? true : recordecm;
+
+       // force descramble for _pvrdesc.ts
+       if (strstr(filename, "_pvrdesc.ts"))
+       {
+               m_pvr_descramble = true;
+               m_descramble = true;
+       }
+
        if (m_state == stateIdle)
        {
                int ret = doPrepare();
@@ -136,7 +167,8 @@ RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t e
                                meta.m_description = descr;
                        if (tags)
                                meta.m_tags = tags;
-                       ret = meta.updateMeta(filename) ? -255 : 0;
+                       meta.m_scrambled = !m_descramble;
+                       ret = meta.updateMeta(m_filename) ? -255 : 0;
                        if (!ret)
                        {
                                const eit_event_struct *event = 0;
@@ -162,7 +194,7 @@ RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t e
                                if ( event )
                                {
                                        eDebug("found event.. store to disc");
-                                       std::string fname = filename;
+                                       std::string fname = m_filename;
                                        fname.erase(fname.length()-2, 2);
                                        fname+="eit";
                                        int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
@@ -234,32 +266,62 @@ int eDVBServiceRecord::doPrepare()
                /* allocate a ts recorder if we don't already have one. */
        if (m_state == stateIdle)
        {
-               bool isstreamclient = false;
+               int use_decode_demux = 0;
+
+               if (m_streaming)
+               {
+                       m_serviceType = m_record_ecm ? eDVBServicePMTHandler::scrambled_streamserver : eDVBServicePMTHandler::streamserver;
+               }
+               else
+               {
+                       m_serviceType = m_record_ecm ? eDVBServicePMTHandler::scrambled_recording : eDVBServicePMTHandler::recording;
+               }
+
                m_pids_active.clear();
                m_state = statePrepared;
                ePtr<iTsSource> source;
-               if (!m_ref.path.empty())
+               if (!m_simulate && !m_ref.path.empty())
                {
                        if (m_is_stream_client)
                        {
-                               isstreamclient = true;
+                               m_descramble = true; // assume stream is scrambled
+                               m_record_ecm = false;
+                               m_serviceType = eDVBServicePMTHandler::streamclient;
                                eHttpStream *f = new eHttpStream();
                                f->open(m_ref.path.c_str());
                                source = ePtr<iTsSource>(f);
                        }
                        else
                        {
+                               use_decode_demux = 1;
+
                                /* re-record a recording */
+                               eDVBMetaParser meta;
+                               if (!meta.parseFile(m_ref.path))
+                               {
+                                       m_descramble = meta.m_scrambled;
+                               }
+
+                               if(m_pvr_descramble)
+                               {
+                                       m_serviceType = eDVBServicePMTHandler::pvrDescramble;
+                               }
+                               else
+                               {
+                                       m_serviceType = eDVBServicePMTHandler::offline;
+                               }
+
                                eRawFile *f = new eRawFile();
                                f->open(m_ref.path.c_str());
                                source = ePtr<iTsSource>(f);
                        }
+                       m_event((iRecordableService*)this, evPvrTuneStart);
                }
                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 m_service_handler.tuneExt(m_ref, use_decode_demux, source, m_ref.path.c_str(), 0, m_simulate, 0, m_serviceType, m_descramble);
        }
        return 0;
 }
@@ -279,6 +341,22 @@ int eDVBServiceRecord::doRecord()
        
        if (!m_record && m_tuned && !m_streaming && !m_simulate)
        {
+               if (m_pvr_descramble)
+               {
+                       if (m_service_handler.isPmtReady())
+                       {
+                               if (!m_service_handler.isCiConnected())
+                               {
+                                       m_event((iRecordableService*)this, evRecordFailed);
+                                       return errNoCiConnected;
+                               }
+                       }
+                       else
+                       {
+                               return 0;
+                       }
+               }
+
                eDebug("Recording to %s...", m_filename.c_str());
                ::remove(m_filename.c_str());
                int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
@@ -404,6 +482,41 @@ int eDVBServiceRecord::doRecord()
                        if (program.textPid != -1)
                                pids_to_record.insert(program.textPid); // Videotext
 
+                       if (m_record_ecm)
+                       {
+                               for (std::list<eDVBServicePMTHandler::program::capid_pair>::const_iterator i(program.caids.begin());
+                                                       i != program.caids.end(); ++i)
+                               {
+                                       if (i->capid >= 0) pids_to_record.insert(i->capid);
+                               }
+                               pids_to_record.insert(EventInformationSection::PID);
+                               pids_to_record.insert(TimeAndDateSection::PID);
+                       }
+
+                       {
+                               int isCrypted = (int)program.isCrypted();
+                               int scrambled = !m_descramble;
+                               if (!m_descramble)
+                               {
+                                       scrambled = isCrypted;
+                               }
+                               else if (!isCrypted)
+                               {
+                                       scrambled = 0;
+                               }
+                               else // m_descramble && isCrypted
+                               {
+                                       if (!m_service_handler.isCiConnected())
+                                               scrambled = 1;
+                                       else
+                                               scrambled = 0;
+                               }
+                               eDVBMetaParser dvbParser;
+                               dvbParser.parseFile(m_filename);
+                               dvbParser.m_scrambled = scrambled;
+                               dvbParser.updateMeta(m_filename);
+                       }
+
                                /* find out which pids are NEW and which pids are obsolete.. */
                        std::set<int> new_pids, obsolete_pids;
 
@@ -446,6 +559,77 @@ int eDVBServiceRecord::doRecord()
        return 0;
 }
 
+void eDVBServiceRecord::updateDecoder()
+{
+       int vpid = -1, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1;
+
+       eDVBServicePMTHandler &h = m_service_handler;
+
+       eDVBServicePMTHandler::program program;
+       if (m_service_handler.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(")");
+               }
+
+               apid = program.audioStreams[program.defaultAudioStream].pid;
+               apidtype = program.audioStreams[program.defaultAudioStream].type;
+
+               eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
+               pcrpid = program.pcrPid;
+       }
+
+       if (!m_decoder)
+       {
+               h.getDecodeDemux(m_decode_demux);
+               if (m_decode_demux)
+               {
+                       m_decode_demux->getMPEGDecoder(m_decoder, 0);
+               }
+       }
+
+       if (m_decoder)
+       {
+               m_decoder->setVideoPID(vpid, vpidtype);
+               m_decoder->setAudioPID(apid, apidtype);
+               m_decoder->setSyncPCR(-1);
+               m_decoder->play();
+       }
+}
+
 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
 {
        ptr = this;
index 7b53803..7662380 100644 (file)
@@ -18,8 +18,10 @@ class eDVBServiceRecord: public eDVBServiceBase,
 {
        DECLARE_REF(eDVBServiceRecord);
 public:
+       eDVBServicePMTHandler::serviceType m_serviceType;
+
        RESULT connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection);
-       RESULT prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags);
+       RESULT prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags, bool descramble, bool recordecm);
        RESULT prepareStreaming();
        RESULT start(bool simulate=false);
        RESULT stop();
@@ -27,6 +29,7 @@ public:
        RESULT getError(int &error) { error = m_error; return 0; }
        RESULT frontendInfo(ePtr<iFrontendInformation> &ptr);
        RESULT subServices(ePtr<iSubserviceList> &ptr);
+       RESULT getServiceType(int &serviceType) { serviceType = m_serviceType; return 0; };
 
                // iStreamableService
        PyObject *getStreamingData();
@@ -34,10 +37,18 @@ public:
                // iSubserviceList
        int getNumberOfSubservices();
        RESULT getSubservice(eServiceReference &subservice, unsigned int n);
+
+protected:
+       ePtr<iDVBDemux> m_decode_demux;
+       ePtr<iTSMPEGDecoder> m_decoder;
+
 private:
        enum { stateIdle, statePrepared, stateRecording };
        bool m_simulate;
        int m_state, m_want_record;
+       bool m_record_ecm;
+       bool m_descramble;
+       bool m_pvr_descramble;
        bool m_is_stream_client;
        friend class eServiceFactoryDVB;
        eDVBServiceRecord(const eServiceReferenceDVB &ref, bool isstreamclient = false);
@@ -60,6 +71,7 @@ private:
        
        int doPrepare();
        int doRecord();
+       void updateDecoder();
 
                        /* events */
        void serviceEvent(int event);
index 2767982..d7c321e 100644 (file)
@@ -205,7 +205,7 @@ eServiceHDMIRecord::eServiceHDMIRecord(const eServiceReference &ref, ePtr<eNavig
        m_nav_instance = nav_instance;
 }
 
-RESULT eServiceHDMIRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags)
+RESULT eServiceHDMIRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags, bool descramble, bool recordecm)
 {
        m_filename = filename;
 
index 6db9c43..1f8e664 100644 (file)
@@ -90,7 +90,7 @@ class eServiceHDMIRecord: public eDVBServiceBase, public iRecordableService, pub
 public:
        eServiceHDMIRecord(const eServiceReference &ref, ePtr<eNavigation> &nav_instance);
        RESULT connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection);
-       RESULT prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags);
+       RESULT prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags, bool descramble, bool recordecm);
        RESULT prepareStreaming();
        RESULT start(bool simulate=false);
        RESULT stop();
@@ -98,6 +98,7 @@ public:
        RESULT frontendInfo(ePtr<iFrontendInformation> &ptr);
        RESULT stream(ePtr<iStreamableService> &ptr);
        RESULT subServices(ePtr<iSubserviceList> &ptr);
+       RESULT getServiceType(int &serviceType) { serviceType = -1; return -1; };
 
 private:
        enum { stateIdle, statePrepared, stateRecording };
index e79907d..4c2fc80 100644 (file)
@@ -17,6 +17,7 @@ public:
        off_t lseek(off_t offset, int whence);
        ssize_t read(off_t offset, void *buf, size_t count);
        off_t length();
+       off_t offset();
        int valid();
 private:
        int m_sync_offset;
@@ -309,6 +310,11 @@ off_t eM2TSFile::length()
        return m_length;
 }
 
+off_t eM2TSFile::offset()
+{
+       return m_current_offset;
+}
+
 eServiceFactoryM2TS::eServiceFactoryM2TS()
 {
        ePtr<eServiceCenter> sc;