/*
 * device.h: The basic device interface
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: device.h 5.9 2026/03/02 14:03:51 kls Exp $
 */

#ifndef __DEVICE_H
#define __DEVICE_H

#include "channels.h"
#include "ci.h"
#include "dvbsubtitle.h"
#include "eit.h"
#include "filter.h"
#include "nit.h"
#include "pat.h"
#include "positioner.h"
#include "remux.h"
#include "ringbuffer.h"
#include "sdt.h"
#include "sections.h"
#include "spu.h"
#include "thread.h"
#include "tools.h"

#define MAXDEVICES         32 // the maximum number of devices in the system
#define MAXPIDHANDLES      64 // the maximum number of different PIDs per device
#define MAXRECEIVERS       16 // the maximum number of receivers per device
#define MAXVOLUME         255
#define VOLUMEDELTA       (MAXVOLUME / Setup.VolumeSteps) // used to increase/decrease the volume
#define MAXOCCUPIEDTIMEOUT 99 // max. time (in seconds) a device may be occupied

enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };

// Note that VDR itself always uses pmAudioVideo when replaying a recording!
enum ePlayMode { pmNone,           // audio/video from decoder
                 pmAudioVideo,     // audio/video from player
                 pmAudioOnly,      // audio only from player, video from decoder
                 pmAudioOnlyBlack, // audio only from player, no video (black screen)
                 pmVideoOnly,      // video only from player, audio from decoder
                 pmExtern_THIS_SHOULD_BE_AVOIDED
                 // external player (e.g. MPlayer), release the device
                 // WARNING: USE THIS MODE ONLY AS A LAST RESORT, IF YOU
                 // ABSOLUTELY, POSITIVELY CAN'T IMPLEMENT YOUR PLAYER
                 // THE WAY IT IS SUPPOSED TO WORK. FORCING THE DEVICE
                 // TO RELEASE ITS FILES HANDLES (OR WHATEVER RESOURCES
                 // IT MAY USE) TO ALLOW AN EXTERNAL PLAYER TO ACCESS
                 // THEM MEANS THAT SUCH A PLAYER WILL NEED TO HAVE
                 // DETAILED KNOWLEDGE ABOUT THE INTERNALS OF THE DEVICE
                 // IN USE. AS A CONSEQUENCE, YOUR PLAYER MAY NOT WORK
                 // IF A PARTICULAR VDR INSTALLATION USES A DEVICE NOT
                 // KNOWN TO YOUR PLAYER.
               };

enum eVideoDisplayFormat { vdfPanAndScan,
                           vdfLetterBox,
                           vdfCenterCutOut
                         };

enum eTrackType { ttNone,
                  ttAudio,
                  ttAudioFirst = ttAudio,
                  ttAudioLast  = ttAudioFirst + 31, // MAXAPIDS - 1
                  ttDolby,
                  ttDolbyFirst = ttDolby,
                  ttDolbyLast  = ttDolbyFirst + 15, // MAXDPIDS - 1
                  ttSubtitle,
                  ttSubtitleFirst = ttSubtitle,
                  ttSubtitleLast  = ttSubtitleFirst + 31, // MAXSPIDS - 1
                  ttMaxTrackTypes
                };

#define IS_AUDIO_TRACK(t) (ttAudioFirst <= (t) && (t) <= ttAudioLast)
#define IS_DOLBY_TRACK(t) (ttDolbyFirst <= (t) && (t) <= ttDolbyLast)
#define IS_SUBTITLE_TRACK(t) (ttSubtitleFirst <= (t) && (t) <= ttSubtitleLast)

struct tTrackId {
  uint16_t id;                  // The PES packet id or the PID.
  char language[MAXLANGCODE2];  // something like either "eng" or "deu+eng"
  char description[32];         // something like "Dolby Digital 5.1"
  };

class cPlayer;
class cReceiver;
class cLiveSubtitle;

class cDeviceHook : public cListObject {
public:
  cDeviceHook(void);
          ///< Creates a new device hook object.
          ///< Do not delete this object - it will be automatically deleted when the
          ///< program ends.
  virtual bool DeviceProvidesTransponder(const cDevice *Device, const cChannel *Channel) const;
          ///< Returns true if the given Device can provide the given Channel's transponder.
  virtual bool DeviceProvidesEIT(const cDevice *Device) const;
          ///< Returns true if the given Device can provide EIT data.
  };

/// The cDevice class is the base from which actual devices can be derived.

#define DTV_STAT_VALID_NONE      0x0000
#define DTV_STAT_VALID_STRENGTH  0x0001
#define DTV_STAT_VALID_CNR       0x0002
#define DTV_STAT_VALID_BERPRE    0x0004
#define DTV_STAT_VALID_BERPOST   0x0008
#define DTV_STAT_VALID_PER       0x0010
#define DTV_STAT_VALID_STATUS    0x0020

#define DTV_STAT_HAS_NONE        0x0000
#define DTV_STAT_HAS_SIGNAL      0x0001
#define DTV_STAT_HAS_CARRIER     0x0002
#define DTV_STAT_HAS_VITERBI     0x0004
#define DTV_STAT_HAS_SYNC        0x0008
#define DTV_STAT_HAS_LOCK        0x0010

class cDevice : public cThread {
  friend class cLiveSubtitle;
  friend class cDeviceHook;
  friend class cReceiver;
private:
  static int numDevices;
  static int useDevice;
  static cDevice *device[MAXDEVICES];
  static cDevice *primaryDevice;
public:
  static int NumDevices(void) { return numDevices; }
         ///< Returns the total number of devices.
  static bool WaitForAllDevicesReady(int Timeout = 0);
         ///< Waits until all devices have become ready, or the given Timeout
         ///< (seconds) has expired. While waiting, the Ready() function of each
         ///< device is called in turn, until they all return true.
         ///< Returns true if all devices have become ready within the given
         ///< timeout.
  static void SetUseDevice(int n);
         ///< Sets the 'useDevice' flag of the given device.
         ///< If this function is not called before initializing, all devices
         ///< will be used.
  static bool UseDevice(int n) { return useDevice == 0 || (useDevice & (1 << n)) != 0; }
         ///< Tells whether the device with the given card index shall be used in
         ///< this instance of VDR.
  static bool SetPrimaryDevice(int n);
         ///< Sets the primary device to 'n'.
         ///< n must be in the range 1...numDevices.
         ///< Returns true if this was possible.
  static cDevice *PrimaryDevice(void) { return primaryDevice; }
         ///< Returns the primary device.
  static cDevice *ActualDevice(void);
         ///< Returns the actual receiving device in case of Transfer Mode, or the
         ///< primary device otherwise.
  static cDevice *GetDevice(int Index);
         ///< Gets the device with the given Index.
         ///< Index must be in the range 0..numDevices-1.
         ///< Returns a pointer to the device, or NULL if the Index was invalid.
  static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView, bool Query = false);
         ///< 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. 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().
         ///< If Query is true, no actual CAM assignments or receiver detachments will
         ///< be done, so that this function can be called without any side effects
         ///< in order to just determine whether a device is available for the given
         ///< Channel.
         ///< See also ProvidesChannel().
  static cDevice *GetDeviceForTransponder(const cChannel *Channel, int Priority);
         ///< Returns a device that is not currently "occupied" and can be tuned to
         ///< the transponder of the given Channel, without disturbing any receiver
         ///< at priorities higher or equal to Priority.
         ///< If no such device is currently available, NULL will be returned.
         ///< Devices recording (Device->Priority(true) >= LIVEPRIORITY) will not be returned,
         ///< even if Priority >= LIVEPRIORITY. Such higher priorities are only used to
         ///< override occupied.
  static void Shutdown(void);
         ///< Closes down all devices.
         ///< Must be called at the end of the program.
private:
  static int nextCardIndex;
  int cardIndex;
protected:
  cDevice(void);
  virtual ~cDevice() override;
  virtual bool Ready(void);
         ///< Returns true if this device is ready. Devices with conditional
         ///< access hardware may need some time until they are up and running.
         ///< This function is called in a loop at startup until all devices
         ///< are ready (see WaitForAllDevicesReady()).
  static int NextCardIndex(int n = 0);
         ///< Calculates the next card index.
         ///< Each device in a given machine must have a unique card index, which
         ///< will be used to identify the device for assigning Ca parameters and
         ///< deciding whether to actually use that device in this particular
         ///< instance of VDR. Every time a new cDevice is created, it will be
         ///< given the current nextCardIndex, and then nextCardIndex will be
         ///< automatically incremented by 1. A derived class can determine whether
         ///< a given device shall be used by checking UseDevice(NextCardIndex()).
         ///< If a device is skipped, or if there are possible device indexes left
         ///< after a derived class has set up all its devices, NextCardIndex(n)
         ///< must be called, where n is the number of card indexes to skip.
  virtual void MakePrimaryDevice(bool On);
         ///< Informs a device that it will be the primary device. If there is
         ///< anything the device needs to set up when it becomes the primary
         ///< device (On = true) or to shut down when it no longer is the primary
         ///< device (On = false), it should do so in this function.
         ///< A derived class must call the MakePrimaryDevice() function of its
         ///< base class.
  virtual bool IsBonded(void) const { return false; }
         ///< Returns true if this device is bonded to an other device.
         ///< Only implemented by cDvbDevice and used in GetDeviceForTransponder().
         ///< May be dropped in a future version, if a better solution is found.
         ///< Do not use otherwise!
public:
  bool IsPrimaryDevice(bool CheckDecoder = true) const { return this == primaryDevice && (!CheckDecoder || HasDecoder()); }
  int CardIndex(void) const { return cardIndex; }
         ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
  int DeviceNumber(void) const;
         ///< Returns the number of this device (0 ... numDevices - 1).
  virtual cString DeviceType(void) const;
         ///< Returns a string identifying the type of this device (like "DVB-S").
         ///< If this device can receive different delivery systems, the returned
         ///< string shall be that of the currently used system.
         ///< The length of the returned string should not exceed 6 characters.
         ///< The default implementation returns an empty string.
  virtual cString DeviceName(void) const;
         ///< Returns a string identifying the name of this device.
         ///< The default implementation returns an empty string.
  virtual bool HasDecoder(void) const;
         ///< Tells whether this device has an MPEG decoder.
  virtual bool AvoidRecording(void) const { return false; }
         ///< Returns true if this device should only be used for recording
         ///< if no other device is available.

// Device hooks

private:
  static cList<cDeviceHook> deviceHooks;
protected:
  bool DeviceHooksProvidesTransponder(const cChannel *Channel) const;
  bool DeviceHooksProvidesEIT(void) const;

// SPU facilities

private:
  cLiveSubtitle *liveSubtitle;
  cDvbSubtitleConverter *dvbSubtitleConverter;
public:
  virtual cSpuDecoder *GetSpuDecoder(void);
         ///< Returns a pointer to the device's SPU decoder (or NULL, if this
         ///< device doesn't have an SPU decoder).

// Channel facilities

private:
  mutable cMutex mutexChannel;
  time_t occupiedFrom;
  time_t occupiedTimeout;
  int occupiedPriority;
protected:
  static int currentChannel;
public:
  virtual bool ProvidesSource(int Source) const;
         ///< Returns true if this device can provide the given source.
  virtual bool ProvidesTransponder(const cChannel *Channel) const;
         ///< 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.
  virtual bool ProvidesChannel(const cChannel *Channel, int Priority = IDLEPRIORITY, bool *NeedsDetachReceivers = NULL) const;
         ///< Returns true if this device can provide the given channel.
         ///< In case the device has cReceivers attached to it, Priority is used to
         ///< decide whether the caller's request can be honored.
         ///< The special Priority value IDLEPRIORITY will tell the caller whether this device
         ///< is principally able to provide the given Channel, regardless of any
         ///< attached cReceivers.
         ///< If NeedsDetachReceivers is given, the resulting value in it will tell the
         ///< caller whether or not it will have to detach any currently attached
         ///< receivers from this device before calling SwitchChannel. Note
         ///< that the return value in NeedsDetachReceivers is only meaningful if the
         ///< function itself actually returns true.
         ///< The default implementation always returns false, so a derived cDevice
         ///< class that can provide channels must implement this function.
  virtual bool ProvidesEIT(void) const;
         ///< Returns true if this device provides EIT data and thus wants to be tuned
         ///< to the channels it can receive regularly to update the data.
         ///< The default implementation returns false.
  virtual int NumProvidedSystems(void) const;
         ///< Returns the number of individual "delivery systems" this device provides.
         ///< The default implementation returns 0, so any derived class that can
         ///< actually provide channels must implement this function.
         ///< The result of this function is used when selecting a device, in order
         ///< to avoid devices that provide more than one system.
  virtual const cPositioner *Positioner(void) const;
         ///< Returns a pointer to the positioner (if any) this device has used to
         ///< move the satellite dish to the requested position (only applies to DVB-S
         ///< devices). If no positioner is involved, or this is not a DVB-S device,
         ///< NULL will be returned.
  virtual bool SignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL, int *Status = NULL) const;
         ///< Returns statistics about the currently received signal (if available).
         ///< Strength is the signal strength in dBm (typical range -100dBm...0dBm).
         ///< Cnr is the carrier to noise ratio in dB (typical range 0dB...40dB).
         ///< BerPre is the bit error rate before the forward error correction (FEC).
         ///< BerPost is the bit error rate after the forward error correction (FEC).
         ///< Per is the block error rate after the forward error correction (FEC).
         ///< Status is the masked frontend status (signal/carrier/viterbi/sync/lock).
         ///< Typical range for BerPre, BerPost and Per is 0...1.
         ///< If any of the given pointers is not NULL, the value of the respective signal
         ///< statistic is returned in it. Upon return, Valid holds a combination of
         ///< DTV_STAT_VALID_* flags, indicating which of the returned values are actually
         ///< valid. If the flag for a particular parameter in Valid is 0, the returned
         ///< value is undefined. It depends on the device which of these parameters
         ///< (if any) are available.
         ///< Returns true if any of the requested parameters is valid.
         ///< If false is returned, the value in Valid is undefined.
  virtual int SignalStrength(void) const;
         ///< Returns the "strength" of the currently received signal.
         ///< This is a value in the range 0 (no signal at all) through
         ///< 100 (best possible signal). A value of -1 indicates that this
         ///< device has no concept of a "signal strength".
  virtual int SignalQuality(void) const;
         ///< Returns the "quality" of the currently received signal.
         ///< This is a value in the range 0 (worst quality) through
         ///< 100 (best possible quality). A value of -1 indicates that this
         ///< device has no concept of a "signal quality".
  virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
         ///< Returns a pointer to the currently tuned transponder.
         ///< This is not one of the channels in the global cChannels list, but rather
         ///< a local copy. The result may be NULL if the device is not tuned to any
         ///< transponder.
  virtual bool IsTunedToTransponder(const cChannel *Channel) const;
         ///< Returns true if this device is currently tuned to the given Channel's
         ///< transponder.
  virtual bool MaySwitchTransponder(const cChannel *Channel) const;
         ///< Returns true if it is ok to switch to the Channel's transponder on this
         ///< device, without disturbing any other activities. If an occupied timeout
         ///< has been set for this device, and that timeout has not yet expired,
         ///< this function returns false.
  virtual void SetPowerSaveMode(bool On);
         ///< Puts the device into power save mode, if applicable.
         ///< If On is true, power save mode shall be activated, if it is false,
         ///< normal operating mode shall be restored.
         ///< The default implementation does nothing.
  void SetPowerSaveIfUnused(void);
         ///< Sets this device into a power save mode if it is not currently used and
         ///< has implemented SetPowerSaveMode().
  bool SwitchChannel(const cChannel *Channel, bool LiveView);
         ///< Switches the device to the given Channel, initiating transfer mode
         ///< if necessary.
  static bool SwitchChannel(int Direction);
         ///< Switches the primary device to the next available channel in the given
         ///< Direction (only the sign of Direction is evaluated, positive values
         ///< switch to higher channel numbers).
private:
  eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
         ///< Sets the device to the given channel (general setup).
protected:
  virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
         ///< Sets the device to the given channel (actual physical setup).
public:
  static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
         ///< Returns the number of the current channel on the primary device.
  static void SetCurrentChannel(int ChannelNumber) { currentChannel = ChannelNumber; }
         ///< Sets the number of the current channel on the primary device, without
         ///< actually switching to it. This can be used to correct the current
         ///< channel number while replaying.
  void ForceTransferMode(void);
         ///< Forces the device into transfermode for the current channel.
  int Occupied(int Priority = MINPRIORITY) const;
         ///< Returns the number of seconds this device is still occupied for
         ///< with a priority >= Priority.
  void SetOccupied(int Seconds, int Priority = MINPRIORITY, time_t From = 0);
         ///< Sets the occupied timeout for this device to the given number of
         ///< Seconds, This can be used to tune a device to a particular transponder
         ///< and make sure it will stay there for a certain amount of time, for
         ///< instance to collect EPG data. This function shall only be called
         ///< after the device has been successfully tuned to the requested transponder.
         ///< Seconds will be silently limited to MAXOCCUPIEDTIMEOUT. Values less than
         ///< 0 will be silently ignored.
         ///< The timeout is counted from the given From time (by default the current time).
         ///< Calling this function several times with the same From time will set the
         ///< priority to the maximum of the given values.
         ///< Priority() may return a value >= Priority until the timeout.
  virtual bool HasLock(int TimeoutMs = 0) const;
         ///< 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.
         ///< If TimeoutMs is not zero, waits for the given number of milliseconds
         ///< before returning false.
  virtual bool HasProgramme(void) const;
         ///< Returns true if the device is currently showing any programme to
         ///< the user, either through replaying or live.

// PID handle facilities

private:
  mutable cMutex mutexPids;
  virtual void Action(void);
protected:
  enum ePidType { ptAudio, ptVideo, ptPcr, ptTeletext, ptDolby, ptOther };
  class cPidHandle {
  public:
    int pid;
    int streamType;
    int handle;
    int used;
    cPidHandle(void) { pid = streamType = used = 0; handle = -1; }
    };
  cPidHandle pidHandles[MAXPIDHANDLES];
  bool HasPid(int Pid) const;
         ///< Returns true if this device is currently receiving the given PID.
  bool AddPid(int Pid, ePidType PidType = ptOther, int StreamType = 0);
         ///< Adds a PID to the set of PIDs this device shall receive.
  void DelPid(int Pid, ePidType PidType = ptOther);
         ///< Deletes a PID from the set of PIDs this device shall receive.
  virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
         ///< Does the actual PID setting on this device.
         ///< On indicates whether the PID shall be added or deleted.
         ///< Handle->handle can be used by the device to store information it
         ///< needs to receive this PID (for instance a file handle).
         ///< Handle->used indicates how many receivers are using this PID.
         ///< Type indicates some special types of PIDs, which the device may
         ///< need to set in a specific way.
public:
  void DelLivePids(void);
         ///< Deletes the live viewing PIDs.

// Section filter facilities

private:
  cSectionHandler *sectionHandler;
  cEitFilter *eitFilter;
  cPatFilter *patFilter;
  cSdtFilter *sdtFilter;
  cNitFilter *nitFilter;
protected:
  void StartSectionHandler(void);
       ///< A derived device that provides section data must call
       ///< this function (typically in its constructor) to actually set
       ///< up the section handler.
  void StopSectionHandler(void);
       ///< A device that has called StartSectionHandler() must call this
       ///< function (typically in its destructor) to stop the section
       ///< handler.
public:
  virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
       ///< Opens a file handle for the given filter data.
       ///< A derived device that provides section data must
       ///< implement this function.
  virtual int ReadFilter(int Handle, void *Buffer, size_t Length);
       ///< Reads data from a handle for the given filter.
       ///< A derived class need not implement this function, because this
       ///< is done by the default implementation.
  virtual void CloseFilter(int Handle);
       ///< Closes a file handle that has previously been opened
       ///< by OpenFilter(). If this is as simple as calling close(Handle),
       ///< a derived class need not implement this function, because this
       ///< is done by the default implementation.
  void AttachFilter(cFilter *Filter);
       ///< Attaches the given filter to this device.
  void Detach(cFilter *Filter);
       ///< Detaches the given filter from this device.
  const cSdtFilter *SdtFilter(void) const { return sdtFilter; }
  cSectionHandler *SectionHandler(void) const { return sectionHandler; }

// Common Interface facilities:

private:
  cCamSlot *camSlot;
  void ReleaseCamSlot(void);
         ///< Releases the CAM slot if it is currently not used.
public:
  virtual bool HasCi(void);
         ///< Returns true if this device has a Common Interface.
  virtual bool HasInternalCam(void) { return false; }
         ///< Returns true if this device handles encrypted channels itself
         ///< without VDR assistance. This can be e.g. if the device is a
         ///< client that gets the stream from another VDR instance that has
         ///< already decrypted the stream. In this case ProvidesChannel()
         ///< shall check whether the channel can be decrypted.
  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

public:
  virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
         ///< Grabs the currently visible screen image.
         ///< Size is the size of the returned data block.
         ///< If Jpeg is true it will write a JPEG file. Otherwise a PNM file will be written.
         ///< Quality is the compression factor for JPEG. 1 will create a very blocky
         ///< and small image, 70..80 will yield reasonable quality images while keeping the
         ///< image file size around 50 KB for a full frame. The default will create a big
         ///< but very high quality image.
         ///< SizeX is the number of horizontal pixels in the frame (default is the current screen width).
         ///< SizeY is the number of vertical pixels in the frame (default is the current screen height).
         ///< Returns a pointer to the grabbed image data, or NULL in case of an error.
         ///< The caller takes ownership of the returned memory and must free() it once it isn't needed any more.
  bool GrabImageFile(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
         ///< Calls GrabImage() and stores the resulting image in a file with the given name.
         ///< Returns true if all went well.
         ///< The caller is responsible for making sure that the given file name
         ///< doesn't lead to overwriting any important other file.

// Video format facilities

public:
  virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat);
         ///< Sets the video display format to the given one (only useful
         ///< if this device has an MPEG decoder).
         ///< A derived class must first call the base class function!
         ///< NOTE: this is only for SD devices. HD devices shall implement their
         ///< own setup menu with the necessary parameters for controlling output.
  virtual void SetVideoFormat(bool VideoFormat16_9);
         ///< Sets the output video format to either 16:9 or 4:3 (only useful
         ///< if this device has an MPEG decoder).
         ///< NOTE: this is only for SD devices. HD devices shall implement their
         ///< own setup menu with the necessary parameters for controlling output.
  virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect);
         ///< Returns the Width, Height and VideoAspect ratio of the currently
         ///< displayed video material. Width and Height are given in pixel
         ///< (e.g. 720x576) and VideoAspect is e.g. 1.33333 for a 4:3 broadcast,
         ///< or 1.77778 for 16:9.
         ///< The default implementation returns 0 for Width and Height
         ///< and 1.0 for VideoAspect.
  virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect);
         ///< Returns the Width, Height and PixelAspect ratio the OSD should use
         ///< to best fit the resolution of the output device. If PixelAspect
         ///< is not 1.0, the OSD may take this as a hint to scale its
         ///< graphics in a way that, e.g., a circle will actually
         ///< show up as a circle on the screen, and not as an ellipse.
         ///< Values greater than 1.0 mean to stretch the graphics in the
         ///< vertical direction (or shrink it in the horizontal direction,
         ///< depending on which dimension shall be fixed). Values less than
         ///< 1.0 work the other way round. Note that the OSD is not guaranteed
         ///< to actually use this hint.

// Track facilities

private:
  tTrackId availableTracks[ttMaxTrackTypes];
  eTrackType currentAudioTrack;
  eTrackType currentSubtitleTrack;
  cMutex mutexCurrentAudioTrack;
  cMutex mutexCurrentSubtitleTrack;
  int currentAudioTrackMissingCount;
  bool autoSelectPreferredSubtitleLanguage;
  bool keepTracks;
  int pre_1_3_19_PrivateStream;
protected:
  virtual void SetAudioTrackDevice(eTrackType Type);
       ///< Sets the current audio track to the given value.
  virtual void SetSubtitleTrackDevice(eTrackType Type);
       ///< Sets the current subtitle track to the given value.
public:
  void ClrAvailableTracks(bool DescriptionsOnly = false, bool IdsOnly = false);
       ///< Clears the list of currently available tracks. If DescriptionsOnly
       ///< is true, only the track descriptions will be cleared. With IdsOnly
       ///< set to true only the ids will be cleared. IdsOnly is only taken
       ///< into account if DescriptionsOnly is false.
  bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = NULL);
       ///< Sets the track of the given Type and Index to the given values.
       ///< Type must be one of the basic eTrackType values, like ttAudio or ttDolby.
       ///< Index tells which track of the given basic type is meant.
       ///< If Id is 0 any existing id will be left untouched and only the
       ///< given Language and Description will be set.
       ///< Returns true if the track was set correctly, false otherwise.
  const tTrackId *GetTrack(eTrackType Type);
       ///< Returns a pointer to the given track id, or NULL if Type is not
       ///< less than ttMaxTrackTypes.
  int NumTracks(eTrackType FirstTrack, eTrackType LastTrack) const;
       ///< Returns the number of tracks in the given range that are currently
       ///< available.
  int NumAudioTracks(void) const;
       ///< Returns the number of audio tracks that are currently available.
       ///< This is just for information, to quickly find out whether there
       ///< is more than one audio track.
  int NumSubtitleTracks(void) const;
       ///< Returns the number of subtitle tracks that are currently available.
  eTrackType GetCurrentAudioTrack(void) const { return currentAudioTrack; }
  bool SetCurrentAudioTrack(eTrackType Type);
       ///< Sets the current audio track to the given Type.
       ///< Returns true if Type is a valid audio track, false otherwise.
  eTrackType GetCurrentSubtitleTrack(void) const { return currentSubtitleTrack; }
  bool SetCurrentSubtitleTrack(eTrackType Type, bool Manual = false);
       ///< Sets the current subtitle track to the given Type.
       ///< IF Manual is true, no automatic preferred subtitle language selection
       ///< will be done for the rest of the current replay session, or until
       ///< the channel is changed.
       ///< Returns true if Type is a valid subtitle track, false otherwise.
  void SetTempSubtitles(void);
       ///< Temporarily turn on subtitles after a fast rewind during reply.
  void EnsureAudioTrack(bool Force = false);
       ///< Makes sure an audio track is selected that is actually available.
       ///< If Force is true, the language and Dolby Digital settings will
       ///< be verified even if the current audio track is available.
  void EnsureSubtitleTrack(void);
       ///< Makes sure one of the preferred language subtitle tracks is selected.
       ///< Only has an effect if Setup.DisplaySubtitles is on.
  void SetKeepTracks(bool KeepTracks) { keepTracks = KeepTracks; }
       ///< Controls whether the current audio and subtitle track settings shall
       ///< be kept as they currently are, or if they shall be automatically
       ///< adjusted. This is used when pausing live video.

// Audio facilities

private:
  bool mute;
  int volume;
protected:
  virtual int GetAudioChannelDevice(void);
       ///< Gets the current audio channel, which is stereo (0), mono left (1) or
       ///< mono right (2).
  virtual void SetAudioChannelDevice(int AudioChannel);
       ///< Sets the audio channel to stereo (0), mono left (1) or mono right (2).
  virtual void SetVolumeDevice(int Volume);
       ///< Sets the audio volume on this device (Volume = 0...255).
  virtual void SetDigitalAudioDevice(bool On);
       ///< Tells the output device that the current audio track is Dolby Digital.
       ///< Only used by the original "full featured" DVB cards - do not use for new
       ///< developments!
public:
  bool IsMute(void) const { return mute; }
  bool ToggleMute(void);
       ///< Turns the volume off or on and returns the new mute state.
  int GetAudioChannel(void);
       ///< Gets the current audio channel, which is stereo (0), mono left (1) or
       ///< mono right (2).
  void SetAudioChannel(int AudioChannel);
       ///< Sets the audio channel to stereo (0), mono left (1) or mono right (2).
       ///< Any other values will be silently ignored.
  void SetVolume(int Volume, bool Absolute = false);
       ///< Sets the volume to the given value, either absolutely or relative to
       ///< the current volume.
  static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???

// Player facilities

private:
  cPlayer *player;
  cPatPmtParser patPmtParser;
  cTsToPes tsToPesVideo;
  cTsToPes tsToPesAudio;
  cTsToPes tsToPesSubtitle;
  bool isPlayingVideo;
protected:
  const cPatPmtParser *PatPmtParser(void) const { return &patPmtParser; }
       ///< Returns a pointer to the patPmtParser, so that a derived device
       ///< can use the stream information from it.
  virtual bool CanReplay(void) const;
       ///< Returns true if this device can currently start a replay session.
  virtual bool SetPlayMode(ePlayMode PlayMode);
       ///< Sets the device into the given play mode.
       ///< Returns true if the operation was successful.
  virtual int PlayVideo(const uchar *Data, int Length);
       ///< Plays the given data block as video.
       ///< Data points to exactly one complete PES packet of the given Length.
       ///< PlayVideo() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< Returns the number of bytes actually taken from Data, or -1
       ///< in case of an error.
  virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
       ///< Plays the given data block as audio.
       ///< Data points to exactly one complete PES packet of the given Length.
       ///< Id indicates the type of audio data this packet holds.
       ///< PlayAudio() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< Returns the number of bytes actually taken from Data, or -1
       ///< in case of an error.
  virtual int PlaySubtitle(const uchar *Data, int Length);
       ///< Plays the given data block as a subtitle.
       ///< Data points to exactly one complete PES packet of the given Length.
       ///< PlaySubtitle() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< Returns the number of bytes actually taken from Data, or -1
       ///< in case of an error.
  virtual int PlayPesPacket(const uchar *Data, int Length, bool VideoOnly = false);
       ///< Plays the single PES packet in Data with the given Length.
       ///< If VideoOnly is true, only the video will be displayed,
       ///< which is necessary for trick modes like 'fast forward'.
       ///< Data must point to one single, complete PES packet.
  virtual int PlayTsVideo(const uchar *Data, int Length);
       ///< Plays the given data block as video.
       ///< Data points to exactly one complete TS packet of the given Length
       ///< (which is always TS_SIZE).
       ///< PlayTsVideo() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< The default implementation collects all incoming TS payload belonging
       ///< to one PES packet and calls PlayVideo() with the resulting packet.
  virtual int PlayTsAudio(const uchar *Data, int Length);
       ///< Plays the given data block as audio.
       ///< Data points to exactly one complete TS packet of the given Length
       ///< (which is always TS_SIZE).
       ///< PlayTsAudio() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< The default implementation collects all incoming TS payload belonging
       ///< to one PES packet and calls PlayAudio() with the resulting packet.
  virtual int PlayTsSubtitle(const uchar *Data, int Length);
       ///< Plays the given data block as a subtitle.
       ///< Data points to exactly one complete TS packet of the given Length
       ///< (which is always TS_SIZE).
       ///< PlayTsSubtitle() shall process the packet either as a whole (returning
       ///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
       ///< The default implementation collects all incoming TS payload belonging
       ///< to one PES packet and displays the resulting subtitle via the OSD.
public:
  virtual int64_t GetSTC(void);
       ///< Gets the current System Time Counter, which can be used to
       ///< synchronize audio, video and subtitles. If this device is able to
       ///< replay, it must provide an STC.
       ///< The value returned doesn't need to be an actual "clock" value,
       ///< it is sufficient if it holds the PTS (Presentation Time Stamp) of
       ///< the most recently presented frame. A proper value must be returned
       ///< in normal replay mode as well as in any trick modes (like slow motion,
       ///< fast forward/rewind).
       ///< Only the lower 32 bit of this value are actually used, since some
       ///< devices can't handle the msb correctly.
  virtual bool IsPlayingVideo(void) const { return isPlayingVideo; }
       ///< Returns true if the currently attached player has delivered
       ///< any video packets.
  virtual cRect CanScaleVideo(const cRect &Rect, int Alignment = taCenter) { return cRect::Null; }
       ///< Asks the output device whether it can scale the currently shown video in
       ///< such a way that it fits into the given Rect, while retaining its proper
       ///< aspect ratio. If the scaled video doesn't exactly fit into Rect, Alignment
       ///< is used to determine how to align the actual rectangle with the requested
       ///< one. The actual rectangle can be smaller, larger or the same size as the
       ///< given Rect, and its location may differ, depending on the capabilities of
       ///< the output device, which may not be able to display a scaled video at
       ///< arbitrary sizes and locations. The device shall, however, do its best to
       ///< match the requested Rect as closely as possible, preferring a size and
       ///< location that fits completely into the requested Rect if possible.
       ///< Returns the rectangle that can actually be used when scaling the video.
       ///< A skin plugin using this function should rearrange its content according
       ///< to the rectangle returned from calling this function, and should especially
       ///< be prepared for cases where the returned rectangle is way off the requested
       ///< Rect, or even Null. In such cases, the skin may want to fall back to
       ///< working with full screen video.
       ///< The coordinates of Rect are in the range of the width and height returned
       ///< by GetOsdSize().
       ///< If this device can't scale the video, a Null rectangle is returned (this
       ///< is also the default implementation).
  virtual void ScaleVideo(const cRect &Rect = cRect::Null) {}
       ///< Scales the currently shown video in such a way that it fits into the given
       ///< Rect. Rect should be one retrieved through a previous call to
       ///< CanScaleVideo() (otherwise results may be undefined).
       ///< Even if video output is scaled, the functions GetVideoSize() and
       ///< GetOsdSize() must still return the same values as if in full screen mode!
       ///< If this device can't scale the video, nothing happens.
       ///< To restore full screen video, call this function with a Null rectangle.
  virtual bool HasIBPTrickSpeed(void) { return false; }
       ///< Returns true if this device can handle all frames in 'fast forward'
       ///< trick speeds.
  virtual void TrickSpeed(int Speed, bool Forward);
       ///< Sets the device into a mode where replay is done slower.
       ///< Every single frame shall then be displayed the given number of
       ///< times. Forward is true if replay is done in the normal (forward)
       ///< direction, false if it is done reverse.
       ///< The cDvbPlayer uses the following values for the various speeds:
       ///<                   1x   2x   3x
       ///< Fast Forward       6    3    1
       ///< Fast Reverse       6    3    1
       ///< Slow Forward       8    4    2
       ///< Slow Reverse      63   48   24
  virtual void Clear(void);
       ///< Clears all video and audio data from the device.
       ///< A derived class must call the base class function to make sure
       ///< all registered cAudio objects are notified.
  virtual void Play(void);
       ///< Sets the device into play mode (after a previous trick
       ///< mode).
  virtual void Freeze(void);
       ///< Puts the device into "freeze frame" mode.
  virtual void Mute(void);
       ///< Turns off audio while replaying.
       ///< A derived class must call the base class function to make sure
       ///< all registered cAudio objects are notified.
  virtual void StillPicture(const uchar *Data, int Length);
       ///< Displays the given I-frame as a still picture.
       ///< Data points either to a series of TS (first byte is 0x47) or PES (first byte
       ///< is 0x00) data of the given Length. The default implementation
       ///< converts TS to PES and calls itself again, allowing a derived class
       ///< to display PES if it can't handle TS directly.
  virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
       ///< Returns true if the device itself or any of the file handles in
       ///< Poller is ready for further action.
       ///< If TimeoutMs is not zero, the device will wait up to the given number
       ///< of milliseconds before returning in case it can't accept any data.
  virtual bool Flush(int TimeoutMs = 0);
       ///< Returns true if the device's output buffers are empty, i. e. any
       ///< data which was buffered so far has been processed.
       ///< If TimeoutMs is not zero, the device will wait up to the given
       ///< number of milliseconds before returning in case there is still
       ///< data in the buffers.
  virtual int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
       ///< Plays all valid PES packets in Data with the given Length.
       ///< If Data is NULL any leftover data from a previous call will be
       ///< discarded. If VideoOnly is true, only the video will be displayed,
       ///< which is necessary for trick modes like 'fast forward'.
       ///< Data should point to a sequence of complete PES packets. If the
       ///< last packet in Data is not complete, it will be copied and combined
       ///< to a complete packet with data from the next call to PlayPes().
       ///< That way any functions called from within PlayPes() will be
       ///< guaranteed to always receive complete PES packets.
  virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false);
       ///< Plays the given TS packet.
       ///< If VideoOnly is true, only the video will be displayed,
       ///< which is necessary for trick modes like 'fast forward'.
       ///< Data points to a single TS packet, Length is always TS_SIZE (the total
       ///< size of a single TS packet).
       ///< If Data is NULL any leftover data from a previous call will be
       ///< discarded.
       ///< A derived device can reimplement this function to handle the
       ///< TS packets itself. Any packets the derived function can't handle
       ///< must be sent to the base class function. This applies especially
       ///< to the PAT/PMT packets.
       ///< Returns -1 in case of error, otherwise the number of actually
       ///< processed bytes is returned.
       ///< PlayTs() shall process the TS packets either as a whole (returning
       ///< TS_SIZE) or not at all, returning 0 or -1 and setting 'errno' accordingly).
  bool Replaying(void) const;
       ///< Returns true if we are currently replaying.
  bool Transferring(void) const;
       ///< Returns true if we are currently in Transfer Mode.
  void StopReplay(void);
       ///< Stops the current replay session (if any).
  bool AttachPlayer(cPlayer *Player);
       ///< Attaches the given player to this device.
  void Detach(cPlayer *Player);
       ///< Detaches the given player from this device.

// Receiver facilities

private:
  mutable cMutex mutexReceiver;
  cReceiver *receiver[MAXRECEIVERS];
public:
  int Priority(bool IgnoreOccupied = false) const;
      ///< Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),
      ///< or IDLEPRIORITY if no receiver is currently active.
      ///< If IgnoreOccupied is true, a priority set with SetOccupied() is ignored.
protected:
  virtual bool OpenDvr(void);
      ///< Opens the DVR of this device and prepares it to deliver a Transport
      ///< Stream for use in a cReceiver.
  virtual void CloseDvr(void);
      ///< Shuts down the DVR.
  virtual bool GetTSPacket(uchar *&Data);
      ///< Gets exactly one TS packet from the DVR of this device and returns
      ///< a pointer to it in Data. Only the first 188 bytes (TS_SIZE) Data
      ///< points to are valid and may be accessed. If there is currently no
      ///< new data available, Data will be set to NULL. The function returns
      ///< false in case of a non recoverable error, otherwise it returns true,
      ///< even if Data is NULL.
public:
  bool Receiving(bool Dummy = false) const;
       ///< Returns true if we are currently receiving. The parameter has no meaning (for backwards compatibility only).
  bool AttachReceiver(cReceiver *Receiver);
       ///< Attaches the given receiver to this device.
  void Detach(cReceiver *Receiver, bool ReleaseCam = true);
       ///< Detaches the given receiver from this device.
       ///< If ReleaseCam is true, the CAM slot will be released if it
       ///< is no longer used. Otherwise the caller must call ReleaseCamSlot() after
       ///< calling this function.
  void DetachAll(int Pid);
       ///< Detaches all receivers from this device for this pid.
  virtual void DetachAllReceivers(void);
       ///< Detaches all receivers from this device.
  };

/// Derived cDevice classes that can receive channels will have to provide
/// Transport Stream (TS) packets one at a time. cTSBuffer implements a
/// simple buffer that allows the device to read a larger amount of data
/// from the driver with each call to Read(), thus avoiding the overhead
/// of getting each TS packet separately from the driver. It also makes
/// sure the returned data points to a TS packet and automatically
/// re-synchronizes after broken packets.

class cTSBuffer : public cThread {
private:
  int f;
  int deviceNumber;
  int delivered;
  cRingBufferLinear *ringBuffer;
  virtual void Action(void) override;
public:
  cTSBuffer(int File, int Size, int DeviceNumber);
  virtual ~cTSBuffer() override;
  uchar *Get(int *Available = NULL, bool CheckAvailable = false);
     ///< Returns a pointer to the first TS packet in the buffer. If Available is given,
     ///< it will return the total number of consecutive bytes pointed to in the buffer.
     ///< It is guaranteed that the returned pointer points to a TS_SYNC_BYTE and that
     ///< there are at least TS_SIZE bytes in the buffer. Otherwise NULL will be
     ///< returned and the value in Available (if given) is undefined.
     ///< Each call to Get() returns a pointer to the next TS packet in the buffer.
     ///< If CheckAvailable is true, the buffer will be checked whether it contains
     ///< at least TS_SIZE bytes before trying to get any data from it. Otherwise, if
     ///< the buffer is empty, this function will wait a little while for the buffer
     ///< to be filled again.
  void Skip(int Count);
     ///< If after a call to Get() more or less than TS_SIZE of the available data
     ///< has been processed, a call to Skip() with the number of processed bytes
     ///< will disable the automatic incrementing of the data pointer as described
     ///< in Get() and skip the given number of bytes instead. Count may be 0 if the
     ///< caller wants the previous TS packet to be delivered again in the next call
     ///< to Get().
  };

#endif //__DEVICE_H
