mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
VDR developer version 2.1.1 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.1.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.0.0-2.1.1.diff MD5 checksums: b17f9838bb8ddee9620f838fea7a171d vdr-2.1.1.tar.bz2 8b8ca593885c380cd370e6d19a5b16a1 vdr-2.0.0-2.1.1.diff WARNING: ======== This is a *developer* version. Even though *I* use it in my productive environment, I strongly recommend that you only use it under controlled conditions and for testing and debugging. The main focus of this version is on adding basic support for positioners to control steerable satellite dishes. Manually controlling the dish position and storing individual positions will follow later. The fixes contained in this version will be released in a stable version 2.0.3 later, if there are no problems. From the HISTORY file: - Fixed initializing cDevice::keepTracks. - Fixed an endless loop in cTextWrapper::Set() in case the given Width is smaller than one character (reported by Stefan Braun). - Removed all "modified since version 1.6" markers from PLUGINS.html. - Added definitions for older DVB API versions, back until 5.0 (based on a patch from Udo Richter). - Changed cThread::SetIOPriority() from "best effort class" to "idle class" in order to improve overall performance when an editing process is running (thanks to Jochen Dolze). - Fixed handling '/' and '~' in recording file names in case DirectoryEncoding is used (thanks to Lars Hanisch). - Changed the sign of the satellite position value in cSource to reflect the standard of western values being negative. The new member function cSource::Position() can be used to retrieve the orbital position of a satellite. - Fixed multiple occurrences of the same directory in the recordings list in case there are directories that only differ in non-alphanumeric characters (was broken by "Fixed selecting the last replayed recording in the Recordings menu in case there are folders and plain recordings with names that differ only in non-alphanumeric characters" in version 1.7.36). - Fixed displaying the frame number when setting an editing mark (thanks to Thomas Günther). - Fixed no longer generating any editing marks if the edited recording results in just one single sequence (reported by Halim Sahin). - Fixed an error message when parsing SCR values in diseqc.conf. - Fixed an unexpected RCS version tag in the newplugin script. - Fixed an endless loop in the DrawEllipse() functions for very small ellipses (reported by Stefan Braun). - Fixed a crash in the LCARS skin's main menu in case there is no current channel (reported by Dominique Dumont). - Added basic support for positioners to control steerable satellite dishes (based on a patch from Seppo Ingalsuo and Ales Jurik). + Supports GotoN (aka "DiSEqC 1.2") and GotoX (aka "USALS"). + The new DiSEqC command code 'P' can be used to instruct a positioner to move the dish to the required satellite position. When a 'P' code is processed, further execution of the remaining DiSEqC sequence (if any) is postponed until the positioner has reached the new satellite position. + The new special source value of "S360E" can be used in diseqc.conf to indicate that an entry using a positioner can move the dish to any requested position within its range. Think of it as "full circle". + The devices a particular cDiseqc or cScr applies to are now stored directly in each cDiseqc or cScr, respectively. + A plugin can implement a custom positioner control (see PLUGINS.html, section "Positioners"). + The new function cSkinDisplayChannel::SetPositioner() can be implemented by skins to show the user a progress display when the dish is being moved. The default implementation calls SetMessage() with a string indicating the new position the dish is being moved to. The LCARS skin shows a progress bar indicating the movement of the dish. + The new parameters "Site latitude", "Site longitude", "Positioner speed", and "Positioner swing" in the "Setup/LNB" menu can be used to configure the necessary values for a steerable dish. + The cDvbTuner now has a new status tsPositioning, in which it waits until a steerable dish has reached its target position. Parsing SI data is paused until the target position has been reached. - The LCARS skin now shows the source value of the current channel in its channel display. - Fixed asserting free disk space in the cutter. - No longer trying to delete old recordings in AssertFreeDiskSpace() if the given Priority is less than 1. - Fixed handling LIRC events in case repeated events are lost. - Fixed a possible crash when shutting down VDR while subtitles are being displayed (reported by Ville Skyttä). - cDevice::IsPrimaryDevice() now also checks whether the primary device actually has a decoder and returns false otherwise. This should improve device allocation on systems that are only used as a receiver and don't actually display anything. - Increased the value of MAXRETRIES to 20 to reduce the probability of disturbances in transfer mode. - All bonded devices (except for the master) now turn off their LNB power completely to avoid problems when receiving vertically polarized transponders (suggested by Manfred Völkel and Oliver Endriss). - Reverted the change from version 1.5.7 that made all logging go to LOG_ERR (thanks to Christopher Reimer). - Added Begin/EndSegmentTransfer() to the EPG handler interface (thanks to Jörg Wendel). - The code for distributing recordings over several video directories is now deprecated and disabled by default. You can re-enable this feature by removing the comment sign ('//') from the beginning of the line //#define DEPRECATED_DISTRIBUTED_VIDEODIR // Code enclosed with this macro is ... in the file videodir.c. Note, though, that this can only be a temporary workaround. This feature will be completely removed in one of the next developer versions. Distributing the video directory over several disks was a useful feature in times when disks were still relatively small, but it also caused serious problems in case one of the disks failed. Nowadays hard disks come in sizes measured in terabytes, and tools like "mhddfs" can be used to combine several disks to form one large volume. A recommended method for a relatively safe disk setup in a VDR system is to use two 1TB (or larger) disks and use them as a RAID-1 (mirrored). That way, if one disk fails, you can replace it without data loss.
262 lines
10 KiB
C++
262 lines
10 KiB
C++
/*
|
|
* ci.h: Common Interface
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: ci.h 3.0 2012/02/29 10:24:27 kls Exp $
|
|
*/
|
|
|
|
#ifndef __CI_H
|
|
#define __CI_H
|
|
|
|
#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 cCamSlot;
|
|
friend class cCiMMI;
|
|
private:
|
|
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
|
|
cCiMMI *mmi;
|
|
cMutex *mutex;
|
|
bool selectable;
|
|
char *titleText;
|
|
char *subTitleText;
|
|
char *bottomText;
|
|
char *entries[MAX_CIMENU_ENTRIES];
|
|
int numEntries;
|
|
bool AddEntry(char *s);
|
|
cCiMenu(cCiMMI *MMI, bool Selectable);
|
|
public:
|
|
~cCiMenu();
|
|
const char *TitleText(void) { return titleText; }
|
|
const char *SubTitleText(void) { return subTitleText; }
|
|
const char *BottomText(void) { return bottomText; }
|
|
const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
|
|
int NumEntries(void) { return numEntries; }
|
|
bool Selectable(void) { return selectable; }
|
|
void Select(int Index);
|
|
void Cancel(void);
|
|
void Abort(void);
|
|
bool HasUpdate(void);
|
|
};
|
|
|
|
class cCiEnquiry {
|
|
friend class cCamSlot;
|
|
friend class cCiMMI;
|
|
private:
|
|
cCiMMI *mmi;
|
|
cMutex *mutex;
|
|
char *text;
|
|
bool blind;
|
|
int expectedLength;
|
|
cCiEnquiry(cCiMMI *MMI);
|
|
public:
|
|
~cCiEnquiry();
|
|
const char *Text(void) { return text; }
|
|
bool Blind(void) { return blind; }
|
|
int ExpectedLength(void) { return expectedLength; }
|
|
void Reply(const char *s);
|
|
void Cancel(void);
|
|
void Abort(void);
|
|
};
|
|
|
|
class cDevice;
|
|
class cCamSlot;
|
|
|
|
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:
|
|
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 cTPDU;
|
|
class cCiTransportConnection;
|
|
class cCiSession;
|
|
class cCiCaProgramData;
|
|
|
|
class cCamSlot : public cListObject {
|
|
friend class cCiAdapter;
|
|
friend class cCiTransportConnection;
|
|
private:
|
|
cMutex mutex;
|
|
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;
|
|
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:
|
|
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 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(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.
|
|
int Priority(void);
|
|
///< Returns the priority if the device this slot is currently assigned
|
|
///< to, or IDLEPRIORITY 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 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.
|
|
void SetPid(int Pid, bool Active);
|
|
///< 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.
|
|
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 initial 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.
|
|
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
|