mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
CAM handling refactored; multiple recordings with one CAM; automatic CAM selection
This commit is contained in:
parent
b4cab10eca
commit
87dd5139ff
53
HISTORY
53
HISTORY
@ -5028,3 +5028,56 @@ Video Disk Recorder Revision History
|
||||
2007-01-07: Version 1.4.5
|
||||
|
||||
- Official release.
|
||||
|
||||
2007-01-07: Version 1.5.0
|
||||
|
||||
- The CAM handling has been refactored. Instead of a cCiHandler per device there
|
||||
is now an abstract cCiAdapter and a cCamSlot. This allows each slot to be
|
||||
accessed individually.
|
||||
- The general 15 seconds workaround time before opening the CAM menu has been
|
||||
removed. If the CAM menu doesn't open within a timeout, the enter menu command
|
||||
is now sent again.
|
||||
- If a CAM is reset or pulled and reinserted, it now automatically starts
|
||||
decrypting the current channel again.
|
||||
- The Setup/CAM menu now dynamically refreshes its items and displays whether
|
||||
a CAM is present or ready. The 'Reset' function no longer leaves the menu.
|
||||
- The CAM menu will now be openend when pressing the Ok key on a slot entry.
|
||||
- The CAM menu now stays within the current menu context and doesn't close and
|
||||
reopen the menu every time an option is selected.
|
||||
- When an encrypted channel is switched to for the first time, VDR now checks
|
||||
explicitly whether a CAM can actually decrypt that channel. If there is more
|
||||
than one CAM in the system that claims to be able to decrypt the channel,
|
||||
they are all tried in turn.
|
||||
To make this possible, an encrypted channel needs to be received in Transfer
|
||||
Mode when it is switched to for the first time, so that VDR can determine
|
||||
whether the TS packets are actually decrypted. Once a channel is known to
|
||||
be decrypted by a particular CAM, the next time it is switched to it will
|
||||
be shown in normal live viewing mode.
|
||||
- A cDevice now automatically detaches all cReceiver objects that receive PIDs
|
||||
that can't be decrypted with the current CAM. A plugin that attaches a cReceiver
|
||||
to a device should therefore watch the receiver's IsAttached() function to
|
||||
see if it is still attached to the device.
|
||||
- The cReceiver constructor no longer takes an 'int Ca' as its first parameter,
|
||||
but rather a 'tChannelID ChannelID'. This is necessary for the device to be
|
||||
able to determine which CAM a particular channel can be decrypted with. If the
|
||||
channel is known to be unencrypted, or a plugin doesn't want to provide the
|
||||
channel id for other reasons, an invalid tChannelID() can be given.
|
||||
- The cThread::Start() function now waits until a previous incarnation of this
|
||||
thread has actually stopped. Before this it could happen that a thread's
|
||||
Cancel(-1) function was called and immediately after that it was started again,
|
||||
but the Start() function still found it to be 'active'.
|
||||
- The parameter NeedsDetachReceivers in cDevice::GetDevice(const cChannel *Channel, ...)
|
||||
has been removed. A call to this function will automatically detach all receivers
|
||||
from the device if it returns a non-NULL pointer.
|
||||
- The cTimeMs class now accepts an initial timeout value in its constructor.
|
||||
- A CAM is now explicitly instructed to stop decrypting when switching away from
|
||||
an encrypted channel.
|
||||
- If the CAM in use can decrypt several channels at the same time, VDR can
|
||||
now make use if this capability. Whether or not a CAM can decrypt more
|
||||
than one channel is determined by sending it an initial empty QUERY command
|
||||
and testing whether it replies to it.
|
||||
- Ca values in the range 0...F in channels.conf can still be used to assign a channel
|
||||
to a particular device, but this will no longer work with encrypted channels because
|
||||
without valid CA ids VDR can't decide which CAM slot to use. However, since VDR now
|
||||
automatically determines which CAM can decrypt which channel, setting fixed
|
||||
channel/device relations should no longer be necessary.
|
||||
|
14
MANUAL
14
MANUAL
@ -667,13 +667,15 @@ Version 1.4
|
||||
|
||||
Use DiSEqC = no Generally turns DiSEqC support on or off.
|
||||
|
||||
CICAM:
|
||||
CAM:
|
||||
|
||||
CICAM DVBn m Shows the CAMs that each device contains, where 'n' is
|
||||
the number of the device, and 'm' is the number of the
|
||||
Common Interface slot of that device. The "Red" key
|
||||
can be pressed to enter the CAM menu, and the "Green" key
|
||||
triggers a reset of the selected CAM.
|
||||
n CAM Name Shows the CAM slots that are present in this system, where
|
||||
'n' is the number of the slot, followed by the name of the
|
||||
CAM. If a CAM slot is empty, '-' is displayed as name, and
|
||||
if it is in the process of being reset, its current status
|
||||
is displayed. The "Red" key can be pressed to enter the CAM
|
||||
menu, and the "Green" key triggers a reset of the selected
|
||||
slot. The "Ok" key also opens the CAM menu.
|
||||
|
||||
Recording:
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Makefile 1.95 2006/08/20 10:44:22 kls Exp $
|
||||
# $Id: Makefile 1.96 2007/01/07 14:38:00 kls Exp $
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@ -32,7 +32,7 @@ DOXYFILE = Doxyfile
|
||||
|
||||
SILIB = $(LSIDIR)/libsi.a
|
||||
|
||||
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\
|
||||
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
|
||||
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
|
||||
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
|
||||
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\
|
||||
|
47
PLUGINS.html
47
PLUGINS.html
@ -6,7 +6,7 @@
|
||||
|
||||
<center><h1>The VDR Plugin System</h1></center>
|
||||
|
||||
<center><b>Version 1.4.1</b></center>
|
||||
<center><b>Version 1.5.0</b></center>
|
||||
<p>
|
||||
<center>
|
||||
Copyright © 2006 Klaus Schmidinger<br>
|
||||
@ -14,6 +14,10 @@ Copyright © 2006 Klaus Schmidinger<br>
|
||||
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
|
||||
</center>
|
||||
<p>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
Important modifications introduced in version 1.5.0 are marked like this.
|
||||
<!--X1.5.0--></td></tr></table>
|
||||
<p>
|
||||
VDR provides an easy to use plugin interface that allows additional functionality
|
||||
to be added to the program by implementing a dynamically loadable library file.
|
||||
This interface allows programmers to develop additional functionality for VDR completely
|
||||
@ -72,6 +76,9 @@ structures and allows it to hook itself into specific areas to perform special a
|
||||
<li><a href="#Devices">Devices</a>
|
||||
<li><a href="#Audio">Audio</a>
|
||||
<li><a href="#Remote Control">Remote Control</a>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
<li><a href="#Conditional Access">Conditional Access</a>
|
||||
<!--X1.5.0--></td></tr></table>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@ -1727,6 +1734,7 @@ selecting which channel it shall tune to:
|
||||
|
||||
<p><table><tr><td bgcolor=#F0F0F0><pre>
|
||||
virtual bool ProvidesSource(int Source) const;
|
||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
|
||||
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
|
||||
</pre></td></tr></table><p>
|
||||
@ -2038,5 +2046,42 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
|
||||
|
||||
The other parameters have the same meaning as in the first version of this function.
|
||||
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
<a name="Conditional Access"><hr><h2>Conditional Access</h2>
|
||||
|
||||
<center><i><b>Members only!</b></i></center><p>
|
||||
|
||||
Pay TV providers usually encrypt their broadcasts, so that only viewers who
|
||||
have the proper smart card can watch them. Such a smart card needs to be inserted
|
||||
into a CAM (Conditional Access Module), which in turn goes into a CI (Common
|
||||
Interface) slot.
|
||||
<p>
|
||||
VDR's mechanisms for supporting Conditional Access are mainly the two classes
|
||||
<tt>cCiAdapter</tt> and <tt>cCamSlot</tt>. A <tt>cCiAdapter</tt> handles exactly
|
||||
one CI, and can provide several CAM slots, represented by the appropriate
|
||||
number of <tt>cCamSlot</tt> objects.
|
||||
<p>
|
||||
In order to decrypt a particular channel, a <tt>cCiAdapter</tt> with a <tt>cCamSlot</tt>
|
||||
that contains the necessary CAM will be assigned to a <tt>cDevice</tt>, and exactly
|
||||
one of its CAM slots will be activated. Whether or not a <tt>cCiAdapter</tt> can
|
||||
be assigned to a particular device depends on the hardware implementation.
|
||||
Some devices (like the Siemens/Technotrend DVB cards) are hardwired with their
|
||||
CI adapters, so the <tt>cCiAdapter</tt> for these can only be used with one device.
|
||||
Other hardware implementations may allow CI adapters and devices to be connected
|
||||
through some sort of matrix switch. When trying to decrypt an encrypted channel,
|
||||
VDR will automatically select a useful combination of device and CAM slot.
|
||||
<p>
|
||||
If a plugin implements a derived <tt>cCiAdapter</tt>, it has to implement
|
||||
several low level functions that handle the actual data transfer (see <tt>dvbci.c</tt>
|
||||
for example). The decision whether the adapter can actually be assigned to different
|
||||
devices is made in the function
|
||||
|
||||
<p><table><tr><td bgcolor=#F0F0F0><pre>
|
||||
virtual bool Assign(cDevice *Device, bool Query = false);
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
See the description of this function in <tt>ci.h</tt> for details.
|
||||
<!--X1.5.0--></td></tr></table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: channels.h 1.42 2006/05/28 15:03:56 kls Exp $
|
||||
* $Id: channels.h 1.43 2007/01/07 14:37:35 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CHANNELS_H
|
||||
@ -179,6 +179,7 @@ public:
|
||||
const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; }
|
||||
const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
|
||||
int Tpid(void) const { return tpid; }
|
||||
const int *Caids(void) const { return caids; }
|
||||
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
|
||||
int Nid(void) const { return nid; }
|
||||
int Tid(void) const { return tid; }
|
||||
|
249
ci.h
249
ci.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: ci.h 1.22 2006/08/12 09:43:31 kls Exp $
|
||||
* $Id: ci.h 1.23 2007/01/07 14:38:00 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CI_H
|
||||
@ -12,13 +12,18 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "channels.h"
|
||||
#include "thread.h"
|
||||
#include "tools.h"
|
||||
|
||||
#define MAX_CAM_SLOTS_PER_ADAPTER 8 // maximum possible value is 255
|
||||
#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
|
||||
#define CAM_READ_TIMEOUT 50 // ms
|
||||
|
||||
class cCiMMI;
|
||||
|
||||
class cCiMenu {
|
||||
friend class cCiHandler;
|
||||
friend class cCamSlot;
|
||||
friend class cCiMMI;
|
||||
private:
|
||||
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
|
||||
@ -40,14 +45,14 @@ public:
|
||||
const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
|
||||
int NumEntries(void) { return numEntries; }
|
||||
bool Selectable(void) { return selectable; }
|
||||
bool Select(int Index);
|
||||
bool Cancel(void);
|
||||
bool Abort(void);
|
||||
void Select(int Index);
|
||||
void Cancel(void);
|
||||
void Abort(void);
|
||||
bool HasUpdate(void);
|
||||
};
|
||||
|
||||
class cCiEnquiry {
|
||||
friend class cCiHandler;
|
||||
friend class cCamSlot;
|
||||
friend class cCiMMI;
|
||||
private:
|
||||
cCiMMI *mmi;
|
||||
@ -61,103 +66,140 @@ public:
|
||||
const char *Text(void) { return text; }
|
||||
bool Blind(void) { return blind; }
|
||||
int ExpectedLength(void) { return expectedLength; }
|
||||
bool Reply(const char *s);
|
||||
bool Cancel(void);
|
||||
bool Abort(void);
|
||||
void Reply(const char *s);
|
||||
void Cancel(void);
|
||||
void Abort(void);
|
||||
};
|
||||
|
||||
#define MAX_CI_SESSION 16 //XXX
|
||||
#define MAX_CI_SLOT 16
|
||||
class cDevice;
|
||||
class cCamSlot;
|
||||
|
||||
class cCiCaPidData : public cListObject {
|
||||
enum eModuleStatus { msNone, msReset, msPresent, msReady };
|
||||
|
||||
class cCiAdapter : public cThread {
|
||||
friend class cCamSlot;
|
||||
private:
|
||||
cDevice *assignedDevice;
|
||||
cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
|
||||
void AddCamSlot(cCamSlot *CamSlot);
|
||||
///< Adds the given CamSlot to this CI adapter.
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
///< Handles the attached CAM slots in a separate thread.
|
||||
///< The derived class must call the Start() function to
|
||||
///< actually start CAM handling.
|
||||
virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
|
||||
///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
|
||||
///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
|
||||
///< Returns the number of bytes read (in case of an error it will also
|
||||
///< return 0).
|
||||
virtual void Write(const uint8_t *Buffer, int Length) = 0;
|
||||
///< Writes Length bytes of the given Buffer.
|
||||
virtual bool Reset(int Slot) = 0;
|
||||
///< Resets the CAM in the given Slot.
|
||||
///< Returns true if the operation was successful.
|
||||
virtual eModuleStatus ModuleStatus(int Slot) = 0;
|
||||
///< Returns the status of the CAM in the given Slot.
|
||||
virtual bool Assign(cDevice *Device, bool Query = false) = 0;
|
||||
///< Assigns this adapter to the given Device, if this is possible.
|
||||
///< If Query is 'true', the adapter only checks whether it can be
|
||||
///< assigned to the Device, but doesn't actually assign itself to it.
|
||||
///< Returns true if the adapter can be assigned to the Device.
|
||||
///< If Device is NULL, the adapter will be unassigned from any
|
||||
///< device it was previously assigned to. The value of Query
|
||||
///< is ignored in that case, and this function always returns
|
||||
///< 'true'.
|
||||
public:
|
||||
bool active;
|
||||
int pid;
|
||||
int streamType;
|
||||
cCiCaPidData(int Pid, int StreamType)
|
||||
{
|
||||
active = false;
|
||||
pid = Pid;
|
||||
streamType = StreamType;
|
||||
}
|
||||
cCiAdapter(void);
|
||||
virtual ~cCiAdapter();
|
||||
///< The derived class must call Cancel(3) in its destructor.
|
||||
virtual bool Ready(void);
|
||||
///< Returns 'true' if all present CAMs in this adapter are ready.
|
||||
};
|
||||
|
||||
class cCiCaProgramData : public cListObject {
|
||||
public:
|
||||
int programNumber;
|
||||
cList<cCiCaPidData> pidList;
|
||||
cCiCaProgramData(int ProgramNumber)
|
||||
{
|
||||
programNumber = ProgramNumber;
|
||||
}
|
||||
};
|
||||
|
||||
class cCiSession;
|
||||
class cCiTransportLayer;
|
||||
class cTPDU;
|
||||
class cCiTransportConnection;
|
||||
class cCiSession;
|
||||
class cCiCaProgramData;
|
||||
|
||||
class cCiHandler {
|
||||
class cCamSlot : public cListObject {
|
||||
friend class cCiAdapter;
|
||||
friend class cCiTransportConnection;
|
||||
private:
|
||||
cMutex mutex;
|
||||
int fd;
|
||||
int numSlots;
|
||||
bool newCaSupport;
|
||||
bool hasUserIO;
|
||||
bool moduleReady[MAX_CI_SLOT];
|
||||
cCiSession *sessions[MAX_CI_SESSION];
|
||||
cCiTransportLayer *tpl;
|
||||
cCiTransportConnection *tc;
|
||||
cCondVar processed;
|
||||
cCiAdapter *ciAdapter;
|
||||
int slotIndex;
|
||||
int slotNumber;
|
||||
cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1]; // connection numbering starts with 1
|
||||
eModuleStatus lastModuleStatus;
|
||||
time_t resetTime;
|
||||
cTimeMs moduleCheckTimer;
|
||||
bool resendPmt;
|
||||
int source;
|
||||
int transponder;
|
||||
cList<cCiCaProgramData> caProgramList;
|
||||
uint32_t ResourceIdToInt(const uint8_t *Data);
|
||||
bool Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
|
||||
const unsigned short *GetCaSystemIds(int Slot);
|
||||
cCiSession *GetSessionBySessionId(uint16_t SessionId);
|
||||
cCiSession *GetSessionByResourceId(uint32_t ResourceId, int Slot);
|
||||
cCiSession *CreateSession(uint32_t ResourceId);
|
||||
bool OpenSession(int Length, const uint8_t *Data);
|
||||
bool CloseSession(uint16_t SessionId);
|
||||
int CloseAllSessions(int Slot);
|
||||
cCiHandler(int Fd, int NumSlots);
|
||||
void SendCaPmt(void);
|
||||
const int *GetCaSystemIds(void);
|
||||
void SendCaPmt(uint8_t CmdId);
|
||||
void NewConnection(void);
|
||||
void DeleteAllConnections(void);
|
||||
void Process(cTPDU *TPDU = NULL);
|
||||
void Write(cTPDU *TPDU);
|
||||
cCiSession *GetSessionByResourceId(uint32_t ResourceId);
|
||||
public:
|
||||
~cCiHandler();
|
||||
static cCiHandler *CreateCiHandler(const char *FileName);
|
||||
///< Creates a new cCiHandler for the given CA device.
|
||||
int NumSlots(void) { return numSlots; }
|
||||
///< Returns the number of CAM slots provided by this CA device.
|
||||
int NumCams(void);
|
||||
///< Returns the number of actual CAMs inserted into this CA device.
|
||||
cCamSlot(cCiAdapter *CiAdapter);
|
||||
///< Creates a new CAM slot for the given CiAdapter.
|
||||
///< The CiAdapter will take care of deleting the CAM slot,
|
||||
///< so the caller must not delete it!
|
||||
virtual ~cCamSlot();
|
||||
bool Assign(cDevice *Device, bool Query = false);
|
||||
///< Assigns this CAM slot to the given Device, if this is possible.
|
||||
///< If Query is 'true', the CI adapter of this slot only checks whether
|
||||
///< it can be assigned to the Device, but doesn't actually assign itself to it.
|
||||
///< Returns true if this slot can be assigned to the Device.
|
||||
///< If Device is NULL, the slot will be unassigned from any
|
||||
///< device it was previously assigned to. The value of Query
|
||||
///< is ignored in that case, and this function always returns
|
||||
///< 'true'.
|
||||
cDevice *Device(void);
|
||||
///< Returns the device this CAM slot is currently assigned to.
|
||||
int SlotIndex(void) { return slotIndex; }
|
||||
///< Returns the index of this CAM slot within its CI adapter.
|
||||
///< The first slot has an index of 0.
|
||||
int SlotNumber(void) { return slotNumber; }
|
||||
///< Returns the number of this CAM slot within the whole system.
|
||||
///< The first slot has the number 1.
|
||||
bool Reset(void);
|
||||
///< Resets the CAM in this slot.
|
||||
///< Returns true if the operation was successful.
|
||||
eModuleStatus ModuleStatus(void);
|
||||
///< Returns the status of the CAM in this slot.
|
||||
const char *GetCamName(void);
|
||||
///< Returns the name of the CAM in this slot, or NULL if there is
|
||||
///< no ready CAM in this slot.
|
||||
bool Ready(void);
|
||||
///< Returns true if all CAMs in this CA device are ready.
|
||||
bool Process(int Slot = -1);
|
||||
///< Processes the given Slot. If Slot is -1, all slots are processed.
|
||||
///< Returns false in case of an error.
|
||||
bool HasUserIO(void) { return hasUserIO; }
|
||||
///< Returns 'true' if the CAM in this slot is ready to decrypt.
|
||||
bool HasMMI(void);
|
||||
///< Returns 'true' if the CAM in this slot has an active MMI.
|
||||
bool HasUserIO(void);
|
||||
///< Returns true if there is a pending user interaction, which shall
|
||||
///< be retrieved via GetMenu() or GetEnquiry().
|
||||
bool EnterMenu(int Slot);
|
||||
///< Requests the CAM in the given Slot to start its menu.
|
||||
bool EnterMenu(void);
|
||||
///< Requests the CAM in this slot to start its menu.
|
||||
cCiMenu *GetMenu(void);
|
||||
///< Gets a pending menu, or NULL if there is no menu.
|
||||
cCiEnquiry *GetEnquiry(void);
|
||||
///< Gets a pending enquiry, or NULL if there is no enquiry.
|
||||
const char *GetCamName(int Slot);
|
||||
///< Returns the name of the CAM in the given Slot, or NULL if there
|
||||
///< is no CAM in that slot.
|
||||
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
|
||||
///< Returns true if any of the CAMs can provide one of the given
|
||||
int Priority(void);
|
||||
///< Returns the priority if the device this slot is currently assigned
|
||||
///< to, or -1 if it is not assigned to any device.
|
||||
bool ProvidesCa(const int *CaSystemIds);
|
||||
///< Returns true if the CAM in this slot provides one of the given
|
||||
///< CaSystemIds. This doesn't necessarily mean that it will be
|
||||
///< possible to actually decrypt such a programme, since CAMs
|
||||
///< usually advertise several CA system ids, while the actual
|
||||
///< decryption is controlled by the smart card inserted into
|
||||
///< the CAM.
|
||||
void SetSource(int Source, int Transponder);
|
||||
///< Sets the Source and Transponder of the device this cCiHandler is
|
||||
///< currently tuned to. If Source or Transponder are different than
|
||||
///< what was given in a previous call to SetSource(), any previously
|
||||
///< added PIDs will be cleared.
|
||||
void AddPid(int ProgramNumber, int Pid, int StreamType);
|
||||
///< Adds the given PID information to the list of PIDs. A later call
|
||||
///< to SetPid() will (de)activate one of these entries.
|
||||
@ -165,16 +207,55 @@ public:
|
||||
///< Sets the given Pid (which has previously been added through a
|
||||
///< call to AddPid()) to Active. A later call to StartDecrypting() will
|
||||
///< send the full list of currently active CA_PMT entries to the CAM.
|
||||
bool CanDecrypt(int ProgramNumber);
|
||||
///< XXX
|
||||
///< Returns true if there is a CAM in this CA device that is able
|
||||
///< to decrypt the programme with the given ProgramNumber. The PIDs
|
||||
///< for this ProgramNumber must have been set through previous calls
|
||||
///< to SetPid().
|
||||
void AddChannel(const cChannel *Channel);
|
||||
///< Adds all PIDs if the given Channel to the current list of PIDs.
|
||||
///< If the source or transponder of the channel are different than
|
||||
///< what was given in a previous call to AddChannel(), any previously
|
||||
///< added PIDs will be cleared.
|
||||
bool CanDecrypt(const cChannel *Channel);
|
||||
///< Returns true if there is a CAM in this slot that is able to decrypt
|
||||
///< the given Channel (or at least claims to be able to do so).
|
||||
///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
|
||||
///< CAMs don't reply to queries at all), we always return true if the
|
||||
///< CAM is currently not decrypting anything. If there is already a
|
||||
///< channel being decrypted, a call to CanDecrypt() checks whether the
|
||||
///< CAM can also decrypt the given channel. Only CAMs that have replied
|
||||
///< to the inital QUERY will perform this check at all. CAMs that never
|
||||
///< replied to the initial QUERY are assumed not to be able to handle
|
||||
///< more than one channel at a time.
|
||||
void StartDecrypting(void);
|
||||
///< Triggers sending all currently active CA_PMT entries to the CAM,
|
||||
///< so that it will start decrypting.
|
||||
bool Reset(int Slot);
|
||||
void StopDecrypting(void);
|
||||
///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
|
||||
bool IsDecrypting(void);
|
||||
///< Returns true if the CAM in this slot is currently used for decrypting.
|
||||
};
|
||||
|
||||
class cCamSlots : public cList<cCamSlot> {};
|
||||
|
||||
extern cCamSlots CamSlots;
|
||||
|
||||
class cChannelCamRelation;
|
||||
|
||||
class cChannelCamRelations : public cList<cChannelCamRelation> {
|
||||
private:
|
||||
cMutex mutex;
|
||||
cChannelCamRelation *GetEntry(tChannelID ChannelID);
|
||||
cChannelCamRelation *AddEntry(tChannelID ChannelID);
|
||||
time_t lastCleanup;
|
||||
void Cleanup(void);
|
||||
public:
|
||||
cChannelCamRelations(void);
|
||||
void Reset(int CamSlotNumber);
|
||||
bool CamChecked(tChannelID ChannelID, int CamSlotNumber);
|
||||
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber);
|
||||
void SetChecked(tChannelID ChannelID, int CamSlotNumber);
|
||||
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber);
|
||||
void ClrChecked(tChannelID ChannelID, int CamSlotNumber);
|
||||
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber);
|
||||
};
|
||||
|
||||
extern cChannelCamRelations ChannelCamRelations;
|
||||
|
||||
#endif //__CI_H
|
||||
|
10
config.h
10
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 1.282 2007/01/07 13:45:19 kls Exp $
|
||||
* $Id: config.h 1.283 2007/01/07 14:09:31 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
@ -21,13 +21,13 @@
|
||||
|
||||
// VDR's own version number:
|
||||
|
||||
#define VDRVERSION "1.4.5"
|
||||
#define VDRVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
|
||||
#define VDRVERSION "1.5.0"
|
||||
#define VDRVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
|
||||
|
||||
// The plugin API's version number:
|
||||
|
||||
#define APIVERSION "1.4.5"
|
||||
#define APIVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
|
||||
#define APIVERSION "1.5.0"
|
||||
#define APIVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
|
||||
|
||||
// When loading plugins, VDR searches them by their APIVERSION, which
|
||||
// may be smaller than VDRVERSION in case there have been no changes to
|
||||
|
274
device.c
274
device.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: device.c 1.137 2006/09/03 10:13:25 kls Exp $
|
||||
* $Id: device.c 1.138 2007/01/07 14:41:07 kls Exp $
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
@ -164,7 +164,9 @@ cDevice::cDevice(void)
|
||||
sdtFilter = NULL;
|
||||
nitFilter = NULL;
|
||||
|
||||
ciHandler = NULL;
|
||||
camSlot = NULL;
|
||||
startScrambleDetection = 0;
|
||||
|
||||
player = NULL;
|
||||
pesAssembler = new cPesAssembler;
|
||||
ClrAvailableTracks();
|
||||
@ -183,9 +185,7 @@ cDevice::cDevice(void)
|
||||
cDevice::~cDevice()
|
||||
{
|
||||
Detach(player);
|
||||
for (int i = 0; i < MAXRECEIVERS; i++)
|
||||
Detach(receiver[i]);
|
||||
delete ciHandler;
|
||||
DetachAllReceivers();
|
||||
delete nitFilter;
|
||||
delete sdtFilter;
|
||||
delete patFilter;
|
||||
@ -199,8 +199,10 @@ bool cDevice::WaitForAllDevicesReady(int Timeout)
|
||||
for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
|
||||
bool ready = true;
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
if (device[i] && !device[i]->Ready())
|
||||
if (device[i] && !device[i]->Ready()) {
|
||||
ready = false;
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
}
|
||||
if (ready)
|
||||
return true;
|
||||
@ -278,39 +280,98 @@ cDevice *cDevice::GetDevice(int Index)
|
||||
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
|
||||
}
|
||||
|
||||
cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
|
||||
cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView)
|
||||
{
|
||||
cDevice *d = NULL;
|
||||
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
// by one bit in the number (or several bits, if the condition is actually
|
||||
// a numeric value). The sequence in which the conditions are listed corresponds
|
||||
// to their individual severity, where the one listed first will make the most
|
||||
// difference, because it results in the most significant bit of the result.
|
||||
uint imp = 0;
|
||||
imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
|
||||
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 8; imp |= min(max(device[i]->ProvidesCa(Channel), 0), 0xFF); // use the device that provides the lowest number of conditional access methods
|
||||
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
|
||||
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
|
||||
if (imp < Impact) {
|
||||
// This device has less impact than any previous one, so we take it.
|
||||
Impact = imp;
|
||||
d = device[i];
|
||||
if (NeedsDetachReceivers)
|
||||
*NeedsDetachReceivers = ndr;
|
||||
// Collect the current priorities of all CAM slots that can decrypt the channel:
|
||||
int NumCamSlots = CamSlots.Count();
|
||||
int SlotPriority[NumCamSlots];
|
||||
int NumUsableSlots = 0;
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
|
||||
if (CamSlot->ModuleStatus() == msReady) {
|
||||
if (CamSlot->ProvidesCa(Channel->Caids())) {
|
||||
if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
|
||||
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
|
||||
NumUsableSlots++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
return NULL; // no CAM is able to decrypt this channel
|
||||
}
|
||||
|
||||
bool NeedsDetachReceivers = false;
|
||||
cDevice *d = NULL;
|
||||
cCamSlot *s = NULL;
|
||||
|
||||
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
|
||||
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
|
||||
continue; // there is no CAM available in this slot
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
|
||||
continue; // a specific card was requested, but not this one
|
||||
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
|
||||
ndr = true; // using a different CAM slot requires detaching receivers
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
// by one bit in the number (or several bits, if the condition is actually
|
||||
// a numeric value). The sequence in which the conditions are listed corresponds
|
||||
// to their individual severity, where the one listed first will make the most
|
||||
// difference, because it results in the most significant bit of the result.
|
||||
uint32_t imp = 0;
|
||||
imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
|
||||
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
|
||||
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
|
||||
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
|
||||
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
|
||||
if (imp < Impact) {
|
||||
// This device has less impact than any previous one, so we take it.
|
||||
Impact = imp;
|
||||
d = device[i];
|
||||
NeedsDetachReceivers = ndr;
|
||||
if (NumUsableSlots)
|
||||
s = CamSlots.Get(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
break; // no CAM necessary, so just one loop over the devices
|
||||
}
|
||||
if (d) {
|
||||
if (NeedsDetachReceivers)
|
||||
d->DetachAllReceivers();
|
||||
if (s) {
|
||||
if (s->Device() != d) {
|
||||
if (s->Device())
|
||||
s->Device()->DetachAllReceivers();
|
||||
if (d->CamSlot())
|
||||
d->CamSlot()->Assign(NULL);
|
||||
s->Assign(d);
|
||||
}
|
||||
}
|
||||
else if (d->CamSlot() && !d->CamSlot()->IsDecrypting())
|
||||
d->CamSlot()->Assign(NULL);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void cDevice::SetCamSlot(cCamSlot *CamSlot)
|
||||
{
|
||||
camSlot = CamSlot;
|
||||
}
|
||||
|
||||
void cDevice::Shutdown(void)
|
||||
{
|
||||
primaryDevice = NULL;
|
||||
@ -422,8 +483,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
|
||||
DelPid(Pid, PidType);
|
||||
return false;
|
||||
}
|
||||
if (ciHandler)
|
||||
ciHandler->SetPid(Pid, true);
|
||||
if (camSlot)
|
||||
camSlot->SetPid(Pid, true);
|
||||
}
|
||||
PRINTPIDS("a");
|
||||
return true;
|
||||
@ -451,8 +512,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
|
||||
DelPid(Pid, PidType);
|
||||
return false;
|
||||
}
|
||||
if (ciHandler)
|
||||
ciHandler->SetPid(Pid, true);
|
||||
if (camSlot)
|
||||
camSlot->SetPid(Pid, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -479,8 +540,8 @@ void cDevice::DelPid(int Pid, ePidType PidType)
|
||||
if (pidHandles[n].used == 0) {
|
||||
pidHandles[n].handle = -1;
|
||||
pidHandles[n].pid = 0;
|
||||
if (ciHandler)
|
||||
ciHandler->SetPid(Pid, false);
|
||||
if (camSlot)
|
||||
camSlot->SetPid(Pid, false);
|
||||
}
|
||||
}
|
||||
PRINTPIDS("E");
|
||||
@ -557,8 +618,10 @@ bool cDevice::MaySwitchTransponder(void)
|
||||
|
||||
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
|
||||
{
|
||||
if (LiveView)
|
||||
if (LiveView) {
|
||||
isyslog("switching to channel %d", Channel->Number());
|
||||
cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
|
||||
}
|
||||
for (int i = 3; i--;) {
|
||||
switch (SetChannel(Channel, LiveView)) {
|
||||
case scrOk: return true;
|
||||
@ -578,12 +641,13 @@ bool cDevice::SwitchChannel(int Direction)
|
||||
bool result = false;
|
||||
Direction = sgn(Direction);
|
||||
if (Direction) {
|
||||
cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
|
||||
int n = CurrentChannel() + Direction;
|
||||
int first = n;
|
||||
cChannel *channel;
|
||||
while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
|
||||
// try only channels which are currently available
|
||||
if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0))
|
||||
if (GetDevice(channel, 0, true))
|
||||
break;
|
||||
n = channel->Number() + Direction;
|
||||
}
|
||||
@ -607,14 +671,9 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
if (LiveView)
|
||||
StopReplay();
|
||||
|
||||
// If this card is switched to an other transponder, any receivers still
|
||||
// attached to it need to be automatically detached:
|
||||
bool NeedsDetachReceivers = false;
|
||||
cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
|
||||
|
||||
// If this card can't receive this channel, we must not actually switch
|
||||
// the channel here, because that would irritate the driver when we
|
||||
// start replaying in Transfer Mode immediately after switching the channel:
|
||||
bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit, &NeedsDetachReceivers));
|
||||
bool NeedsTransferMode = Device != this;
|
||||
|
||||
eSetChannelResult Result = scrOk;
|
||||
|
||||
@ -622,14 +681,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
// use the card that actually can receive it and transfer data from there:
|
||||
|
||||
if (NeedsTransferMode) {
|
||||
cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers);
|
||||
if (CaDevice && CanReplay()) {
|
||||
if (Device && CanReplay()) {
|
||||
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
|
||||
if (CaDevice->SetChannel(Channel, false) == scrOk) { // calling SetChannel() directly, not SwitchChannel()!
|
||||
if (NeedsDetachReceivers)
|
||||
CaDevice->DetachAllReceivers();
|
||||
cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
|
||||
}
|
||||
if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
|
||||
cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
|
||||
else
|
||||
Result = scrNoTransfer;
|
||||
}
|
||||
@ -644,27 +699,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
sectionHandler->SetStatus(false);
|
||||
sectionHandler->SetChannel(NULL);
|
||||
}
|
||||
// Tell the ciHandler about the channel switch and add all PIDs of this
|
||||
// Tell the camSlot about the channel switch and add all PIDs of this
|
||||
// channel to it, for possible later decryption:
|
||||
if (ciHandler) {
|
||||
ciHandler->SetSource(Channel->Source(), Channel->Transponder());
|
||||
// Men at work - please stand clear! ;-)
|
||||
#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
#endif
|
||||
ciHandler->AddPid(Channel->Sid(), Channel->Vpid(), 2);
|
||||
for (const int *Apid = Channel->Apids(); *Apid; Apid++)
|
||||
ciHandler->AddPid(Channel->Sid(), *Apid, 4);
|
||||
for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
|
||||
ciHandler->AddPid(Channel->Sid(), *Dpid, 0);
|
||||
#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
|
||||
bool CanDecrypt = ciHandler->CanDecrypt(Channel->Sid());//XXX
|
||||
dsyslog("CanDecrypt %d %d %d %s", CardIndex() + 1, CanDecrypt, Channel->Number(), Channel->Name());//XXX
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (NeedsDetachReceivers)
|
||||
DetachAllReceivers();
|
||||
if (camSlot)
|
||||
camSlot->AddChannel(Channel);
|
||||
if (SetChannelDevice(Channel, LiveView)) {
|
||||
// Start section handling:
|
||||
if (sectionHandler) {
|
||||
@ -672,8 +710,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
sectionHandler->SetStatus(true);
|
||||
}
|
||||
// Start decrypting any PIDs that might have been set in SetChannelDevice():
|
||||
if (ciHandler)
|
||||
ciHandler->StartDecrypting();
|
||||
if (camSlot)
|
||||
camSlot->StartDecrypting();
|
||||
}
|
||||
else
|
||||
Result = scrFailed;
|
||||
@ -968,9 +1006,10 @@ bool cDevice::AttachPlayer(cPlayer *Player)
|
||||
void cDevice::Detach(cPlayer *Player)
|
||||
{
|
||||
if (Player && player == Player) {
|
||||
player->Activate(false);
|
||||
player->device = NULL;
|
||||
player = NULL;
|
||||
cPlayer *p = player;
|
||||
player = NULL; // avoids recursive calls to Detach()
|
||||
p->Activate(false);
|
||||
p->device = NULL;
|
||||
SetPlayMode(pmNone);
|
||||
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
|
||||
Audios.ClearAudio();
|
||||
@ -1143,16 +1182,6 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
|
||||
return Length;
|
||||
}
|
||||
|
||||
int cDevice::Ca(void) const
|
||||
{
|
||||
int ca = 0;
|
||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||
if (receiver[i] && (ca = receiver[i]->ca) != 0)
|
||||
break; // all receivers have the same ca
|
||||
}
|
||||
return ca;
|
||||
}
|
||||
|
||||
int cDevice::Priority(void) const
|
||||
{
|
||||
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
|
||||
@ -1168,16 +1197,6 @@ bool cDevice::Ready(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDevice::ProvidesCa(const cChannel *Channel) const
|
||||
{
|
||||
int Ca = Channel->Ca();
|
||||
if (Ca == CardIndex() + 1)
|
||||
return 1; // exactly _this_ card was requested
|
||||
if (Ca && Ca <= CA_DVB_MAX)
|
||||
return 0; // a specific card was requested, but not _this_ one
|
||||
return !Ca; // by default every card can provide FTA
|
||||
}
|
||||
|
||||
bool cDevice::Receiving(bool CheckAny) const
|
||||
{
|
||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||
@ -1187,6 +1206,10 @@ bool cDevice::Receiving(bool CheckAny) const
|
||||
return false;
|
||||
}
|
||||
|
||||
#define TS_SCRAMBLING_CONTROL 0xC0
|
||||
#define TS_SCRAMBLING_TIMEOUT 3 // seconds to wait until a TS becomes unscrambled
|
||||
#define TS_SCRAMBLING_TIME_OK 10 // seconds before a Channel/CAM combination is marked a known to decrypt
|
||||
|
||||
void cDevice::Action(void)
|
||||
{
|
||||
if (Running() && OpenDvr()) {
|
||||
@ -1196,11 +1219,39 @@ void cDevice::Action(void)
|
||||
if (GetTSPacket(b)) {
|
||||
if (b) {
|
||||
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
|
||||
// Check whether the TS packets are scrambled:
|
||||
bool DetachReceivers = false;
|
||||
bool DescramblingOk = false;
|
||||
int CamSlotNumber = 0;
|
||||
if (startScrambleDetection) {
|
||||
cCamSlot *cs = CamSlot();
|
||||
CamSlotNumber = cs ? cs->SlotNumber() : 0;
|
||||
if (CamSlotNumber) {
|
||||
bool Scrambled = b[3] & TS_SCRAMBLING_CONTROL;
|
||||
int t = time(NULL) - startScrambleDetection;
|
||||
if (Scrambled) {
|
||||
if (t > TS_SCRAMBLING_TIMEOUT)
|
||||
DetachReceivers = true;
|
||||
}
|
||||
else if (t > TS_SCRAMBLING_TIME_OK) {
|
||||
DescramblingOk = true;
|
||||
startScrambleDetection = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Distribute the packet to all attached receivers:
|
||||
Lock();
|
||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||
if (receiver[i] && receiver[i]->WantsPid(Pid))
|
||||
receiver[i]->Receive(b, TS_SIZE);
|
||||
if (receiver[i] && receiver[i]->WantsPid(Pid)) {
|
||||
if (DetachReceivers) {
|
||||
ChannelCamRelations.SetChecked(receiver[i]->ChannelID(), CamSlotNumber);
|
||||
Detach(receiver[i]);
|
||||
}
|
||||
else
|
||||
receiver[i]->Receive(b, TS_SIZE);
|
||||
if (DescramblingOk)
|
||||
ChannelCamRelations.SetDecrypt(receiver[i]->ChannelID(), CamSlotNumber);
|
||||
}
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
@ -1256,10 +1307,11 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
|
||||
Receiver->device = this;
|
||||
receiver[i] = Receiver;
|
||||
Unlock();
|
||||
if (!Running())
|
||||
Start();
|
||||
if (ciHandler)
|
||||
ciHandler->StartDecrypting();
|
||||
if (camSlot) {
|
||||
camSlot->StartDecrypting();
|
||||
startScrambleDetection = time(NULL);
|
||||
}
|
||||
Start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1286,10 +1338,10 @@ void cDevice::Detach(cReceiver *Receiver)
|
||||
else if (receiver[i])
|
||||
receiversLeft = true;
|
||||
}
|
||||
if (ciHandler)
|
||||
ciHandler->StartDecrypting();
|
||||
if (camSlot)
|
||||
camSlot->StartDecrypting();
|
||||
if (!receiversLeft)
|
||||
Cancel(3);
|
||||
Cancel(-1);
|
||||
}
|
||||
|
||||
void cDevice::DetachAll(int Pid)
|
||||
@ -1307,10 +1359,8 @@ void cDevice::DetachAll(int Pid)
|
||||
void cDevice::DetachAllReceivers(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutexReceiver);
|
||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||
if (receiver[i])
|
||||
Detach(receiver[i]);
|
||||
}
|
||||
for (int i = 0; i < MAXRECEIVERS; i++)
|
||||
Detach(receiver[i]);
|
||||
}
|
||||
|
||||
// --- cTSBuffer -------------------------------------------------------------
|
||||
|
51
device.h
51
device.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: device.h 1.79 2006/06/15 09:32:48 kls Exp $
|
||||
* $Id: device.h 1.80 2007/01/07 14:38:00 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DEVICE_H
|
||||
@ -128,12 +128,21 @@ public:
|
||||
///< Gets the device with the given Index.
|
||||
///< \param Index must be in the range 0..numDevices-1.
|
||||
///< \return A pointer to the device, or NULL if the Index was invalid.
|
||||
static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
|
||||
static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView);
|
||||
///< Returns a device that is able to receive the given Channel at the
|
||||
///< given Priority, with the least impact on active recordings and
|
||||
///< live viewing.
|
||||
///< See ProvidesChannel() for more information on how
|
||||
///< priorities are handled, and the meaning of NeedsDetachReceivers.
|
||||
///< live viewing. The LiveView parameter tells whether the device will
|
||||
///< be used for live viewing or a recording.
|
||||
///< If the Channel is encrypted, a CAM slot that claims to be able to
|
||||
///< decrypt the channel is automatically selected and assigned to the
|
||||
///< returned device. Whether or not this combination of device and CAM
|
||||
///< slot is actually able to decrypt the channel can only be determined
|
||||
///< by checking the "scrambling control" bits of the received TS packets.
|
||||
///< The Action() function automatically does this and takes care that
|
||||
///< after detaching any receivers because the channel can't be decrypted,
|
||||
///< this device/CAM combination will be skipped in the next call to
|
||||
///< GetDevice().
|
||||
///< See also ProvidesChannel().
|
||||
static void Shutdown(void);
|
||||
///< Closes down all devices.
|
||||
///< Must be called at the end of the program.
|
||||
@ -171,16 +180,6 @@ public:
|
||||
///< Returns the card index of this device (0 ... MAXDEVICES - 1).
|
||||
int DeviceNumber(void) const;
|
||||
///< Returns the number of this device (0 ... numDevices).
|
||||
virtual int ProvidesCa(const cChannel *Channel) const;
|
||||
///< Checks whether this device provides the conditional access
|
||||
///< facilities to decrypt the given Channel.
|
||||
///< Returns 0 if the Channel can't be decrypted, 1 if this is a
|
||||
///< Free To Air channel or only exactly this device can decrypt it,
|
||||
///< and > 1 if this device can decrypt the Channel.
|
||||
///< If the result is greater than 1 and the device has more than one
|
||||
///< CAM, the value will be increased by the number of CAMs, which
|
||||
///< allows to select the device with the smallest number of CAMs
|
||||
///< in order to preserve resources for other recordings.
|
||||
virtual bool HasDecoder(void) const;
|
||||
///< Tells whether this device has an MPEG decoder.
|
||||
|
||||
@ -199,7 +198,9 @@ public:
|
||||
virtual bool ProvidesSource(int Source) const;
|
||||
///< Returns true if this device can provide the given source.
|
||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||
///< XXX -> PLUGINS.html!
|
||||
///< Returns true if this device can provide the transponder of the
|
||||
///< given Channel (which implies that it can provide the Channel's
|
||||
///< source).
|
||||
virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const;
|
||||
///< Returns true if this is the only device that is able to provide
|
||||
///< the given channel's transponder.
|
||||
@ -246,7 +247,7 @@ public:
|
||||
///< channel number while replaying.
|
||||
void ForceTransferMode(void);
|
||||
///< Forces the device into transfermode for the current channel.
|
||||
virtual bool HasLock(int TimeoutMs = 0);//XXX PLUGINS.html
|
||||
virtual bool HasLock(int TimeoutMs = 0);
|
||||
///< Returns true if the device has a lock on the requested transponder.
|
||||
///< Default is true, a specific device implementation may return false
|
||||
///< to indicate that it is not ready yet.
|
||||
@ -309,10 +310,15 @@ public:
|
||||
|
||||
// Common Interface facilities:
|
||||
|
||||
protected:
|
||||
cCiHandler *ciHandler;
|
||||
private:
|
||||
time_t startScrambleDetection;
|
||||
cCamSlot *camSlot;
|
||||
public:
|
||||
cCiHandler *CiHandler(void) { return ciHandler; }
|
||||
void SetCamSlot(cCamSlot *CamSlot);
|
||||
///< Sets the given CamSlot to be used with this device.
|
||||
cCamSlot *CamSlot(void) const { return camSlot; }
|
||||
///< Returns the CAM slot that is currently used with this device,
|
||||
///< or NULL if no CAM slot is in use.
|
||||
|
||||
// Image Grab facilities
|
||||
|
||||
@ -512,11 +518,12 @@ public:
|
||||
private:
|
||||
cMutex mutexReceiver;
|
||||
cReceiver *receiver[MAXRECEIVERS];
|
||||
protected:
|
||||
public:
|
||||
int Priority(void) const;
|
||||
///< Returns the priority of the current receiving session (0..MAXPRIORITY),
|
||||
///< or -1 if no receiver is currently active. The primary device will
|
||||
///< always return at least Setup.PrimaryLimit-1.
|
||||
protected:
|
||||
virtual bool OpenDvr(void);
|
||||
///< Opens the DVR of this device and prepares it to deliver a Transport
|
||||
///< Stream for use in a cReceiver.
|
||||
@ -530,8 +537,6 @@ protected:
|
||||
///< false in case of a non recoverable error, otherwise it returns true,
|
||||
///< even if Data is NULL.
|
||||
public:
|
||||
int Ca(void) const;
|
||||
///< Returns the ca of the current receiving session(s).
|
||||
bool Receiving(bool CheckAny = false) const;
|
||||
///< Returns true if we are currently receiving.
|
||||
bool AttachReceiver(cReceiver *Receiver);
|
||||
|
108
dvbci.c
Normal file
108
dvbci.c
Normal 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
31
dvbci.h
Normal 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
|
77
dvbdevice.c
77
dvbdevice.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbdevice.c 1.160 2006/08/14 09:38:32 kls Exp $
|
||||
* $Id: dvbdevice.c 1.161 2007/01/07 14:09:51 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbdevice.h"
|
||||
@ -19,6 +19,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include "channels.h"
|
||||
#include "diseqc.h"
|
||||
#include "dvbci.h"
|
||||
#include "dvbosd.h"
|
||||
#include "eitscan.h"
|
||||
#include "player.h"
|
||||
@ -28,7 +29,6 @@
|
||||
|
||||
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
|
||||
#define DO_MULTIPLE_RECORDINGS 1
|
||||
//#define DO_MULTIPLE_CA_CHANNELS
|
||||
|
||||
#define DEV_VIDEO "/dev/video"
|
||||
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
|
||||
@ -77,7 +77,6 @@ private:
|
||||
int lockTimeout;
|
||||
time_t lastTimeoutReport;
|
||||
fe_type_t frontendType;
|
||||
cCiHandler *ciHandler;
|
||||
cChannel channel;
|
||||
const char *diseqcCommands;
|
||||
eTunerStatus tunerStatus;
|
||||
@ -88,19 +87,18 @@ private:
|
||||
bool SetFrontend(void);
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler);
|
||||
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType);
|
||||
virtual ~cDvbTuner();
|
||||
bool IsTunedTo(const cChannel *Channel) const;
|
||||
void Set(const cChannel *Channel, bool Tune);
|
||||
bool Locked(int TimeoutMs = 0);
|
||||
};
|
||||
|
||||
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler)
|
||||
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
|
||||
{
|
||||
fd_frontend = Fd_Frontend;
|
||||
cardIndex = CardIndex;
|
||||
frontendType = FrontendType;
|
||||
ciHandler = CiHandler;
|
||||
tuneTimeout = 0;
|
||||
lockTimeout = 0;
|
||||
lastTimeoutReport = 0;
|
||||
@ -346,8 +344,6 @@ void cDvbTuner::Action(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (ciHandler)
|
||||
ciHandler->Process();
|
||||
if (tunerStatus != tsTuned)
|
||||
newSet.TimedWait(mutex, 1000);
|
||||
}
|
||||
@ -360,6 +356,7 @@ int cDvbDevice::setTransferModeForDolbyDigital = 1;
|
||||
|
||||
cDvbDevice::cDvbDevice(int n)
|
||||
{
|
||||
ciAdapter = NULL;
|
||||
dvbTuner = NULL;
|
||||
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
|
||||
spuDecoder = NULL;
|
||||
@ -377,6 +374,12 @@ cDvbDevice::cDvbDevice(int n)
|
||||
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
|
||||
fd_stc = DvbOpen(DEV_DVB_DEMUX, n, O_RDWR);
|
||||
|
||||
// Common Interface:
|
||||
|
||||
fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
|
||||
if (fd_ca >= 0)
|
||||
ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
|
||||
|
||||
// The DVR device (will be opened and closed as needed):
|
||||
|
||||
fd_dvr = -1;
|
||||
@ -420,8 +423,7 @@ cDvbDevice::cDvbDevice(int n)
|
||||
dvb_frontend_info feinfo;
|
||||
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
|
||||
frontendType = feinfo.type;
|
||||
ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n));
|
||||
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
|
||||
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
|
||||
}
|
||||
else
|
||||
LOG_ERROR;
|
||||
@ -436,6 +438,7 @@ cDvbDevice::~cDvbDevice()
|
||||
{
|
||||
delete spuDecoder;
|
||||
delete dvbTuner;
|
||||
delete ciAdapter;
|
||||
// We're not explicitly closing any device files here, since this sometimes
|
||||
// caused segfaults. Besides, the program is about to terminate anyway...
|
||||
}
|
||||
@ -494,32 +497,11 @@ bool cDvbDevice::HasDecoder(void) const
|
||||
|
||||
bool cDvbDevice::Ready(void)
|
||||
{
|
||||
if (ciHandler) {
|
||||
ciHandler->Process();
|
||||
return ciHandler->Ready();
|
||||
}
|
||||
if (ciAdapter)
|
||||
return ciAdapter->Ready();
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDvbDevice::ProvidesCa(const cChannel *Channel) const
|
||||
{
|
||||
int NumCams = 0;
|
||||
if (ciHandler) {
|
||||
NumCams = ciHandler->NumCams();
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
unsigned short ids[MAXCAIDS + 1];
|
||||
for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0!
|
||||
ids[i] = Channel->Ca(i);
|
||||
if (ciHandler->ProvidesCa(ids))
|
||||
return NumCams + 1;
|
||||
}
|
||||
}
|
||||
int result = cDevice::ProvidesCa(Channel);
|
||||
if (result > 0)
|
||||
result += NumCams;
|
||||
return result;
|
||||
}
|
||||
|
||||
cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
|
||||
{
|
||||
if (!spuDecoder && IsPrimaryDevice())
|
||||
@ -770,18 +752,19 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
|
||||
bool hasPriority = Priority < 0 || Priority > this->Priority();
|
||||
bool needsDetachReceivers = false;
|
||||
|
||||
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {
|
||||
if (ProvidesSource(Channel->Source())) {
|
||||
result = hasPriority;
|
||||
if (Priority >= 0 && Receiving(true)) {
|
||||
if (dvbTuner->IsTunedTo(Channel)) {
|
||||
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
|
||||
#ifdef DO_MULTIPLE_RECORDINGS
|
||||
#ifndef DO_MULTIPLE_CA_CHANNELS
|
||||
if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
|
||||
needsDetachReceivers = Ca() != Channel->Ca();
|
||||
else
|
||||
#endif
|
||||
if (!IsPrimaryDevice())
|
||||
if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
if (CamSlot()->CanDecrypt(Channel))
|
||||
result = true;
|
||||
else
|
||||
needsDetachReceivers = true;
|
||||
}
|
||||
else if (!IsPrimaryDevice())
|
||||
result = true;
|
||||
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
|
||||
else
|
||||
@ -821,6 +804,8 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
||||
&& (LiveView && HasPid(Channel->Vpid() ? Channel->Vpid() : Channel->Apid(0)) && (pidHandles[ptVideo].pid != Channel->Vpid() || (pidHandles[ptAudio].pid != Channel->Apid(0) && (Channel->Dpid(0) ? pidHandles[ptAudio].pid != Channel->Dpid(0) : true)))// the PID is already set as DMX_PES_OTHER
|
||||
|| !LiveView && (pidHandles[ptVideo].pid == Channel->Vpid() || pidHandles[ptAudio].pid == Channel->Apid(0)) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
|
||||
);
|
||||
if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber()))
|
||||
StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
|
||||
|
||||
bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode && LiveView;
|
||||
|
||||
@ -861,7 +846,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
||||
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
|
||||
}
|
||||
else if (StartTransferMode)
|
||||
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
|
||||
cControl::Launch(new cTransferControl(this, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -922,13 +907,13 @@ void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
|
||||
if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
|
||||
if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
|
||||
DetachAll(pidHandles[ptAudio].pid);
|
||||
if (ciHandler)
|
||||
ciHandler->SetPid(pidHandles[ptAudio].pid, false);
|
||||
if (CamSlot())
|
||||
CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
|
||||
pidHandles[ptAudio].pid = TrackId->id;
|
||||
SetPid(&pidHandles[ptAudio], ptAudio, true);
|
||||
if (ciHandler) {
|
||||
ciHandler->SetPid(pidHandles[ptAudio].pid, true);
|
||||
ciHandler->StartDecrypting();
|
||||
if (CamSlot()) {
|
||||
CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
|
||||
CamSlot()->StartDecrypting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
dvbdevice.h
10
dvbdevice.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbdevice.h 1.41 2006/05/28 15:05:19 kls Exp $
|
||||
* $Id: dvbdevice.h 1.42 2007/01/07 14:39:52 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBDEVICE_H
|
||||
@ -36,16 +36,20 @@ public:
|
||||
///< \return True if any devices are available.
|
||||
private:
|
||||
fe_type_t frontendType;
|
||||
int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc;
|
||||
int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc, fd_ca;
|
||||
protected:
|
||||
virtual void MakePrimaryDevice(bool On);
|
||||
public:
|
||||
cDvbDevice(int n);
|
||||
virtual ~cDvbDevice();
|
||||
virtual bool Ready(void);
|
||||
virtual int ProvidesCa(const cChannel *Channel) const;
|
||||
virtual bool HasDecoder(void) const;
|
||||
|
||||
// Common Interface facilities:
|
||||
|
||||
private:
|
||||
cCiAdapter *ciAdapter;
|
||||
|
||||
// SPU facilities
|
||||
|
||||
private:
|
||||
|
196
i18n.c
196
i18n.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: i18n.c 1.285 2006/10/14 09:26:41 kls Exp $
|
||||
* $Id: i18n.c 1.286 2007/01/07 14:19:15 kls Exp $
|
||||
*
|
||||
* Translations provided by:
|
||||
*
|
||||
@ -2598,27 +2598,27 @@ const tI18nPhrase Phrases[] = {
|
||||
"Kan ikke åbne CAM menuen!",
|
||||
"Menu CAM není dostupné",
|
||||
},
|
||||
{ "Resetting CAM...",
|
||||
"CAM wird zurückgesetzt...",
|
||||
"Resetiram CAM...",
|
||||
"Reimpostazione modulo CAM...",
|
||||
"CAM wordt herstart...",
|
||||
{ "CAM is in use - really reset?"
|
||||
"CAM wird benutzt - wirklich zurücksetzen?",
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"Réinitialisation du CAM",
|
||||
"",//TODO
|
||||
"CA-moduuli palautetaan alkutilaan...",
|
||||
"Resetujê CAM...",
|
||||
"Reiniciando CAM...",
|
||||
"",//TODO
|
||||
"Återställer CAM ...",
|
||||
"Se reseteazã CAM...",
|
||||
"A CAM újra indul...",
|
||||
"",//TODO
|
||||
"¿ÕàÕÓàã×ÚÐ CAM...",
|
||||
"",//TODO
|
||||
"CAM mooduli taaskäivitus...",
|
||||
"Nulstiller CAM...",
|
||||
"Restartuje se CAM...",
|
||||
},
|
||||
{ "Can't reset CAM!",
|
||||
"Zurücksetzen des CAM fehlgeschlagen!",
|
||||
@ -2642,27 +2642,93 @@ const tI18nPhrase Phrases[] = {
|
||||
"Kan ikke nulstille CAM!",
|
||||
"CAM modul nelze restartovat!",
|
||||
},
|
||||
{ "CAM has been reset",
|
||||
"CAM wurde zurückgesetzt",
|
||||
"CAM je resetiran",
|
||||
"Modulo CAM reimpostato",
|
||||
"CAM is herstart",
|
||||
{ "CAM reset",
|
||||
"CAM zurückgesetzt",
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
},
|
||||
{ "CAM present",
|
||||
"CAM vorhanden",
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
},
|
||||
{ "CAM ready",
|
||||
"CAM bereit",
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
},
|
||||
{ "CAM not responding!",
|
||||
"CAM antwortet nicht!",
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"",//TODO
|
||||
"La CAM a été réinitialisée",
|
||||
"",//TODO
|
||||
"CA-moduuli palautettu alkutilaan",
|
||||
"CAM zosta³ zresetowany",
|
||||
"CAM reiniciado",
|
||||
"¸ãéíå åðáíáöïñÜ óôï CAM",
|
||||
"CA modulen har återställts",
|
||||
"CAM-ul a fost resetat",
|
||||
"A CAM vissza lett állítva",
|
||||
"CAM reiniciada",
|
||||
"CAM-ÜÞÔãÛì ßÕàÕ×ÐßãéÕÝ",
|
||||
"CAM je resetiran",
|
||||
"CAM mooduli taaskäivitus tehtud",
|
||||
"CAM er blevet nulstillet",
|
||||
"CAM byl restartován",
|
||||
},
|
||||
{ "Please enter %d digits!",
|
||||
"Bitte geben Sie %d Ziffern ein!",
|
||||
@ -2797,27 +2863,27 @@ const tI18nPhrase Phrases[] = {
|
||||
"LNB",
|
||||
"LNB",
|
||||
},
|
||||
{ "CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"Accesso condizionato CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
{ "CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"Accesso condizionato CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"Accès conditionnel",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CI Accés condicional",
|
||||
"ÃáÛÞÒÝëÙ ÔÞáâãß",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CICAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
"CAM",
|
||||
},
|
||||
{ "Recording",
|
||||
"Aufnahme",
|
||||
@ -3964,28 +4030,6 @@ const tI18nPhrase Phrases[] = {
|
||||
"Anvend DiSEqC",
|
||||
"Pou¾ívat DiSEqC",
|
||||
},
|
||||
{ "Setup.CICAM$CICAM DVB",
|
||||
"CICAM-DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"Accès conditionnel",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"Accés condicional CICAM",
|
||||
"CAM-ÜÞÔãÛì DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
"CICAM DVB",
|
||||
},
|
||||
{ "Setup.Recording$Margin at start (min)",
|
||||
"Vorlauf zum Timer-Beginn (min)",
|
||||
"Premik zaèetka snemanja (min)",
|
||||
|
420
menu.c
420
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.446 2006/12/02 11:12:02 kls Exp $
|
||||
* $Id: menu.c 1.447 2007/01/07 14:19:48 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -38,7 +38,9 @@
|
||||
|
||||
#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
|
||||
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
|
||||
#define MAXWAITFORCAMMENU 4 // seconds to wait for the CAM menu to open
|
||||
#define MAXWAITFORCAMMENU 10 // seconds to wait for the CAM menu to open
|
||||
#define CAMMENURETYTIMEOUT 3 // seconds after which opening the CAM menu is retried
|
||||
#define CAMRESPONSETIMEOUT 5 // seconds to wait for a response from a CAM
|
||||
#define MINFREEDISK 300 // minimum free disk space (in MB) required to start recording
|
||||
#define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages
|
||||
|
||||
@ -1579,38 +1581,104 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
|
||||
|
||||
// --- cMenuCam --------------------------------------------------------------
|
||||
|
||||
cMenuCam::cMenuCam(cCiMenu *CiMenu)
|
||||
:cOsdMenu("")
|
||||
class cMenuCam : public cOsdMenu {
|
||||
private:
|
||||
cCamSlot *camSlot;
|
||||
cCiMenu *ciMenu;
|
||||
cCiEnquiry *ciEnquiry;
|
||||
char *input;
|
||||
int offset;
|
||||
time_t lastCamExchange;
|
||||
void GenerateTitle(const char *s = NULL);
|
||||
void QueryCam(void);
|
||||
void AddMultiLineItem(const char *s);
|
||||
void Set(void);
|
||||
eOSState Select(void);
|
||||
public:
|
||||
cMenuCam(cCamSlot *CamSlot);
|
||||
virtual ~cMenuCam();
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
};
|
||||
|
||||
cMenuCam::cMenuCam(cCamSlot *CamSlot)
|
||||
:cOsdMenu("", 1) // tab necessary for enquiry!
|
||||
{
|
||||
dsyslog("CAM: Menu ------------------");
|
||||
ciMenu = CiMenu;
|
||||
selected = false;
|
||||
camSlot = CamSlot;
|
||||
ciMenu = NULL;
|
||||
ciEnquiry = NULL;
|
||||
input = NULL;
|
||||
offset = 0;
|
||||
if (ciMenu->Selectable())
|
||||
SetHasHotkeys();
|
||||
SetTitle(*ciMenu->TitleText() ? ciMenu->TitleText() : "CAM");
|
||||
dsyslog("CAM: '%s'", ciMenu->TitleText());
|
||||
if (*ciMenu->SubTitleText()) {
|
||||
dsyslog("CAM: '%s'", ciMenu->SubTitleText());
|
||||
AddMultiLineItem(ciMenu->SubTitleText());
|
||||
offset = Count();
|
||||
}
|
||||
for (int i = 0; i < ciMenu->NumEntries(); i++) {
|
||||
Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
|
||||
dsyslog("CAM: '%s'", ciMenu->Entry(i));
|
||||
}
|
||||
if (*ciMenu->BottomText()) {
|
||||
AddMultiLineItem(ciMenu->BottomText());
|
||||
dsyslog("CAM: '%s'", ciMenu->BottomText());
|
||||
}
|
||||
Display();
|
||||
lastCamExchange = time(NULL);
|
||||
SetNeedsFastResponse(true);
|
||||
QueryCam();
|
||||
}
|
||||
|
||||
cMenuCam::~cMenuCam()
|
||||
{
|
||||
if (!selected)
|
||||
if (ciMenu)
|
||||
ciMenu->Abort();
|
||||
delete ciMenu;
|
||||
if (ciEnquiry)
|
||||
ciEnquiry->Abort();
|
||||
delete ciEnquiry;
|
||||
free(input);
|
||||
}
|
||||
|
||||
void cMenuCam::GenerateTitle(const char *s)
|
||||
{
|
||||
SetTitle(cString::sprintf("CAM %d - %s", camSlot->SlotNumber(), (s && *s) ? s : camSlot->GetCamName()));
|
||||
}
|
||||
|
||||
void cMenuCam::QueryCam(void)
|
||||
{
|
||||
delete ciMenu;
|
||||
ciMenu = NULL;
|
||||
delete ciEnquiry;
|
||||
ciEnquiry = NULL;
|
||||
if (camSlot->HasUserIO()) {
|
||||
ciMenu = camSlot->GetMenu();
|
||||
ciEnquiry = camSlot->GetEnquiry();
|
||||
}
|
||||
Set();
|
||||
}
|
||||
|
||||
void cMenuCam::Set(void)
|
||||
{
|
||||
if (ciMenu) {
|
||||
Clear();
|
||||
free(input);
|
||||
input = NULL;
|
||||
dsyslog("CAM %d: Menu ------------------", camSlot->SlotNumber());
|
||||
offset = 0;
|
||||
SetHasHotkeys(ciMenu->Selectable());
|
||||
GenerateTitle(ciMenu->TitleText());
|
||||
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->TitleText());
|
||||
if (*ciMenu->SubTitleText()) {
|
||||
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->SubTitleText());
|
||||
AddMultiLineItem(ciMenu->SubTitleText());
|
||||
offset = Count();
|
||||
}
|
||||
for (int i = 0; i < ciMenu->NumEntries(); i++) {
|
||||
Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
|
||||
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->Entry(i));
|
||||
}
|
||||
if (*ciMenu->BottomText()) {
|
||||
AddMultiLineItem(ciMenu->BottomText());
|
||||
dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->BottomText());
|
||||
}
|
||||
}
|
||||
else if (ciEnquiry) {
|
||||
Clear();
|
||||
int Length = ciEnquiry->ExpectedLength();
|
||||
free(input);
|
||||
input = MALLOC(char, Length + 1);
|
||||
*input = 0;
|
||||
GenerateTitle();
|
||||
Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
|
||||
Add(new cOsdItem("", osUnknown, false));
|
||||
Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
|
||||
}
|
||||
Display();
|
||||
}
|
||||
|
||||
void cMenuCam::AddMultiLineItem(const char *s)
|
||||
@ -1628,90 +1696,61 @@ void cMenuCam::AddMultiLineItem(const char *s)
|
||||
|
||||
eOSState cMenuCam::Select(void)
|
||||
{
|
||||
if (ciMenu->Selectable()) {
|
||||
ciMenu->Select(Current() - offset);
|
||||
dsyslog("CAM: select %d", Current() - offset);
|
||||
if (ciMenu) {
|
||||
if (ciMenu->Selectable()) {
|
||||
ciMenu->Select(Current() - offset);
|
||||
dsyslog("CAM %d: select %d", camSlot->SlotNumber(), Current() - offset);
|
||||
}
|
||||
else
|
||||
ciMenu->Cancel();
|
||||
}
|
||||
else
|
||||
ciMenu->Cancel();
|
||||
selected = true;
|
||||
return osEnd;
|
||||
else if (ciEnquiry) {
|
||||
if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
|
||||
Skins.Message(mtError, buffer);
|
||||
return osContinue;
|
||||
}
|
||||
ciEnquiry->Reply(input);
|
||||
dsyslog("CAM %d: entered '%s'", camSlot->SlotNumber(), ciEnquiry->Blind() ? "****" : input);
|
||||
}
|
||||
QueryCam();
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
eOSState cMenuCam::ProcessKey(eKeys Key)
|
||||
{
|
||||
if (!camSlot->HasMMI())
|
||||
return osBack;
|
||||
|
||||
eOSState state = cOsdMenu::ProcessKey(Key);
|
||||
|
||||
if (state == osUnknown) {
|
||||
switch (Key) {
|
||||
case kOk: return Select();
|
||||
default: break;
|
||||
}
|
||||
if (ciMenu || ciEnquiry) {
|
||||
lastCamExchange = time(NULL);
|
||||
if (state == osUnknown) {
|
||||
switch (Key) {
|
||||
case kOk: return Select();
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
else if (state == osBack) {
|
||||
if (ciMenu)
|
||||
ciMenu->Cancel();
|
||||
if (ciEnquiry)
|
||||
ciEnquiry->Cancel();
|
||||
QueryCam();
|
||||
return osContinue;
|
||||
}
|
||||
if (ciMenu && ciMenu->HasUpdate()) {
|
||||
QueryCam();
|
||||
return osContinue;
|
||||
}
|
||||
}
|
||||
else if (state == osBack) {
|
||||
ciMenu->Cancel();
|
||||
selected = true;
|
||||
return osEnd;
|
||||
}
|
||||
if (ciMenu->HasUpdate()) {
|
||||
selected = true;
|
||||
return osEnd;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
// --- cMenuCamEnquiry -------------------------------------------------------
|
||||
|
||||
cMenuCamEnquiry::cMenuCamEnquiry(cCiEnquiry *CiEnquiry)
|
||||
:cOsdMenu("", 1)
|
||||
{
|
||||
ciEnquiry = CiEnquiry;
|
||||
int Length = ciEnquiry->ExpectedLength();
|
||||
input = MALLOC(char, Length + 1);
|
||||
*input = 0;
|
||||
replied = false;
|
||||
SetTitle("CAM");
|
||||
Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
|
||||
Add(new cOsdItem("", osUnknown, false));
|
||||
Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
|
||||
Display();
|
||||
}
|
||||
|
||||
cMenuCamEnquiry::~cMenuCamEnquiry()
|
||||
{
|
||||
if (!replied)
|
||||
ciEnquiry->Abort();
|
||||
free(input);
|
||||
delete ciEnquiry;
|
||||
}
|
||||
|
||||
eOSState cMenuCamEnquiry::Reply(void)
|
||||
{
|
||||
if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
|
||||
Skins.Message(mtError, buffer);
|
||||
return osContinue;
|
||||
}
|
||||
ciEnquiry->Reply(input);
|
||||
replied = true;
|
||||
return osEnd;
|
||||
}
|
||||
|
||||
eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
|
||||
{
|
||||
eOSState state = cOsdMenu::ProcessKey(Key);
|
||||
|
||||
if (state == osUnknown) {
|
||||
switch (Key) {
|
||||
case kOk: return Reply();
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
else if (state == osBack) {
|
||||
ciEnquiry->Cancel();
|
||||
replied = true;
|
||||
return osEnd;
|
||||
else if (time(NULL) - lastCamExchange < CAMRESPONSETIMEOUT)
|
||||
QueryCam();
|
||||
else {
|
||||
Skins.Message(mtError, tr("CAM not responding!"));
|
||||
return osBack;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@ -1720,21 +1759,9 @@ eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
|
||||
|
||||
cOsdObject *CamControl(void)
|
||||
{
|
||||
for (int d = 0; d < cDevice::NumDevices(); d++) {
|
||||
cDevice *Device = cDevice::GetDevice(d);
|
||||
if (Device) {
|
||||
cCiHandler *CiHandler = Device->CiHandler();
|
||||
if (CiHandler && CiHandler->HasUserIO()) {
|
||||
cCiMenu *CiMenu = CiHandler->GetMenu();
|
||||
if (CiMenu)
|
||||
return new cMenuCam(CiMenu);
|
||||
else {
|
||||
cCiEnquiry *CiEnquiry = CiHandler->GetEnquiry();
|
||||
if (CiEnquiry)
|
||||
return new cMenuCamEnquiry(CiEnquiry);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
if (CamSlot->HasUserIO())
|
||||
return new cMenuCam(CamSlot);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -2454,95 +2481,117 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
|
||||
return state;
|
||||
}
|
||||
|
||||
// --- cMenuSetupCICAM -------------------------------------------------------
|
||||
// --- cMenuSetupCAM ---------------------------------------------------------
|
||||
|
||||
class cMenuSetupCICAMItem : public cOsdItem {
|
||||
class cMenuSetupCAMItem : public cOsdItem {
|
||||
private:
|
||||
cCiHandler *ciHandler;
|
||||
int slot;
|
||||
cCamSlot *camSlot;
|
||||
public:
|
||||
cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot);
|
||||
cCiHandler *CiHandler(void) { return ciHandler; }
|
||||
int Slot(void) { return slot; }
|
||||
cMenuSetupCAMItem(cCamSlot *CamSlot);
|
||||
cCamSlot *CamSlot(void) { return camSlot; }
|
||||
bool Changed(void);
|
||||
};
|
||||
|
||||
cMenuSetupCICAMItem::cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot)
|
||||
cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot)
|
||||
{
|
||||
ciHandler = CiHandler;
|
||||
slot = Slot;
|
||||
char buffer[32];
|
||||
const char *CamName = CiHandler->GetCamName(slot);
|
||||
snprintf(buffer, sizeof(buffer), "%s%d %d\t%s", tr("Setup.CICAM$CICAM DVB"), Device + 1, slot + 1, CamName ? CamName : "-");
|
||||
SetText(buffer);
|
||||
camSlot = CamSlot;
|
||||
SetText("");
|
||||
Changed();
|
||||
}
|
||||
|
||||
class cMenuSetupCICAM : public cMenuSetupBase {
|
||||
bool cMenuSetupCAMItem::Changed(void)
|
||||
{
|
||||
char buffer[32];
|
||||
const char *CamName = camSlot->GetCamName();
|
||||
if (!CamName) {
|
||||
switch (camSlot->ModuleStatus()) {
|
||||
case msReset: CamName = tr("CAM reset"); break;
|
||||
case msPresent: CamName = tr("CAM present"); break;
|
||||
case msReady: CamName = tr("CAM ready"); break;
|
||||
default: CamName = "-"; break;
|
||||
}
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), " %d %s", camSlot->SlotNumber(), CamName);
|
||||
if (strcmp(buffer, Text()) != 0) {
|
||||
SetText(buffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class cMenuSetupCAM : public cMenuSetupBase {
|
||||
private:
|
||||
eOSState Menu(void);
|
||||
eOSState Reset(void);
|
||||
public:
|
||||
cMenuSetupCICAM(void);
|
||||
cMenuSetupCAM(void);
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
};
|
||||
|
||||
cMenuSetupCICAM::cMenuSetupCICAM(void)
|
||||
cMenuSetupCAM::cMenuSetupCAM(void)
|
||||
{
|
||||
SetSection(tr("CICAM"));
|
||||
for (int d = 0; d < cDevice::NumDevices(); d++) {
|
||||
cDevice *Device = cDevice::GetDevice(d);
|
||||
if (Device) {
|
||||
cCiHandler *CiHandler = Device->CiHandler();
|
||||
if (CiHandler) {
|
||||
for (int Slot = 0; Slot < CiHandler->NumSlots(); Slot++)
|
||||
Add(new cMenuSetupCICAMItem(Device->CardIndex(), CiHandler, Slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
SetSection(tr("CAM"));
|
||||
SetCols(15);
|
||||
SetHasHotkeys();
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
|
||||
Add(new cMenuSetupCAMItem(CamSlot));
|
||||
SetHelp(tr("Button$Menu"), tr("Button$Reset"));
|
||||
}
|
||||
|
||||
eOSState cMenuSetupCICAM::Menu(void)
|
||||
eOSState cMenuSetupCAM::Menu(void)
|
||||
{
|
||||
cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
|
||||
cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
|
||||
if (item) {
|
||||
if (item->CiHandler()->EnterMenu(item->Slot())) {
|
||||
Skins.Message(mtWarning, tr("Opening CAM menu..."));
|
||||
time_t t = time(NULL);
|
||||
while (time(NULL) - t < MAXWAITFORCAMMENU && !item->CiHandler()->HasUserIO())
|
||||
item->CiHandler()->Process();
|
||||
return osEnd; // the CAM menu will be executed explicitly from the main loop
|
||||
if (item->CamSlot()->EnterMenu()) {
|
||||
Skins.Message(mtStatus, tr("Opening CAM menu..."));
|
||||
time_t t0 = time(NULL);
|
||||
time_t t1 = t0;
|
||||
while (time(NULL) - t0 <= MAXWAITFORCAMMENU) {
|
||||
if (item->CamSlot()->HasUserIO())
|
||||
break;
|
||||
if (time(NULL) - t1 >= CAMMENURETYTIMEOUT) {
|
||||
dsyslog("CAM %d: retrying to enter CAM menu...", item->CamSlot()->SlotNumber());
|
||||
item->CamSlot()->EnterMenu();
|
||||
t1 = time(NULL);
|
||||
}
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
Skins.Message(mtStatus, NULL);
|
||||
if (item->CamSlot()->HasUserIO())
|
||||
return AddSubMenu(new cMenuCam(item->CamSlot()));
|
||||
}
|
||||
else
|
||||
Skins.Message(mtError, tr("Can't open CAM menu!"));
|
||||
Skins.Message(mtError, tr("Can't open CAM menu!"));
|
||||
}
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
eOSState cMenuSetupCICAM::Reset(void)
|
||||
eOSState cMenuSetupCAM::Reset(void)
|
||||
{
|
||||
cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
|
||||
cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
|
||||
if (item) {
|
||||
Skins.Message(mtWarning, tr("Resetting CAM..."));
|
||||
if (item->CiHandler()->Reset(item->Slot())) {
|
||||
Skins.Message(mtInfo, tr("CAM has been reset"));
|
||||
return osEnd;
|
||||
if (!item->CamSlot()->Device() || Interface->Confirm(tr("CAM is in use - really reset?"))) {
|
||||
if (!item->CamSlot()->Reset())
|
||||
Skins.Message(mtError, tr("Can't reset CAM!"));
|
||||
}
|
||||
else
|
||||
Skins.Message(mtError, tr("Can't reset CAM!"));
|
||||
}
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
|
||||
eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
|
||||
{
|
||||
eOSState state = cMenuSetupBase::ProcessKey(Key);
|
||||
eOSState state = HasSubMenu() ? cMenuSetupBase::ProcessKey(Key) : cOsdMenu::ProcessKey(Key);
|
||||
|
||||
if (state == osUnknown) {
|
||||
if (!HasSubMenu()) {
|
||||
switch (Key) {
|
||||
case kOk:
|
||||
case kRed: return Menu();
|
||||
case kGreen: return Reset();
|
||||
case kGreen: state = Reset(); break;
|
||||
default: break;
|
||||
}
|
||||
for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
|
||||
if (ci->Changed())
|
||||
DisplayItem(ci);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@ -2710,7 +2759,7 @@ void cMenuSetup::Set(void)
|
||||
Add(new cOsdItem(hk(tr("EPG")), osUser2));
|
||||
Add(new cOsdItem(hk(tr("DVB")), osUser3));
|
||||
Add(new cOsdItem(hk(tr("LNB")), osUser4));
|
||||
Add(new cOsdItem(hk(tr("CICAM")), osUser5));
|
||||
Add(new cOsdItem(hk(tr("CAM")), osUser5));
|
||||
Add(new cOsdItem(hk(tr("Recording")), osUser6));
|
||||
Add(new cOsdItem(hk(tr("Replay")), osUser7));
|
||||
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
|
||||
@ -2740,7 +2789,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
|
||||
case osUser2: return AddSubMenu(new cMenuSetupEPG);
|
||||
case osUser3: return AddSubMenu(new cMenuSetupDVB);
|
||||
case osUser4: return AddSubMenu(new cMenuSetupLNB);
|
||||
case osUser5: return AddSubMenu(new cMenuSetupCICAM);
|
||||
case osUser5: return AddSubMenu(new cMenuSetupCAM);
|
||||
case osUser6: return AddSubMenu(new cMenuSetupRecord);
|
||||
case osUser7: return AddSubMenu(new cMenuSetupReplay);
|
||||
case osUser8: return AddSubMenu(new cMenuSetupMisc);
|
||||
@ -3126,7 +3175,7 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
|
||||
if (Direction) {
|
||||
while (Channel) {
|
||||
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
|
||||
if (Channel && !Channel->GroupSep() && (cDevice::PrimaryDevice()->ProvidesChannel(Channel, Setup.PrimaryLimit) || cDevice::GetDevice(Channel, 0)))
|
||||
if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
|
||||
return Channel;
|
||||
}
|
||||
}
|
||||
@ -3541,7 +3590,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
|
||||
isyslog("record %s", fileName);
|
||||
if (MakeDirs(fileName, true)) {
|
||||
const cChannel *ch = timer->Channel();
|
||||
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
|
||||
recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
|
||||
if (device->AttachReceiver(recorder)) {
|
||||
Recording.WriteInfo();
|
||||
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
|
||||
@ -3610,7 +3659,7 @@ void cRecordControl::Stop(void)
|
||||
|
||||
bool cRecordControl::Process(time_t t)
|
||||
{
|
||||
if (!recorder || !timer || !timer->Matches(t))
|
||||
if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t))
|
||||
return false;
|
||||
AssertFreeDiskSpace(timer->Priority());
|
||||
return true;
|
||||
@ -3645,15 +3694,9 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
|
||||
cChannel *channel = Channels.GetByNumber(ch);
|
||||
|
||||
if (channel) {
|
||||
bool NeedsDetachReceivers = false;
|
||||
int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
|
||||
cDevice *device = cDevice::GetDevice(channel, Priority, &NeedsDetachReceivers);
|
||||
cDevice *device = cDevice::GetDevice(channel, Priority, false);
|
||||
if (device) {
|
||||
if (NeedsDetachReceivers) {
|
||||
Stop(device);
|
||||
if (device == cTransferControl::ReceiverDevice())
|
||||
cControl::Shutdown(); // in case this device was used for Transfer Mode
|
||||
}
|
||||
dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
|
||||
if (!device->SwitchChannel(channel, false)) {
|
||||
cThread::EmergencyExit(true);
|
||||
@ -3698,19 +3741,6 @@ void cRecordControls::Stop(const char *InstantId)
|
||||
}
|
||||
}
|
||||
|
||||
void cRecordControls::Stop(cDevice *Device)
|
||||
{
|
||||
ChangeState();
|
||||
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
|
||||
if (RecordControls[i]) {
|
||||
if (RecordControls[i]->Device() == Device) {
|
||||
isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
|
||||
RecordControls[i]->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cRecordControls::PauseLiveVideo(void)
|
||||
{
|
||||
Skins.Message(mtStatus, tr("Pausing live video..."));
|
||||
@ -3882,7 +3912,8 @@ void cReplayControl::Hide(void)
|
||||
if (visible) {
|
||||
delete displayReplay;
|
||||
displayReplay = NULL;
|
||||
needsFastResponse = visible = false;
|
||||
SetNeedsFastResponse(false);
|
||||
visible = false;
|
||||
modeOnly = false;
|
||||
lastPlay = lastForward = false;
|
||||
lastSpeed = -2; // an invalid value
|
||||
@ -3923,7 +3954,8 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
if (!visible) {
|
||||
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
|
||||
displayReplay->SetMarks(&marks);
|
||||
needsFastResponse = visible = true;
|
||||
SetNeedsFastResponse(true);
|
||||
visible = true;
|
||||
}
|
||||
if (Initial) {
|
||||
if (title)
|
||||
|
28
menu.h
28
menu.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.h 1.86 2006/10/20 13:09:57 kls Exp $
|
||||
* $Id: menu.h 1.87 2007/01/07 14:40:54 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MENU_H
|
||||
@ -128,31 +128,6 @@ public:
|
||||
eOSState ProcessKey(eKeys Key);
|
||||
};
|
||||
|
||||
class cMenuCam : public cOsdMenu {
|
||||
private:
|
||||
cCiMenu *ciMenu;
|
||||
bool selected;
|
||||
int offset;
|
||||
void AddMultiLineItem(const char *s);
|
||||
eOSState Select(void);
|
||||
public:
|
||||
cMenuCam(cCiMenu *CiMenu);
|
||||
virtual ~cMenuCam();
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
};
|
||||
|
||||
class cMenuCamEnquiry : public cOsdMenu {
|
||||
private:
|
||||
cCiEnquiry *ciEnquiry;
|
||||
char *input;
|
||||
bool replied;
|
||||
eOSState Reply(void);
|
||||
public:
|
||||
cMenuCamEnquiry(cCiEnquiry *CiEnquiry);
|
||||
virtual ~cMenuCamEnquiry();
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
};
|
||||
|
||||
cOsdObject *CamControl(void);
|
||||
|
||||
class cMenuRecordingItem;
|
||||
@ -206,7 +181,6 @@ private:
|
||||
public:
|
||||
static bool Start(cTimer *Timer = NULL, bool Pause = false);
|
||||
static void Stop(const char *InstantId);
|
||||
static void Stop(cDevice *Device);
|
||||
static bool PauseLiveVideo(void);
|
||||
static const char *GetInstantId(const char *LastInstantId);
|
||||
static cRecordControl *GetRecordControl(const char *FileName);
|
||||
|
23
osdbase.c
23
osdbase.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osdbase.c 1.29 2006/02/05 14:37:03 kls Exp $
|
||||
* $Id: osdbase.c 1.30 2007/01/07 14:41:16 kls Exp $
|
||||
*/
|
||||
|
||||
#include "osdbase.h"
|
||||
@ -127,9 +127,9 @@ void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
|
||||
cols[4] = c4;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetHasHotkeys(void)
|
||||
void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
|
||||
{
|
||||
hasHotkeys = true;
|
||||
hasHotkeys = HasHotkeys;
|
||||
digit = 0;
|
||||
}
|
||||
|
||||
@ -256,6 +256,20 @@ void cOsdMenu::DisplayCurrent(bool Current)
|
||||
}
|
||||
}
|
||||
|
||||
void cOsdMenu::DisplayItem(cOsdItem *Item)
|
||||
{
|
||||
if (Item) {
|
||||
int Index = Item->Index();
|
||||
int Offset = Index - first;
|
||||
if (Offset >= 0 && Offset < first + displayMenuItems) {
|
||||
bool Current = Index == current;
|
||||
displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
|
||||
if (Current && Item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(Item->Text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cOsdMenu::Clear(void)
|
||||
{
|
||||
if (marked >= 0)
|
||||
@ -432,6 +446,8 @@ eOSState cOsdMenu::HotKey(eKeys Key)
|
||||
if (s && (s = skipspace(s)) != NULL) {
|
||||
if (*s == Key - k1 + '1') {
|
||||
current = item->Index();
|
||||
RefreshCurrent();
|
||||
Display();
|
||||
cRemote::Put(kOk, true);
|
||||
break;
|
||||
}
|
||||
@ -499,4 +515,3 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
|
||||
}
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
|
11
osdbase.h
11
osdbase.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osdbase.h 1.14 2006/01/05 15:35:06 kls Exp $
|
||||
* $Id: osdbase.h 1.15 2007/01/07 14:41:32 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __OSDBASE_H
|
||||
@ -70,12 +70,13 @@ class cOsdObject {
|
||||
friend class cOsdMenu;
|
||||
private:
|
||||
bool isMenu;
|
||||
protected:
|
||||
bool needsFastResponse;
|
||||
protected:
|
||||
void SetNeedsFastResponse(bool NeedsFastResponse) { needsFastResponse = NeedsFastResponse; }
|
||||
public:
|
||||
cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; }
|
||||
virtual ~cOsdObject() {}
|
||||
bool NeedsFastResponse(void) { return needsFastResponse; }
|
||||
virtual bool NeedsFastResponse(void) { return needsFastResponse; }
|
||||
bool IsMenu(void) { return isMenu; }
|
||||
virtual void Show(void);
|
||||
virtual eOSState ProcessKey(eKeys Key) { return osUnknown; }
|
||||
@ -98,12 +99,13 @@ protected:
|
||||
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
|
||||
const char *hk(const char *s);
|
||||
void SetCols(int c0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
|
||||
void SetHasHotkeys(void);
|
||||
void SetHasHotkeys(bool HasHotkeys = true);
|
||||
virtual void Clear(void);
|
||||
bool SelectableItem(int idx);
|
||||
void SetCurrent(cOsdItem *Item);
|
||||
void RefreshCurrent(void);
|
||||
void DisplayCurrent(bool Current);
|
||||
void DisplayItem(cOsdItem *Item);
|
||||
void CursorUp(void);
|
||||
void CursorDown(void);
|
||||
void PageUp(void);
|
||||
@ -120,6 +122,7 @@ protected:
|
||||
public:
|
||||
cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
|
||||
virtual ~cOsdMenu();
|
||||
virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
|
||||
int Current(void) { return current; }
|
||||
void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
|
||||
void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
|
||||
|
14
pat.c
14
pat.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: pat.c 1.16 2006/03/31 12:39:34 kls Exp $
|
||||
* $Id: pat.c 1.17 2007/01/07 14:41:55 kls Exp $
|
||||
*/
|
||||
|
||||
#include "pat.h"
|
||||
@ -78,7 +78,7 @@ public:
|
||||
bool Is(cCaDescriptors * CaDescriptors);
|
||||
bool Empty(void) { return caDescriptors.Count() == 0; }
|
||||
void AddCaDescriptor(SI::CaDescriptor *d, bool Stream);
|
||||
int GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
const int *CaIds(void) { return caIds; }
|
||||
};
|
||||
|
||||
@ -148,7 +148,7 @@ void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream)
|
||||
#endif
|
||||
}
|
||||
|
||||
int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
{
|
||||
if (!CaSystemIds || !*CaSystemIds)
|
||||
return 0;
|
||||
@ -156,7 +156,7 @@ int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufS
|
||||
int length = 0;
|
||||
int IsStream = -1;
|
||||
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
|
||||
const unsigned short *caids = CaSystemIds;
|
||||
const int *caids = CaSystemIds;
|
||||
do {
|
||||
if (d->CaSystem() == *caids) {
|
||||
if (length + d->Length() <= BufSize) {
|
||||
@ -187,7 +187,7 @@ public:
|
||||
// Returns 0 if this is an already known descriptor,
|
||||
// 1 if it is an all new descriptor with actual contents,
|
||||
// and 2 if an existing descriptor was changed.
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
};
|
||||
|
||||
int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
|
||||
@ -208,7 +208,7 @@ int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
|
||||
return CaDescriptors->Empty() ? 0 : 1;
|
||||
}
|
||||
|
||||
int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
StreamFlag = false;
|
||||
@ -221,7 +221,7 @@ int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int Serv
|
||||
|
||||
cCaDescriptorHandler CaDescriptorHandler;
|
||||
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
|
||||
{
|
||||
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
|
||||
}
|
||||
|
4
pat.h
4
pat.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: pat.h 1.6 2006/03/29 15:18:38 kls Exp $
|
||||
* $Id: pat.h 1.7 2007/01/07 14:42:11 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __PAT_H
|
||||
@ -32,7 +32,7 @@ public:
|
||||
void Trigger(void);
|
||||
};
|
||||
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
|
||||
///< Gets all CA descriptors for a given channel.
|
||||
///< Copies all available CA descriptors for the given Source, Transponder and ServiceId
|
||||
///< into the provided buffer at Data (at most BufSize bytes). Only those CA descriptors
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: receiver.c 1.5 2006/03/26 14:07:21 kls Exp $
|
||||
* $Id: receiver.c 1.6 2007/01/07 14:42:29 kls Exp $
|
||||
*/
|
||||
|
||||
#include "receiver.h"
|
||||
@ -12,10 +12,10 @@
|
||||
#include <stdio.h>
|
||||
#include "tools.h"
|
||||
|
||||
cReceiver::cReceiver(int Ca, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
|
||||
cReceiver::cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
|
||||
{
|
||||
device = NULL;
|
||||
ca = Ca;
|
||||
channelID = ChannelID;
|
||||
priority = Priority;
|
||||
numPids = 0;
|
||||
if (Pid)
|
||||
|
19
receiver.h
19
receiver.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: receiver.h 1.4 2006/05/27 09:04:22 kls Exp $
|
||||
* $Id: receiver.h 1.5 2007/01/07 14:40:36 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECEIVER_H
|
||||
@ -18,7 +18,7 @@ class cReceiver {
|
||||
friend class cDevice;
|
||||
private:
|
||||
cDevice *device;
|
||||
int ca;
|
||||
tChannelID channelID;
|
||||
int priority;
|
||||
int pids[MAXRECEIVEPIDS];
|
||||
int numPids;
|
||||
@ -38,8 +38,8 @@ protected:
|
||||
///< will be delivered only ONCE, so the cReceiver must make sure that
|
||||
///< it will be able to buffer the data if necessary.
|
||||
public:
|
||||
cReceiver(int Ca, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
|
||||
///< Creates a new receiver that requires conditional access Ca and has
|
||||
cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
|
||||
///< Creates a new receiver for the channel with the given ChannelID with
|
||||
///< the given Priority. Pid is a single PID (typically the video PID), while
|
||||
///< Pids1...Pids3 are pointers to zero terminated lists of PIDs.
|
||||
///< If any of these PIDs are 0, they will be silently ignored.
|
||||
@ -47,7 +47,18 @@ public:
|
||||
///< Priority may be any value in the range -99..99. Negative values indicate
|
||||
///< that this cReceiver may be detached at any time (without blocking the
|
||||
///< cDevice it is attached to).
|
||||
///< The ChannelID is necessary to allow the device that will be used for this
|
||||
///< receiver to detect and store whether the channel can be decrypted in case
|
||||
///< this is an encrypted channel. If the channel is not encrypted or this
|
||||
///< detection is not wanted, an invalid tChannelID may be given.
|
||||
virtual ~cReceiver();
|
||||
tChannelID ChannelID(void) { return channelID; }
|
||||
bool IsAttached(void) { return device != NULL; }
|
||||
///< Returns true if this receiver is (still) attached to a device.
|
||||
///< A receiver may be automatically detached from its device in
|
||||
///< case the device is needed otherwise, so code that uses a cReceiver
|
||||
///< should repeatedly check whether it is still attached, and if
|
||||
///< it isn't, delete it (or take any other appropriate measures).
|
||||
};
|
||||
|
||||
#endif //__RECEIVER_H
|
||||
|
10
recorder.c
10
recorder.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.c 1.17 2006/01/08 11:01:25 kls Exp $
|
||||
* $Id: recorder.c 1.18 2007/01/07 14:43:09 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recorder.h"
|
||||
@ -21,6 +21,8 @@
|
||||
#define MINFREEDISKSPACE (512) // MB
|
||||
#define DISKCHECKINTERVAL 100 // seconds
|
||||
|
||||
// --- cFileWriter -----------------------------------------------------------
|
||||
|
||||
class cFileWriter : public cThread {
|
||||
private:
|
||||
cRemux *remux;
|
||||
@ -121,8 +123,10 @@ void cFileWriter::Action(void)
|
||||
}
|
||||
}
|
||||
|
||||
cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
// --- cRecorder -------------------------------------------------------------
|
||||
|
||||
cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
,cThread("recording")
|
||||
{
|
||||
// Make sure the disk is up and running:
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.h 1.4 2005/08/13 11:31:18 kls Exp $
|
||||
* $Id: recorder.h 1.5 2007/01/07 14:44:05 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDER_H
|
||||
@ -28,9 +28,9 @@ protected:
|
||||
virtual void Receive(uchar *Data, int Length);
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
// Creates a new recorder that requires conditional access Ca, has
|
||||
// the given Priority and will record the given PIDs into the file FileName.
|
||||
cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
// Creates a new recorder for the channel with the given ChannelID and
|
||||
// the given Priority that will record the given PIDs into the file FileName.
|
||||
virtual ~cRecorder();
|
||||
};
|
||||
|
||||
|
30
thread.c
30
thread.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.c 1.58 2006/09/24 12:54:47 kls Exp $
|
||||
* $Id: thread.c 1.59 2007/01/07 14:44:22 kls Exp $
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
@ -249,17 +249,29 @@ void *cThread::StartThread(cThread *Thread)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
|
||||
#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
|
||||
|
||||
bool cThread::Start(void)
|
||||
{
|
||||
if (!active) {
|
||||
active = running = true;
|
||||
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
|
||||
pthread_detach(childTid); // auto-reap
|
||||
if (!running) {
|
||||
if (active) {
|
||||
// Wait until the previous incarnation of this thread has completely ended
|
||||
// before starting it newly:
|
||||
cTimeMs RestartTimeout;
|
||||
while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
|
||||
cCondWait::SleepMs(THREAD_STOP_SLEEP);
|
||||
}
|
||||
else {
|
||||
LOG_ERROR;
|
||||
active = running = false;
|
||||
return false;
|
||||
if (!active) {
|
||||
active = running = true;
|
||||
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
|
||||
pthread_detach(childTid); // auto-reap
|
||||
}
|
||||
else {
|
||||
LOG_ERROR;
|
||||
active = running = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
3
thread.h
3
thread.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.h 1.37 2006/09/24 10:10:37 kls Exp $
|
||||
* $Id: thread.h 1.38 2007/01/07 14:44:38 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __THREAD_H
|
||||
@ -115,6 +115,7 @@ public:
|
||||
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
bool Start(void);
|
||||
///< Actually starts the thread.
|
||||
///< If the thread is already running, nothing happens.
|
||||
bool Active(void);
|
||||
///< Checks whether the thread is still alive.
|
||||
static bool EmergencyExit(bool Request = false);
|
||||
|
6
tools.c
6
tools.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.c 1.121 2006/12/02 11:12:59 kls Exp $
|
||||
* $Id: tools.c 1.122 2007/01/07 14:44:57 kls Exp $
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
@ -542,9 +542,9 @@ time_t LastModifiedTime(const char *FileName)
|
||||
|
||||
// --- cTimeMs ---------------------------------------------------------------
|
||||
|
||||
cTimeMs::cTimeMs(void)
|
||||
cTimeMs::cTimeMs(int Ms)
|
||||
{
|
||||
Set();
|
||||
Set(Ms);
|
||||
}
|
||||
|
||||
uint64_t cTimeMs::Now(void)
|
||||
|
6
tools.h
6
tools.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.h 1.96 2006/12/03 17:38:38 kls Exp $
|
||||
* $Id: tools.h 1.97 2007/01/07 14:45:11 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TOOLS_H
|
||||
@ -23,7 +23,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef unsigned char uchar;
|
||||
#define uint64 uint64_t // for compatibility - TODO remove in version 1.5
|
||||
|
||||
extern int SysLogLevel;
|
||||
|
||||
@ -162,7 +161,8 @@ class cTimeMs {
|
||||
private:
|
||||
uint64_t begin;
|
||||
public:
|
||||
cTimeMs(void);
|
||||
cTimeMs(int Ms = 0);
|
||||
///< Creates a timer with ms resolution and an initial timeout of Ms.
|
||||
static uint64_t Now(void);
|
||||
void Set(int Ms = 0);
|
||||
bool TimedOut(void);
|
||||
|
17
transfer.c
17
transfer.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: transfer.c 1.33 2006/01/29 17:24:39 kls Exp $
|
||||
* $Id: transfer.c 1.34 2007/01/07 14:45:28 kls Exp $
|
||||
*/
|
||||
|
||||
#include "transfer.h"
|
||||
@ -14,8 +14,8 @@
|
||||
|
||||
// --- cTransfer -------------------------------------------------------------
|
||||
|
||||
cTransfer::cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(0, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
,cThread("transfer")
|
||||
{
|
||||
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
|
||||
@ -34,17 +34,18 @@ void cTransfer::Activate(bool On)
|
||||
{
|
||||
if (On)
|
||||
Start();
|
||||
else
|
||||
else {
|
||||
Cancel(3);
|
||||
cPlayer::Detach();
|
||||
}
|
||||
}
|
||||
|
||||
void cTransfer::Receive(uchar *Data, int Length)
|
||||
{
|
||||
if (IsAttached() && Running()) {
|
||||
if (cPlayer::IsAttached() && Running()) {
|
||||
int p = ringBuffer->Put(Data, Length);
|
||||
if (p != Length && Running())
|
||||
ringBuffer->ReportOverflow(Length - p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,8 +111,8 @@ void cTransfer::Action(void)
|
||||
|
||||
cDevice *cTransferControl::receiverDevice = NULL;
|
||||
|
||||
cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cControl(transfer = new cTransfer(VPid, APids, DPids, SPids), true)
|
||||
cTransferControl::cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cControl(transfer = new cTransfer(ChannelID, VPid, APids, DPids, SPids), true)
|
||||
{
|
||||
ReceiverDevice->AttachReceiver(transfer);
|
||||
receiverDevice = ReceiverDevice;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: transfer.h 1.11 2006/01/29 17:24:43 kls Exp $
|
||||
* $Id: transfer.h 1.12 2007/01/07 14:45:45 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TRANSFER_H
|
||||
@ -25,7 +25,7 @@ protected:
|
||||
virtual void Receive(uchar *Data, int Length);
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
virtual ~cTransfer();
|
||||
};
|
||||
|
||||
@ -34,7 +34,7 @@ private:
|
||||
cTransfer *transfer;
|
||||
static cDevice *receiverDevice;
|
||||
public:
|
||||
cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
~cTransferControl();
|
||||
virtual void Hide(void) {}
|
||||
static cDevice *ReceiverDevice(void) { return receiverDevice; }
|
||||
|
17
vdr.c
17
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* The project's page is at http://www.cadsoft.de/vdr
|
||||
*
|
||||
* $Id: vdr.c 1.282 2006/12/02 16:22:12 kls Exp $
|
||||
* $Id: vdr.c 1.283 2007/01/07 14:46:14 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -68,8 +68,6 @@
|
||||
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
|
||||
#define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
|
||||
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
|
||||
#define LASTCAMMENUTIMEOUT 3 // seconds to run the main loop 'fast' after a CAM menu has been closed
|
||||
// in order to react on a possible new CAM menu as soon as possible
|
||||
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
|
||||
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
|
||||
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down
|
||||
@ -501,7 +499,6 @@ int main(int argc, char *argv[])
|
||||
int PreviousChannelIndex = 0;
|
||||
time_t LastChannelChanged = time(NULL);
|
||||
time_t LastActivity = 0;
|
||||
time_t LastCamMenu = 0;
|
||||
int MaxLatencyTime = 0;
|
||||
bool ForceShutdown = false;
|
||||
bool UserShutdown = false;
|
||||
@ -851,19 +848,14 @@ int main(int argc, char *argv[])
|
||||
DeletedRecordings.Update();
|
||||
}
|
||||
// CAM control:
|
||||
if (!Menu && !cOsd::IsOpen()) {
|
||||
if (!Menu && !cOsd::IsOpen())
|
||||
Menu = CamControl();
|
||||
if (Menu)
|
||||
LastCamMenu = 0;
|
||||
else if (!LastCamMenu)
|
||||
LastCamMenu = time(NULL);
|
||||
}
|
||||
// Queued messages:
|
||||
if (!Skins.IsOpen())
|
||||
Skins.ProcessQueuedMessages();
|
||||
// User Input:
|
||||
cOsdObject *Interact = Menu ? Menu : cControl::Control();
|
||||
eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);
|
||||
eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
|
||||
if (NORMALKEY(key) != kNone) {
|
||||
EITScanner.Activity();
|
||||
LastActivity = time(NULL);
|
||||
@ -1052,9 +1044,6 @@ int main(int argc, char *argv[])
|
||||
else if (time(NULL) - LastActivity > MENUTIMEOUT)
|
||||
state = osEnd;
|
||||
}
|
||||
// TODO make the CAM menu stay open in case of automatic updates and have it return osContinue; then the following two lines can be removed again
|
||||
else if (state == osEnd && LastActivity > 1)
|
||||
LastActivity = time(NULL);
|
||||
switch (state) {
|
||||
case osPause: DELETE_MENU;
|
||||
cControl::Shutdown(); // just in case
|
||||
|
Loading…
Reference in New Issue
Block a user