1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

CAM handling refactored; multiple recordings with one CAM; automatic CAM selection

This commit is contained in:
Klaus Schmidinger 2007-01-07 14:46:14 +01:00
parent b4cab10eca
commit 87dd5139ff
32 changed files with 2116 additions and 1457 deletions

53
HISTORY
View File

@ -5028,3 +5028,56 @@ Video Disk Recorder Revision History
2007-01-07: Version 1.4.5
- Official release.
2007-01-07: Version 1.5.0
- The CAM handling has been refactored. Instead of a cCiHandler per device there
is now an abstract cCiAdapter and a cCamSlot. This allows each slot to be
accessed individually.
- The general 15 seconds workaround time before opening the CAM menu has been
removed. If the CAM menu doesn't open within a timeout, the enter menu command
is now sent again.
- If a CAM is reset or pulled and reinserted, it now automatically starts
decrypting the current channel again.
- The Setup/CAM menu now dynamically refreshes its items and displays whether
a CAM is present or ready. The 'Reset' function no longer leaves the menu.
- The CAM menu will now be openend when pressing the Ok key on a slot entry.
- The CAM menu now stays within the current menu context and doesn't close and
reopen the menu every time an option is selected.
- When an encrypted channel is switched to for the first time, VDR now checks
explicitly whether a CAM can actually decrypt that channel. If there is more
than one CAM in the system that claims to be able to decrypt the channel,
they are all tried in turn.
To make this possible, an encrypted channel needs to be received in Transfer
Mode when it is switched to for the first time, so that VDR can determine
whether the TS packets are actually decrypted. Once a channel is known to
be decrypted by a particular CAM, the next time it is switched to it will
be shown in normal live viewing mode.
- A cDevice now automatically detaches all cReceiver objects that receive PIDs
that can't be decrypted with the current CAM. A plugin that attaches a cReceiver
to a device should therefore watch the receiver's IsAttached() function to
see if it is still attached to the device.
- The cReceiver constructor no longer takes an 'int Ca' as its first parameter,
but rather a 'tChannelID ChannelID'. This is necessary for the device to be
able to determine which CAM a particular channel can be decrypted with. If the
channel is known to be unencrypted, or a plugin doesn't want to provide the
channel id for other reasons, an invalid tChannelID() can be given.
- The cThread::Start() function now waits until a previous incarnation of this
thread has actually stopped. Before this it could happen that a thread's
Cancel(-1) function was called and immediately after that it was started again,
but the Start() function still found it to be 'active'.
- The parameter NeedsDetachReceivers in cDevice::GetDevice(const cChannel *Channel, ...)
has been removed. A call to this function will automatically detach all receivers
from the device if it returns a non-NULL pointer.
- The cTimeMs class now accepts an initial timeout value in its constructor.
- A CAM is now explicitly instructed to stop decrypting when switching away from
an encrypted channel.
- If the CAM in use can decrypt several channels at the same time, VDR can
now make use if this capability. Whether or not a CAM can decrypt more
than one channel is determined by sending it an initial empty QUERY command
and testing whether it replies to it.
- Ca values in the range 0...F in channels.conf can still be used to assign a channel
to a particular device, but this will no longer work with encrypted channels because
without valid CA ids VDR can't decide which CAM slot to use. However, since VDR now
automatically determines which CAM can decrypt which channel, setting fixed
channel/device relations should no longer be necessary.

14
MANUAL
View File

@ -667,13 +667,15 @@ Version 1.4
Use DiSEqC = no Generally turns DiSEqC support on or off.
CICAM:
CAM:
CICAM DVBn m Shows the CAMs that each device contains, where 'n' is
the number of the device, and 'm' is the number of the
Common Interface slot of that device. The "Red" key
can be pressed to enter the CAM menu, and the "Green" key
triggers a reset of the selected CAM.
n CAM Name Shows the CAM slots that are present in this system, where
'n' is the number of the slot, followed by the name of the
CAM. If a CAM slot is empty, '-' is displayed as name, and
if it is in the process of being reset, its current status
is displayed. The "Red" key can be pressed to enter the CAM
menu, and the "Green" key triggers a reset of the selected
slot. The "Ok" key also opens the CAM menu.
Recording:

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.95 2006/08/20 10:44:22 kls Exp $
# $Id: Makefile 1.96 2007/01/07 14:38:00 kls Exp $
.DELETE_ON_ERROR:
@ -32,7 +32,7 @@ DOXYFILE = Doxyfile
SILIB = $(LSIDIR)/libsi.a
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\

View File

@ -6,7 +6,7 @@
<center><h1>The VDR Plugin System</h1></center>
<center><b>Version 1.4.1</b></center>
<center><b>Version 1.5.0</b></center>
<p>
<center>
Copyright &copy; 2006 Klaus Schmidinger<br>
@ -14,6 +14,10 @@ Copyright &copy; 2006 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.5.0 are marked like this.
<!--X1.5.0--></td></tr></table>
<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
This interface allows programmers to develop additional functionality for VDR completely
@ -72,6 +76,9 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Devices">Devices</a>
<li><a href="#Audio">Audio</a>
<li><a href="#Remote Control">Remote Control</a>
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<li><a href="#Conditional Access">Conditional Access</a>
<!--X1.5.0--></td></tr></table>
</ul>
</ul>
@ -1727,6 +1734,7 @@ selecting which channel it shall tune to:
<p><table><tr><td bgcolor=#F0F0F0><pre>
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
</pre></td></tr></table><p>
@ -2038,5 +2046,42 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
The other parameters have the same meaning as in the first version of this function.
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<a name="Conditional Access"><hr><h2>Conditional Access</h2>
<center><i><b>Members only!</b></i></center><p>
Pay TV providers usually encrypt their broadcasts, so that only viewers who
have the proper smart card can watch them. Such a smart card needs to be inserted
into a CAM (Conditional Access Module), which in turn goes into a CI (Common
Interface) slot.
<p>
VDR's mechanisms for supporting Conditional Access are mainly the two classes
<tt>cCiAdapter</tt> and <tt>cCamSlot</tt>. A <tt>cCiAdapter</tt> handles exactly
one CI, and can provide several CAM slots, represented by the appropriate
number of <tt>cCamSlot</tt> objects.
<p>
In order to decrypt a particular channel, a <tt>cCiAdapter</tt> with a <tt>cCamSlot</tt>
that contains the necessary CAM will be assigned to a <tt>cDevice</tt>, and exactly
one of its CAM slots will be activated. Whether or not a <tt>cCiAdapter</tt> can
be assigned to a particular device depends on the hardware implementation.
Some devices (like the Siemens/Technotrend DVB cards) are hardwired with their
CI adapters, so the <tt>cCiAdapter</tt> for these can only be used with one device.
Other hardware implementations may allow CI adapters and devices to be connected
through some sort of matrix switch. When trying to decrypt an encrypted channel,
VDR will automatically select a useful combination of device and CAM slot.
<p>
If a plugin implements a derived <tt>cCiAdapter</tt>, it has to implement
several low level functions that handle the actual data transfer (see <tt>dvbci.c</tt>
for example). The decision whether the adapter can actually be assigned to different
devices is made in the function
<p><table><tr><td bgcolor=#F0F0F0><pre>
virtual bool Assign(cDevice *Device, bool Query = false);
</pre></td></tr></table><p>
See the description of this function in <tt>ci.h</tt> for details.
<!--X1.5.0--></td></tr></table>
</body>
</html>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: channels.h 1.42 2006/05/28 15:03:56 kls Exp $
* $Id: channels.h 1.43 2007/01/07 14:37:35 kls Exp $
*/
#ifndef __CHANNELS_H
@ -179,6 +179,7 @@ public:
const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; }
const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
int Tpid(void) const { return tpid; }
const int *Caids(void) const { return caids; }
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; }
int Tid(void) const { return tid; }

1818
ci.c

File diff suppressed because it is too large Load Diff

249
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 1.22 2006/08/12 09:43:31 kls Exp $
* $Id: ci.h 1.23 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __CI_H
@ -12,13 +12,18 @@
#include <stdint.h>
#include <stdio.h>
#include "channels.h"
#include "thread.h"
#include "tools.h"
#define MAX_CAM_SLOTS_PER_ADAPTER 8 // maximum possible value is 255
#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
#define CAM_READ_TIMEOUT 50 // ms
class cCiMMI;
class cCiMenu {
friend class cCiHandler;
friend class cCamSlot;
friend class cCiMMI;
private:
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
@ -40,14 +45,14 @@ public:
const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
int NumEntries(void) { return numEntries; }
bool Selectable(void) { return selectable; }
bool Select(int Index);
bool Cancel(void);
bool Abort(void);
void Select(int Index);
void Cancel(void);
void Abort(void);
bool HasUpdate(void);
};
class cCiEnquiry {
friend class cCiHandler;
friend class cCamSlot;
friend class cCiMMI;
private:
cCiMMI *mmi;
@ -61,103 +66,140 @@ public:
const char *Text(void) { return text; }
bool Blind(void) { return blind; }
int ExpectedLength(void) { return expectedLength; }
bool Reply(const char *s);
bool Cancel(void);
bool Abort(void);
void Reply(const char *s);
void Cancel(void);
void Abort(void);
};
#define MAX_CI_SESSION 16 //XXX
#define MAX_CI_SLOT 16
class cDevice;
class cCamSlot;
class cCiCaPidData : public cListObject {
enum eModuleStatus { msNone, msReset, msPresent, msReady };
class cCiAdapter : public cThread {
friend class cCamSlot;
private:
cDevice *assignedDevice;
cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
void AddCamSlot(cCamSlot *CamSlot);
///< Adds the given CamSlot to this CI adapter.
protected:
virtual void Action(void);
///< Handles the attached CAM slots in a separate thread.
///< The derived class must call the Start() function to
///< actually start CAM handling.
virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
///< Returns the number of bytes read (in case of an error it will also
///< return 0).
virtual void Write(const uint8_t *Buffer, int Length) = 0;
///< Writes Length bytes of the given Buffer.
virtual bool Reset(int Slot) = 0;
///< Resets the CAM in the given Slot.
///< Returns true if the operation was successful.
virtual eModuleStatus ModuleStatus(int Slot) = 0;
///< Returns the status of the CAM in the given Slot.
virtual bool Assign(cDevice *Device, bool Query = false) = 0;
///< Assigns this adapter to the given Device, if this is possible.
///< If Query is 'true', the adapter only checks whether it can be
///< assigned to the Device, but doesn't actually assign itself to it.
///< Returns true if the adapter can be assigned to the Device.
///< If Device is NULL, the adapter will be unassigned from any
///< device it was previously assigned to. The value of Query
///< is ignored in that case, and this function always returns
///< 'true'.
public:
bool active;
int pid;
int streamType;
cCiCaPidData(int Pid, int StreamType)
{
active = false;
pid = Pid;
streamType = StreamType;
}
cCiAdapter(void);
virtual ~cCiAdapter();
///< The derived class must call Cancel(3) in its destructor.
virtual bool Ready(void);
///< Returns 'true' if all present CAMs in this adapter are ready.
};
class cCiCaProgramData : public cListObject {
public:
int programNumber;
cList<cCiCaPidData> pidList;
cCiCaProgramData(int ProgramNumber)
{
programNumber = ProgramNumber;
}
};
class cCiSession;
class cCiTransportLayer;
class cTPDU;
class cCiTransportConnection;
class cCiSession;
class cCiCaProgramData;
class cCiHandler {
class cCamSlot : public cListObject {
friend class cCiAdapter;
friend class cCiTransportConnection;
private:
cMutex mutex;
int fd;
int numSlots;
bool newCaSupport;
bool hasUserIO;
bool moduleReady[MAX_CI_SLOT];
cCiSession *sessions[MAX_CI_SESSION];
cCiTransportLayer *tpl;
cCiTransportConnection *tc;
cCondVar processed;
cCiAdapter *ciAdapter;
int slotIndex;
int slotNumber;
cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1]; // connection numbering starts with 1
eModuleStatus lastModuleStatus;
time_t resetTime;
cTimeMs moduleCheckTimer;
bool resendPmt;
int source;
int transponder;
cList<cCiCaProgramData> caProgramList;
uint32_t ResourceIdToInt(const uint8_t *Data);
bool Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
const unsigned short *GetCaSystemIds(int Slot);
cCiSession *GetSessionBySessionId(uint16_t SessionId);
cCiSession *GetSessionByResourceId(uint32_t ResourceId, int Slot);
cCiSession *CreateSession(uint32_t ResourceId);
bool OpenSession(int Length, const uint8_t *Data);
bool CloseSession(uint16_t SessionId);
int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots);
void SendCaPmt(void);
const int *GetCaSystemIds(void);
void SendCaPmt(uint8_t CmdId);
void NewConnection(void);
void DeleteAllConnections(void);
void Process(cTPDU *TPDU = NULL);
void Write(cTPDU *TPDU);
cCiSession *GetSessionByResourceId(uint32_t ResourceId);
public:
~cCiHandler();
static cCiHandler *CreateCiHandler(const char *FileName);
///< Creates a new cCiHandler for the given CA device.
int NumSlots(void) { return numSlots; }
///< Returns the number of CAM slots provided by this CA device.
int NumCams(void);
///< Returns the number of actual CAMs inserted into this CA device.
cCamSlot(cCiAdapter *CiAdapter);
///< 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!
virtual ~cCamSlot();
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.
///< Returns true if this slot can be assigned to the Device.
///< If Device is NULL, the slot will be unassigned from any
///< device it was previously assigned to. The value of Query
///< is ignored in that case, and this function always returns
///< 'true'.
cDevice *Device(void);
///< Returns the device this CAM slot is currently assigned to.
int SlotIndex(void) { return slotIndex; }
///< Returns the index of this CAM slot within its CI adapter.
///< The first slot has an index of 0.
int SlotNumber(void) { return slotNumber; }
///< Returns the number of this CAM slot within the whole system.
///< The first slot has the number 1.
bool Reset(void);
///< Resets the CAM in this slot.
///< Returns true if the operation was successful.
eModuleStatus ModuleStatus(void);
///< Returns the status of the CAM in this slot.
const char *GetCamName(void);
///< Returns the name of the CAM in this slot, or NULL if there is
///< no ready CAM in this slot.
bool Ready(void);
///< Returns true if all CAMs in this CA device are ready.
bool Process(int Slot = -1);
///< Processes the given Slot. If Slot is -1, all slots are processed.
///< Returns false in case of an error.
bool HasUserIO(void) { return hasUserIO; }
///< Returns 'true' if the CAM in this slot is ready to decrypt.
bool HasMMI(void);
///< Returns 'true' if the CAM in this slot has an active MMI.
bool HasUserIO(void);
///< Returns true if there is a pending user interaction, which shall
///< be retrieved via GetMenu() or GetEnquiry().
bool EnterMenu(int Slot);
///< Requests the CAM in the given Slot to start its menu.
bool EnterMenu(void);
///< Requests the CAM in this slot to start its menu.
cCiMenu *GetMenu(void);
///< Gets a pending menu, or NULL if there is no menu.
cCiEnquiry *GetEnquiry(void);
///< Gets a pending enquiry, or NULL if there is no enquiry.
const char *GetCamName(int Slot);
///< Returns the name of the CAM in the given Slot, or NULL if there
///< is no CAM in that slot.
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
///< Returns true if any of the CAMs can provide one of the given
int Priority(void);
///< Returns the priority if the device this slot is currently assigned
///< to, or -1 if it is not assigned to any device.
bool ProvidesCa(const int *CaSystemIds);
///< Returns true if the CAM in this slot provides one of the given
///< CaSystemIds. This doesn't necessarily mean that it will be
///< possible to actually decrypt such a programme, since CAMs
///< usually advertise several CA system ids, while the actual
///< decryption is controlled by the smart card inserted into
///< the CAM.
void SetSource(int Source, int Transponder);
///< Sets the Source and Transponder of the device this cCiHandler is
///< currently tuned to. If Source or Transponder are different than
///< what was given in a previous call to SetSource(), any previously
///< added PIDs will be cleared.
void AddPid(int ProgramNumber, int Pid, int StreamType);
///< Adds the given PID information to the list of PIDs. A later call
///< to SetPid() will (de)activate one of these entries.
@ -165,16 +207,55 @@ public:
///< Sets the given Pid (which has previously been added through a
///< call to AddPid()) to Active. A later call to StartDecrypting() will
///< send the full list of currently active CA_PMT entries to the CAM.
bool CanDecrypt(int ProgramNumber);
///< XXX
///< Returns true if there is a CAM in this CA device that is able
///< to decrypt the programme with the given ProgramNumber. The PIDs
///< for this ProgramNumber must have been set through previous calls
///< to SetPid().
void AddChannel(const cChannel *Channel);
///< Adds all PIDs if the given Channel to the current list of PIDs.
///< If the source or transponder of the channel are different than
///< what was given in a previous call to AddChannel(), any previously
///< added PIDs will be cleared.
bool CanDecrypt(const cChannel *Channel);
///< Returns true if there is a CAM in this slot that is able to decrypt
///< the given Channel (or at least claims to be able to do so).
///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
///< CAMs don't reply to queries at all), we always return true if the
///< CAM is currently not decrypting anything. If there is already a
///< channel being decrypted, a call to CanDecrypt() checks whether the
///< CAM can also decrypt the given channel. Only CAMs that have replied
///< to the inital QUERY will perform this check at all. CAMs that never
///< replied to the initial QUERY are assumed not to be able to handle
///< more than one channel at a time.
void StartDecrypting(void);
///< Triggers sending all currently active CA_PMT entries to the CAM,
///< so that it will start decrypting.
bool Reset(int Slot);
void StopDecrypting(void);
///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
bool IsDecrypting(void);
///< Returns true if the CAM in this slot is currently used for decrypting.
};
class cCamSlots : public cList<cCamSlot> {};
extern cCamSlots CamSlots;
class cChannelCamRelation;
class cChannelCamRelations : public cList<cChannelCamRelation> {
private:
cMutex mutex;
cChannelCamRelation *GetEntry(tChannelID ChannelID);
cChannelCamRelation *AddEntry(tChannelID ChannelID);
time_t lastCleanup;
void Cleanup(void);
public:
cChannelCamRelations(void);
void Reset(int CamSlotNumber);
bool CamChecked(tChannelID ChannelID, int CamSlotNumber);
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber);
void SetChecked(tChannelID ChannelID, int CamSlotNumber);
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber);
void ClrChecked(tChannelID ChannelID, int CamSlotNumber);
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber);
};
extern cChannelCamRelations ChannelCamRelations;
#endif //__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: config.h 1.282 2007/01/07 13:45:19 kls Exp $
* $Id: config.h 1.283 2007/01/07 14:09:31 kls Exp $
*/
#ifndef __CONFIG_H
@ -21,13 +21,13 @@
// VDR's own version number:
#define VDRVERSION "1.4.5"
#define VDRVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
#define VDRVERSION "1.5.0"
#define VDRVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number:
#define APIVERSION "1.4.5"
#define APIVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
#define APIVERSION "1.5.0"
#define APIVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to

274
device.c
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 1.137 2006/09/03 10:13:25 kls Exp $
* $Id: device.c 1.138 2007/01/07 14:41:07 kls Exp $
*/
#include "device.h"
@ -164,7 +164,9 @@ cDevice::cDevice(void)
sdtFilter = NULL;
nitFilter = NULL;
ciHandler = NULL;
camSlot = NULL;
startScrambleDetection = 0;
player = NULL;
pesAssembler = new cPesAssembler;
ClrAvailableTracks();
@ -183,9 +185,7 @@ cDevice::cDevice(void)
cDevice::~cDevice()
{
Detach(player);
for (int i = 0; i < MAXRECEIVERS; i++)
Detach(receiver[i]);
delete ciHandler;
DetachAllReceivers();
delete nitFilter;
delete sdtFilter;
delete patFilter;
@ -199,8 +199,10 @@ bool cDevice::WaitForAllDevicesReady(int Timeout)
for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
bool ready = true;
for (int i = 0; i < numDevices; i++) {
if (device[i] && !device[i]->Ready())
if (device[i] && !device[i]->Ready()) {
ready = false;
cCondWait::SleepMs(100);
}
}
if (ready)
return true;
@ -278,39 +280,98 @@ cDevice *cDevice::GetDevice(int Index)
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
}
cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView)
{
cDevice *d = NULL;
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int i = 0; i < numDevices; i++) {
bool ndr;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
// Put together an integer number that reflects the "impact" using
// this device would have on the overall system. Each condition is represented
// by one bit in the number (or several bits, if the condition is actually
// a numeric value). The sequence in which the conditions are listed corresponds
// to their individual severity, where the one listed first will make the most
// difference, because it results in the most significant bit of the result.
uint imp = 0;
imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 8; imp |= min(max(device[i]->ProvidesCa(Channel), 0), 0xFF); // use the device that provides the lowest number of conditional access methods
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
if (imp < Impact) {
// This device has less impact than any previous one, so we take it.
Impact = imp;
d = device[i];
if (NeedsDetachReceivers)
*NeedsDetachReceivers = ndr;
// Collect the current priorities of all CAM slots that can decrypt the channel:
int NumCamSlots = CamSlots.Count();
int SlotPriority[NumCamSlots];
int NumUsableSlots = 0;
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
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())) {
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
NumUsableSlots++;
}
}
}
}
if (!NumUsableSlots)
return NULL; // no CAM is able to decrypt this channel
}
bool NeedsDetachReceivers = false;
cDevice *d = NULL;
cCamSlot *s = NULL;
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
continue; // there is no CAM available in this slot
for (int i = 0; i < numDevices; i++) {
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
continue; // a specific card was requested, but not this one
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
continue; // CAM slot can't be used with this device
bool ndr;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
ndr = true; // using a different CAM slot requires detaching receivers
// Put together an integer number that reflects the "impact" using
// this device would have on the overall system. Each condition is represented
// by one bit in the number (or several bits, if the condition is actually
// a numeric value). The sequence in which the conditions are listed corresponds
// to their individual severity, where the one listed first will make the most
// difference, because it results in the most significant bit of the result.
uint32_t imp = 0;
imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
if (imp < Impact) {
// This device has less impact than any previous one, so we take it.
Impact = imp;
d = device[i];
NeedsDetachReceivers = ndr;
if (NumUsableSlots)
s = CamSlots.Get(j);
}
}
}
if (!NumUsableSlots)
break; // no CAM necessary, so just one loop over the devices
}
if (d) {
if (NeedsDetachReceivers)
d->DetachAllReceivers();
if (s) {
if (s->Device() != d) {
if (s->Device())
s->Device()->DetachAllReceivers();
if (d->CamSlot())
d->CamSlot()->Assign(NULL);
s->Assign(d);
}
}
else if (d->CamSlot() && !d->CamSlot()->IsDecrypting())
d->CamSlot()->Assign(NULL);
}
return d;
}
void cDevice::SetCamSlot(cCamSlot *CamSlot)
{
camSlot = CamSlot;
}
void cDevice::Shutdown(void)
{
primaryDevice = NULL;
@ -422,8 +483,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
if (ciHandler)
ciHandler->SetPid(Pid, true);
if (camSlot)
camSlot->SetPid(Pid, true);
}
PRINTPIDS("a");
return true;
@ -451,8 +512,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
if (ciHandler)
ciHandler->SetPid(Pid, true);
if (camSlot)
camSlot->SetPid(Pid, true);
}
}
return true;
@ -479,8 +540,8 @@ void cDevice::DelPid(int Pid, ePidType PidType)
if (pidHandles[n].used == 0) {
pidHandles[n].handle = -1;
pidHandles[n].pid = 0;
if (ciHandler)
ciHandler->SetPid(Pid, false);
if (camSlot)
camSlot->SetPid(Pid, false);
}
}
PRINTPIDS("E");
@ -557,8 +618,10 @@ bool cDevice::MaySwitchTransponder(void)
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
{
if (LiveView)
if (LiveView) {
isyslog("switching to channel %d", Channel->Number());
cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
}
for (int i = 3; i--;) {
switch (SetChannel(Channel, LiveView)) {
case scrOk: return true;
@ -578,12 +641,13 @@ bool cDevice::SwitchChannel(int Direction)
bool result = false;
Direction = sgn(Direction);
if (Direction) {
cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
int n = CurrentChannel() + Direction;
int first = n;
cChannel *channel;
while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
// try only channels which are currently available
if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0))
if (GetDevice(channel, 0, true))
break;
n = channel->Number() + Direction;
}
@ -607,14 +671,9 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (LiveView)
StopReplay();
// If this card is switched to an other transponder, any receivers still
// attached to it need to be automatically detached:
bool NeedsDetachReceivers = false;
cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
// If this card can't receive this channel, we must not actually switch
// the channel here, because that would irritate the driver when we
// start replaying in Transfer Mode immediately after switching the channel:
bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit, &NeedsDetachReceivers));
bool NeedsTransferMode = Device != this;
eSetChannelResult Result = scrOk;
@ -622,14 +681,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
// use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) {
cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers);
if (CaDevice && CanReplay()) {
if (Device && CanReplay()) {
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
if (CaDevice->SetChannel(Channel, false) == scrOk) { // calling SetChannel() directly, not SwitchChannel()!
if (NeedsDetachReceivers)
CaDevice->DetachAllReceivers();
cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
}
if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
else
Result = scrNoTransfer;
}
@ -644,27 +699,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(false);
sectionHandler->SetChannel(NULL);
}
// Tell the ciHandler about the channel switch and add all PIDs of this
// Tell the camSlot about the channel switch and add all PIDs of this
// channel to it, for possible later decryption:
if (ciHandler) {
ciHandler->SetSource(Channel->Source(), Channel->Transponder());
// Men at work - please stand clear! ;-)
#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
#endif
ciHandler->AddPid(Channel->Sid(), Channel->Vpid(), 2);
for (const int *Apid = Channel->Apids(); *Apid; Apid++)
ciHandler->AddPid(Channel->Sid(), *Apid, 4);
for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
ciHandler->AddPid(Channel->Sid(), *Dpid, 0);
#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
bool CanDecrypt = ciHandler->CanDecrypt(Channel->Sid());//XXX
dsyslog("CanDecrypt %d %d %d %s", CardIndex() + 1, CanDecrypt, Channel->Number(), Channel->Name());//XXX
}
#endif
}
if (NeedsDetachReceivers)
DetachAllReceivers();
if (camSlot)
camSlot->AddChannel(Channel);
if (SetChannelDevice(Channel, LiveView)) {
// Start section handling:
if (sectionHandler) {
@ -672,8 +710,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(true);
}
// Start decrypting any PIDs that might have been set in SetChannelDevice():
if (ciHandler)
ciHandler->StartDecrypting();
if (camSlot)
camSlot->StartDecrypting();
}
else
Result = scrFailed;
@ -968,9 +1006,10 @@ bool cDevice::AttachPlayer(cPlayer *Player)
void cDevice::Detach(cPlayer *Player)
{
if (Player && player == Player) {
player->Activate(false);
player->device = NULL;
player = NULL;
cPlayer *p = player;
player = NULL; // avoids recursive calls to Detach()
p->Activate(false);
p->device = NULL;
SetPlayMode(pmNone);
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
Audios.ClearAudio();
@ -1143,16 +1182,6 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
return Length;
}
int cDevice::Ca(void) const
{
int ca = 0;
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i] && (ca = receiver[i]->ca) != 0)
break; // all receivers have the same ca
}
return ca;
}
int cDevice::Priority(void) const
{
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
@ -1168,16 +1197,6 @@ bool cDevice::Ready(void)
return true;
}
int cDevice::ProvidesCa(const cChannel *Channel) const
{
int Ca = Channel->Ca();
if (Ca == CardIndex() + 1)
return 1; // exactly _this_ card was requested
if (Ca && Ca <= CA_DVB_MAX)
return 0; // a specific card was requested, but not _this_ one
return !Ca; // by default every card can provide FTA
}
bool cDevice::Receiving(bool CheckAny) const
{
for (int i = 0; i < MAXRECEIVERS; i++) {
@ -1187,6 +1206,10 @@ bool cDevice::Receiving(bool CheckAny) const
return false;
}
#define TS_SCRAMBLING_CONTROL 0xC0
#define TS_SCRAMBLING_TIMEOUT 3 // seconds to wait until a TS becomes unscrambled
#define TS_SCRAMBLING_TIME_OK 10 // seconds before a Channel/CAM combination is marked a known to decrypt
void cDevice::Action(void)
{
if (Running() && OpenDvr()) {
@ -1196,11 +1219,39 @@ void cDevice::Action(void)
if (GetTSPacket(b)) {
if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
// Check whether the TS packets are scrambled:
bool DetachReceivers = false;
bool DescramblingOk = false;
int CamSlotNumber = 0;
if (startScrambleDetection) {
cCamSlot *cs = CamSlot();
CamSlotNumber = cs ? cs->SlotNumber() : 0;
if (CamSlotNumber) {
bool Scrambled = b[3] & TS_SCRAMBLING_CONTROL;
int t = time(NULL) - startScrambleDetection;
if (Scrambled) {
if (t > TS_SCRAMBLING_TIMEOUT)
DetachReceivers = true;
}
else if (t > TS_SCRAMBLING_TIME_OK) {
DescramblingOk = true;
startScrambleDetection = 0;
}
}
}
// Distribute the packet to all attached receivers:
Lock();
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i] && receiver[i]->WantsPid(Pid))
receiver[i]->Receive(b, TS_SIZE);
if (receiver[i] && receiver[i]->WantsPid(Pid)) {
if (DetachReceivers) {
ChannelCamRelations.SetChecked(receiver[i]->ChannelID(), CamSlotNumber);
Detach(receiver[i]);
}
else
receiver[i]->Receive(b, TS_SIZE);
if (DescramblingOk)
ChannelCamRelations.SetDecrypt(receiver[i]->ChannelID(), CamSlotNumber);
}
}
Unlock();
}
@ -1256,10 +1307,11 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
Receiver->device = this;
receiver[i] = Receiver;
Unlock();
if (!Running())
Start();
if (ciHandler)
ciHandler->StartDecrypting();
if (camSlot) {
camSlot->StartDecrypting();
startScrambleDetection = time(NULL);
}
Start();
return true;
}
}
@ -1286,10 +1338,10 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i])
receiversLeft = true;
}
if (ciHandler)
ciHandler->StartDecrypting();
if (camSlot)
camSlot->StartDecrypting();
if (!receiversLeft)
Cancel(3);
Cancel(-1);
}
void cDevice::DetachAll(int Pid)
@ -1307,10 +1359,8 @@ void cDevice::DetachAll(int Pid)
void cDevice::DetachAllReceivers(void)
{
cMutexLock MutexLock(&mutexReceiver);
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i])
Detach(receiver[i]);
}
for (int i = 0; i < MAXRECEIVERS; i++)
Detach(receiver[i]);
}
// --- cTSBuffer -------------------------------------------------------------

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.79 2006/06/15 09:32:48 kls Exp $
* $Id: device.h 1.80 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __DEVICE_H
@ -128,12 +128,21 @@ public:
///< Gets the device with the given Index.
///< \param Index must be in the range 0..numDevices-1.
///< \return A pointer to the device, or NULL if the Index was invalid.
static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView);
///< Returns a device that is able to receive the given Channel at the
///< given Priority, with the least impact on active recordings and
///< live viewing.
///< See ProvidesChannel() for more information on how
///< priorities are handled, and the meaning of NeedsDetachReceivers.
///< live viewing. The LiveView parameter tells whether the device will
///< be used for live viewing or a recording.
///< If the Channel is encrypted, a CAM slot that claims to be able to
///< decrypt the channel is automatically selected and assigned to the
///< returned device. Whether or not this combination of device and CAM
///< slot is actually able to decrypt the channel can only be determined
///< by checking the "scrambling control" bits of the received TS packets.
///< The Action() function automatically does this and takes care that
///< after detaching any receivers because the channel can't be decrypted,
///< this device/CAM combination will be skipped in the next call to
///< GetDevice().
///< See also ProvidesChannel().
static void Shutdown(void);
///< Closes down all devices.
///< Must be called at the end of the program.
@ -171,16 +180,6 @@ public:
///< Returns the card index of this device (0 ... MAXDEVICES - 1).
int DeviceNumber(void) const;
///< Returns the number of this device (0 ... numDevices).
virtual int ProvidesCa(const cChannel *Channel) const;
///< Checks whether this device provides the conditional access
///< facilities to decrypt the given Channel.
///< Returns 0 if the Channel can't be decrypted, 1 if this is a
///< Free To Air channel or only exactly this device can decrypt it,
///< and > 1 if this device can decrypt the Channel.
///< If the result is greater than 1 and the device has more than one
///< CAM, the value will be increased by the number of CAMs, which
///< allows to select the device with the smallest number of CAMs
///< in order to preserve resources for other recordings.
virtual bool HasDecoder(void) const;
///< Tells whether this device has an MPEG decoder.
@ -199,7 +198,9 @@ public:
virtual bool ProvidesSource(int Source) const;
///< Returns true if this device can provide the given source.
virtual bool ProvidesTransponder(const cChannel *Channel) const;
///< XXX -> PLUGINS.html!
///< Returns true if this device can provide the transponder of the
///< given Channel (which implies that it can provide the Channel's
///< source).
virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const;
///< Returns true if this is the only device that is able to provide
///< the given channel's transponder.
@ -246,7 +247,7 @@ public:
///< channel number while replaying.
void ForceTransferMode(void);
///< Forces the device into transfermode for the current channel.
virtual bool HasLock(int TimeoutMs = 0);//XXX PLUGINS.html
virtual bool HasLock(int TimeoutMs = 0);
///< Returns true if the device has a lock on the requested transponder.
///< Default is true, a specific device implementation may return false
///< to indicate that it is not ready yet.
@ -309,10 +310,15 @@ public:
// Common Interface facilities:
protected:
cCiHandler *ciHandler;
private:
time_t startScrambleDetection;
cCamSlot *camSlot;
public:
cCiHandler *CiHandler(void) { return ciHandler; }
void SetCamSlot(cCamSlot *CamSlot);
///< Sets the given CamSlot to be used with this device.
cCamSlot *CamSlot(void) const { return camSlot; }
///< Returns the CAM slot that is currently used with this device,
///< or NULL if no CAM slot is in use.
// Image Grab facilities
@ -512,11 +518,12 @@ public:
private:
cMutex mutexReceiver;
cReceiver *receiver[MAXRECEIVERS];
protected:
public:
int Priority(void) const;
///< Returns the priority of the current receiving session (0..MAXPRIORITY),
///< or -1 if no receiver is currently active. The primary device will
///< always return at least Setup.PrimaryLimit-1.
protected:
virtual bool OpenDvr(void);
///< Opens the DVR of this device and prepares it to deliver a Transport
///< Stream for use in a cReceiver.
@ -530,8 +537,6 @@ protected:
///< false in case of a non recoverable error, otherwise it returns true,
///< even if Data is NULL.
public:
int Ca(void) const;
///< Returns the ca of the current receiving session(s).
bool Receiving(bool CheckAny = false) const;
///< Returns true if we are currently receiving.
bool AttachReceiver(cReceiver *Receiver);

108
dvbci.c Normal file
View File

@ -0,0 +1,108 @@
/*
* dvbci.h: Common Interface for DVB devices
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbci.c 1.1 2007/01/07 14:38:00 kls Exp $
*/
#include "dvbci.h"
#include <linux/dvb/ca.h>
#include <sys/ioctl.h>
#include "device.h"
// --- cDvbCiAdapter ---------------------------------------------------------
cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
{
device = Device;
SetDescription("CI adapter on device %d", device->DeviceNumber());
fd = Fd;
ca_caps_t Caps;
if (ioctl(fd, CA_GET_CAP, &Caps) == 0) {
if ((Caps.slot_type & CA_CI_LINK) != 0) {
int NumSlots = Caps.slot_num;
if (NumSlots > 0) {
for (int i = 0; i < NumSlots; i++)
new cCamSlot(this);
Start();
}
else
esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber());
}
else
isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber());
}
else
esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber());
}
cDvbCiAdapter::~cDvbCiAdapter()
{
Cancel(3);
}
int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
{
if (Buffer && MaxLength > 0) {
struct pollfd pfd[1];
pfd[0].fd = fd;
pfd[0].events = POLLIN;
if (poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) {
int n = safe_read(fd, Buffer, MaxLength);
if (n >= 0)
return n;
esyslog("ERROR: can't read from CI adapter on device %d: %m", device->DeviceNumber());
}
}
return 0;
}
void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
{
if (Buffer && Length > 0) {
if (safe_write(fd, Buffer, Length) != Length)
esyslog("ERROR: can't write to CI adapter on device %d: %m", device->DeviceNumber());
}
}
bool cDvbCiAdapter::Reset(int Slot)
{
if (ioctl(fd, CA_RESET, 1 << Slot) != -1)
return true;
else
esyslog("ERROR: can't reset CAM slot %d on device %d: %m", Slot, device->DeviceNumber());
return false;
}
eModuleStatus cDvbCiAdapter::ModuleStatus(int Slot)
{
ca_slot_info_t sinfo;
sinfo.num = Slot;
if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) {
if ((sinfo.flags & CA_CI_MODULE_READY) != 0)
return msReady;
else if ((sinfo.flags & CA_CI_MODULE_PRESENT) != 0)
return msPresent;
}
else
esyslog("ERROR: can't get info of CAM slot %d on device %d: %m", Slot, device->DeviceNumber());
return msNone;
}
bool cDvbCiAdapter::Assign(cDevice *Device, bool Query)
{
// The CI is hardwired to its device, so there's not really much to do here
if (Device)
return Device == device;
return true;
}
cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd)
{
// TODO check whether a CI is actually present?
if (Device)
return new cDvbCiAdapter(Device, Fd);
return NULL;
}

31
dvbci.h Normal file
View File

@ -0,0 +1,31 @@
/*
* dvbci.h: Common Interface for DVB devices
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbci.h 1.1 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __DVBCI_H
#define __DVBCI_H
#include "ci.h"
class cDvbCiAdapter : public cCiAdapter {
private:
cDevice *device;
int fd;
protected:
virtual int Read(uint8_t *Buffer, int MaxLength);
virtual void Write(const uint8_t *Buffer, int Length);
virtual bool Reset(int Slot);
virtual eModuleStatus ModuleStatus(int Slot);
virtual bool Assign(cDevice *Device, bool Query = false);
cDvbCiAdapter(cDevice *Device, int Fd);
public:
virtual ~cDvbCiAdapter();
static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd);
};
#endif //__DVBCI_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.160 2006/08/14 09:38:32 kls Exp $
* $Id: dvbdevice.c 1.161 2007/01/07 14:09:51 kls Exp $
*/
#include "dvbdevice.h"
@ -19,6 +19,7 @@
#include <sys/mman.h>
#include "channels.h"
#include "diseqc.h"
#include "dvbci.h"
#include "dvbosd.h"
#include "eitscan.h"
#include "player.h"
@ -28,7 +29,6 @@
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
#define DO_MULTIPLE_RECORDINGS 1
//#define DO_MULTIPLE_CA_CHANNELS
#define DEV_VIDEO "/dev/video"
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@ -77,7 +77,6 @@ private:
int lockTimeout;
time_t lastTimeoutReport;
fe_type_t frontendType;
cCiHandler *ciHandler;
cChannel channel;
const char *diseqcCommands;
eTunerStatus tunerStatus;
@ -88,19 +87,18 @@ private:
bool SetFrontend(void);
virtual void Action(void);
public:
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler);
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType);
virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel, bool Tune);
bool Locked(int TimeoutMs = 0);
};
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler)
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
{
fd_frontend = Fd_Frontend;
cardIndex = CardIndex;
frontendType = FrontendType;
ciHandler = CiHandler;
tuneTimeout = 0;
lockTimeout = 0;
lastTimeoutReport = 0;
@ -346,8 +344,6 @@ void cDvbTuner::Action(void)
}
}
if (ciHandler)
ciHandler->Process();
if (tunerStatus != tsTuned)
newSet.TimedWait(mutex, 1000);
}
@ -360,6 +356,7 @@ int cDvbDevice::setTransferModeForDolbyDigital = 1;
cDvbDevice::cDvbDevice(int n)
{
ciAdapter = NULL;
dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
spuDecoder = NULL;
@ -377,6 +374,12 @@ cDvbDevice::cDvbDevice(int n)
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
fd_stc = DvbOpen(DEV_DVB_DEMUX, n, O_RDWR);
// Common Interface:
fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
if (fd_ca >= 0)
ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
// The DVR device (will be opened and closed as needed):
fd_dvr = -1;
@ -420,8 +423,7 @@ cDvbDevice::cDvbDevice(int n)
dvb_frontend_info feinfo;
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
frontendType = feinfo.type;
ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n));
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
}
else
LOG_ERROR;
@ -436,6 +438,7 @@ cDvbDevice::~cDvbDevice()
{
delete spuDecoder;
delete dvbTuner;
delete ciAdapter;
// We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway...
}
@ -494,32 +497,11 @@ bool cDvbDevice::HasDecoder(void) const
bool cDvbDevice::Ready(void)
{
if (ciHandler) {
ciHandler->Process();
return ciHandler->Ready();
}
if (ciAdapter)
return ciAdapter->Ready();
return true;
}
int cDvbDevice::ProvidesCa(const cChannel *Channel) const
{
int NumCams = 0;
if (ciHandler) {
NumCams = ciHandler->NumCams();
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
unsigned short ids[MAXCAIDS + 1];
for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0!
ids[i] = Channel->Ca(i);
if (ciHandler->ProvidesCa(ids))
return NumCams + 1;
}
}
int result = cDevice::ProvidesCa(Channel);
if (result > 0)
result += NumCams;
return result;
}
cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
{
if (!spuDecoder && IsPrimaryDevice())
@ -770,18 +752,19 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false;
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {
if (ProvidesSource(Channel->Source())) {
result = hasPriority;
if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS
#ifndef DO_MULTIPLE_CA_CHANNELS
if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
needsDetachReceivers = Ca() != Channel->Ca();
else
#endif
if (!IsPrimaryDevice())
if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
if (CamSlot()->CanDecrypt(Channel))
result = true;
else
needsDetachReceivers = true;
}
else if (!IsPrimaryDevice())
result = true;
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else
@ -821,6 +804,8 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
&& (LiveView && HasPid(Channel->Vpid() ? Channel->Vpid() : Channel->Apid(0)) && (pidHandles[ptVideo].pid != Channel->Vpid() || (pidHandles[ptAudio].pid != Channel->Apid(0) && (Channel->Dpid(0) ? pidHandles[ptAudio].pid != Channel->Dpid(0) : true)))// the PID is already set as DMX_PES_OTHER
|| !LiveView && (pidHandles[ptVideo].pid == Channel->Vpid() || pidHandles[ptAudio].pid == Channel->Apid(0)) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
);
if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber()))
StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode && LiveView;
@ -861,7 +846,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
}
else if (StartTransferMode)
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
cControl::Launch(new cTransferControl(this, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
return true;
}
@ -922,13 +907,13 @@ void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
DetachAll(pidHandles[ptAudio].pid);
if (ciHandler)
ciHandler->SetPid(pidHandles[ptAudio].pid, false);
if (CamSlot())
CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
pidHandles[ptAudio].pid = TrackId->id;
SetPid(&pidHandles[ptAudio], ptAudio, true);
if (ciHandler) {
ciHandler->SetPid(pidHandles[ptAudio].pid, true);
ciHandler->StartDecrypting();
if (CamSlot()) {
CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
CamSlot()->StartDecrypting();
}
}
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.41 2006/05/28 15:05:19 kls Exp $
* $Id: dvbdevice.h 1.42 2007/01/07 14:39:52 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -36,16 +36,20 @@ public:
///< \return True if any devices are available.
private:
fe_type_t frontendType;
int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc;
int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc, fd_ca;
protected:
virtual void MakePrimaryDevice(bool On);
public:
cDvbDevice(int n);
virtual ~cDvbDevice();
virtual bool Ready(void);
virtual int ProvidesCa(const cChannel *Channel) const;
virtual bool HasDecoder(void) const;
// Common Interface facilities:
private:
cCiAdapter *ciAdapter;
// SPU facilities
private:

196
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: i18n.c 1.285 2006/10/14 09:26:41 kls Exp $
* $Id: i18n.c 1.286 2007/01/07 14:19:15 kls Exp $
*
* Translations provided by:
*
@ -2598,27 +2598,27 @@ const tI18nPhrase Phrases[] = {
"Kan ikke åbne CAM menuen!",
"Menu CAM není dostupné",
},
{ "Resetting CAM...",
"CAM wird zurückgesetzt...",
"Resetiram CAM...",
"Reimpostazione modulo CAM...",
"CAM wordt herstart...",
{ "CAM is in use - really reset?"
"CAM wird benutzt - wirklich zurücksetzen?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"Réinitialisation du CAM",
"",//TODO
"CA-moduuli palautetaan alkutilaan...",
"Resetujê CAM...",
"Reiniciando CAM...",
"",//TODO
"Återställer CAM ...",
"Se reseteazã CAM...",
"A CAM újra indul...",
"",//TODO
"¿ÕàÕÓàã×ÚÐ CAM...",
"",//TODO
"CAM mooduli taaskäivitus...",
"Nulstiller CAM...",
"Restartuje se CAM...",
},
{ "Can't reset CAM!",
"Zurücksetzen des CAM fehlgeschlagen!",
@ -2642,27 +2642,93 @@ const tI18nPhrase Phrases[] = {
"Kan ikke nulstille CAM!",
"CAM modul nelze restartovat!",
},
{ "CAM has been reset",
"CAM wurde zurückgesetzt",
"CAM je resetiran",
"Modulo CAM reimpostato",
"CAM is herstart",
{ "CAM reset",
"CAM zurückgesetzt",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "CAM present",
"CAM vorhanden",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "CAM ready",
"CAM bereit",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "CAM not responding!",
"CAM antwortet nicht!",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"La CAM a été réinitialisée",
"",//TODO
"CA-moduuli palautettu alkutilaan",
"CAM zosta³ zresetowany",
"CAM reiniciado",
"¸ãéíå åðáíáöïñÜ óôï CAM",
"CA modulen har återställts",
"CAM-ul a fost resetat",
"A CAM vissza lett állítva",
"CAM reiniciada",
"CAM-ÜÞÔãÛì ßÕàÕ×ÐßãéÕÝ",
"CAM je resetiran",
"CAM mooduli taaskäivitus tehtud",
"CAM er blevet nulstillet",
"CAM byl restartován",
},
{ "Please enter %d digits!",
"Bitte geben Sie %d Ziffern ein!",
@ -2797,27 +2863,27 @@ const tI18nPhrase Phrases[] = {
"LNB",
"LNB",
},
{ "CICAM",
"CICAM",
"CICAM",
"Accesso condizionato CICAM",
"CICAM",
"CICAM",
{ "CAM",
"CAM",
"CAM",
"Accesso condizionato CAM",
"CAM",
"CAM",
"Accès conditionnel",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CAM",
"CAM",
"CAM",
"CAM",
"CAM",
"CAM",
"CAM",
"CAM",
"CI Accés condicional",
"ÃáÛÞÒÝëÙ ÔÞáâãß",
"CICAM",
"CICAM",
"CICAM",
"CICAM",
"CAM",
"CAM",
"CAM",
"CAM",
},
{ "Recording",
"Aufnahme",
@ -3964,28 +4030,6 @@ const tI18nPhrase Phrases[] = {
"Anvend DiSEqC",
"Pou¾ívat DiSEqC",
},
{ "Setup.CICAM$CICAM DVB",
"CICAM-DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"Accès conditionnel",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"Accés condicional CICAM",
"CAM-ÜÞÔãÛì DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
},
{ "Setup.Recording$Margin at start (min)",
"Vorlauf zum Timer-Beginn (min)",
"Premik zaèetka snemanja (min)",

420
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 1.446 2006/12/02 11:12:02 kls Exp $
* $Id: menu.c 1.447 2007/01/07 14:19:48 kls Exp $
*/
#include "menu.h"
@ -38,7 +38,9 @@
#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
#define MAXWAITFORCAMMENU 4 // seconds to wait for the CAM menu to open
#define MAXWAITFORCAMMENU 10 // seconds to wait for the CAM menu to open
#define CAMMENURETYTIMEOUT 3 // seconds after which opening the CAM menu is retried
#define CAMRESPONSETIMEOUT 5 // seconds to wait for a response from a CAM
#define MINFREEDISK 300 // minimum free disk space (in MB) required to start recording
#define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages
@ -1579,38 +1581,104 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
// --- cMenuCam --------------------------------------------------------------
cMenuCam::cMenuCam(cCiMenu *CiMenu)
:cOsdMenu("")
class cMenuCam : public cOsdMenu {
private:
cCamSlot *camSlot;
cCiMenu *ciMenu;
cCiEnquiry *ciEnquiry;
char *input;
int offset;
time_t lastCamExchange;
void GenerateTitle(const char *s = NULL);
void QueryCam(void);
void AddMultiLineItem(const char *s);
void Set(void);
eOSState Select(void);
public:
cMenuCam(cCamSlot *CamSlot);
virtual ~cMenuCam();
virtual eOSState ProcessKey(eKeys Key);
};
cMenuCam::cMenuCam(cCamSlot *CamSlot)
:cOsdMenu("", 1) // tab necessary for enquiry!
{
dsyslog("CAM: Menu ------------------");
ciMenu = CiMenu;
selected = false;
camSlot = CamSlot;
ciMenu = NULL;
ciEnquiry = NULL;
input = NULL;
offset = 0;
if (ciMenu->Selectable())
SetHasHotkeys();
SetTitle(*ciMenu->TitleText() ? ciMenu->TitleText() : "CAM");
dsyslog("CAM: '%s'", ciMenu->TitleText());
if (*ciMenu->SubTitleText()) {
dsyslog("CAM: '%s'", ciMenu->SubTitleText());
AddMultiLineItem(ciMenu->SubTitleText());
offset = Count();
}
for (int i = 0; i < ciMenu->NumEntries(); i++) {
Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
dsyslog("CAM: '%s'", ciMenu->Entry(i));
}
if (*ciMenu->BottomText()) {
AddMultiLineItem(ciMenu->BottomText());
dsyslog("CAM: '%s'", ciMenu->BottomText());
}
Display();
lastCamExchange = time(NULL);
SetNeedsFastResponse(true);
QueryCam();
}
cMenuCam::~cMenuCam()
{
if (!selected)
if (ciMenu)
ciMenu->Abort();
delete ciMenu;
if (ciEnquiry)
ciEnquiry->Abort();
delete ciEnquiry;
free(input);
}
void cMenuCam::GenerateTitle(const char *s)
{
SetTitle(cString::sprintf("CAM %d - %s", camSlot->SlotNumber(), (s && *s) ? s : camSlot->GetCamName()));
}
void cMenuCam::QueryCam(void)
{
delete ciMenu;
ciMenu = NULL;
delete ciEnquiry;
ciEnquiry = NULL;
if (camSlot->HasUserIO()) {
ciMenu = camSlot->GetMenu();
ciEnquiry = camSlot->GetEnquiry();
}
Set();
}
void cMenuCam::Set(void)
{
if (ciMenu) {
Clear();
free(input);
input = NULL;
dsyslog("CAM %d: Menu ------------------", camSlot->SlotNumber());
offset = 0;
SetHasHotkeys(ciMenu->Selectable());
GenerateTitle(ciMenu->TitleText());
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->TitleText());
if (*ciMenu->SubTitleText()) {
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->SubTitleText());
AddMultiLineItem(ciMenu->SubTitleText());
offset = Count();
}
for (int i = 0; i < ciMenu->NumEntries(); i++) {
Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->Entry(i));
}
if (*ciMenu->BottomText()) {
AddMultiLineItem(ciMenu->BottomText());
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->BottomText());
}
}
else if (ciEnquiry) {
Clear();
int Length = ciEnquiry->ExpectedLength();
free(input);
input = MALLOC(char, Length + 1);
*input = 0;
GenerateTitle();
Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
Add(new cOsdItem("", osUnknown, false));
Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
}
Display();
}
void cMenuCam::AddMultiLineItem(const char *s)
@ -1628,90 +1696,61 @@ void cMenuCam::AddMultiLineItem(const char *s)
eOSState cMenuCam::Select(void)
{
if (ciMenu->Selectable()) {
ciMenu->Select(Current() - offset);
dsyslog("CAM: select %d", Current() - offset);
if (ciMenu) {
if (ciMenu->Selectable()) {
ciMenu->Select(Current() - offset);
dsyslog("CAM %d: select %d", camSlot->SlotNumber(), Current() - offset);
}
else
ciMenu->Cancel();
}
else
ciMenu->Cancel();
selected = true;
return osEnd;
else if (ciEnquiry) {
if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
char buffer[64];
snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
Skins.Message(mtError, buffer);
return osContinue;
}
ciEnquiry->Reply(input);
dsyslog("CAM %d: entered '%s'", camSlot->SlotNumber(), ciEnquiry->Blind() ? "****" : input);
}
QueryCam();
return osContinue;
}
eOSState cMenuCam::ProcessKey(eKeys Key)
{
if (!camSlot->HasMMI())
return osBack;
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kOk: return Select();
default: break;
}
if (ciMenu || ciEnquiry) {
lastCamExchange = time(NULL);
if (state == osUnknown) {
switch (Key) {
case kOk: return Select();
default: break;
}
}
else if (state == osBack) {
if (ciMenu)
ciMenu->Cancel();
if (ciEnquiry)
ciEnquiry->Cancel();
QueryCam();
return osContinue;
}
if (ciMenu && ciMenu->HasUpdate()) {
QueryCam();
return osContinue;
}
}
else if (state == osBack) {
ciMenu->Cancel();
selected = true;
return osEnd;
}
if (ciMenu->HasUpdate()) {
selected = true;
return osEnd;
}
return state;
}
// --- cMenuCamEnquiry -------------------------------------------------------
cMenuCamEnquiry::cMenuCamEnquiry(cCiEnquiry *CiEnquiry)
:cOsdMenu("", 1)
{
ciEnquiry = CiEnquiry;
int Length = ciEnquiry->ExpectedLength();
input = MALLOC(char, Length + 1);
*input = 0;
replied = false;
SetTitle("CAM");
Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
Add(new cOsdItem("", osUnknown, false));
Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
Display();
}
cMenuCamEnquiry::~cMenuCamEnquiry()
{
if (!replied)
ciEnquiry->Abort();
free(input);
delete ciEnquiry;
}
eOSState cMenuCamEnquiry::Reply(void)
{
if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
char buffer[64];
snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
Skins.Message(mtError, buffer);
return osContinue;
}
ciEnquiry->Reply(input);
replied = true;
return osEnd;
}
eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kOk: return Reply();
default: break;
}
}
else if (state == osBack) {
ciEnquiry->Cancel();
replied = true;
return osEnd;
else if (time(NULL) - lastCamExchange < CAMRESPONSETIMEOUT)
QueryCam();
else {
Skins.Message(mtError, tr("CAM not responding!"));
return osBack;
}
return state;
}
@ -1720,21 +1759,9 @@ eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
cOsdObject *CamControl(void)
{
for (int d = 0; d < cDevice::NumDevices(); d++) {
cDevice *Device = cDevice::GetDevice(d);
if (Device) {
cCiHandler *CiHandler = Device->CiHandler();
if (CiHandler && CiHandler->HasUserIO()) {
cCiMenu *CiMenu = CiHandler->GetMenu();
if (CiMenu)
return new cMenuCam(CiMenu);
else {
cCiEnquiry *CiEnquiry = CiHandler->GetEnquiry();
if (CiEnquiry)
return new cMenuCamEnquiry(CiEnquiry);
}
}
}
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
if (CamSlot->HasUserIO())
return new cMenuCam(CamSlot);
}
return NULL;
}
@ -2454,95 +2481,117 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
return state;
}
// --- cMenuSetupCICAM -------------------------------------------------------
// --- cMenuSetupCAM ---------------------------------------------------------
class cMenuSetupCICAMItem : public cOsdItem {
class cMenuSetupCAMItem : public cOsdItem {
private:
cCiHandler *ciHandler;
int slot;
cCamSlot *camSlot;
public:
cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot);
cCiHandler *CiHandler(void) { return ciHandler; }
int Slot(void) { return slot; }
cMenuSetupCAMItem(cCamSlot *CamSlot);
cCamSlot *CamSlot(void) { return camSlot; }
bool Changed(void);
};
cMenuSetupCICAMItem::cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot)
cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot)
{
ciHandler = CiHandler;
slot = Slot;
char buffer[32];
const char *CamName = CiHandler->GetCamName(slot);
snprintf(buffer, sizeof(buffer), "%s%d %d\t%s", tr("Setup.CICAM$CICAM DVB"), Device + 1, slot + 1, CamName ? CamName : "-");
SetText(buffer);
camSlot = CamSlot;
SetText("");
Changed();
}
class cMenuSetupCICAM : public cMenuSetupBase {
bool cMenuSetupCAMItem::Changed(void)
{
char buffer[32];
const char *CamName = camSlot->GetCamName();
if (!CamName) {
switch (camSlot->ModuleStatus()) {
case msReset: CamName = tr("CAM reset"); break;
case msPresent: CamName = tr("CAM present"); break;
case msReady: CamName = tr("CAM ready"); break;
default: CamName = "-"; break;
}
}
snprintf(buffer, sizeof(buffer), " %d %s", camSlot->SlotNumber(), CamName);
if (strcmp(buffer, Text()) != 0) {
SetText(buffer);
return true;
}
return false;
}
class cMenuSetupCAM : public cMenuSetupBase {
private:
eOSState Menu(void);
eOSState Reset(void);
public:
cMenuSetupCICAM(void);
cMenuSetupCAM(void);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuSetupCICAM::cMenuSetupCICAM(void)
cMenuSetupCAM::cMenuSetupCAM(void)
{
SetSection(tr("CICAM"));
for (int d = 0; d < cDevice::NumDevices(); d++) {
cDevice *Device = cDevice::GetDevice(d);
if (Device) {
cCiHandler *CiHandler = Device->CiHandler();
if (CiHandler) {
for (int Slot = 0; Slot < CiHandler->NumSlots(); Slot++)
Add(new cMenuSetupCICAMItem(Device->CardIndex(), CiHandler, Slot));
}
}
}
SetSection(tr("CAM"));
SetCols(15);
SetHasHotkeys();
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
Add(new cMenuSetupCAMItem(CamSlot));
SetHelp(tr("Button$Menu"), tr("Button$Reset"));
}
eOSState cMenuSetupCICAM::Menu(void)
eOSState cMenuSetupCAM::Menu(void)
{
cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
if (item) {
if (item->CiHandler()->EnterMenu(item->Slot())) {
Skins.Message(mtWarning, tr("Opening CAM menu..."));
time_t t = time(NULL);
while (time(NULL) - t < MAXWAITFORCAMMENU && !item->CiHandler()->HasUserIO())
item->CiHandler()->Process();
return osEnd; // the CAM menu will be executed explicitly from the main loop
if (item->CamSlot()->EnterMenu()) {
Skins.Message(mtStatus, tr("Opening CAM menu..."));
time_t t0 = time(NULL);
time_t t1 = t0;
while (time(NULL) - t0 <= MAXWAITFORCAMMENU) {
if (item->CamSlot()->HasUserIO())
break;
if (time(NULL) - t1 >= CAMMENURETYTIMEOUT) {
dsyslog("CAM %d: retrying to enter CAM menu...", item->CamSlot()->SlotNumber());
item->CamSlot()->EnterMenu();
t1 = time(NULL);
}
cCondWait::SleepMs(100);
}
Skins.Message(mtStatus, NULL);
if (item->CamSlot()->HasUserIO())
return AddSubMenu(new cMenuCam(item->CamSlot()));
}
else
Skins.Message(mtError, tr("Can't open CAM menu!"));
Skins.Message(mtError, tr("Can't open CAM menu!"));
}
return osContinue;
}
eOSState cMenuSetupCICAM::Reset(void)
eOSState cMenuSetupCAM::Reset(void)
{
cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
if (item) {
Skins.Message(mtWarning, tr("Resetting CAM..."));
if (item->CiHandler()->Reset(item->Slot())) {
Skins.Message(mtInfo, tr("CAM has been reset"));
return osEnd;
if (!item->CamSlot()->Device() || Interface->Confirm(tr("CAM is in use - really reset?"))) {
if (!item->CamSlot()->Reset())
Skins.Message(mtError, tr("Can't reset CAM!"));
}
else
Skins.Message(mtError, tr("Can't reset CAM!"));
}
return osContinue;
}
eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
{
eOSState state = cMenuSetupBase::ProcessKey(Key);
eOSState state = HasSubMenu() ? cMenuSetupBase::ProcessKey(Key) : cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
if (!HasSubMenu()) {
switch (Key) {
case kOk:
case kRed: return Menu();
case kGreen: return Reset();
case kGreen: state = Reset(); break;
default: break;
}
for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
if (ci->Changed())
DisplayItem(ci);
}
}
return state;
}
@ -2710,7 +2759,7 @@ void cMenuSetup::Set(void)
Add(new cOsdItem(hk(tr("EPG")), osUser2));
Add(new cOsdItem(hk(tr("DVB")), osUser3));
Add(new cOsdItem(hk(tr("LNB")), osUser4));
Add(new cOsdItem(hk(tr("CICAM")), osUser5));
Add(new cOsdItem(hk(tr("CAM")), osUser5));
Add(new cOsdItem(hk(tr("Recording")), osUser6));
Add(new cOsdItem(hk(tr("Replay")), osUser7));
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
@ -2740,7 +2789,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
case osUser2: return AddSubMenu(new cMenuSetupEPG);
case osUser3: return AddSubMenu(new cMenuSetupDVB);
case osUser4: return AddSubMenu(new cMenuSetupLNB);
case osUser5: return AddSubMenu(new cMenuSetupCICAM);
case osUser5: return AddSubMenu(new cMenuSetupCAM);
case osUser6: return AddSubMenu(new cMenuSetupRecord);
case osUser7: return AddSubMenu(new cMenuSetupReplay);
case osUser8: return AddSubMenu(new cMenuSetupMisc);
@ -3126,7 +3175,7 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
if (Direction) {
while (Channel) {
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
if (Channel && !Channel->GroupSep() && (cDevice::PrimaryDevice()->ProvidesChannel(Channel, Setup.PrimaryLimit) || cDevice::GetDevice(Channel, 0)))
if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
return Channel;
}
}
@ -3541,7 +3590,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
if (device->AttachReceiver(recorder)) {
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
@ -3610,7 +3659,7 @@ void cRecordControl::Stop(void)
bool cRecordControl::Process(time_t t)
{
if (!recorder || !timer || !timer->Matches(t))
if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t))
return false;
AssertFreeDiskSpace(timer->Priority());
return true;
@ -3645,15 +3694,9 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
bool NeedsDetachReceivers = false;
int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
cDevice *device = cDevice::GetDevice(channel, Priority, &NeedsDetachReceivers);
cDevice *device = cDevice::GetDevice(channel, Priority, false);
if (device) {
if (NeedsDetachReceivers) {
Stop(device);
if (device == cTransferControl::ReceiverDevice())
cControl::Shutdown(); // in case this device was used for Transfer Mode
}
dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true);
@ -3698,19 +3741,6 @@ void cRecordControls::Stop(const char *InstantId)
}
}
void cRecordControls::Stop(cDevice *Device)
{
ChangeState();
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (RecordControls[i]->Device() == Device) {
isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
RecordControls[i]->Stop();
}
}
}
}
bool cRecordControls::PauseLiveVideo(void)
{
Skins.Message(mtStatus, tr("Pausing live video..."));
@ -3882,7 +3912,8 @@ void cReplayControl::Hide(void)
if (visible) {
delete displayReplay;
displayReplay = NULL;
needsFastResponse = visible = false;
SetNeedsFastResponse(false);
visible = false;
modeOnly = false;
lastPlay = lastForward = false;
lastSpeed = -2; // an invalid value
@ -3923,7 +3954,8 @@ bool cReplayControl::ShowProgress(bool Initial)
if (!visible) {
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
displayReplay->SetMarks(&marks);
needsFastResponse = visible = true;
SetNeedsFastResponse(true);
visible = true;
}
if (Initial) {
if (title)

28
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.86 2006/10/20 13:09:57 kls Exp $
* $Id: menu.h 1.87 2007/01/07 14:40:54 kls Exp $
*/
#ifndef __MENU_H
@ -128,31 +128,6 @@ public:
eOSState ProcessKey(eKeys Key);
};
class cMenuCam : public cOsdMenu {
private:
cCiMenu *ciMenu;
bool selected;
int offset;
void AddMultiLineItem(const char *s);
eOSState Select(void);
public:
cMenuCam(cCiMenu *CiMenu);
virtual ~cMenuCam();
virtual eOSState ProcessKey(eKeys Key);
};
class cMenuCamEnquiry : public cOsdMenu {
private:
cCiEnquiry *ciEnquiry;
char *input;
bool replied;
eOSState Reply(void);
public:
cMenuCamEnquiry(cCiEnquiry *CiEnquiry);
virtual ~cMenuCamEnquiry();
virtual eOSState ProcessKey(eKeys Key);
};
cOsdObject *CamControl(void);
class cMenuRecordingItem;
@ -206,7 +181,6 @@ private:
public:
static bool Start(cTimer *Timer = NULL, bool Pause = false);
static void Stop(const char *InstantId);
static void Stop(cDevice *Device);
static bool PauseLiveVideo(void);
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osdbase.c 1.29 2006/02/05 14:37:03 kls Exp $
* $Id: osdbase.c 1.30 2007/01/07 14:41:16 kls Exp $
*/
#include "osdbase.h"
@ -127,9 +127,9 @@ void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
cols[4] = c4;
}
void cOsdMenu::SetHasHotkeys(void)
void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
{
hasHotkeys = true;
hasHotkeys = HasHotkeys;
digit = 0;
}
@ -256,6 +256,20 @@ void cOsdMenu::DisplayCurrent(bool Current)
}
}
void cOsdMenu::DisplayItem(cOsdItem *Item)
{
if (Item) {
int Index = Item->Index();
int Offset = Index - first;
if (Offset >= 0 && Offset < first + displayMenuItems) {
bool Current = Index == current;
displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
if (Current && Item->Selectable())
cStatus::MsgOsdCurrentItem(Item->Text());
}
}
}
void cOsdMenu::Clear(void)
{
if (marked >= 0)
@ -432,6 +446,8 @@ eOSState cOsdMenu::HotKey(eKeys Key)
if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') {
current = item->Index();
RefreshCurrent();
Display();
cRemote::Put(kOk, true);
break;
}
@ -499,4 +515,3 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
}
return osContinue;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osdbase.h 1.14 2006/01/05 15:35:06 kls Exp $
* $Id: osdbase.h 1.15 2007/01/07 14:41:32 kls Exp $
*/
#ifndef __OSDBASE_H
@ -70,12 +70,13 @@ class cOsdObject {
friend class cOsdMenu;
private:
bool isMenu;
protected:
bool needsFastResponse;
protected:
void SetNeedsFastResponse(bool NeedsFastResponse) { needsFastResponse = NeedsFastResponse; }
public:
cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; }
virtual ~cOsdObject() {}
bool NeedsFastResponse(void) { return needsFastResponse; }
virtual bool NeedsFastResponse(void) { return needsFastResponse; }
bool IsMenu(void) { return isMenu; }
virtual void Show(void);
virtual eOSState ProcessKey(eKeys Key) { return osUnknown; }
@ -98,12 +99,13 @@ protected:
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
const char *hk(const char *s);
void SetCols(int c0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
void SetHasHotkeys(void);
void SetHasHotkeys(bool HasHotkeys = true);
virtual void Clear(void);
bool SelectableItem(int idx);
void SetCurrent(cOsdItem *Item);
void RefreshCurrent(void);
void DisplayCurrent(bool Current);
void DisplayItem(cOsdItem *Item);
void CursorUp(void);
void CursorDown(void);
void PageUp(void);
@ -120,6 +122,7 @@ protected:
public:
cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
virtual ~cOsdMenu();
virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
int Current(void) { return current; }
void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);

14
pat.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.c 1.16 2006/03/31 12:39:34 kls Exp $
* $Id: pat.c 1.17 2007/01/07 14:41:55 kls Exp $
*/
#include "pat.h"
@ -78,7 +78,7 @@ public:
bool Is(cCaDescriptors * CaDescriptors);
bool Empty(void) { return caDescriptors.Count() == 0; }
void AddCaDescriptor(SI::CaDescriptor *d, bool Stream);
int GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
const int *CaIds(void) { return caIds; }
};
@ -148,7 +148,7 @@ void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream)
#endif
}
int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
if (!CaSystemIds || !*CaSystemIds)
return 0;
@ -156,7 +156,7 @@ int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufS
int length = 0;
int IsStream = -1;
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
const unsigned short *caids = CaSystemIds;
const int *caids = CaSystemIds;
do {
if (d->CaSystem() == *caids) {
if (length + d->Length() <= BufSize) {
@ -187,7 +187,7 @@ public:
// Returns 0 if this is an already known descriptor,
// 1 if it is an all new descriptor with actual contents,
// and 2 if an existing descriptor was changed.
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
};
int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
@ -208,7 +208,7 @@ int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
return CaDescriptors->Empty() ? 0 : 1;
}
int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
cMutexLock MutexLock(&mutex);
StreamFlag = false;
@ -221,7 +221,7 @@ int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int Serv
cCaDescriptorHandler CaDescriptorHandler;
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
}

4
pat.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.h 1.6 2006/03/29 15:18:38 kls Exp $
* $Id: pat.h 1.7 2007/01/07 14:42:11 kls Exp $
*/
#ifndef __PAT_H
@ -32,7 +32,7 @@ public:
void Trigger(void);
};
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
///< Gets all CA descriptors for a given channel.
///< Copies all available CA descriptors for the given Source, Transponder and ServiceId
///< into the provided buffer at Data (at most BufSize bytes). Only those CA descriptors

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: receiver.c 1.5 2006/03/26 14:07:21 kls Exp $
* $Id: receiver.c 1.6 2007/01/07 14:42:29 kls Exp $
*/
#include "receiver.h"
@ -12,10 +12,10 @@
#include <stdio.h>
#include "tools.h"
cReceiver::cReceiver(int Ca, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
cReceiver::cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
{
device = NULL;
ca = Ca;
channelID = ChannelID;
priority = Priority;
numPids = 0;
if (Pid)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: receiver.h 1.4 2006/05/27 09:04:22 kls Exp $
* $Id: receiver.h 1.5 2007/01/07 14:40:36 kls Exp $
*/
#ifndef __RECEIVER_H
@ -18,7 +18,7 @@ class cReceiver {
friend class cDevice;
private:
cDevice *device;
int ca;
tChannelID channelID;
int priority;
int pids[MAXRECEIVEPIDS];
int numPids;
@ -38,8 +38,8 @@ protected:
///< will be delivered only ONCE, so the cReceiver must make sure that
///< it will be able to buffer the data if necessary.
public:
cReceiver(int Ca, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
///< Creates a new receiver that requires conditional access Ca and has
cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
///< Creates a new receiver for the channel with the given ChannelID with
///< the given Priority. Pid is a single PID (typically the video PID), while
///< Pids1...Pids3 are pointers to zero terminated lists of PIDs.
///< If any of these PIDs are 0, they will be silently ignored.
@ -47,7 +47,18 @@ public:
///< Priority may be any value in the range -99..99. Negative values indicate
///< that this cReceiver may be detached at any time (without blocking the
///< cDevice it is attached to).
///< The ChannelID is necessary to allow the device that will be used for this
///< receiver to detect and store whether the channel can be decrypted in case
///< this is an encrypted channel. If the channel is not encrypted or this
///< detection is not wanted, an invalid tChannelID may be given.
virtual ~cReceiver();
tChannelID ChannelID(void) { return channelID; }
bool IsAttached(void) { return device != NULL; }
///< Returns true if this receiver is (still) attached to a device.
///< A receiver may be automatically detached from its device in
///< case the device is needed otherwise, so code that uses a cReceiver
///< should repeatedly check whether it is still attached, and if
///< it isn't, delete it (or take any other appropriate measures).
};
#endif //__RECEIVER_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recorder.c 1.17 2006/01/08 11:01:25 kls Exp $
* $Id: recorder.c 1.18 2007/01/07 14:43:09 kls Exp $
*/
#include "recorder.h"
@ -21,6 +21,8 @@
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
// --- cFileWriter -----------------------------------------------------------
class cFileWriter : public cThread {
private:
cRemux *remux;
@ -121,8 +123,10 @@ void cFileWriter::Action(void)
}
}
cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
// --- cRecorder -------------------------------------------------------------
cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("recording")
{
// Make sure the disk is up and running:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recorder.h 1.4 2005/08/13 11:31:18 kls Exp $
* $Id: recorder.h 1.5 2007/01/07 14:44:05 kls Exp $
*/
#ifndef __RECORDER_H
@ -28,9 +28,9 @@ protected:
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
// Creates a new recorder that requires conditional access Ca, has
// the given Priority and will record the given PIDs into the file FileName.
cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
// Creates a new recorder for the channel with the given ChannelID and
// the given Priority that will record the given PIDs into the file FileName.
virtual ~cRecorder();
};

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.c 1.58 2006/09/24 12:54:47 kls Exp $
* $Id: thread.c 1.59 2007/01/07 14:44:22 kls Exp $
*/
#include "thread.h"
@ -249,17 +249,29 @@ void *cThread::StartThread(cThread *Thread)
return NULL;
}
#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
bool cThread::Start(void)
{
if (!active) {
active = running = true;
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
pthread_detach(childTid); // auto-reap
if (!running) {
if (active) {
// Wait until the previous incarnation of this thread has completely ended
// before starting it newly:
cTimeMs RestartTimeout;
while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
cCondWait::SleepMs(THREAD_STOP_SLEEP);
}
else {
LOG_ERROR;
active = running = false;
return false;
if (!active) {
active = running = true;
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
pthread_detach(childTid); // auto-reap
}
else {
LOG_ERROR;
active = running = false;
return false;
}
}
}
return true;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.h 1.37 2006/09/24 10:10:37 kls Exp $
* $Id: thread.h 1.38 2007/01/07 14:44:38 kls Exp $
*/
#ifndef __THREAD_H
@ -115,6 +115,7 @@ public:
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
bool Start(void);
///< Actually starts the thread.
///< If the thread is already running, nothing happens.
bool Active(void);
///< Checks whether the thread is still alive.
static bool EmergencyExit(bool Request = false);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.c 1.121 2006/12/02 11:12:59 kls Exp $
* $Id: tools.c 1.122 2007/01/07 14:44:57 kls Exp $
*/
#include "tools.h"
@ -542,9 +542,9 @@ time_t LastModifiedTime(const char *FileName)
// --- cTimeMs ---------------------------------------------------------------
cTimeMs::cTimeMs(void)
cTimeMs::cTimeMs(int Ms)
{
Set();
Set(Ms);
}
uint64_t cTimeMs::Now(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.h 1.96 2006/12/03 17:38:38 kls Exp $
* $Id: tools.h 1.97 2007/01/07 14:45:11 kls Exp $
*/
#ifndef __TOOLS_H
@ -23,7 +23,6 @@
#include <sys/types.h>
typedef unsigned char uchar;
#define uint64 uint64_t // for compatibility - TODO remove in version 1.5
extern int SysLogLevel;
@ -162,7 +161,8 @@ class cTimeMs {
private:
uint64_t begin;
public:
cTimeMs(void);
cTimeMs(int Ms = 0);
///< Creates a timer with ms resolution and an initial timeout of Ms.
static uint64_t Now(void);
void Set(int Ms = 0);
bool TimedOut(void);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: transfer.c 1.33 2006/01/29 17:24:39 kls Exp $
* $Id: transfer.c 1.34 2007/01/07 14:45:28 kls Exp $
*/
#include "transfer.h"
@ -14,8 +14,8 @@
// --- cTransfer -------------------------------------------------------------
cTransfer::cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(0, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("transfer")
{
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
@ -34,17 +34,18 @@ void cTransfer::Activate(bool On)
{
if (On)
Start();
else
else {
Cancel(3);
cPlayer::Detach();
}
}
void cTransfer::Receive(uchar *Data, int Length)
{
if (IsAttached() && Running()) {
if (cPlayer::IsAttached() && Running()) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p);
return;
}
}
@ -110,8 +111,8 @@ void cTransfer::Action(void)
cDevice *cTransferControl::receiverDevice = NULL;
cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids)
:cControl(transfer = new cTransfer(VPid, APids, DPids, SPids), true)
cTransferControl::cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
:cControl(transfer = new cTransfer(ChannelID, VPid, APids, DPids, SPids), true)
{
ReceiverDevice->AttachReceiver(transfer);
receiverDevice = ReceiverDevice;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: transfer.h 1.11 2006/01/29 17:24:43 kls Exp $
* $Id: transfer.h 1.12 2007/01/07 14:45:45 kls Exp $
*/
#ifndef __TRANSFER_H
@ -25,7 +25,7 @@ protected:
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids);
cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
virtual ~cTransfer();
};
@ -34,7 +34,7 @@ private:
cTransfer *transfer;
static cDevice *receiverDevice;
public:
cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids);
cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
~cTransferControl();
virtual void Hide(void) {}
static cDevice *ReceiverDevice(void) { return receiverDevice; }

17
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.282 2006/12/02 16:22:12 kls Exp $
* $Id: vdr.c 1.283 2007/01/07 14:46:14 kls Exp $
*/
#include <getopt.h>
@ -68,8 +68,6 @@
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
#define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
#define LASTCAMMENUTIMEOUT 3 // seconds to run the main loop 'fast' after a CAM menu has been closed
// in order to react on a possible new CAM menu as soon as possible
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down
@ -501,7 +499,6 @@ int main(int argc, char *argv[])
int PreviousChannelIndex = 0;
time_t LastChannelChanged = time(NULL);
time_t LastActivity = 0;
time_t LastCamMenu = 0;
int MaxLatencyTime = 0;
bool ForceShutdown = false;
bool UserShutdown = false;
@ -851,19 +848,14 @@ int main(int argc, char *argv[])
DeletedRecordings.Update();
}
// CAM control:
if (!Menu && !cOsd::IsOpen()) {
if (!Menu && !cOsd::IsOpen())
Menu = CamControl();
if (Menu)
LastCamMenu = 0;
else if (!LastCamMenu)
LastCamMenu = time(NULL);
}
// Queued messages:
if (!Skins.IsOpen())
Skins.ProcessQueuedMessages();
// User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control();
eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);
eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
if (NORMALKEY(key) != kNone) {
EITScanner.Activity();
LastActivity = time(NULL);
@ -1052,9 +1044,6 @@ int main(int argc, char *argv[])
else if (time(NULL) - LastActivity > MENUTIMEOUT)
state = osEnd;
}
// TODO make the CAM menu stay open in case of automatic updates and have it return osContinue; then the following two lines can be removed again
else if (state == osEnd && LastActivity > 1)
LastActivity = time(NULL);
switch (state) {
case osPause: DELETE_MENU;
cControl::Shutdown(); // just in case