diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 9b058318..dc774b0f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3383,6 +3383,7 @@ Dietmar Spingler for suggesting to provide a way of using no DVB devices at all for suggesting that the -V and -h options should list the plugins in alphabetical order for suggesting to implement the setup option "Recording/Record key handling" + for suggesting to cache the channel/CAM relations in the file 'cam.data' Stefan Schallenberg for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement() diff --git a/HISTORY b/HISTORY index ecd549c9..ae1f54bb 100644 --- a/HISTORY +++ b/HISTORY @@ -8882,10 +8882,16 @@ Video Disk Recorder Revision History - Added a short sleep to cTSBuffer::Action() to avoid high CPU usage (thanks to Sergey Chernyavskiy). -2017-01-08: Version 2.3.3 +2017-01-09: Version 2.3.3 - Added 'S3W ABS-3A' to sources.conf (thanks to Frank Richter). - Fixed a possible deadlock in the recordings handler thread. - Updated the Russian OSD texts (thanks to Andrey Pridvorov). - Added a missing dependency to the Makefile to avoid error messages in the clean-plugins target (thanks to Tobias Grimm). +- The channel/CAM relations (i.e. the information which CAM can decrypt a given + channel) are now stored in the file 'cam.data' in the cache directory (suggested + by Dietmar Spingler). This speeds up switching to encrypted channels after + newly starting VDR, in case there is more than one CAM in the system. +- Fixed a flaw in handling timeouts for encrypted channels. + diff --git a/ci.c b/ci.c index 606875b8..fd3acd84 100644 --- a/ci.c +++ b/ci.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.c 4.3 2016/12/23 14:00:45 kls Exp $ + * $Id: ci.c 4.4 2017/01/09 12:51:05 kls Exp $ */ #include "ci.h" @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -2317,6 +2318,7 @@ void cChannelCamRelation::ClrDecrypt(int CamSlotNumber) // --- cChannelCamRelations -------------------------------------------------- +#define MAX_CAM_NUMBER 32 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups cChannelCamRelations ChannelCamRelations; @@ -2414,3 +2416,60 @@ void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber) if (ccr) ccr->ClrDecrypt(CamSlotNumber); } + +void cChannelCamRelations::Load(const char *FileName) +{ + cMutexLock MutexLock(&mutex); + fileName = FileName; + if (access(fileName, R_OK) == 0) { + dsyslog("loading %s", *fileName); + if (FILE *f = fopen(fileName, "r")) { + cReadLine ReadLine; + char *s; + while ((s = ReadLine.Read(f)) != NULL) { + if (char *p = strchr(s, ' ')) { + *p = 0; + if (*++p) { + tChannelID ChannelID = tChannelID::FromString(s); + if (ChannelID.Valid()) { + char *q; + char *strtok_next; + while ((q = strtok_r(p, " ", &strtok_next)) != NULL) { + int CamSlotNumber = atoi(q); + if (CamSlotNumber >= 1 && CamSlotNumber <= MAX_CAM_NUMBER) + SetDecrypt(ChannelID, CamSlotNumber); + p = NULL; + } + } + } + } + } + fclose(f); + } + else + LOG_ERROR_STR(*fileName); + } +} + +void cChannelCamRelations::Save(void) +{ + cMutexLock MutexLock(&mutex); + dsyslog("saving %s", *fileName); + cSafeFile f(fileName); + if (f.Open()) { + for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) { + if (ccr->ChannelID().Valid()) { + cString s; + for (int i = 1; i <= MAX_CAM_NUMBER; i++) { + if (ccr->CamDecrypt(i)) + s = cString::sprintf("%s%s%d", *s ? *s : "", *s ? " " : "", i); + } + if (*s) + fprintf(f, "%s %s\n", *ccr->ChannelID().ToString(), *s); + } + } + f.Close(); + } + else + LOG_ERROR_STR(*fileName); +} diff --git a/ci.h b/ci.h index 5a4dc98d..c61599e5 100644 --- a/ci.h +++ b/ci.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.h 3.11 2015/01/31 14:36:41 kls Exp $ + * $Id: ci.h 4.1 2017/01/09 12:51:05 kls Exp $ */ #ifndef __CI_H @@ -310,6 +310,7 @@ class cChannelCamRelation; class cChannelCamRelations : public cList { private: cMutex mutex; + cString fileName; cChannelCamRelation *GetEntry(tChannelID ChannelID); cChannelCamRelation *AddEntry(tChannelID ChannelID); time_t lastCleanup; @@ -323,6 +324,8 @@ public: void SetDecrypt(tChannelID ChannelID, int CamSlotNumber); void ClrChecked(tChannelID ChannelID, int CamSlotNumber); void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber); + void Load(const char *FileName); + void Save(void); }; extern cChannelCamRelations ChannelCamRelations; diff --git a/device.c b/device.c index 3c97b8ce..19997e45 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 4.3 2016/12/23 14:43:44 kls Exp $ + * $Id: device.c 4.4 2017/01/09 12:51:05 kls Exp $ */ #include "device.h" @@ -90,6 +90,7 @@ cDevice::cDevice(void) camSlot = NULL; startScrambleDetection = 0; + scramblingTimeout = 0; occupiedTimeout = 0; @@ -1574,6 +1575,7 @@ bool cDevice::Receiving(bool Dummy) const void cDevice::Action(void) { + time_t LastScrambledPacket = 0; if (Running() && OpenDvr()) { while (Running()) { // Read data from the DVR device: @@ -1590,15 +1592,16 @@ void cDevice::Action(void) cs = CamSlot(); CamSlotNumber = cs ? cs->SlotNumber() : 0; if (CamSlotNumber) { - int t = time(NULL) - startScrambleDetection; + if (LastScrambledPacket < startScrambleDetection) + LastScrambledPacket = startScrambleDetection; + time_t Now = time(NULL); if (TsIsScrambled(b)) { - if (t > TS_SCRAMBLING_TIMEOUT) + LastScrambledPacket = Now; + if (Now - startScrambleDetection > scramblingTimeout) DetachReceivers = true; } - else if (t > TS_SCRAMBLING_TIME_OK) { + if (Now - LastScrambledPacket > TS_SCRAMBLING_TIME_OK) DescramblingOk = true; - startScrambleDetection = 0; - } } } // Distribute the packet to all attached receivers: @@ -1606,14 +1609,17 @@ void cDevice::Action(void) for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] && receiver[i]->WantsPid(Pid)) { if (DetachReceivers && cs && (!cs->IsActivating() || receiver[i]->Priority() >= LIVEPRIORITY)) { - dsyslog("detaching receiver - won't decrypt channel %s with CAM %d", *receiver[i]->ChannelID().ToString(), CamSlotNumber); + dsyslog("CAM %d: won't decrypt channel %s, detaching receiver", CamSlotNumber, *receiver[i]->ChannelID().ToString()); ChannelCamRelations.SetChecked(receiver[i]->ChannelID(), CamSlotNumber); Detach(receiver[i]); } else receiver[i]->Receive(b, TS_SIZE); - if (DescramblingOk) + if (DescramblingOk) { + dsyslog("CAM %d: decrypts channel %s", CamSlotNumber, *receiver[i]->ChannelID().ToString()); ChannelCamRelations.SetDecrypt(receiver[i]->ChannelID(), CamSlotNumber); + startScrambleDetection = 0; + } } } Unlock(); @@ -1673,6 +1679,11 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) if (camSlot && Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver camSlot->StartDecrypting(); startScrambleDetection = time(NULL); + scramblingTimeout = TS_SCRAMBLING_TIMEOUT; + bool KnownToDecrypt = ChannelCamRelations.CamDecrypt(Receiver->ChannelID(), camSlot->SlotNumber()); + if (KnownToDecrypt) + scramblingTimeout *= 10; // give it time to receive ECM/EMM + dsyslog("CAM %d: %sknown to decrypt channel %s (scramblingTimeout = %ds)", camSlot->SlotNumber(), KnownToDecrypt ? "" : "not ", *Receiver->ChannelID().ToString(), scramblingTimeout); } Start(); return true; diff --git a/device.h b/device.h index 9475d23a..2639e855 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 4.2 2016/12/06 14:12:39 kls Exp $ + * $Id: device.h 4.3 2017/01/09 12:51:05 kls Exp $ */ #ifndef __DEVICE_H @@ -425,6 +425,7 @@ public: private: time_t startScrambleDetection; + int scramblingTimeout; cCamSlot *camSlot; public: virtual bool HasCi(void); diff --git a/vdr.5 b/vdr.5 index fa233d72..947bdc72 100644 --- a/vdr.5 +++ b/vdr.5 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 3.4 2015/02/17 13:43:53 kls Exp $ +.\" $Id: vdr.5 4.1 2017/01/09 13:35:08 kls Exp $ .\" .TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files" .SH NAME @@ -899,6 +899,20 @@ Note that the \fBevent id\fR that comes from the DVB data stream is actually just 16 bit wide. The internal representation in VDR allows for 32 bit to be used, so that external tools can generate EPG data that is guaranteed not to collide with the ids of existing data. +.SS CAM DATA +The file \fIcam.data\fR contains information about which CAM in the system can +decrypt a particular channel. +Each line in this file contains a channel id, followed by one or more (blank +separated) numbers, indicating the CAMs that have successfully decrypted this +channel earlier. + +When tuning to an encrypted channel, this information is used to select the +proper CAM for decrypting this channel. This channel/CAM relationship is not +hardcoded, though. If a given channel can't be decrypted with a CAM listed +in this file, other CAMs will be tried just as well. The main purpose of this +file is to speed up channel switching in systems with more than one CAM. + +This file will be read at program startup and saved when the program ends. .SS COMMANDLINE OPTIONS If started without any options, vdr tries to read any files in the directory /etc/vdr/conf.d with names that do not begin with a '.' and that end with '.conf'. diff --git a/vdr.c b/vdr.c index 8a494714..15dd2c02 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 4.9 2016/12/23 14:34:37 kls Exp $ + * $Id: vdr.c 4.10 2017/01/09 13:22:09 kls Exp $ */ #include @@ -878,6 +878,10 @@ int main(int argc, char *argv[]) if (!cPositioner::GetPositioner()) // no plugin has created a positioner new cDiseqcPositioner; + // CAM data: + + ChannelCamRelations.Load(AddDirectory(CacheDirectory, "cam.data")); + // Channel: if (!cDevice::WaitForAllDevicesReady(DEVICEREADYTIMEOUT)) @@ -1555,6 +1559,7 @@ Exit: StopSVDRPClientHandler(); StopSVDRPServerHandler(); + ChannelCamRelations.Save(); PluginManager.StopPlugins(); cRecordControls::Shutdown(); RecordingsHandler.DelAll();