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 2007-01-07: Version 1.4.5
- Official release. - 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. 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 n CAM Name Shows the CAM slots that are present in this system, where
the number of the device, and 'm' is the number of the 'n' is the number of the slot, followed by the name of the
Common Interface slot of that device. The "Red" key CAM. If a CAM slot is empty, '-' is displayed as name, and
can be pressed to enter the CAM menu, and the "Green" key if it is in the process of being reset, its current status
triggers a reset of the selected CAM. 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: Recording:

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # 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: .DELETE_ON_ERROR:
@ -32,7 +32,7 @@ DOXYFILE = Doxyfile
SILIB = $(LSIDIR)/libsi.a 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\ 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\ 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\ 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><h1>The VDR Plugin System</h1></center>
<center><b>Version 1.4.1</b></center> <center><b>Version 1.5.0</b></center>
<p> <p>
<center> <center>
Copyright &copy; 2006 Klaus Schmidinger<br> 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> <a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center> </center>
<p> <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 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. to be added to the program by implementing a dynamically loadable library file.
This interface allows programmers to develop additional functionality for VDR completely 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="#Devices">Devices</a>
<li><a href="#Audio">Audio</a> <li><a href="#Audio">Audio</a>
<li><a href="#Remote Control">Remote Control</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>
</ul> </ul>
@ -1727,6 +1734,7 @@ selecting which channel it shall tune to:
<p><table><tr><td bgcolor=#F0F0F0><pre> <p><table><tr><td bgcolor=#F0F0F0><pre>
virtual bool ProvidesSource(int Source) const; 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 ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
</pre></td></tr></table><p> </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. 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> </body>
</html> </html>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CHANNELS_H
@ -179,6 +179,7 @@ public:
const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; } 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] : ""; } const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
int Tpid(void) const { return tpid; } 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 Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; } int Nid(void) const { return nid; }
int Tid(void) const { return tid; } 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CI_H
@ -12,13 +12,18 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "channels.h"
#include "thread.h" #include "thread.h"
#include "tools.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 cCiMMI;
class cCiMenu { class cCiMenu {
friend class cCiHandler; friend class cCamSlot;
friend class cCiMMI; friend class cCiMMI;
private: private:
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum? 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; } const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
int NumEntries(void) { return numEntries; } int NumEntries(void) { return numEntries; }
bool Selectable(void) { return selectable; } bool Selectable(void) { return selectable; }
bool Select(int Index); void Select(int Index);
bool Cancel(void); void Cancel(void);
bool Abort(void); void Abort(void);
bool HasUpdate(void); bool HasUpdate(void);
}; };
class cCiEnquiry { class cCiEnquiry {
friend class cCiHandler; friend class cCamSlot;
friend class cCiMMI; friend class cCiMMI;
private: private:
cCiMMI *mmi; cCiMMI *mmi;
@ -61,103 +66,140 @@ public:
const char *Text(void) { return text; } const char *Text(void) { return text; }
bool Blind(void) { return blind; } bool Blind(void) { return blind; }
int ExpectedLength(void) { return expectedLength; } int ExpectedLength(void) { return expectedLength; }
bool Reply(const char *s); void Reply(const char *s);
bool Cancel(void); void Cancel(void);
bool Abort(void); void Abort(void);
}; };
#define MAX_CI_SESSION 16 //XXX class cDevice;
#define MAX_CI_SLOT 16 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: public:
bool active; cCiAdapter(void);
int pid; virtual ~cCiAdapter();
int streamType; ///< The derived class must call Cancel(3) in its destructor.
cCiCaPidData(int Pid, int StreamType) virtual bool Ready(void);
{ ///< Returns 'true' if all present CAMs in this adapter are ready.
active = false;
pid = Pid;
streamType = StreamType;
}
}; };
class cCiCaProgramData : public cListObject { class cTPDU;
public:
int programNumber;
cList<cCiCaPidData> pidList;
cCiCaProgramData(int ProgramNumber)
{
programNumber = ProgramNumber;
}
};
class cCiSession;
class cCiTransportLayer;
class cCiTransportConnection; class cCiTransportConnection;
class cCiSession;
class cCiCaProgramData;
class cCiHandler { class cCamSlot : public cListObject {
friend class cCiAdapter;
friend class cCiTransportConnection;
private: private:
cMutex mutex; cMutex mutex;
int fd; cCondVar processed;
int numSlots; cCiAdapter *ciAdapter;
bool newCaSupport; int slotIndex;
bool hasUserIO; int slotNumber;
bool moduleReady[MAX_CI_SLOT]; cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1]; // connection numbering starts with 1
cCiSession *sessions[MAX_CI_SESSION]; eModuleStatus lastModuleStatus;
cCiTransportLayer *tpl; time_t resetTime;
cCiTransportConnection *tc; cTimeMs moduleCheckTimer;
bool resendPmt;
int source; int source;
int transponder; int transponder;
cList<cCiCaProgramData> caProgramList; cList<cCiCaProgramData> caProgramList;
uint32_t ResourceIdToInt(const uint8_t *Data); const int *GetCaSystemIds(void);
bool Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1); void SendCaPmt(uint8_t CmdId);
const unsigned short *GetCaSystemIds(int Slot); void NewConnection(void);
cCiSession *GetSessionBySessionId(uint16_t SessionId); void DeleteAllConnections(void);
cCiSession *GetSessionByResourceId(uint32_t ResourceId, int Slot); void Process(cTPDU *TPDU = NULL);
cCiSession *CreateSession(uint32_t ResourceId); void Write(cTPDU *TPDU);
bool OpenSession(int Length, const uint8_t *Data); cCiSession *GetSessionByResourceId(uint32_t ResourceId);
bool CloseSession(uint16_t SessionId);
int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots);
void SendCaPmt(void);
public: public:
~cCiHandler(); cCamSlot(cCiAdapter *CiAdapter);
static cCiHandler *CreateCiHandler(const char *FileName); ///< Creates a new CAM slot for the given CiAdapter.
///< Creates a new cCiHandler for the given CA device. ///< The CiAdapter will take care of deleting the CAM slot,
int NumSlots(void) { return numSlots; } ///< so the caller must not delete it!
///< Returns the number of CAM slots provided by this CA device. virtual ~cCamSlot();
int NumCams(void); bool Assign(cDevice *Device, bool Query = false);
///< Returns the number of actual CAMs inserted into this CA device. ///< 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); bool Ready(void);
///< Returns true if all CAMs in this CA device are ready. ///< Returns 'true' if the CAM in this slot is ready to decrypt.
bool Process(int Slot = -1); bool HasMMI(void);
///< Processes the given Slot. If Slot is -1, all slots are processed. ///< Returns 'true' if the CAM in this slot has an active MMI.
///< Returns false in case of an error. bool HasUserIO(void);
bool HasUserIO(void) { return hasUserIO; }
///< Returns true if there is a pending user interaction, which shall ///< Returns true if there is a pending user interaction, which shall
///< be retrieved via GetMenu() or GetEnquiry(). ///< be retrieved via GetMenu() or GetEnquiry().
bool EnterMenu(int Slot); bool EnterMenu(void);
///< Requests the CAM in the given Slot to start its menu. ///< Requests the CAM in this slot to start its menu.
cCiMenu *GetMenu(void); cCiMenu *GetMenu(void);
///< Gets a pending menu, or NULL if there is no menu. ///< Gets a pending menu, or NULL if there is no menu.
cCiEnquiry *GetEnquiry(void); cCiEnquiry *GetEnquiry(void);
///< Gets a pending enquiry, or NULL if there is no enquiry. ///< Gets a pending enquiry, or NULL if there is no enquiry.
const char *GetCamName(int Slot); int Priority(void);
///< Returns the name of the CAM in the given Slot, or NULL if there ///< Returns the priority if the device this slot is currently assigned
///< is no CAM in that slot. ///< to, or -1 if it is not assigned to any device.
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot??? bool ProvidesCa(const int *CaSystemIds);
///< Returns true if any of the CAMs can provide one of the given ///< Returns true if the CAM in this slot provides one of the given
///< CaSystemIds. This doesn't necessarily mean that it will be ///< CaSystemIds. This doesn't necessarily mean that it will be
///< possible to actually decrypt such a programme, since CAMs ///< possible to actually decrypt such a programme, since CAMs
///< usually advertise several CA system ids, while the actual ///< usually advertise several CA system ids, while the actual
///< decryption is controlled by the smart card inserted into ///< decryption is controlled by the smart card inserted into
///< the CAM. ///< 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); void AddPid(int ProgramNumber, int Pid, int StreamType);
///< Adds the given PID information to the list of PIDs. A later call ///< Adds the given PID information to the list of PIDs. A later call
///< to SetPid() will (de)activate one of these entries. ///< 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 ///< Sets the given Pid (which has previously been added through a
///< call to AddPid()) to Active. A later call to StartDecrypting() will ///< call to AddPid()) to Active. A later call to StartDecrypting() will
///< send the full list of currently active CA_PMT entries to the CAM. ///< send the full list of currently active CA_PMT entries to the CAM.
bool CanDecrypt(int ProgramNumber); void AddChannel(const cChannel *Channel);
///< XXX ///< Adds all PIDs if the given Channel to the current list of PIDs.
///< Returns true if there is a CAM in this CA device that is able ///< If the source or transponder of the channel are different than
///< to decrypt the programme with the given ProgramNumber. The PIDs ///< what was given in a previous call to AddChannel(), any previously
///< for this ProgramNumber must have been set through previous calls ///< added PIDs will be cleared.
///< to SetPid(). 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); void StartDecrypting(void);
///< Triggers sending all currently active CA_PMT entries to the CAM, ///< Triggers sending all currently active CA_PMT entries to the CAM,
///< so that it will start decrypting. ///< 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 #endif //__CI_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CONFIG_H
@ -21,13 +21,13 @@
// VDR's own version number: // VDR's own version number:
#define VDRVERSION "1.4.5" #define VDRVERSION "1.5.0"
#define VDRVERSNUM 10405 // Version * 10000 + Major * 100 + Minor #define VDRVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number: // The plugin API's version number:
#define APIVERSION "1.4.5" #define APIVERSION "1.5.0"
#define APIVERSNUM 10405 // Version * 10000 + Major * 100 + Minor #define APIVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which // When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to // 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "device.h"
@ -164,7 +164,9 @@ cDevice::cDevice(void)
sdtFilter = NULL; sdtFilter = NULL;
nitFilter = NULL; nitFilter = NULL;
ciHandler = NULL; camSlot = NULL;
startScrambleDetection = 0;
player = NULL; player = NULL;
pesAssembler = new cPesAssembler; pesAssembler = new cPesAssembler;
ClrAvailableTracks(); ClrAvailableTracks();
@ -183,9 +185,7 @@ cDevice::cDevice(void)
cDevice::~cDevice() cDevice::~cDevice()
{ {
Detach(player); Detach(player);
for (int i = 0; i < MAXRECEIVERS; i++) DetachAllReceivers();
Detach(receiver[i]);
delete ciHandler;
delete nitFilter; delete nitFilter;
delete sdtFilter; delete sdtFilter;
delete patFilter; delete patFilter;
@ -199,8 +199,10 @@ bool cDevice::WaitForAllDevicesReady(int Timeout)
for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) { for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
bool ready = true; bool ready = true;
for (int i = 0; i < numDevices; i++) { for (int i = 0; i < numDevices; i++) {
if (device[i] && !device[i]->Ready()) if (device[i] && !device[i]->Ready()) {
ready = false; ready = false;
cCondWait::SleepMs(100);
}
} }
if (ready) if (ready)
return true; return true;
@ -278,39 +280,98 @@ cDevice *cDevice::GetDevice(int Index)
return (0 <= Index && Index < numDevices) ? device[Index] : NULL; 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; // Collect the current priorities of all CAM slots that can decrypt the channel:
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact int NumCamSlots = CamSlots.Count();
for (int i = 0; i < numDevices; i++) { int SlotPriority[NumCamSlots];
bool ndr; int NumUsableSlots = 0;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
// Put together an integer number that reflects the "impact" using for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
// this device would have on the overall system. Each condition is represented SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
// by one bit in the number (or several bits, if the condition is actually if (CamSlot->ModuleStatus() == msReady) {
// a numeric value). The sequence in which the conditions are listed corresponds if (CamSlot->ProvidesCa(Channel->Caids())) {
// to their individual severity, where the one listed first will make the most if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
// difference, because it results in the most significant bit of the result. SlotPriority[CamSlot->Index()] = CamSlot->Priority();
uint imp = 0; NumUsableSlots++;
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;
} }
} }
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; return d;
} }
void cDevice::SetCamSlot(cCamSlot *CamSlot)
{
camSlot = CamSlot;
}
void cDevice::Shutdown(void) void cDevice::Shutdown(void)
{ {
primaryDevice = NULL; primaryDevice = NULL;
@ -422,8 +483,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType); DelPid(Pid, PidType);
return false; return false;
} }
if (ciHandler) if (camSlot)
ciHandler->SetPid(Pid, true); camSlot->SetPid(Pid, true);
} }
PRINTPIDS("a"); PRINTPIDS("a");
return true; return true;
@ -451,8 +512,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType); DelPid(Pid, PidType);
return false; return false;
} }
if (ciHandler) if (camSlot)
ciHandler->SetPid(Pid, true); camSlot->SetPid(Pid, true);
} }
} }
return true; return true;
@ -479,8 +540,8 @@ void cDevice::DelPid(int Pid, ePidType PidType)
if (pidHandles[n].used == 0) { if (pidHandles[n].used == 0) {
pidHandles[n].handle = -1; pidHandles[n].handle = -1;
pidHandles[n].pid = 0; pidHandles[n].pid = 0;
if (ciHandler) if (camSlot)
ciHandler->SetPid(Pid, false); camSlot->SetPid(Pid, false);
} }
} }
PRINTPIDS("E"); PRINTPIDS("E");
@ -557,8 +618,10 @@ bool cDevice::MaySwitchTransponder(void)
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView) bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
{ {
if (LiveView) if (LiveView) {
isyslog("switching to channel %d", Channel->Number()); 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--;) { for (int i = 3; i--;) {
switch (SetChannel(Channel, LiveView)) { switch (SetChannel(Channel, LiveView)) {
case scrOk: return true; case scrOk: return true;
@ -578,12 +641,13 @@ bool cDevice::SwitchChannel(int Direction)
bool result = false; bool result = false;
Direction = sgn(Direction); Direction = sgn(Direction);
if (Direction) { if (Direction) {
cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
int n = CurrentChannel() + Direction; int n = CurrentChannel() + Direction;
int first = n; int first = n;
cChannel *channel; cChannel *channel;
while ((channel = Channels.GetByNumber(n, Direction)) != NULL) { while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
// try only channels which are currently available // try only channels which are currently available
if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0)) if (GetDevice(channel, 0, true))
break; break;
n = channel->Number() + Direction; n = channel->Number() + Direction;
} }
@ -607,14 +671,9 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (LiveView) if (LiveView)
StopReplay(); StopReplay();
// If this card is switched to an other transponder, any receivers still cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
// attached to it need to be automatically detached:
bool NeedsDetachReceivers = false;
// If this card can't receive this channel, we must not actually switch bool NeedsTransferMode = Device != this;
// 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));
eSetChannelResult Result = scrOk; 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: // use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) { if (NeedsTransferMode) {
cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers); if (Device && CanReplay()) {
if (CaDevice && CanReplay()) {
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel 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 (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
if (NeedsDetachReceivers) cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
CaDevice->DetachAllReceivers();
cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
}
else else
Result = scrNoTransfer; Result = scrNoTransfer;
} }
@ -644,27 +699,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(false); sectionHandler->SetStatus(false);
sectionHandler->SetChannel(NULL); 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: // channel to it, for possible later decryption:
if (ciHandler) { if (camSlot)
ciHandler->SetSource(Channel->Source(), Channel->Transponder()); camSlot->AddChannel(Channel);
// 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 (SetChannelDevice(Channel, LiveView)) { if (SetChannelDevice(Channel, LiveView)) {
// Start section handling: // Start section handling:
if (sectionHandler) { if (sectionHandler) {
@ -672,8 +710,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(true); sectionHandler->SetStatus(true);
} }
// Start decrypting any PIDs that might have been set in SetChannelDevice(): // Start decrypting any PIDs that might have been set in SetChannelDevice():
if (ciHandler) if (camSlot)
ciHandler->StartDecrypting(); camSlot->StartDecrypting();
} }
else else
Result = scrFailed; Result = scrFailed;
@ -968,9 +1006,10 @@ bool cDevice::AttachPlayer(cPlayer *Player)
void cDevice::Detach(cPlayer *Player) void cDevice::Detach(cPlayer *Player)
{ {
if (Player && player == Player) { if (Player && player == Player) {
player->Activate(false); cPlayer *p = player;
player->device = NULL; player = NULL; // avoids recursive calls to Detach()
player = NULL; p->Activate(false);
p->device = NULL;
SetPlayMode(pmNone); SetPlayMode(pmNone);
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat)); SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
Audios.ClearAudio(); Audios.ClearAudio();
@ -1143,16 +1182,6 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
return Length; 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 cDevice::Priority(void) const
{ {
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY; int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
@ -1168,16 +1197,6 @@ bool cDevice::Ready(void)
return true; 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 bool cDevice::Receiving(bool CheckAny) const
{ {
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++) {
@ -1187,6 +1206,10 @@ bool cDevice::Receiving(bool CheckAny) const
return false; 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) void cDevice::Action(void)
{ {
if (Running() && OpenDvr()) { if (Running() && OpenDvr()) {
@ -1196,11 +1219,39 @@ void cDevice::Action(void)
if (GetTSPacket(b)) { if (GetTSPacket(b)) {
if (b) { if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2]; 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: // Distribute the packet to all attached receivers:
Lock(); Lock();
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i] && receiver[i]->WantsPid(Pid)) if (receiver[i] && receiver[i]->WantsPid(Pid)) {
receiver[i]->Receive(b, TS_SIZE); 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(); Unlock();
} }
@ -1256,10 +1307,11 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
Receiver->device = this; Receiver->device = this;
receiver[i] = Receiver; receiver[i] = Receiver;
Unlock(); Unlock();
if (!Running()) if (camSlot) {
Start(); camSlot->StartDecrypting();
if (ciHandler) startScrambleDetection = time(NULL);
ciHandler->StartDecrypting(); }
Start();
return true; return true;
} }
} }
@ -1286,10 +1338,10 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i]) else if (receiver[i])
receiversLeft = true; receiversLeft = true;
} }
if (ciHandler) if (camSlot)
ciHandler->StartDecrypting(); camSlot->StartDecrypting();
if (!receiversLeft) if (!receiversLeft)
Cancel(3); Cancel(-1);
} }
void cDevice::DetachAll(int Pid) void cDevice::DetachAll(int Pid)
@ -1307,10 +1359,8 @@ void cDevice::DetachAll(int Pid)
void cDevice::DetachAllReceivers(void) void cDevice::DetachAllReceivers(void)
{ {
cMutexLock MutexLock(&mutexReceiver); cMutexLock MutexLock(&mutexReceiver);
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++)
if (receiver[i]) Detach(receiver[i]);
Detach(receiver[i]);
}
} }
// --- cTSBuffer ------------------------------------------------------------- // --- cTSBuffer -------------------------------------------------------------

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DEVICE_H
@ -128,12 +128,21 @@ public:
///< Gets the device with the given Index. ///< Gets the device with the given Index.
///< \param Index must be in the range 0..numDevices-1. ///< \param Index must be in the range 0..numDevices-1.
///< \return A pointer to the device, or NULL if the Index was invalid. ///< \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 ///< Returns a device that is able to receive the given Channel at the
///< given Priority, with the least impact on active recordings and ///< given Priority, with the least impact on active recordings and
///< live viewing. ///< live viewing. The LiveView parameter tells whether the device will
///< See ProvidesChannel() for more information on how ///< be used for live viewing or a recording.
///< priorities are handled, and the meaning of NeedsDetachReceivers. ///< 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); static void Shutdown(void);
///< Closes down all devices. ///< Closes down all devices.
///< Must be called at the end of the program. ///< Must be called at the end of the program.
@ -171,16 +180,6 @@ public:
///< Returns the card index of this device (0 ... MAXDEVICES - 1). ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
int DeviceNumber(void) const; int DeviceNumber(void) const;
///< Returns the number of this device (0 ... numDevices). ///< 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; virtual bool HasDecoder(void) const;
///< Tells whether this device has an MPEG decoder. ///< Tells whether this device has an MPEG decoder.
@ -199,7 +198,9 @@ public:
virtual bool ProvidesSource(int Source) const; virtual bool ProvidesSource(int Source) const;
///< Returns true if this device can provide the given source. ///< Returns true if this device can provide the given source.
virtual bool ProvidesTransponder(const cChannel *Channel) const; 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; virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const;
///< Returns true if this is the only device that is able to provide ///< Returns true if this is the only device that is able to provide
///< the given channel's transponder. ///< the given channel's transponder.
@ -246,7 +247,7 @@ public:
///< channel number while replaying. ///< channel number while replaying.
void ForceTransferMode(void); void ForceTransferMode(void);
///< Forces the device into transfermode for the current channel. ///< 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. ///< Returns true if the device has a lock on the requested transponder.
///< Default is true, a specific device implementation may return false ///< Default is true, a specific device implementation may return false
///< to indicate that it is not ready yet. ///< to indicate that it is not ready yet.
@ -309,10 +310,15 @@ public:
// Common Interface facilities: // Common Interface facilities:
protected: private:
cCiHandler *ciHandler; time_t startScrambleDetection;
cCamSlot *camSlot;
public: 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 // Image Grab facilities
@ -512,11 +518,12 @@ public:
private: private:
cMutex mutexReceiver; cMutex mutexReceiver;
cReceiver *receiver[MAXRECEIVERS]; cReceiver *receiver[MAXRECEIVERS];
protected: public:
int Priority(void) const; int Priority(void) const;
///< Returns the priority of the current receiving session (0..MAXPRIORITY), ///< Returns the priority of the current receiving session (0..MAXPRIORITY),
///< or -1 if no receiver is currently active. The primary device will ///< or -1 if no receiver is currently active. The primary device will
///< always return at least Setup.PrimaryLimit-1. ///< always return at least Setup.PrimaryLimit-1.
protected:
virtual bool OpenDvr(void); virtual bool OpenDvr(void);
///< Opens the DVR of this device and prepares it to deliver a Transport ///< Opens the DVR of this device and prepares it to deliver a Transport
///< Stream for use in a cReceiver. ///< Stream for use in a cReceiver.
@ -530,8 +537,6 @@ protected:
///< false in case of a non recoverable error, otherwise it returns true, ///< false in case of a non recoverable error, otherwise it returns true,
///< even if Data is NULL. ///< even if Data is NULL.
public: public:
int Ca(void) const;
///< Returns the ca of the current receiving session(s).
bool Receiving(bool CheckAny = false) const; bool Receiving(bool CheckAny = false) const;
///< Returns true if we are currently receiving. ///< Returns true if we are currently receiving.
bool AttachReceiver(cReceiver *Receiver); 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "dvbdevice.h"
@ -19,6 +19,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include "channels.h" #include "channels.h"
#include "diseqc.h" #include "diseqc.h"
#include "dvbci.h"
#include "dvbosd.h" #include "dvbosd.h"
#include "eitscan.h" #include "eitscan.h"
#include "player.h" #include "player.h"
@ -28,7 +29,6 @@
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1 #define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
#define DO_MULTIPLE_RECORDINGS 1 #define DO_MULTIPLE_RECORDINGS 1
//#define DO_MULTIPLE_CA_CHANNELS
#define DEV_VIDEO "/dev/video" #define DEV_VIDEO "/dev/video"
#define DEV_DVB_ADAPTER "/dev/dvb/adapter" #define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@ -77,7 +77,6 @@ private:
int lockTimeout; int lockTimeout;
time_t lastTimeoutReport; time_t lastTimeoutReport;
fe_type_t frontendType; fe_type_t frontendType;
cCiHandler *ciHandler;
cChannel channel; cChannel channel;
const char *diseqcCommands; const char *diseqcCommands;
eTunerStatus tunerStatus; eTunerStatus tunerStatus;
@ -88,19 +87,18 @@ private:
bool SetFrontend(void); bool SetFrontend(void);
virtual void Action(void); virtual void Action(void);
public: 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(); virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const; bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel, bool Tune); void Set(const cChannel *Channel, bool Tune);
bool Locked(int TimeoutMs = 0); 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; fd_frontend = Fd_Frontend;
cardIndex = CardIndex; cardIndex = CardIndex;
frontendType = FrontendType; frontendType = FrontendType;
ciHandler = CiHandler;
tuneTimeout = 0; tuneTimeout = 0;
lockTimeout = 0; lockTimeout = 0;
lastTimeoutReport = 0; lastTimeoutReport = 0;
@ -346,8 +344,6 @@ void cDvbTuner::Action(void)
} }
} }
if (ciHandler)
ciHandler->Process();
if (tunerStatus != tsTuned) if (tunerStatus != tsTuned)
newSet.TimedWait(mutex, 1000); newSet.TimedWait(mutex, 1000);
} }
@ -360,6 +356,7 @@ int cDvbDevice::setTransferModeForDolbyDigital = 1;
cDvbDevice::cDvbDevice(int n) cDvbDevice::cDvbDevice(int n)
{ {
ciAdapter = NULL;
dvbTuner = NULL; dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
spuDecoder = NULL; spuDecoder = NULL;
@ -377,6 +374,12 @@ cDvbDevice::cDvbDevice(int n)
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK); fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
fd_stc = DvbOpen(DEV_DVB_DEMUX, n, O_RDWR); 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): // The DVR device (will be opened and closed as needed):
fd_dvr = -1; fd_dvr = -1;
@ -420,8 +423,7 @@ cDvbDevice::cDvbDevice(int n)
dvb_frontend_info feinfo; dvb_frontend_info feinfo;
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) { if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
frontendType = feinfo.type; frontendType = feinfo.type;
ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n)); dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
} }
else else
LOG_ERROR; LOG_ERROR;
@ -436,6 +438,7 @@ cDvbDevice::~cDvbDevice()
{ {
delete spuDecoder; delete spuDecoder;
delete dvbTuner; delete dvbTuner;
delete ciAdapter;
// We're not explicitly closing any device files here, since this sometimes // We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway... // caused segfaults. Besides, the program is about to terminate anyway...
} }
@ -494,32 +497,11 @@ bool cDvbDevice::HasDecoder(void) const
bool cDvbDevice::Ready(void) bool cDvbDevice::Ready(void)
{ {
if (ciHandler) { if (ciAdapter)
ciHandler->Process(); return ciAdapter->Ready();
return ciHandler->Ready();
}
return true; 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) cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
{ {
if (!spuDecoder && IsPrimaryDevice()) 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 hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false; bool needsDetachReceivers = false;
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) { if (ProvidesSource(Channel->Source())) {
result = hasPriority; result = hasPriority;
if (Priority >= 0 && Receiving(true)) { if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) { if (dvbTuner->IsTunedTo(Channel)) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) { if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS #ifdef DO_MULTIPLE_RECORDINGS
#ifndef DO_MULTIPLE_CA_CHANNELS if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN) if (CamSlot()->CanDecrypt(Channel))
needsDetachReceivers = Ca() != Channel->Ca(); result = true;
else else
#endif needsDetachReceivers = true;
if (!IsPrimaryDevice()) }
else if (!IsPrimaryDevice())
result = true; result = true;
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE #ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else 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 && 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 || !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; 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)); CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
} }
else if (StartTransferMode) 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; return true;
} }
@ -922,13 +907,13 @@ void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) { if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) { if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
DetachAll(pidHandles[ptAudio].pid); DetachAll(pidHandles[ptAudio].pid);
if (ciHandler) if (CamSlot())
ciHandler->SetPid(pidHandles[ptAudio].pid, false); CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
pidHandles[ptAudio].pid = TrackId->id; pidHandles[ptAudio].pid = TrackId->id;
SetPid(&pidHandles[ptAudio], ptAudio, true); SetPid(&pidHandles[ptAudio], ptAudio, true);
if (ciHandler) { if (CamSlot()) {
ciHandler->SetPid(pidHandles[ptAudio].pid, true); CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
ciHandler->StartDecrypting(); CamSlot()->StartDecrypting();
} }
} }
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DVBDEVICE_H
@ -36,16 +36,20 @@ public:
///< \return True if any devices are available. ///< \return True if any devices are available.
private: private:
fe_type_t frontendType; 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: protected:
virtual void MakePrimaryDevice(bool On); virtual void MakePrimaryDevice(bool On);
public: public:
cDvbDevice(int n); cDvbDevice(int n);
virtual ~cDvbDevice(); virtual ~cDvbDevice();
virtual bool Ready(void); virtual bool Ready(void);
virtual int ProvidesCa(const cChannel *Channel) const;
virtual bool HasDecoder(void) const; virtual bool HasDecoder(void) const;
// Common Interface facilities:
private:
cCiAdapter *ciAdapter;
// SPU facilities // SPU facilities
private: private:

196
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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: * Translations provided by:
* *
@ -2598,27 +2598,27 @@ const tI18nPhrase Phrases[] = {
"Kan ikke åbne CAM menuen!", "Kan ikke åbne CAM menuen!",
"Menu CAM není dostupné", "Menu CAM není dostupné",
}, },
{ "Resetting CAM...", { "CAM is in use - really reset?"
"CAM wird zurückgesetzt...", "CAM wird benutzt - wirklich zurücksetzen?",
"Resetiram CAM...", "",//TODO
"Reimpostazione modulo CAM...", "",//TODO
"CAM wordt herstart...", "",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO "",//TODO
"Réinitialisation du CAM",
"",//TODO "",//TODO
"CA-moduuli palautetaan alkutilaan...",
"Resetujê CAM...",
"Reiniciando CAM...",
"",//TODO "",//TODO
"Återställer CAM ...",
"Se reseteazã CAM...",
"A CAM újra indul...",
"",//TODO "",//TODO
"¿ÕàÕÓàã×ÚÐ CAM...",
"",//TODO "",//TODO
"CAM mooduli taaskäivitus...",
"Nulstiller CAM...",
"Restartuje se CAM...",
}, },
{ "Can't reset CAM!", { "Can't reset CAM!",
"Zurücksetzen des CAM fehlgeschlagen!", "Zurücksetzen des CAM fehlgeschlagen!",
@ -2642,27 +2642,93 @@ const tI18nPhrase Phrases[] = {
"Kan ikke nulstille CAM!", "Kan ikke nulstille CAM!",
"CAM modul nelze restartovat!", "CAM modul nelze restartovat!",
}, },
{ "CAM has been reset", { "CAM reset",
"CAM wurde zurückgesetzt", "CAM zurückgesetzt",
"CAM je resetiran", "",//TODO
"Modulo CAM reimpostato", "",//TODO
"CAM is herstart", "",//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 "",//TODO
"La CAM a été réinitialisée",
"",//TODO "",//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!", { "Please enter %d digits!",
"Bitte geben Sie %d Ziffern ein!", "Bitte geben Sie %d Ziffern ein!",
@ -2797,27 +2863,27 @@ const tI18nPhrase Phrases[] = {
"LNB", "LNB",
"LNB", "LNB",
}, },
{ "CICAM", { "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"Accesso condizionato CICAM", "Accesso condizionato CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"Accès conditionnel", "Accès conditionnel",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CI Accés condicional", "CI Accés condicional",
"ÃáÛÞÒÝëÙ ÔÞáâãß", "ÃáÛÞÒÝëÙ ÔÞáâãß",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
"CICAM", "CAM",
}, },
{ "Recording", { "Recording",
"Aufnahme", "Aufnahme",
@ -3964,28 +4030,6 @@ const tI18nPhrase Phrases[] = {
"Anvend DiSEqC", "Anvend DiSEqC",
"Pou¾ívat 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)", { "Setup.Recording$Margin at start (min)",
"Vorlauf zum Timer-Beginn (min)", "Vorlauf zum Timer-Beginn (min)",
"Premik zaèetka snemanja (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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "menu.h"
@ -38,7 +38,9 @@
#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS) #define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours #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 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 #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::cMenuCam(cCiMenu *CiMenu) class cMenuCam : public cOsdMenu {
: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 ------------------"); camSlot = CamSlot;
ciMenu = CiMenu; ciMenu = NULL;
selected = false; ciEnquiry = NULL;
input = NULL;
offset = 0; offset = 0;
if (ciMenu->Selectable()) lastCamExchange = time(NULL);
SetHasHotkeys(); SetNeedsFastResponse(true);
SetTitle(*ciMenu->TitleText() ? ciMenu->TitleText() : "CAM"); QueryCam();
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();
} }
cMenuCam::~cMenuCam() cMenuCam::~cMenuCam()
{ {
if (!selected) if (ciMenu)
ciMenu->Abort(); ciMenu->Abort();
delete ciMenu; 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) void cMenuCam::AddMultiLineItem(const char *s)
@ -1628,90 +1696,61 @@ void cMenuCam::AddMultiLineItem(const char *s)
eOSState cMenuCam::Select(void) eOSState cMenuCam::Select(void)
{ {
if (ciMenu->Selectable()) { if (ciMenu) {
ciMenu->Select(Current() - offset); if (ciMenu->Selectable()) {
dsyslog("CAM: select %d", Current() - offset); ciMenu->Select(Current() - offset);
dsyslog("CAM %d: select %d", camSlot->SlotNumber(), Current() - offset);
}
else
ciMenu->Cancel();
} }
else else if (ciEnquiry) {
ciMenu->Cancel(); if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
selected = true; char buffer[64];
return osEnd; 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) eOSState cMenuCam::ProcessKey(eKeys Key)
{ {
if (!camSlot->HasMMI())
return osBack;
eOSState state = cOsdMenu::ProcessKey(Key); eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) { if (ciMenu || ciEnquiry) {
switch (Key) { lastCamExchange = time(NULL);
case kOk: return Select(); if (state == osUnknown) {
default: break; 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) { else if (time(NULL) - lastCamExchange < CAMRESPONSETIMEOUT)
ciMenu->Cancel(); QueryCam();
selected = true; else {
return osEnd; Skins.Message(mtError, tr("CAM not responding!"));
} return osBack;
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;
} }
return state; return state;
} }
@ -1720,21 +1759,9 @@ eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
cOsdObject *CamControl(void) cOsdObject *CamControl(void)
{ {
for (int d = 0; d < cDevice::NumDevices(); d++) { for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
cDevice *Device = cDevice::GetDevice(d); if (CamSlot->HasUserIO())
if (Device) { return new cMenuCam(CamSlot);
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);
}
}
}
} }
return NULL; return NULL;
} }
@ -2454,95 +2481,117 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuSetupCICAM ------------------------------------------------------- // --- cMenuSetupCAM ---------------------------------------------------------
class cMenuSetupCICAMItem : public cOsdItem { class cMenuSetupCAMItem : public cOsdItem {
private: private:
cCiHandler *ciHandler; cCamSlot *camSlot;
int slot;
public: public:
cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot); cMenuSetupCAMItem(cCamSlot *CamSlot);
cCiHandler *CiHandler(void) { return ciHandler; } cCamSlot *CamSlot(void) { return camSlot; }
int Slot(void) { return slot; } bool Changed(void);
}; };
cMenuSetupCICAMItem::cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot) cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot)
{ {
ciHandler = CiHandler; camSlot = CamSlot;
slot = Slot; SetText("");
char buffer[32]; Changed();
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);
} }
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: private:
eOSState Menu(void); eOSState Menu(void);
eOSState Reset(void); eOSState Reset(void);
public: public:
cMenuSetupCICAM(void); cMenuSetupCAM(void);
virtual eOSState ProcessKey(eKeys Key); virtual eOSState ProcessKey(eKeys Key);
}; };
cMenuSetupCICAM::cMenuSetupCICAM(void) cMenuSetupCAM::cMenuSetupCAM(void)
{ {
SetSection(tr("CICAM")); SetSection(tr("CAM"));
for (int d = 0; d < cDevice::NumDevices(); d++) { SetCols(15);
cDevice *Device = cDevice::GetDevice(d); SetHasHotkeys();
if (Device) { for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
cCiHandler *CiHandler = Device->CiHandler(); Add(new cMenuSetupCAMItem(CamSlot));
if (CiHandler) {
for (int Slot = 0; Slot < CiHandler->NumSlots(); Slot++)
Add(new cMenuSetupCICAMItem(Device->CardIndex(), CiHandler, Slot));
}
}
}
SetHelp(tr("Button$Menu"), tr("Button$Reset")); 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) {
if (item->CiHandler()->EnterMenu(item->Slot())) { if (item->CamSlot()->EnterMenu()) {
Skins.Message(mtWarning, tr("Opening CAM menu...")); Skins.Message(mtStatus, tr("Opening CAM menu..."));
time_t t = time(NULL); time_t t0 = time(NULL);
while (time(NULL) - t < MAXWAITFORCAMMENU && !item->CiHandler()->HasUserIO()) time_t t1 = t0;
item->CiHandler()->Process(); while (time(NULL) - t0 <= MAXWAITFORCAMMENU) {
return osEnd; // the CAM menu will be executed explicitly from the main loop 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; return osContinue;
} }
eOSState cMenuSetupCICAM::Reset(void) eOSState cMenuSetupCAM::Reset(void)
{ {
cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current()); cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
if (item) { if (item) {
Skins.Message(mtWarning, tr("Resetting CAM...")); if (!item->CamSlot()->Device() || Interface->Confirm(tr("CAM is in use - really reset?"))) {
if (item->CiHandler()->Reset(item->Slot())) { if (!item->CamSlot()->Reset())
Skins.Message(mtInfo, tr("CAM has been reset")); Skins.Message(mtError, tr("Can't reset CAM!"));
return osEnd;
} }
else
Skins.Message(mtError, tr("Can't reset CAM!"));
} }
return osContinue; 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) { switch (Key) {
case kOk:
case kRed: return Menu(); case kRed: return Menu();
case kGreen: return Reset(); case kGreen: state = Reset(); break;
default: break; default: break;
} }
for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
if (ci->Changed())
DisplayItem(ci);
}
} }
return state; return state;
} }
@ -2710,7 +2759,7 @@ void cMenuSetup::Set(void)
Add(new cOsdItem(hk(tr("EPG")), osUser2)); Add(new cOsdItem(hk(tr("EPG")), osUser2));
Add(new cOsdItem(hk(tr("DVB")), osUser3)); Add(new cOsdItem(hk(tr("DVB")), osUser3));
Add(new cOsdItem(hk(tr("LNB")), osUser4)); 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("Recording")), osUser6));
Add(new cOsdItem(hk(tr("Replay")), osUser7)); Add(new cOsdItem(hk(tr("Replay")), osUser7));
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8)); Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
@ -2740,7 +2789,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
case osUser2: return AddSubMenu(new cMenuSetupEPG); case osUser2: return AddSubMenu(new cMenuSetupEPG);
case osUser3: return AddSubMenu(new cMenuSetupDVB); case osUser3: return AddSubMenu(new cMenuSetupDVB);
case osUser4: return AddSubMenu(new cMenuSetupLNB); 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 osUser6: return AddSubMenu(new cMenuSetupRecord);
case osUser7: return AddSubMenu(new cMenuSetupReplay); case osUser7: return AddSubMenu(new cMenuSetupReplay);
case osUser8: return AddSubMenu(new cMenuSetupMisc); case osUser8: return AddSubMenu(new cMenuSetupMisc);
@ -3126,7 +3175,7 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
if (Direction) { if (Direction) {
while (Channel) { while (Channel) {
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(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; return Channel;
} }
} }
@ -3541,7 +3590,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName); isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) { if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel(); 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)) { if (device->AttachReceiver(recorder)) {
Recording.WriteInfo(); Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true); cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
@ -3610,7 +3659,7 @@ void cRecordControl::Stop(void)
bool cRecordControl::Process(time_t t) bool cRecordControl::Process(time_t t)
{ {
if (!recorder || !timer || !timer->Matches(t)) if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t))
return false; return false;
AssertFreeDiskSpace(timer->Priority()); AssertFreeDiskSpace(timer->Priority());
return true; return true;
@ -3645,15 +3694,9 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
cChannel *channel = Channels.GetByNumber(ch); cChannel *channel = Channels.GetByNumber(ch);
if (channel) { if (channel) {
bool NeedsDetachReceivers = false;
int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority; 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 (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()); dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
if (!device->SwitchChannel(channel, false)) { if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true); 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) bool cRecordControls::PauseLiveVideo(void)
{ {
Skins.Message(mtStatus, tr("Pausing live video...")); Skins.Message(mtStatus, tr("Pausing live video..."));
@ -3882,7 +3912,8 @@ void cReplayControl::Hide(void)
if (visible) { if (visible) {
delete displayReplay; delete displayReplay;
displayReplay = NULL; displayReplay = NULL;
needsFastResponse = visible = false; SetNeedsFastResponse(false);
visible = false;
modeOnly = false; modeOnly = false;
lastPlay = lastForward = false; lastPlay = lastForward = false;
lastSpeed = -2; // an invalid value lastSpeed = -2; // an invalid value
@ -3923,7 +3954,8 @@ bool cReplayControl::ShowProgress(bool Initial)
if (!visible) { if (!visible) {
displayReplay = Skins.Current()->DisplayReplay(modeOnly); displayReplay = Skins.Current()->DisplayReplay(modeOnly);
displayReplay->SetMarks(&marks); displayReplay->SetMarks(&marks);
needsFastResponse = visible = true; SetNeedsFastResponse(true);
visible = true;
} }
if (Initial) { if (Initial) {
if (title) if (title)

28
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __MENU_H
@ -128,31 +128,6 @@ public:
eOSState ProcessKey(eKeys Key); 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); cOsdObject *CamControl(void);
class cMenuRecordingItem; class cMenuRecordingItem;
@ -206,7 +181,6 @@ private:
public: public:
static bool Start(cTimer *Timer = NULL, bool Pause = false); static bool Start(cTimer *Timer = NULL, bool Pause = false);
static void Stop(const char *InstantId); static void Stop(const char *InstantId);
static void Stop(cDevice *Device);
static bool PauseLiveVideo(void); static bool PauseLiveVideo(void);
static const char *GetInstantId(const char *LastInstantId); static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName); static cRecordControl *GetRecordControl(const char *FileName);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "osdbase.h"
@ -127,9 +127,9 @@ void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
cols[4] = c4; cols[4] = c4;
} }
void cOsdMenu::SetHasHotkeys(void) void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
{ {
hasHotkeys = true; hasHotkeys = HasHotkeys;
digit = 0; 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) void cOsdMenu::Clear(void)
{ {
if (marked >= 0) if (marked >= 0)
@ -432,6 +446,8 @@ eOSState cOsdMenu::HotKey(eKeys Key)
if (s && (s = skipspace(s)) != NULL) { if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') { if (*s == Key - k1 + '1') {
current = item->Index(); current = item->Index();
RefreshCurrent();
Display();
cRemote::Put(kOk, true); cRemote::Put(kOk, true);
break; break;
} }
@ -499,4 +515,3 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
} }
return osContinue; return osContinue;
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __OSDBASE_H
@ -70,12 +70,13 @@ class cOsdObject {
friend class cOsdMenu; friend class cOsdMenu;
private: private:
bool isMenu; bool isMenu;
protected:
bool needsFastResponse; bool needsFastResponse;
protected:
void SetNeedsFastResponse(bool NeedsFastResponse) { needsFastResponse = NeedsFastResponse; }
public: public:
cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; } cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; }
virtual ~cOsdObject() {} virtual ~cOsdObject() {}
bool NeedsFastResponse(void) { return needsFastResponse; } virtual bool NeedsFastResponse(void) { return needsFastResponse; }
bool IsMenu(void) { return isMenu; } bool IsMenu(void) { return isMenu; }
virtual void Show(void); virtual void Show(void);
virtual eOSState ProcessKey(eKeys Key) { return osUnknown; } virtual eOSState ProcessKey(eKeys Key) { return osUnknown; }
@ -98,12 +99,13 @@ protected:
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; } cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
const char *hk(const char *s); const char *hk(const char *s);
void SetCols(int c0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); 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); virtual void Clear(void);
bool SelectableItem(int idx); bool SelectableItem(int idx);
void SetCurrent(cOsdItem *Item); void SetCurrent(cOsdItem *Item);
void RefreshCurrent(void); void RefreshCurrent(void);
void DisplayCurrent(bool Current); void DisplayCurrent(bool Current);
void DisplayItem(cOsdItem *Item);
void CursorUp(void); void CursorUp(void);
void CursorDown(void); void CursorDown(void);
void PageUp(void); void PageUp(void);
@ -120,6 +122,7 @@ protected:
public: public:
cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
virtual ~cOsdMenu(); virtual ~cOsdMenu();
virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
int Current(void) { return current; } int Current(void) { return current; }
void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL); void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "pat.h"
@ -78,7 +78,7 @@ public:
bool Is(cCaDescriptors * CaDescriptors); bool Is(cCaDescriptors * CaDescriptors);
bool Empty(void) { return caDescriptors.Count() == 0; } bool Empty(void) { return caDescriptors.Count() == 0; }
void AddCaDescriptor(SI::CaDescriptor *d, bool Stream); 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; } const int *CaIds(void) { return caIds; }
}; };
@ -148,7 +148,7 @@ void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream)
#endif #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) if (!CaSystemIds || !*CaSystemIds)
return 0; return 0;
@ -156,7 +156,7 @@ int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufS
int length = 0; int length = 0;
int IsStream = -1; int IsStream = -1;
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) { for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
const unsigned short *caids = CaSystemIds; const int *caids = CaSystemIds;
do { do {
if (d->CaSystem() == *caids) { if (d->CaSystem() == *caids) {
if (length + d->Length() <= BufSize) { if (length + d->Length() <= BufSize) {
@ -187,7 +187,7 @@ public:
// Returns 0 if this is an already known descriptor, // Returns 0 if this is an already known descriptor,
// 1 if it is an all new descriptor with actual contents, // 1 if it is an all new descriptor with actual contents,
// and 2 if an existing descriptor was changed. // 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) int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
@ -208,7 +208,7 @@ int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
return CaDescriptors->Empty() ? 0 : 1; 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); cMutexLock MutexLock(&mutex);
StreamFlag = false; StreamFlag = false;
@ -221,7 +221,7 @@ int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int Serv
cCaDescriptorHandler CaDescriptorHandler; 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); 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __PAT_H
@ -32,7 +32,7 @@ public:
void Trigger(void); 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. ///< Gets all CA descriptors for a given channel.
///< Copies all available CA descriptors for the given Source, Transponder and ServiceId ///< 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 ///< 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "receiver.h"
@ -12,10 +12,10 @@
#include <stdio.h> #include <stdio.h>
#include "tools.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; device = NULL;
ca = Ca; channelID = ChannelID;
priority = Priority; priority = Priority;
numPids = 0; numPids = 0;
if (Pid) if (Pid)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __RECEIVER_H
@ -18,7 +18,7 @@ class cReceiver {
friend class cDevice; friend class cDevice;
private: private:
cDevice *device; cDevice *device;
int ca; tChannelID channelID;
int priority; int priority;
int pids[MAXRECEIVEPIDS]; int pids[MAXRECEIVEPIDS];
int numPids; int numPids;
@ -38,8 +38,8 @@ protected:
///< will be delivered only ONCE, so the cReceiver must make sure that ///< will be delivered only ONCE, so the cReceiver must make sure that
///< it will be able to buffer the data if necessary. ///< it will be able to buffer the data if necessary.
public: public:
cReceiver(int Ca, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL); cReceiver(tChannelID ChannelID, 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 ///< 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 ///< the given Priority. Pid is a single PID (typically the video PID), while
///< Pids1...Pids3 are pointers to zero terminated lists of PIDs. ///< Pids1...Pids3 are pointers to zero terminated lists of PIDs.
///< If any of these PIDs are 0, they will be silently ignored. ///< 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 ///< 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 ///< that this cReceiver may be detached at any time (without blocking the
///< cDevice it is attached to). ///< 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(); 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 #endif //__RECEIVER_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "recorder.h"
@ -21,6 +21,8 @@
#define MINFREEDISKSPACE (512) // MB #define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds #define DISKCHECKINTERVAL 100 // seconds
// --- cFileWriter -----------------------------------------------------------
class cFileWriter : public cThread { class cFileWriter : public cThread {
private: private:
cRemux *remux; 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) // --- cRecorder -------------------------------------------------------------
:cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
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") ,cThread("recording")
{ {
// Make sure the disk is up and running: // 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __RECORDER_H
@ -28,9 +28,9 @@ protected:
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
virtual void Action(void); virtual void Action(void);
public: public:
cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids); cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
// Creates a new recorder that requires conditional access Ca, has // Creates a new recorder for the channel with the given ChannelID and
// the given Priority and will record the given PIDs into the file FileName. // the given Priority that will record the given PIDs into the file FileName.
virtual ~cRecorder(); virtual ~cRecorder();
}; };

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "thread.h"
@ -249,17 +249,29 @@ void *cThread::StartThread(cThread *Thread)
return NULL; 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) bool cThread::Start(void)
{ {
if (!active) { if (!running) {
active = running = true; if (active) {
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) { // Wait until the previous incarnation of this thread has completely ended
pthread_detach(childTid); // auto-reap // before starting it newly:
cTimeMs RestartTimeout;
while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
cCondWait::SleepMs(THREAD_STOP_SLEEP);
} }
else { if (!active) {
LOG_ERROR; active = running = true;
active = running = false; if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
return false; pthread_detach(childTid); // auto-reap
}
else {
LOG_ERROR;
active = running = false;
return false;
}
} }
} }
return true; return true;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __THREAD_H
@ -115,6 +115,7 @@ public:
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3))); void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
bool Start(void); bool Start(void);
///< Actually starts the thread. ///< Actually starts the thread.
///< If the thread is already running, nothing happens.
bool Active(void); bool Active(void);
///< Checks whether the thread is still alive. ///< Checks whether the thread is still alive.
static bool EmergencyExit(bool Request = false); static bool EmergencyExit(bool Request = false);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "tools.h"
@ -542,9 +542,9 @@ time_t LastModifiedTime(const char *FileName)
// --- cTimeMs --------------------------------------------------------------- // --- cTimeMs ---------------------------------------------------------------
cTimeMs::cTimeMs(void) cTimeMs::cTimeMs(int Ms)
{ {
Set(); Set(Ms);
} }
uint64_t cTimeMs::Now(void) uint64_t cTimeMs::Now(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __TOOLS_H
@ -23,7 +23,6 @@
#include <sys/types.h> #include <sys/types.h>
typedef unsigned char uchar; typedef unsigned char uchar;
#define uint64 uint64_t // for compatibility - TODO remove in version 1.5
extern int SysLogLevel; extern int SysLogLevel;
@ -162,7 +161,8 @@ class cTimeMs {
private: private:
uint64_t begin; uint64_t begin;
public: public:
cTimeMs(void); cTimeMs(int Ms = 0);
///< Creates a timer with ms resolution and an initial timeout of Ms.
static uint64_t Now(void); static uint64_t Now(void);
void Set(int Ms = 0); void Set(int Ms = 0);
bool TimedOut(void); bool TimedOut(void);

View File

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

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __TRANSFER_H
@ -25,7 +25,7 @@ protected:
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
virtual void Action(void); virtual void Action(void);
public: 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(); virtual ~cTransfer();
}; };
@ -34,7 +34,7 @@ private:
cTransfer *transfer; cTransfer *transfer;
static cDevice *receiverDevice; static cDevice *receiverDevice;
public: 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(); ~cTransferControl();
virtual void Hide(void) {} virtual void Hide(void) {}
static cDevice *ReceiverDevice(void) { return receiverDevice; } 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 * 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> #include <getopt.h>
@ -68,8 +68,6 @@
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown #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 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 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 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 MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down #define SHUTDOWNRETRY 300 // seconds before trying again to shut down
@ -501,7 +499,6 @@ int main(int argc, char *argv[])
int PreviousChannelIndex = 0; int PreviousChannelIndex = 0;
time_t LastChannelChanged = time(NULL); time_t LastChannelChanged = time(NULL);
time_t LastActivity = 0; time_t LastActivity = 0;
time_t LastCamMenu = 0;
int MaxLatencyTime = 0; int MaxLatencyTime = 0;
bool ForceShutdown = false; bool ForceShutdown = false;
bool UserShutdown = false; bool UserShutdown = false;
@ -851,19 +848,14 @@ int main(int argc, char *argv[])
DeletedRecordings.Update(); DeletedRecordings.Update();
} }
// CAM control: // CAM control:
if (!Menu && !cOsd::IsOpen()) { if (!Menu && !cOsd::IsOpen())
Menu = CamControl(); Menu = CamControl();
if (Menu)
LastCamMenu = 0;
else if (!LastCamMenu)
LastCamMenu = time(NULL);
}
// Queued messages: // Queued messages:
if (!Skins.IsOpen()) if (!Skins.IsOpen())
Skins.ProcessQueuedMessages(); Skins.ProcessQueuedMessages();
// User Input: // User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control(); 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) { if (NORMALKEY(key) != kNone) {
EITScanner.Activity(); EITScanner.Activity();
LastActivity = time(NULL); LastActivity = time(NULL);
@ -1052,9 +1044,6 @@ int main(int argc, char *argv[])
else if (time(NULL) - LastActivity > MENUTIMEOUT) else if (time(NULL) - LastActivity > MENUTIMEOUT)
state = osEnd; 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) { switch (state) {
case osPause: DELETE_MENU; case osPause: DELETE_MENU;
cControl::Shutdown(); // just in case cControl::Shutdown(); // just in case