Implemented the concept of 'master cams'

This commit is contained in:
Klaus Schmidinger 2017-01-23 12:01:48 +01:00
parent 6121095a30
commit 9b9d15438e
6 changed files with 81 additions and 24 deletions

12
HISTORY
View File

@ -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.

26
ci.c
View File

@ -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 ---------------------------------------------------

32
ci.h
View File

@ -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<cCamSlot> {
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

View File

@ -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);

19
menu.c
View File

@ -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();
}

View File

@ -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));
}