diff --git a/ci.c b/ci.c index 9a4a829..88b50e7 100644 --- a/ci.c +++ b/ci.c @@ -1571,6 +1571,8 @@ cCamSlot::cCamSlot(cCiAdapter *CiAdapter) cCamSlot::~cCamSlot() { + if (ciAdapter && ciAdapter->assignedDevice) + ciAdapter->assignedDevice->SetCamSlot(NULL); CamSlots.Del(this, false); DeleteAllConnections(); } diff --git a/ci.h b/ci.h index 74e0270..818ea29 100644 --- a/ci.h +++ b/ci.h @@ -72,6 +72,7 @@ public: }; class cDevice; +class cTSBufferBase; class cCamSlot; enum eModuleStatus { msNone, msReset, msPresent, msReady }; @@ -115,6 +116,13 @@ public: ///< 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. +#define EXTERNALCI_PATCH + virtual cTSBufferBase *GetTSBuffer(int FdDvr) { return NULL; } + ///< Derived classes can return a special TS buffer with features + ///< like rerouting the stream through an external ci. + ///< The caller must delete the buffer. + virtual bool SetIdle(bool Idle, bool TestOnly) { return false; } + virtual bool IsIdle(void) const { return false; } }; class cTPDU; diff --git a/device.c b/device.c index ff51a11..dfdc081 100644 --- a/device.c +++ b/device.c @@ -69,12 +69,22 @@ int cDevice::currentChannel = 1; cDevice *cDevice::device[MAXDEVICES] = { NULL }; cDevice *cDevice::primaryDevice = NULL; cList cDevice::deviceHooks; - -cDevice::cDevice(void) +cDevice *cDevice::nextParentDevice = NULL; + +cDevice::cDevice(cDevice *ParentDevice) :patPmtParser(true) -{ - cardIndex = nextCardIndex++; - dsyslog("new device number %d", CardIndex() + 1); +,isIdle(false) +,parentDevice(ParentDevice) +,subDevice(NULL) +{ + if (!ParentDevice) + parentDevice = nextParentDevice; + cDevice::nextParentDevice = NULL; + if (parentDevice) + cardIndex = parentDevice->cardIndex; + else + cardIndex = nextCardIndex++; + dsyslog("new %sdevice number %d", parentDevice ? "sub-" : "", CardIndex() + 1); SetDescription("receiver on device %d", CardIndex() + 1); @@ -106,10 +116,14 @@ cDevice::cDevice(void) for (int i = 0; i < MAXRECEIVERS; i++) receiver[i] = NULL; - if (numDevices < MAXDEVICES) - device[numDevices++] = this; + if (!parentDevice) { + if (numDevices < MAXDEVICES) + device[numDevices++] = this; + else + esyslog("ERROR: too many devices or \"dynamite\"-unpatched device creator!"); + } else - esyslog("ERROR: too many devices!"); + parentDevice->subDevice = this; } cDevice::~cDevice() @@ -118,6 +132,29 @@ cDevice::~cDevice() DetachAllReceivers(); delete liveSubtitle; delete dvbSubtitleConverter; + if (parentDevice && (parentDevice->subDevice == this)) + parentDevice->subDevice = NULL; +} + +bool cDevice::SetIdle(bool Idle) +{ + if (parentDevice) + return parentDevice->SetIdle(Idle); + if (isIdle == Idle) + return true; + if (Receiving(false)) + return false; + if (Idle) { + Detach(player); + DetachAllReceivers(); + } + if (!SetIdleDevice(Idle, true)) + return false; + isIdle = Idle; + if (SetIdleDevice(Idle, false)) + return true; + isIdle = !Idle; + return false; } bool cDevice::WaitForAllDevicesReady(int Timeout) @@ -156,6 +193,8 @@ int cDevice::NextCardIndex(int n) int cDevice::DeviceNumber(void) const { + if (parentDevice) + return parentDevice->DeviceNumber(); for (int i = 0; i < numDevices; i++) { if (device[i] == this) return i; @@ -356,6 +395,8 @@ bool cDevice::HasCi(void) void cDevice::SetCamSlot(cCamSlot *CamSlot) { + if (parentDevice) + return parentDevice->SetCamSlot(CamSlot); camSlot = CamSlot; } @@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) void cDevice::StartSectionHandler(void) { + if (parentDevice) { + parentDevice->StartSectionHandler(); + return; + } if (!sectionHandler) { sectionHandler = new cSectionHandler(this); AttachFilter(eitFilter = new cEitFilter); @@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) void cDevice::StopSectionHandler(void) { + if (parentDevice) { + parentDevice->StopSectionHandler(); + return; + } if (sectionHandler) { delete nitFilter; delete sdtFilter; @@ -610,12 +659,17 @@ void cDevice::CloseFilter(int Handle) void cDevice::AttachFilter(cFilter *Filter) { + if (parentDevice) + return parentDevice->AttachFilter(Filter); + SetIdle(false); if (sectionHandler) sectionHandler->Attach(Filter); } void cDevice::Detach(cFilter *Filter) { + if (parentDevice) + return parentDevice->Detach(Filter); if (sectionHandler) sectionHandler->Detach(Filter); } @@ -777,6 +831,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) sectionHandler->SetStatus(false); sectionHandler->SetChannel(NULL); } + SetIdle(false); // Tell the camSlot about the channel switch and add all PIDs of this // channel to it, for possible later decryption: if (camSlot) @@ -823,19 +878,27 @@ void cDevice::ForceTransferMode(void) { if (!cTransferControl::ReceiverDevice()) { cChannel *Channel = Channels.GetByNumber(CurrentChannel()); - if (Channel) + if (Channel) { + SetIdle(false); SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode + } } } int cDevice::Occupied(void) const { + if (parentDevice) + return parentDevice->Occupied(); int Seconds = occupiedTimeout - time(NULL); return Seconds > 0 ? Seconds : 0; } void cDevice::SetOccupied(int Seconds) { + if (parentDevice) { + parentDevice->SetOccupied(Seconds); + return; + } if (Seconds >= 0) occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT); } @@ -1216,7 +1279,10 @@ bool cDevice::Transferring(void) const bool cDevice::AttachPlayer(cPlayer *Player) { + if (parentDevice) + return parentDevice->AttachPlayer(Player); if (CanReplay()) { + SetIdle(false); if (player) Detach(player); DELETENULL(liveSubtitle); @@ -1235,6 +1301,8 @@ bool cDevice::AttachPlayer(cPlayer *Player) void cDevice::Detach(cPlayer *Player) { + if (parentDevice) + return parentDevice->Detach(Player); if (Player && player == Player) { cPlayer *p = player; player = NULL; // avoids recursive calls to Detach() @@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) void cDevice::StopReplay(void) { + if (parentDevice) + return parentDevice->StopReplay(); if (player) { Detach(player); if (IsPrimaryDevice()) @@ -1534,6 +1604,8 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly) int cDevice::Priority(void) const { + if (parentDevice) + return parentDevice->Priority(); int priority = IDLEPRIORITY; if (IsPrimaryDevice() && !Replaying() && HasProgramme()) priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing @@ -1552,6 +1624,8 @@ bool cDevice::Ready(void) bool cDevice::Receiving(bool Dummy) const { + if (parentDevice) + return parentDevice->Receiving(Dummy); cMutexLock MutexLock(&mutexReceiver); for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i]) @@ -1632,10 +1706,13 @@ bool cDevice::GetTSPacket(uchar *&Data) bool cDevice::AttachReceiver(cReceiver *Receiver) { + if (parentDevice) + return parentDevice->AttachReceiver(Receiver); if (!Receiver) return false; if (Receiver->device == this) return true; + SetIdle(false); // activate the following line if you need it - actually the driver should be fixed! //#define WAIT_FOR_TUNER_LOCK #ifdef WAIT_FOR_TUNER_LOCK @@ -1674,6 +1751,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) void cDevice::Detach(cReceiver *Receiver) { + if (parentDevice) + return parentDevice->Detach(Receiver); if (!Receiver || Receiver->device != this) return; bool receiversLeft = false; @@ -1699,6 +1778,8 @@ void cDevice::Detach(cReceiver *Receiver) void cDevice::DetachAll(int Pid) { + if (parentDevice) + return parentDevice->DetachAll(Pid); if (Pid) { cMutexLock MutexLock(&mutexReceiver); for (int i = 0; i < MAXRECEIVERS; i++) { @@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) } return NULL; } + +// --- cDynamicDeviceProbe ------------------------------------------------------- + +cList DynamicDeviceProbes; + +cList cDynamicDeviceProbe::commandQueue; + +void cDynamicDeviceProbe::QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath) +{ + if (DevPath) + commandQueue.Add(new cDynamicDeviceProbeItem(Cmd, new cString(DevPath))); +} + +cDynamicDeviceProbe::cDynamicDeviceProbe(void) +{ + DynamicDeviceProbes.Add(this); +} + +cDynamicDeviceProbe::~cDynamicDeviceProbe() +{ + DynamicDeviceProbes.Del(this, false); +} diff --git a/device.h b/device.h index fd010d4..ed3b1da 100644 --- a/device.h +++ b/device.h @@ -24,6 +24,7 @@ #include "spu.h" #include "thread.h" #include "tools.h" +#include #define MAXDEVICES 16 // the maximum number of devices in the system #define MAXPIDHANDLES 64 // the maximum number of different PIDs per device @@ -169,7 +170,6 @@ private: static int nextCardIndex; int cardIndex; protected: - cDevice(void); virtual ~cDevice(); virtual bool Ready(void); ///< Returns true if this device is ready. Devices with conditional @@ -196,9 +196,6 @@ protected: ///< A derived class must call the MakePrimaryDevice() function of its ///< base class. public: - bool IsPrimaryDevice(void) const { return this == primaryDevice; } - 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). virtual cString DeviceType(void) const; @@ -338,6 +335,7 @@ public: ///< Returns true if the device is currently showing any programme to ///< the user, either through replaying or live. + virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) {return false;} // PID handle facilities private: @@ -423,9 +421,6 @@ public: ///< 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 @@ -586,9 +581,6 @@ private: 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); @@ -802,6 +794,38 @@ public: ///< Detaches all receivers from this device for this pid. virtual void DetachAllReceivers(void); ///< Detaches all receivers from this device. + +// --- dynamite subdevice patch start --- + friend class cDynamicDevice; +private: + static cDevice *nextParentDevice; + ///< Holds the parent device for the next subdevice + ///< so the dynamite-plugin can work with unpatched plugins + bool isIdle; +protected: + cDevice *parentDevice; + cDevice *subDevice; + cDevice(cDevice *ParentDevice = NULL); + const cPatPmtParser *PatPmtParser(void) const { if (parentDevice) return parentDevice->PatPmtParser(); return &patPmtParser; } + ///< Returns a pointer to the patPmtParser, so that a derived device + ///< can use the stream information from it. +public: + bool IsPrimaryDevice(void) const { if (parentDevice) return parentDevice->IsPrimaryDevice(); return this == primaryDevice; } + int CardIndex(void) const { if (parentDevice) return parentDevice->cardIndex; return cardIndex; } + ///< Returns the card index of this device (0 ... MAXDEVICES - 1). + cCamSlot *CamSlot(void) const { if (parentDevice) return parentDevice->CamSlot(); return camSlot; } + ///< Returns the CAM slot that is currently used with this device, + ///< or NULL if no CAM slot is in use. + bool IsSubDevice(void) const { return (parentDevice != NULL); } + bool HasSubDevice(void) const { return (subDevice != NULL); } + cDevice *SubDevice(void) const { return subDevice; } + bool IsIdle(void) const { if (parentDevice) return parentDevice->IsIdle(); return isIdle; } + bool SetIdle(bool Idle); + virtual bool SetIdleDevice(bool Idle, bool TestOnly) { return false; } + ///< Called by SetIdle + ///< if TestOnly, don't do anything, just return, if the device + ///< can be set to the new idle state + // --- dynamite subdevice patch end --- }; /// Derived cDevice classes that can receive channels will have to provide @@ -812,7 +836,14 @@ public: /// sure the returned data points to a TS packet and automatically /// re-synchronizes after broken packets. -class cTSBuffer : public cThread { +class cTSBufferBase { +public: + cTSBufferBase() {} + virtual ~cTSBufferBase() {} + virtual uchar *Get(void) = 0; + }; + +class cTSBuffer : public cTSBufferBase, public cThread { private: int f; int cardIndex; @@ -821,8 +852,51 @@ private: virtual void Action(void); public: cTSBuffer(int File, int Size, int CardIndex); - ~cTSBuffer(); - uchar *Get(void); + virtual ~cTSBuffer(); + virtual uchar *Get(void); + }; + +/// A plugin that want to create devices handled by the dynamite-plugin needs to create +/// a cDynamicDeviceProbe derived object on the heap in order to have its Probe() +/// function called, where it can actually create the appropriate device. +/// The cDynamicDeviceProbe object must be created in the plugin's constructor, +/// and deleted in its destructor. +/// The "DevPath" hasn't to be a physical device or a path in the filesystem. +/// It can be any string a plugin may react on. + +#define __DYNAMIC_DEVICE_PROBE + +enum eDynamicDeviceProbeCommand { ddpcAttach, ddpcDetach, ddpcService }; + +class cDynamicDeviceProbe : public cListObject { + friend class cDynamicDevice; +private: + class cDynamicDeviceProbeItem : public cListObject { + public: + eDynamicDeviceProbeCommand cmd; + cString *devpath; + cDynamicDeviceProbeItem(eDynamicDeviceProbeCommand Cmd, cString *DevPath):cmd(Cmd),devpath(DevPath) {} + virtual ~cDynamicDeviceProbeItem() { if (devpath) delete devpath; } + }; + static cList commandQueue; + ///< A list where all attach/detach commands are queued + ///< so they can be processed in the MainThreadHook of + ///< the dynamite plugin. +public: + static void QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath); + ///< Plugins which support cDynamicDeviceProbe must use this function + ///< to queue the devices they normally create in their Initialize method. + ///< These devices are created as subdevices in the Start-method of the dynamite-plugin. + cDynamicDeviceProbe(void); + virtual ~cDynamicDeviceProbe(); + virtual cDevice *Attach(cDevice *ParentDevice, const char *DevPath) = 0; + ///< Probes for a device at the given device-path like /dev/dvb/adapter0/frontend0 + ///< or /dev/video0 etc. and creates the appropriate + ///< object derived from cDevice if applicable. + ///< Returns the device that has been created or NULL if not. + ///< The dynamite-plugin will delete the device if it is detached. }; +extern cList DynamicDeviceProbes; + #endif //__DEVICE_H diff --git a/dvbci.c b/dvbci.c index 5289bbd..baa70bc 100644 --- a/dvbci.c +++ b/dvbci.c @@ -10,41 +10,70 @@ #include "dvbci.h" #include #include -#include "device.h" +#include "dvbdevice.h" // --- cDvbCiAdapter --------------------------------------------------------- -cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd) +cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) { 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()); + adapter = Adapter; + frontend = Frontend; + idle = false; + GetNumCamSlots(Device, Fd, this); + Start(); } cDvbCiAdapter::~cDvbCiAdapter() { Cancel(3); + if (device->IsSubDevice() || device->HasSubDevice()) + CloseCa(); +} + +bool cDvbCiAdapter::OpenCa(void) +{ + if (fd >= 0) + return true; + fd = cDvbDevice::DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); + return (fd >= 0); +} + +void cDvbCiAdapter::CloseCa(void) +{ + if (fd < 0) + return; + close(fd); + fd = -1; +} + +bool cDvbCiAdapter::SetIdle(bool Idle, bool TestOnly) +{ + if ((adapter < 0) || (frontend < 0)) + return false; + if (TestOnly || (idle == Idle)) + return true; + if (Idle) + CloseCa(); + else + OpenCa(); + idle = Idle; + return true; +} + +cTSBufferBase *cDvbCiAdapter::GetTSBuffer(int FdDvr) +{ + if (device) + return new cTSBuffer(FdDvr, MEGABYTE(5), device->CardIndex() + 1); + return NULL; } int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength) { + if (idle || (fd < 0)) + return 0; if (Buffer && MaxLength > 0) { struct pollfd pfd[1]; pfd[0].fd = fd; @@ -61,6 +90,8 @@ int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength) void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length) { + if (idle || (fd < 0)) + return; 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()); @@ -69,6 +100,8 @@ void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length) bool cDvbCiAdapter::Reset(int Slot) { + if (idle || (fd < 0)) + return false; if (ioctl(fd, CA_RESET, 1 << Slot) != -1) return true; else @@ -78,6 +111,8 @@ bool cDvbCiAdapter::Reset(int Slot) eModuleStatus cDvbCiAdapter::ModuleStatus(int Slot) { + if (idle || (fd < 0)) + return msNone; ca_slot_info_t sinfo; sinfo.num = Slot; if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) { @@ -99,10 +134,60 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query) return true; } -cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) +int cDvbCiAdapter::GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter) { - // TODO check whether a CI is actually present? - if (Device) - return new cDvbCiAdapter(Device, Fd); - return NULL; + int NumSlots = -1; + if (Fd >= 0) { + ca_caps_t Caps; + if (ioctl(Fd, CA_GET_CAP, &Caps) == 0) { + if ((Caps.slot_type & CA_CI_LINK) != 0) { + NumSlots = Caps.slot_num; + if (NumSlots == 0) + esyslog("ERROR: no CAM slots found on device %d", Device->DeviceNumber()); + else if (CiAdapter != NULL) { + for (int i = 0; i < NumSlots; i++) + new cCamSlot(CiAdapter); + } + else + return NumSlots; + } + 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()); + } + return -1; +} + +cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) +{ + // don't create a ci-adapter if it's not useable + if (Device && (Fd >= 0) && (GetNumCamSlots(Device, Fd, NULL) > 0)) + return new cDvbCiAdapter(Device, Fd, Adapter, Frontend); + + if (Fd >= 0) + close(Fd); + + // try to find an external ci-adapter + for (cDvbCiAdapterProbe *cp = DvbCiAdapterProbes.First(); cp; cp = DvbCiAdapterProbes.Next(cp)) { + cDvbCiAdapter *ca = cp->Probe(Device); + if (ca) + return ca; + } + return NULL; +} + +// --- cDvbCiAdapterProbe ------------------------------------------------------- + +cList DvbCiAdapterProbes; + +cDvbCiAdapterProbe::cDvbCiAdapterProbe(void) +{ + DvbCiAdapterProbes.Add(this); +} + +cDvbCiAdapterProbe::~cDvbCiAdapterProbe() +{ + DvbCiAdapterProbes.Del(this, false); } diff --git a/dvbci.h b/dvbci.h index adbe40d..d908b2f 100644 --- a/dvbci.h +++ b/dvbci.h @@ -16,16 +16,48 @@ class cDvbCiAdapter : public cCiAdapter { private: cDevice *device; int fd; + int adapter; + int frontend; + bool idle; + + bool OpenCa(void); + void CloseCa(void); 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); + cDvbCiAdapter(cDevice *Device, int Fd, int Adapter = -1, int Frontend = -1); public: virtual ~cDvbCiAdapter(); - static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd); + virtual cTSBufferBase *GetTSBuffer(int FdDvr); + static int GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter); + ///< Tests if the CA device is usable for vdr. + ///< If CiAdapter is not NULL it will create the CamSlots for the given ci-adapter. + virtual bool SetIdle(bool Idle, bool TestOnly); + virtual bool IsIdle(void) const { return idle; } + static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd, int Adapter = -1, int Frontend = -1); + }; + +// A plugin that implements an external DVB ci-adapter derived from cDvbCiAdapter needs to create +// a cDvbCiAdapterProbe derived object on the heap in order to have its Probe() +// function called, where it can actually create the appropriate ci-adapter. +// The cDvbCiAdapterProbe object must be created in the plugin's constructor, +// and deleted in its destructor. +// Every plugin has to track its own list of already used device nodes. +// The Probes are always called if the base cDvbCiAdapter can't create a ci-adapter on its own. + +class cDvbCiAdapterProbe : public cListObject { +public: + cDvbCiAdapterProbe(void); + virtual ~cDvbCiAdapterProbe(); + virtual cDvbCiAdapter *Probe(cDevice *Device) = 0; + ///< Probes for a DVB ci-adapter for the given Device and creates the appropriate + ///< object derived from cDvbCiAdapter if applicable. + ///< Returns NULL if no adapter has been created. }; +extern cList DvbCiAdapterProbes; + #endif //__DVBCI_H diff --git a/dvbdevice.c b/dvbdevice.c index ab6149f..351f1ed 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -288,9 +288,10 @@ class cDvbTuner : public cThread { private: static cMutex bondMutex; enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + bool SendDiseqc; int frontendType; const cDvbDevice *device; - int fd_frontend; + mutable int fd_frontend; int adapter, frontend; uint32_t subsystemId; int tuneTimeout; @@ -301,7 +302,7 @@ private: const cScr *scr; bool lnbPowerTurnedOn; eTunerStatus tunerStatus; - cMutex mutex; + mutable cMutex mutex; cCondVar locked; cCondVar newSet; cDvbTuner *bondedTuner; @@ -311,11 +312,16 @@ private: cDvbTuner *GetBondedMaster(void); bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; } void ClearEventQueue(void) const; + dvb_diseqc_master_cmd diseqc_cmd; bool GetFrontendStatus(fe_status_t &Status) const; void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency); void ResetToneAndVoltage(void); bool SetFrontend(void); virtual void Action(void); + + mutable bool isIdle; + bool OpenFrontend(void) const; + bool CloseFrontend(void); public: cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend); virtual ~cDvbTuner(); @@ -327,9 +333,13 @@ public: uint32_t SubsystemId(void) const { return subsystemId; } bool IsTunedTo(const cChannel *Channel) const; void SetChannel(const cChannel *Channel); + bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); bool Locked(int TimeoutMs = 0); int GetSignalStrength(void) const; int GetSignalQuality(void) const; + + bool SetIdle(bool Idle); + bool IsIdle(void) const { return isIdle; } }; cMutex cDvbTuner::bondMutex; @@ -339,6 +349,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int frontendType = SYS_UNDEFINED; device = Device; fd_frontend = Fd_Frontend; + SendDiseqc=false; adapter = Adapter; frontend = Frontend; subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); @@ -351,6 +362,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int tunerStatus = tsIdle; bondedTuner = NULL; bondedMaster = false; + isIdle = false; SetDescription("tuner on frontend %d/%d", adapter, frontend); Start(); } @@ -368,6 +380,8 @@ cDvbTuner::~cDvbTuner() ExecuteDiseqc(lastDiseqc, &Frequency); } */ + if (device && device->IsSubDevice()) + CloseFrontend(); } bool cDvbTuner::Bond(cDvbTuner *Tuner) @@ -512,6 +526,8 @@ bool cDvbTuner::Locked(int TimeoutMs) void cDvbTuner::ClearEventQueue(void) const { + if (!OpenFrontend()) + return; cPoller Poller(fd_frontend); if (Poller.Poll(TUNER_POLL_TIMEOUT)) { dvb_frontend_event Event; @@ -720,8 +736,28 @@ static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTranspon return ds; } +bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) +{ + cMutexLock MutexLock(&mutex); + cDvbTransponderParameters dtp(channel.Parameters()); + + // Determine the required frontend type: + int frontendType = GetRequiredDeliverySystem(&channel, &dtp); + + if ((frontendType!=SYS_DVBS2 && frontendType!=SYS_DVBS) || SendDiseqc) + return false; + if (!OpenFrontend()) + return false; + diseqc_cmd=cmd; + SendDiseqc=true; + newSet.Broadcast(); + return true; +} + bool cDvbTuner::SetFrontend(void) { + if (!OpenFrontend()) + return false; #define MAXFRONTENDCMDS 16 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\ Frontend[CmdSeq.num].u.data = (d);\ @@ -868,10 +904,16 @@ void cDvbTuner::Action(void) bool LostLock = false; fe_status_t Status = (fe_status_t)0; while (Running()) { - fe_status_t NewStatus; - if (GetFrontendStatus(NewStatus)) - Status = NewStatus; + if (!isIdle) { + fe_status_t NewStatus; + if (GetFrontendStatus(NewStatus)) + Status = NewStatus; + } cMutexLock MutexLock(&mutex); + if (SendDiseqc) { + CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd)); + SendDiseqc=false; + } int WaitTime = 1000; switch (tunerStatus) { case tsIdle: @@ -923,6 +965,40 @@ void cDvbTuner::Action(void) } } +bool cDvbTuner::SetIdle(bool Idle) +{ + if (isIdle == Idle) + return true; + isIdle = Idle; + if (Idle) + return CloseFrontend(); + return OpenFrontend(); +} + +bool cDvbTuner::OpenFrontend(void) const +{ + if (fd_frontend >= 0) + return true; + cMutexLock MutexLock(&mutex); + fd_frontend = cDvbDevice::DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK); + if (fd_frontend < 0) + return false; + isIdle = false; + return true; +} + +bool cDvbTuner::CloseFrontend(void) +{ + if (fd_frontend < 0) + return true; + cMutexLock MutexLock(&mutex); + tunerStatus = tsIdle; + newSet.Broadcast(); + close(fd_frontend); + fd_frontend = -1; + return true; +} + // --- cDvbSourceParam ------------------------------------------------------- class cDvbSourceParam : public cSourceParam { @@ -1008,7 +1084,8 @@ const char *DeliverySystemNames[] = { NULL }; -cDvbDevice::cDvbDevice(int Adapter, int Frontend) +cDvbDevice::cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice) +:cDevice(ParentDevice) { adapter = Adapter; frontend = Frontend; @@ -1026,9 +1103,8 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) // Common Interface: - fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); - if (fd_ca >= 0) - ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); + int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); + ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend); // The DVR device (will be opened and closed as needed): @@ -1256,7 +1332,11 @@ bool cDvbDevice::BondDevices(const char *Bondings) if (d >= 0) { int ErrorDevice = 0; if (cDevice *Device1 = cDevice::GetDevice(i)) { + if (Device1->HasSubDevice()) + Device1 = Device1->SubDevice(); if (cDevice *Device2 = cDevice::GetDevice(d)) { + if (Device2->HasSubDevice()) + Device2 = Device2->SubDevice(); if (cDvbDevice *DvbDevice1 = dynamic_cast(Device1)) { if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { if (!DvbDevice1->Bond(DvbDevice2)) @@ -1290,7 +1370,10 @@ bool cDvbDevice::BondDevices(const char *Bondings) void cDvbDevice::UnBondDevices(void) { for (int i = 0; i < cDevice::NumDevices(); i++) { - if (cDvbDevice *d = dynamic_cast(cDevice::GetDevice(i))) + cDevice *dev = cDevice::GetDevice(i); + if (dev && dev->HasSubDevice()) + dev = dev->SubDevice(); + if (cDvbDevice *d = dynamic_cast(dev)) d->UnBond(); } } @@ -1344,6 +1427,26 @@ bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const return true; } +bool cDvbDevice::SetIdleDevice(bool Idle, bool TestOnly) +{ + if (TestOnly) { + if (ciAdapter) + return ciAdapter->SetIdle(Idle, true); + return true; + } + if (!dvbTuner->SetIdle(Idle)) + return false; + if (ciAdapter && !ciAdapter->SetIdle(Idle, false)) { + dvbTuner->SetIdle(!Idle); + return false; + } + if (Idle) + StopSectionHandler(); + else + StartSectionHandler(); + return true; +} + bool cDvbDevice::HasCi(void) { return ciAdapter; @@ -1513,7 +1616,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne bool cDvbDevice::ProvidesEIT(void) const { - return dvbTuner != NULL; + return !IsIdle() && (dvbTuner != NULL) && !dvbTuner->IsIdle() && ((ciAdapter == NULL) || !ciAdapter->IsIdle()); } int cDvbDevice::NumProvidedSystems(void) const @@ -1558,6 +1661,11 @@ bool cDvbDevice::HasLock(int TimeoutMs) const return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false; } +bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) +{ + return dvbTuner->SendDiseqcCmd(cmd); +} + void cDvbDevice::SetTransferModeForDolbyDigital(int Mode) { setTransferModeForDolbyDigital = Mode; @@ -1567,8 +1675,12 @@ bool cDvbDevice::OpenDvr(void) { CloseDvr(); fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); - if (fd_dvr >= 0) - tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); + if (fd_dvr >= 0) { + if (ciAdapter) + tsBuffer = ciAdapter->GetTSBuffer(fd_dvr); + if (tsBuffer == NULL) + tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); + } return fd_dvr >= 0; } diff --git a/dvbdevice.h b/dvbdevice.h index b4d07f5..339c52b 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -107,7 +107,7 @@ class cDvbTuner; /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API. class cDvbDevice : public cDevice { -protected: +public: static cString DvbName(const char *Name, int Adapter, int Frontend); static int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError = false); private: @@ -127,19 +127,20 @@ private: int deliverySystems[MAXDELIVERYSYSTEMS]; int numDeliverySystems; int numModulations; - int fd_dvr, fd_ca; + int fd_dvr; static cMutex bondMutex; cDvbDevice *bondedDevice; mutable bool needsDetachBondedReceivers; bool QueryDeliverySystems(int fd_frontend); public: - cDvbDevice(int Adapter, int Frontend); + cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice = NULL); virtual ~cDvbDevice(); int Adapter(void) const { return adapter; } int Frontend(void) const { return frontend; } virtual bool Ready(void); virtual cString DeviceType(void) const; virtual cString DeviceName(void) const; + virtual bool SetIdleDevice(bool Idle, bool TestOnly); static bool BondDevices(const char *Bondings); ///< Bonds the devices as defined in the given Bondings string. ///< A bonding is a sequence of device numbers (starting at 1), @@ -193,6 +194,7 @@ protected: virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); public: virtual bool HasLock(int TimeoutMs = 0) const; + virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); // PID handle facilities @@ -225,7 +227,7 @@ public: // Receiver facilities private: - cTSBuffer *tsBuffer; + cTSBufferBase *tsBuffer; protected: virtual bool OpenDvr(void); virtual void CloseDvr(void); diff --git a/menu.c b/menu.c index 31d25d0..8a39ccf 100644 --- a/menu.c +++ b/menu.c @@ -2323,6 +2323,19 @@ cString cMenuRecordings::DirectoryName(void) char *s = ExchangeChars(strdup(base), true); d = AddDirectory(d, s); free(s); + if (!DirectoryOk(*d, false, true)) { + cString e; + if (LockExtraVideoDirectories(true)) { + for (int i = 0; i < ExtraVideoDirectories.Size(); i++) { + e = AddDirectory(ExtraVideoDirectories.At(i), s); + if (DirectoryOk(*e, false, true)) { + UnlockExtraVideoDirectories(); + return e; + } + } + UnlockExtraVideoDirectories(); + } + } } return d; } diff --git a/recording.c b/recording.c index f43ce52..3238800 100644 --- a/recording.c +++ b/recording.c @@ -724,6 +724,7 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) resume = RESUME_NOT_INITIALIZED; titleBuffer = NULL; sortBufferName = sortBufferTime = NULL; + videoDir = VideoDirectory; fileName = NULL; name = NULL; fileSizeMB = -1; // unknown @@ -776,7 +777,7 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) info->lifetime = lifetime; } -cRecording::cRecording(const char *FileName) +cRecording::cRecording(const char *FileName, const char *VideoDir) { resume = RESUME_NOT_INITIALIZED; fileSizeMB = -1; // unknown @@ -791,11 +792,12 @@ cRecording::cRecording(const char *FileName) deleted = 0; titleBuffer = NULL; sortBufferName = sortBufferTime = NULL; + videoDir = VideoDir == NULL ? VideoDirectory : strdup(VideoDir); FileName = fileName = strdup(FileName); if (*(fileName + strlen(fileName) - 1) == '/') *(fileName + strlen(fileName) - 1) = 0; - if (strstr(FileName, VideoDirectory) == FileName) - FileName += strlen(VideoDirectory) + 1; + if (strstr(FileName, videoDir) == FileName) + FileName += strlen(videoDir) + 1; const char *p = strrchr(FileName, '/'); name = NULL; @@ -902,6 +904,8 @@ cRecording::cRecording(const char *FileName) cRecording::~cRecording() { + if (videoDir != VideoDirectory) + free((char*)videoDir); free(titleBuffer); free(sortBufferName); free(sortBufferTime); @@ -943,7 +947,7 @@ char *cRecording::SortName(void) const { char **sb = (RecordingsSortMode == rsmName) ? &sortBufferName : &sortBufferTime; if (!*sb) { - char *s = strdup(FileName() + strlen(VideoDirectory)); + char *s = strdup(FileName() + strlen(videoDir)); if (RecordingsSortMode != rsmName || Setup.AlwaysSortFoldersFirst) s = StripEpisodeName(s, RecordingsSortMode != rsmName); strreplace(s, '/', '0'); // some locales ignore '/' when sorting @@ -984,11 +988,11 @@ const char *cRecording::FileName(void) const const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS; int ch = isPesRecording ? priority : channel; int ri = isPesRecording ? lifetime : instanceId; - char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(VideoDirectory) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve + char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(videoDir) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve if (strcmp(Name, name) != 0) dsyslog("recording file name '%s' truncated to '%s'", name, Name); Name = ExchangeChars(Name, true); - fileName = strdup(cString::sprintf(fmt, VideoDirectory, Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri)); + fileName = strdup(cString::sprintf(fmt, videoDir, Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri)); free(Name); } return fileName; @@ -1256,9 +1260,14 @@ void cRecordings::Refresh(bool Foreground) ChangeState(); Unlock(); ScanVideoDir(VideoDirectory, Foreground); + if (LockExtraVideoDirectories()) { + for (int i = 0; i < ExtraVideoDirectories.Size(); i++) + ScanVideoDir(ExtraVideoDirectories.At(i), Foreground, 0, ExtraVideoDirectories.At(i)); + UnlockExtraVideoDirectories(); + } } -void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel) +void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel, const char *BaseVideoDir) { cReadDir d(DirName); struct dirent *e; @@ -1278,7 +1287,7 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev } if (S_ISDIR(st.st_mode)) { if (endswith(buffer, deleted ? DELEXT : RECEXT)) { - cRecording *r = new cRecording(buffer); + cRecording *r = new cRecording(buffer, BaseVideoDir); if (r->Name()) { r->NumFrames(); // initializes the numFrames member r->FileSizeMB(); // initializes the fileSizeMB member @@ -1293,7 +1302,7 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev delete r; } else - ScanVideoDir(buffer, Foreground, LinkLevel + Link); + ScanVideoDir(buffer, Foreground, LinkLevel + Link, BaseVideoDir); } } } diff --git a/recording.h b/recording.h index ff3119d..5f9e832 100644 --- a/recording.h +++ b/recording.h @@ -105,9 +105,10 @@ private: int priority; int lifetime; time_t deleted; + const char *videoDir; public: cRecording(cTimer *Timer, const cEvent *Event); - cRecording(const char *FileName); + cRecording(const char *FileName, const char *VideoDir = NULL); virtual ~cRecording(); time_t Start(void) const { return start; } int Priority(void) const { return priority; } @@ -164,7 +165,7 @@ private: int state; const char *UpdateFileName(void); void Refresh(bool Foreground = false); - void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0); + void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0, const char *BaseVideoDir = NULL); protected: void Action(void); public: diff --git a/svdrp.c b/svdrp.c index 8a50dae..a12ad6c 100644 --- a/svdrp.c +++ b/svdrp.c @@ -323,6 +323,14 @@ const char *HelpPages[] = { " be turned up or down, respectively. The option 'mute' will toggle the\n" " audio muting. If no option is given, the current audio volume level will\n" " be returned.", + "AXVD directory\n" + " add directory to extra video directory list", + "CXVD\n" + " clear extra video directory list", + "DXVD directory\n" + " delete directory from extra video directory list", + "LXVD\n" + " list extra video directories", "QUIT\n" " Exit vdr (SVDRP).\n" " You can also hit Ctrl-D to exit.", @@ -1620,6 +1628,62 @@ void cSVDRP::CmdVOLU(const char *Option) Reply(250, "Audio volume is %d", cDevice::CurrentVolume()); } +void cSVDRP::CmdAXVD(const char *Option) +{ + if (*Option) { + if (!LockExtraVideoDirectories(false)) { + Reply(550, "Unable to lock extra video directory list"); + return; + } + AddExtraVideoDirectory(Option); + UnlockExtraVideoDirectories(); + Reply(250, "added '%s' to extra video directory list", Option); + return; + } + Reply(501, "Missing directory name"); +} + +void cSVDRP::CmdCXVD(const char *Option) +{ + if (!LockExtraVideoDirectories(false)) { + Reply(550, "Unable to lock extra video directory list"); + return; + } + ExtraVideoDirectories.Clear(); + UnlockExtraVideoDirectories(); + Reply(250, "cleared extra video directory list"); +} + +void cSVDRP::CmdDXVD(const char *Option) +{ + if (*Option) { + if (!LockExtraVideoDirectories(false)) { + Reply(550, "Unable to lock extra video directory list"); + return; + } + DelExtraVideoDirectory(Option); + UnlockExtraVideoDirectories(); + Reply(250, "removed '%s' from extra video directory list", Option); + return; + } + Reply(501, "Missing directory name"); +} + +void cSVDRP::CmdLXVD(const char *Option) +{ + if (!LockExtraVideoDirectories(false)) { + Reply(550, "Unable to lock extra video directory list"); + return; + } + if (ExtraVideoDirectories.Size() == 0) + Reply(550, "no extra video directories in list"); + else { + for (int i = 0; i < ExtraVideoDirectories.Size(); i++) + Reply(i < ExtraVideoDirectories.Size() - 1 ? -250 : 250, "%s", ExtraVideoDirectories.At(i)); + } + UnlockExtraVideoDirectories(); +} + #define CMD(c) (strcasecmp(Cmd, c) == 0) void cSVDRP::Execute(char *Cmd) @@ -1671,6 +1735,10 @@ void cSVDRP::Execute(char *Cmd) else if (CMD("UPDR")) CmdUPDR(s); else if (CMD("UPDT")) CmdUPDT(s); else if (CMD("VOLU")) CmdVOLU(s); + else if (CMD("AXVD")) CmdAXVD(s); + else if (CMD("CXVD")) CmdCXVD(s); + else if (CMD("DXVD")) CmdDXVD(s); + else if (CMD("LXVD")) CmdLXVD(s); else if (CMD("QUIT")) Close(true); else Reply(500, "Command unrecognized: \"%s\"", Cmd); } diff --git a/svdrp.h b/svdrp.h index 5ec9bc7..74f137d 100644 --- a/svdrp.h +++ b/svdrp.h @@ -83,6 +83,10 @@ private: void CmdUPDT(const char *Option); void CmdUPDR(const char *Option); void CmdVOLU(const char *Option); + void CmdAXVD(const char *Option); + void CmdCXVD(const char *Option); + void CmdDXVD(const char *Option); + void CmdLXVD(const char *Option); void Execute(char *Cmd); public: cSVDRP(int Port); diff --git a/tools.c b/tools.c index ab46d02..c2ac5ab 100644 --- a/tools.c +++ b/tools.c @@ -375,12 +375,15 @@ int FreeDiskSpaceMB(const char *Directory, int *UsedMB) return Free; } -bool DirectoryOk(const char *DirName, bool LogErrors) +bool DirectoryOk(const char *DirName, bool LogErrors, bool JustReadOnly) { + int mode = R_OK; + if (!JustReadOnly) + mode |= W_OK | X_OK; struct stat ds; if (stat(DirName, &ds) == 0) { if (S_ISDIR(ds.st_mode)) { - if (access(DirName, R_OK | W_OK | X_OK) == 0) + if (access(DirName, mode) == 0) return true; else if (LogErrors) esyslog("ERROR: can't access %s", DirName); diff --git a/tools.h b/tools.h index d6a778e..6cf9b01 100644 --- a/tools.h +++ b/tools.h @@ -228,7 +228,7 @@ cString itoa(int n); cString AddDirectory(const char *DirName, const char *FileName); bool EntriesOnSameFileSystem(const char *File1, const char *File2); int FreeDiskSpaceMB(const char *Directory, int *UsedMB = NULL); -bool DirectoryOk(const char *DirName, bool LogErrors = false); +bool DirectoryOk(const char *DirName, bool LogErrors = false, bool JustReadOnly = false); bool MakeDirs(const char *FileName, bool IsDirectory = false); bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false); bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false, const char *IgnoreFiles[] = NULL); diff --git a/vdr.c b/vdr.c index c63eeca..bf04658 100644 --- a/vdr.c +++ b/vdr.c @@ -198,6 +198,7 @@ int main(int argc, char *argv[]) int SVDRPport = DEFAULTSVDRPPORT; const char *AudioCommand = NULL; const char *VideoDirectory = DEFAULTVIDEODIR; + const char *ExtraVideoDirectory = NULL; const char *ConfigDirectory = NULL; const char *CacheDirectory = NULL; const char *ResourceDirectory = NULL; @@ -257,6 +258,7 @@ int main(int argc, char *argv[]) { "version", no_argument, NULL, 'V' }, { "vfat", no_argument, NULL, 'v' | 0x100 }, { "video", required_argument, NULL, 'v' }, + { "extravideo", required_argument, NULL, 'v' | 0x200 }, { "watchdog", required_argument, NULL, 'w' }, { NULL, no_argument, NULL, 0 } }; @@ -444,6 +446,12 @@ int main(int argc, char *argv[]) while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/') optarg[strlen(optarg) - 1] = 0; break; + case 'v' | 0x200: + ExtraVideoDirectory = optarg; + while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/') + optarg[strlen(optarg) - 1] = 0; + AddExtraVideoDirectory(ExtraVideoDirectory); + break; case 'w': if (isnumber(optarg)) { int t = atoi(optarg); if (t >= 0) { @@ -538,6 +546,8 @@ int main(int argc, char *argv[]) " root\n" " --userdump allow coredumps if -u is given (debugging)\n" " -v DIR, --video=DIR use DIR as video directory (default: %s)\n" + " --extravideo=DIR use DIR as an additional readonly video directory\n" + " can be used multiple times\n" " -V, --version print version information and exit\n" " --vfat for backwards compatibility (same as\n" " --dirnames=250,40,1\n" diff --git a/videodir.c b/videodir.c index d39ab05..d2e0f8c 100644 --- a/videodir.c +++ b/videodir.c @@ -19,6 +19,43 @@ #include "recording.h" #include "tools.h" +cStringList ExtraVideoDirectories; +bool ExtraVideoDirectoriesIsLocked = false; +cMutex ExtraVideoDirectoriesMutex; + +bool LockExtraVideoDirectories(bool Wait) +{ + if (!Wait && ExtraVideoDirectoriesIsLocked) + return false; + ExtraVideoDirectoriesMutex.Lock(); + ExtraVideoDirectoriesIsLocked = true; + return true; +} + +void UnlockExtraVideoDirectories(void) +{ + ExtraVideoDirectoriesIsLocked = false; + ExtraVideoDirectoriesMutex.Unlock(); +} + +void AddExtraVideoDirectory(const char *Directory) +{ + if ((Directory != NULL) && (ExtraVideoDirectories.Find(Directory) < 0)) + ExtraVideoDirectories.Append(strdup(Directory)); +} + +void DelExtraVideoDirectory(const char *Directory) +{ + if (Directory != NULL) { + int index = ExtraVideoDirectories.Find(Directory); + if (index < 0) + return; + char *dir = ExtraVideoDirectories.At(index); + ExtraVideoDirectories.Remove(index); + free(dir); + } +} + const char *VideoDirectory = VIDEODIR; void SetVideoDirectory(const char *Directory) diff --git a/videodir.h b/videodir.h index a25ac31..6e7acde 100644 --- a/videodir.h +++ b/videodir.h @@ -13,6 +13,15 @@ #include #include "tools.h" +#define EXTRA_VIDEO_DIRECTORIES_PATCH 1 + +extern cStringList ExtraVideoDirectories; + +bool LockExtraVideoDirectories(bool Wait = true); +void UnlockExtraVideoDirectories(void); +void AddExtraVideoDirectory(const char *Directory); +void DelExtraVideoDirectory(const char *Directory); + extern const char *VideoDirectory; void SetVideoDirectory(const char *Directory);