diff --git a/HISTORY b/HISTORY index 8e32be5b..50ba2162 100644 --- a/HISTORY +++ b/HISTORY @@ -8882,7 +8882,7 @@ Video Disk Recorder Revision History - Added a short sleep to cTSBuffer::Action() to avoid high CPU usage (thanks to Sergey Chernyavskiy). -2017-01-09: Version 2.3.3 +2017-01-23: Version 2.3.3 - Added 'S3W ABS-3A' to sources.conf (thanks to Frank Richter). - Fixed a possible deadlock in the recordings handler thread. @@ -8900,3 +8900,13 @@ Video Disk Recorder Revision History forward/rewind. - Changed 'unsigned' to 'signed' in some places to avoid trouble with abs() in gcc6+ (reported by Derek Kelly). +- CAMs that can handle multiple devices at the same time can now indicate this + by creating the first cCamSlot as usual, and every other cCamSlot by giving + it the first one as its "MasterSlot". To VDR this means that when searching + for a CAM that can decrypt a particular channel, it only needs to ask the + master CAM slot whether it is suitable for decrypting, and can skip all the + other slots belonging to the same master. This can greatly speed up channel + switching on systems with more than one CAM (that can handle multiple devices). +- The LCARS skin now displays the master CAM's number when a device is tuned to + an encrypted channel. +- The Setup/CAM menu now only displays master CAMs. diff --git a/ci.c b/ci.c index fd3acd84..1096721b 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.4 2017/01/09 12:51:05 kls Exp $ + * $Id: ci.c 4.5 2017/01/23 11:42:14 kls Exp $ */ #include "ci.h" @@ -1731,11 +1731,12 @@ void cCiAdapter::Action(void) #define MODULE_CHECK_INTERVAL 500 // ms #define MODULE_RESET_TIMEOUT 2 // s -cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool ReceiveCaPids) +cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData, cCamSlot *MasterSlot) { ciAdapter = CiAdapter; + masterSlot = MasterSlot; assignedDevice = NULL; - caPidReceiver = ReceiveCaPids ? new cCaPidReceiver : NULL; + caPidReceiver = WantsTsData ? new cCaPidReceiver : NULL; caActivationReceiver = NULL; slotIndex = -1; lastModuleStatus = msReset; // avoids initial reset log message @@ -2227,10 +2228,21 @@ uchar *cCamSlot::Decrypt(uchar *Data, int &Count) cCamSlots CamSlots; +int cCamSlots::NumReadyMasterSlots(void) +{ + int n = 0; + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { + if (CamSlot->IsMasterSlot() && CamSlot->ModuleStatus() == msReady) + n++; + } + return n; +} + bool cCamSlots::WaitForAllCamSlotsReady(int Timeout) { + bool ready = true; for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) { - bool ready = true; + ready = true; for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { if (!CamSlot->Ready()) { ready = false; @@ -2238,9 +2250,11 @@ bool cCamSlots::WaitForAllCamSlotsReady(int Timeout) } } if (ready) - return true; + break; } - return false; + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) + dsyslog("CAM %d: %sready, %s", CamSlot->SlotNumber(), CamSlot->Ready() ? "" : "not ", CamSlot->IsMasterSlot() ? *cString::sprintf("master (%s)", CamSlot->GetCamName() ? CamSlot->GetCamName() : "empty") : *cString::sprintf("slave of CAM %d", CamSlot->MasterSlotNumber())); + return ready; } // --- cChannelCamRelation --------------------------------------------------- diff --git a/ci.h b/ci.h index c61599e5..a66cd2a2 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 4.1 2017/01/09 12:51:05 kls Exp $ + * $Id: ci.h 4.2 2017/01/23 11:27:39 kls Exp $ */ #ifndef __CI_H @@ -16,7 +16,7 @@ #include "thread.h" #include "tools.h" -#define MAX_CAM_SLOTS_PER_ADAPTER 8 // maximum possible value is 255 +#define MAX_CAM_SLOTS_PER_ADAPTER 16 // maximum possible value is 255 (same value as MAXDEVICES!) #define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254 #define CAM_READ_TIMEOUT 50 // ms @@ -132,6 +132,7 @@ private: cMutex mutex; cCondVar processed; cCiAdapter *ciAdapter; + cCamSlot *masterSlot; cDevice *assignedDevice; cCaPidReceiver *caPidReceiver; cCaActivationReceiver *caActivationReceiver; @@ -153,15 +154,28 @@ private: void Write(cTPDU *TPDU); cCiSession *GetSessionByResourceId(uint32_t ResourceId); public: - cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false); + cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false, cCamSlot *MasterSlot = NULL); ///< Creates a new CAM slot for the given CiAdapter. ///< The CiAdapter will take care of deleting the CAM slot, ///< so the caller must not delete it! ///< If WantsTsData is true, the device this CAM slot is assigned to will ///< call the Decrypt() function of this CAM slot, presenting it the complete ///< TS data stream of the encrypted programme, including the CA pids. + ///< If this CAM slot is basically the same as an other one, MasterSlot can + ///< be given to indicate this. This can be used for instance for CAM slots + ///< that can do MTD ("Multi Transponder Decryption"), where the first cCamSlot + ///< is created without giving a MasterSlot, and all others are given the first + ///< one as their MasterSlot. This can speed up the search for a suitable CAM + ///< when tuning to an encrypted channel, and it also makes the Setup/CAM menu + ///< clearer because only the master CAM slots will be shown there. virtual ~cCamSlot(); - bool Assign(cDevice *Device, bool Query = false); + bool IsMasterSlot(void) { return !masterSlot; } + ///< Returns true if this CAM slot itself is a master slot (which means that + ///< it doesn't have pointer to another CAM slot that's its master). + cCamSlot *MasterSlot(void) { return masterSlot ? masterSlot : this; } + ///< Returns this CAM slot's master slot, or a pointer to itself if it is a + ///< master slot. + virtual bool Assign(cDevice *Device, bool Query = false); ///< Assigns this CAM slot to the given Device, if this is possible. ///< If Query is 'true', the CI adapter of this slot only checks whether ///< it can be assigned to the Device, but doesn't actually assign itself to it. @@ -170,6 +184,10 @@ public: ///< device it was previously assigned to. The value of Query ///< is ignored in that case, and this function always returns ///< 'true'. + ///< If a derived class reimplements this function, it can return 'false' + ///< if this CAM can't be assigned to the given Device. If the CAM can be + ///< assigned to the Device, or if Device is NULL, it must call the base + ///< class function. cDevice *Device(void) { return assignedDevice; } ///< Returns the device this CAM slot is currently assigned to. bool WantsTsData(void) const { return caPidReceiver != NULL; } @@ -181,6 +199,9 @@ public: int SlotNumber(void) { return slotNumber; } ///< Returns the number of this CAM slot within the whole system. ///< The first slot has the number 1. + int MasterSlotNumber(void) { return masterSlot ? masterSlot->SlotNumber() : slotNumber; } + ///< Returns the number of this CAM's master slot within the whole system. + ///< The first slot has the number 1. virtual bool Reset(void); ///< Resets the CAM in this slot. ///< Returns true if the operation was successful. @@ -295,6 +316,9 @@ public: class cCamSlots : public cList { public: + int NumReadyMasterSlots(void); + ///< Returns the number of master CAM slots in the system that are ready + ///< to decrypt. bool WaitForAllCamSlotsReady(int Timeout = 0); ///< Waits until all CAM slots have become ready, or the given Timeout ///< (seconds) has expired. While waiting, the Ready() function of each diff --git a/device.c b/device.c index 9bb00b42..2b350e86 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.5 2017/01/09 14:25:38 kls Exp $ + * $Id: device.c 4.6 2017/01/23 11:43:05 kls Exp $ */ #include "device.h" @@ -252,7 +252,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used if (CamSlot->ModuleStatus() == msReady) { if (CamSlot->ProvidesCa(Channel->Caids())) { - if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) { + if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->MasterSlotNumber())) { SlotPriority[CamSlot->Index()] = CamSlot->Priority(); NumUsableSlots++; } @@ -300,7 +300,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels imp <<= 1; imp |= device[i]->AvoidRecording(); // avoid SD full featured cards - imp <<= 1; imp |= (NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel + imp <<= 1; imp |= (NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlots.Get(j)->MasterSlotNumber()) : 0; // prefer CAMs that are known to decrypt this channel imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device if (imp < Impact) { // This device has less impact than any previous one, so we take it. @@ -1590,7 +1590,7 @@ void cDevice::Action(void) cCamSlot *cs = NULL; if (startScrambleDetection) { cs = CamSlot(); - CamSlotNumber = cs ? cs->SlotNumber() : 0; + CamSlotNumber = cs ? cs->MasterSlotNumber() : 0; if (CamSlotNumber) { if (LastScrambledPacket < startScrambleDetection) LastScrambledPacket = startScrambleDetection; @@ -1678,10 +1678,10 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) Unlock(); if (camSlot && Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver camSlot->StartDecrypting(); - if (CamSlots.Count() > 1) { // don't try different CAMs if there is only one + if (CamSlots.NumReadyMasterSlots() > 1) { // don't try different CAMs if there is only one startScrambleDetection = time(NULL); scramblingTimeout = TS_SCRAMBLING_TIMEOUT; - bool KnownToDecrypt = ChannelCamRelations.CamDecrypt(Receiver->ChannelID(), camSlot->SlotNumber()); + bool KnownToDecrypt = ChannelCamRelations.CamDecrypt(Receiver->ChannelID(), camSlot->MasterSlotNumber()); 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); diff --git a/menu.c b/menu.c index f784d16f..642432ac 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 4.20 2017/01/09 14:45:50 kls Exp $ + * $Id: menu.c 4.21 2017/01/23 12:01:48 kls Exp $ */ #include "menu.h" @@ -3770,8 +3770,15 @@ bool cMenuSetupCAMItem::Changed(void) else if (camSlot->IsActivating()) // TRANSLATORS: note the leading blank! Activating = tr(" (activating)"); - if (cDevice *Device = camSlot->Device()) - AssignedDevice = cString::sprintf(" %s %d", tr("@ device"), Device->CardIndex() + 1); + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { + if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot) { + if (cDevice *Device = CamSlot->Device()) { + if (!**AssignedDevice) + AssignedDevice = cString::sprintf(" %s", tr("@ device")); + AssignedDevice = cString::sprintf("%s %d", *AssignedDevice, Device->CardIndex() + 1); + } + } + } cString buffer = cString::sprintf(" %d %s%s%s", camSlot->SlotNumber(), CamName, *AssignedDevice, Activating); if (strcmp(buffer, Text()) != 0) { SetText(buffer); @@ -3799,8 +3806,10 @@ cMenuSetupCAM::cMenuSetupCAM(void) SetSection(tr("CAM")); SetCols(15); SetHasHotkeys(); - for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) - Add(new cMenuSetupCAMItem(CamSlot)); + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { + if (CamSlot->IsMasterSlot()) // we only list master CAM slots + Add(new cMenuSetupCAMItem(CamSlot)); + } SetHelpKeys(); } diff --git a/skinlcars.c b/skinlcars.c index 0fae3b85..93ada5ee 100644 --- a/skinlcars.c +++ b/skinlcars.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinlcars.c 4.2 2016/12/22 14:05:56 kls Exp $ + * $Id: skinlcars.c 4.3 2017/01/19 15:27:48 kls Exp $ */ // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures, @@ -254,7 +254,7 @@ static bool DrawDeviceData(cOsd *Osd, const cDevice *Device, int x0, int y0, int LastDeviceType = DeviceType; // CAM: if (CamSlot) { - cString s = cString::sprintf("CAM %d", CamSlot->SlotNumber()); + cString s = cString::sprintf("CAM %d", CamSlot->MasterSlotNumber()); Osd->DrawText(x, y1 - TinyFont->Height(), s, ColorFg, ColorBg, TinyFont); xs = max(xs, x + TinyFont->Width(s)); }