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:
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))
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")
# 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.")))
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
<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>
{
return (off_t)-1;
}
+
+off_t eHttpStream::offset()
+{
+ return 0;
+}
+
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:
virtual off_t length()=0;
virtual int valid()=0;
+ virtual off_t offset() = 0;
virtual bool isStream() { return false; }
};
{
return m_totallength;
}
+
+off_t eRawFile::offset()
+{
+ return m_last_offset;
+}
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 */
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! */
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;
::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()
} 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");
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;
m_data_ok = 0;
m_length = 0;
m_filesize = 0;
+ m_scrambled = 0;
}
int eDVBMetaParser::parseFile(const std::string &basename)
case 7:
m_service_data = line;
break;
+ case 8:
+ m_scrambled = atoi(line);
+ break;
default:
break;
}
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;
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;
}
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;
};
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);
}
}
+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)
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)
{
else
eDebug("eDVBServicePMTHandler cannot call buildCAPMT");
}
+
+ if (m_service_type == pvrDescramble)
+ serviceEvent(eventStartPvrDescramble);
}
}
}
}
-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;
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
{
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;
}
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
eDVBCIInterfaces::getInstance()->removePMTHandler(this);
}
+bool eDVBServicePMTHandler::isCiConnected()
+{
+ eDVBCIInterfaces::getInstance()->isCiConnected(this);
+}
CAServiceMap eDVBCAService::exist;
ChannelMap eDVBCAService::exist_channels;
eAUTable<eTable<ApplicationInformationSection> > m_AIT;
eAUTable<eTable<OCSection> > m_OC;
+ void registerCAService();
void PMTready(int error);
void PATready(int error);
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;
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
};
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
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;
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);
eDVBFrontend *fe = (eDVBFrontend*) &(*frontend);
tunernum = fe->getSlotID();
}
+ else // no frontend, PVR
+ tunernum = 99;
}
ASSERT(tunernum != -1);
data_source tuner_source = TUNER_A;
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
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();
}
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)
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];
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);
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];
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);
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;
}
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;
void removePMTHandler(eDVBServicePMTHandler *pmthandler);
void recheckPMTHandlers();
void gotPMT(eDVBServicePMTHandler *pmthandler);
+ bool isCiConnected(eDVBServicePMTHandler *pmthandler);
void ciRemoved(eDVBCISlot *slot);
int getSlotState(int slot);
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
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")
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
--- /dev/null
+installdir = $(pkglibdir)/python/Plugins/SystemPlugins/PvrDescrambleConvert
+
+SUBDIRS = meta
+
+install_PYTHON = \
+ __init__.py \
+ plugin.py \
+ PvrDescrambleConvert.py
+
--- /dev/null
+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()
+
--- /dev/null
+installdir = $(datadir)/meta
+
+dist_install_DATA = plugin_pvrdescrambleconvert.xml
+
--- /dev/null
+<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>
--- /dev/null
+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
+
sHBBTVUrl,
sLiveStreamDemuxId,
+ sIsScrambled,
sUser = 0x100
};
evRecordWriteError,
evNewEventInfo,
evTuneStart,
+ evPvrTuneStart,
+ evPvrEof,
};
enum {
NoError=0,
errTuneFailed=-255,
errMisconfiguration = -256,
errNoResources = -257,
+ errNoCiConnected = -258
};
};
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);
return m_parser.m_time_create;
else
return iServiceInformation::resNA;
+ case iServiceInformation::sIsScrambled:
+ return m_parser.m_scrambled;
default:
return iServiceInformation::resNA;
}
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)
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);
}
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);
}
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)
{
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();
#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 */
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)
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;
}
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();
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;
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);
/* 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;
}
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);
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;
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;
{
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();
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();
// 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);
int doPrepare();
int doRecord();
+ void updateDecoder();
/* events */
void serviceEvent(int event);
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;
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();
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 };
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;
return m_length;
}
+off_t eM2TSFile::offset()
+{
+ return m_current_offset;
+}
+
eServiceFactoryM2TS::eServiceFactoryM2TS()
{
ePtr<eServiceCenter> sc;