Re: Making VDR run under Systemd

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi.

Here you can find an updated version of the patch:
https://github.com/glenvt18/vdr/commits/vdr-2.4.6

On Mon, Oct 11, 2021 at 04:02:43PM +0100, Richard F wrote:
> Mon, Sep 13, 2021 at 08:51:05PM +0200, Martin Wache wrote:
> > So, back to the plan with the Raspberry Pi 2B. One problem that I wanted
> > to solve is the power consumption of the DVB-T dongle when it is not in
> > use. I found https://github.com/mvp/uhubctl which can switch off power
> > on individual USB ports. I wrote a VDR shutdown script that does the
> > following:
> > 
> > sudo service lircd stop
> > uhubctl -p ... -a off
> > sudo service vdr stop
> > 
> > I did not refine the uhubctl invocation yet. The physical port could
> > vary.
> > 
> > An easy way to start up VDR could be a udev rule that would start up the
> > lircd and vdr services when the USB DVB stick is plugged in. Obviously,
> > if we power off the port during VDR shutdown, the adapter would have to
> > be plugged into a different port.
> > 
> > A simpler option for the occasional use of VDR might be to simply write
> > udev rules that will start up lircd and vdr when the DVB adapter is
> > plugged in, and shut down the services when the adapter is removed. 
> > That would be too risky if recording timers are being used.
> > 
> > I am not yet sure whether powering off the USB port makes any
> > difference, because the plastic case of the DVB stick feels slightly
> > warm to the touch even when the port is supposedly powered off. It might
> > be that some internal heat produced by the Rasberry is being dissipated
> > via the USB port. The metal frame of the USB jacks feels a bit warm too.
> > 
> > I think that I must measure the input power of the Raspberry Pi as well
> > as the voltage on the USB port when it is supposedly powered off.
> > 
> >     Marko
> 
> Powersaving for DVB receivers - try the attached patch from glenvt18
> <glenvt18@xxxxxxxxx>
> 
> This has worked for me for a few years without noticeable problems on both
> USB and PCI cards.  I'm still on V2.20 by the way.  There's a little bit of
> log chatter as it powers up/down receivers when VDR scans the EPG etc, but
> you can filter that   I reckoned it saved around 3W at the mains for a
> 2-receiver server setup when I tested it back in 2016 - which adds up over
> time.
> 
> usbhubctl is a bit of a sledgehammer !
> 
> HTH
> 
> Richard
> 

> From 656cce97750882fd945d9ba76c47cb93a74c3059 Mon Sep 17 00:00:00 2001
> From: glenvt18 <glenvt18@xxxxxxxxx>
> Date: Tue, 24 May 2016 00:39:01 +0300
> Subject: [PATCH] Device power saving feature
> 
> ---
>  config.c    |  9 ++++++
>  config.h    |  3 ++
>  device.c    | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  device.h    | 29 +++++++++++++++++++
>  dvbdevice.c | 39 +++++++++++++++++++++++++
>  dvbdevice.h |  7 +++++
>  eitscan.c   |  7 ++++-
>  menu.c      |  9 +++++-
>  vdr.c       |  6 ++++
>  9 files changed, 201 insertions(+), 4 deletions(-)
> 
> diff --git a/config.c b/config.c
> index 9c6b71e..e196353 100644
> --- a/config.c
> +++ b/config.c
> @@ -395,6 +395,9 @@ cSetup::cSetup(void)
>    PositionerSpeed = 15;
>    PositionerSwing = 650;
>    PositionerLastLon = 0;
> +  PowerdownEnabled = 0;
> +  PowerdownTimeoutM = 15;
> +  PowerdownWakeupH = 4;
>    SetSystemTime = 0;
>    TimeSource = 0;
>    TimeTransponder = 0;
> @@ -617,6 +620,9 @@ bool cSetup::Parse(const char *Name, const char *Value)
>    else if (!strcasecmp(Name, "PositionerSpeed"))     PositionerSpeed    = atoi(Value);
>    else if (!strcasecmp(Name, "PositionerSwing"))     PositionerSwing    = atoi(Value);
>    else if (!strcasecmp(Name, "PositionerLastLon"))   PositionerLastLon  = atoi(Value);
> +  else if (!strcasecmp(Name, "PowerdownEnabled"))    PowerdownEnabled   = atoi(Value);
> +  else if (!strcasecmp(Name, "PowerdownTimeoutM"))   PowerdownTimeoutM  = atoi(Value);
> +  else if (!strcasecmp(Name, "PowerdownWakeupH"))    PowerdownWakeupH   = atoi(Value);
>    else if (!strcasecmp(Name, "SetSystemTime"))       SetSystemTime      = atoi(Value);
>    else if (!strcasecmp(Name, "TimeSource"))          TimeSource         = cSource::FromString(Value);
>    else if (!strcasecmp(Name, "TimeTransponder"))     TimeTransponder    = atoi(Value);
> @@ -743,6 +749,9 @@ bool cSetup::Save(void)
>    Store("PositionerSpeed",    PositionerSpeed);
>    Store("PositionerSwing",    PositionerSwing);
>    Store("PositionerLastLon",  PositionerLastLon);
> +  Store("PowerdownEnabled",   PowerdownEnabled);
> +  Store("PowerdownTimeoutM",  PowerdownTimeoutM);
> +  Store("PowerdownWakeupH",   PowerdownWakeupH);
>    Store("SetSystemTime",      SetSystemTime);
>    Store("TimeSource",         cSource::ToString(TimeSource));
>    Store("TimeTransponder",    TimeTransponder);
> diff --git a/config.h b/config.h
> index d1bae04..dbe84bb 100644
> --- a/config.h
> +++ b/config.h
> @@ -273,6 +273,9 @@ public:
>    int PositionerSpeed;
>    int PositionerSwing;
>    int PositionerLastLon;
> +  int PowerdownEnabled;
> +  int PowerdownTimeoutM;
> +  int PowerdownWakeupH;
>    int SetSystemTime;
>    int TimeSource;
>    int TimeTransponder;
> diff --git a/device.c b/device.c
> index 4db7cc2..1c29677 100644
> --- a/device.c
> +++ b/device.c
> @@ -104,6 +104,9 @@ cDevice::cDevice(void)
>    dvbSubtitleConverter = NULL;
>    autoSelectPreferredSubtitleLanguage = true;
>  
> +  idleTimerExpires = time(NULL) + Setup.PowerdownTimeoutM * 60;
> +  wakeupTimerExpires = 0;
> +
>    for (int i = 0; i < MAXRECEIVERS; i++)
>        receiver[i] = NULL;
>  
> @@ -744,6 +747,11 @@ bool cDevice::SwitchChannel(int Direction)
>    return result;
>  }
>  
> +// While switching to a channel, the device will be kept powered up
> +// for at least this number of seconds before a receiver is attached.
> +// Must be less than cEITScanner::ScanTimeout.
> +#define CHANNEL_SWITCH_POWERUP_TIMEOUT  10
> +
>  eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
>  {
>    cStatus::MsgChannelSwitch(this, 0, LiveView);
> @@ -778,6 +786,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
>       }
>    else {
>       Channels.Lock(false);
> +     // Power up the device
> +     PowerUp(CHANNEL_SWITCH_POWERUP_TIMEOUT);
>       // Stop section handling:
>       if (sectionHandler) {
>          sectionHandler->SetStatus(false);
> @@ -843,8 +853,11 @@ int cDevice::Occupied(void) const
>  
>  void cDevice::SetOccupied(int Seconds)
>  {
> -  if (Seconds >= 0)
> +  if (Seconds >= 0) {
>       occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT);
> +     // avoid short power-down/power-up cycles
> +     SetIdleTimer(true, Seconds + 30);
> +     }
>  }
>  
>  bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
> @@ -1675,6 +1688,7 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
>              startScrambleDetection = time(NULL);
>              }
>           Start();
> +         SetIdleTimer(false);
>           return true;
>           }
>        }
> @@ -1708,8 +1722,10 @@ void cDevice::Detach(cReceiver *Receiver)
>             camSlot->Assign(NULL);
>          }
>       }
> -  if (!receiversLeft)
> +  if (!receiversLeft) {
>       Cancel(-1);
> +     SetIdleTimer(true);
> +     }
>  }
>  
>  void cDevice::DetachAll(int Pid)
> @@ -1731,6 +1747,82 @@ void cDevice::DetachAllReceivers(void)
>        Detach(receiver[i]);
>  }
>  
> +void cDevice::CheckIdle(void)
> +{
> +  if (!SupportsPowerDown() || !Setup.PowerdownEnabled)
> +     return;
> +  cMutexLock MutexLock(&mutexPowerSaving);
> +  if (idleTimerExpires != 0 && time(NULL) > idleTimerExpires) {
> +     // idle, powered up
> +     dsyslog("power saving: device %d idle timer expired", CardIndex() + 1);
> +     SetIdleTimer(false);
> +     if (Setup.PowerdownWakeupH != 0)
> +        wakeupTimerExpires = time(NULL) + Setup.PowerdownWakeupH * 3600;
> +     else
> +        dsyslog("power saving: waking up is disabled");
> +     if (!IsPoweredDown()) {
> +        dsyslog("power saving: powering device %d down", CardIndex() + 1);
> +        if (sectionHandler) {
> +           sectionHandler->SetStatus(false);
> +           sectionHandler->SetChannel(NULL);
> +           }
> +        PowerDown(true);
> +        }
> +     }
> +  if (wakeupTimerExpires != 0 && time(NULL) > wakeupTimerExpires) {
> +     // idle, powered down
> +     dsyslog("power saving: device %d wakeup timer expired", CardIndex() + 1);
> +     SetIdleTimer(true);
> +     if (IsPoweredDown()) {
> +        dsyslog("power saving: waking up device %d", CardIndex() + 1);
> +        PowerDown(false);
> +        }
> +     }
> +}
> +
> +void cDevice::SetIdleTimer(bool On, int ExtraTimeoutS)
> +{
> +  if (!SupportsPowerDown())
> +     return;
> +  cMutexLock MutexLock(&mutexPowerSaving);
> +  if (On) {
> +     int Tout = Setup.PowerdownTimeoutM * 60;
> +     time_t Now = time(NULL);
> +     if (ExtraTimeoutS > 0) {
> +        if (idleTimerExpires >= Now + ExtraTimeoutS)
> +           return;
> +        Tout = ExtraTimeoutS;
> +        }
> +     idleTimerExpires = Now + Tout;
> +     if (Setup.PowerdownEnabled)
> +        dsyslog("power saving: set device %d idle timer to %d sec", CardIndex() + 1, Tout);
> +     }
> +  else {
> +     idleTimerExpires = 0;
> +     if (Setup.PowerdownEnabled)
> +        dsyslog("power saving: disable device %d idle timer", CardIndex() + 1);
> +     }
> +  wakeupTimerExpires = 0;
> +}
> +
> +bool cDevice::PoweredDown(void)
> +{
> +  if (SupportsPowerDown() && Setup.PowerdownEnabled) {
> +     cMutexLock MutexLock(&mutexPowerSaving);
> +     return IsPoweredDown();
> +     }
> +  else
> +     return false;
> +}
> +
> +void cDevice::PowerUp(int ExtraTimeoutS)
> +{
> +  cMutexLock MutexLock(&mutexPowerSaving);
> +  SetIdleTimer(true, ExtraTimeoutS);
> +  if (SupportsPowerDown() && IsPoweredDown())
> +     PowerDown(false);
> +}
> +
>  // --- cTSBuffer -------------------------------------------------------------
>  
>  cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
> diff --git a/device.h b/device.h
> index b06d977..56c4878 100644
> --- a/device.h
> +++ b/device.h
> @@ -821,6 +821,35 @@ public:
>         ///< Detaches all receivers from this device for this pid.
>    virtual void DetachAllReceivers(void);
>         ///< Detaches all receivers from this device.
> +
> +// Power saving facilities
> +
> +private:
> +  cMutex mutexPowerSaving;
> +  time_t idleTimerExpires, wakeupTimerExpires;
> +  void PowerUp(int ExtraTimeoutS);
> +       ///< If the device is powered down, powers it up and keeps it
> +       ///< powered up for at least ExtraTimeoutS seconds (see
> +       ///< cDevice::SetIdleTimer()).
> +public:
> +  void CheckIdle(void);
> +       ///< Should be called periodically in the main loop.
> +  bool PoweredDown(void);
> +       ///< Returns true if the device is powered down "logically", that is,
> +       ///< idle tasks like EPG scanning are disabled.
> +  void SetIdleTimer(bool On, int ExtraTimeoutS = 0);
> +       ///< Starts/disables the idle timer. This timer must be started when
> +       ///< a device gets idle and must be disabled when it is receiving.
> +       ///< If ExtraTimeoutS is greater than zero and On is true, a new timer
> +       ///< won't be set, but the device will be kept powered up for at least
> +       ///< ExtraTimeoutS seconds.
> +protected:
> +  virtual bool IsPoweredDown(void) {return false;}
> +       ///< Returns true if the device is powered down "physically".
> +  virtual void PowerDown(bool On) {};
> +       ///< Actually powers the device down/up.
> +  virtual bool SupportsPowerDown() {return false;}
> +       ///< Returns true if a derived device supports power saving.
>    };
>  
>  /// Derived cDevice classes that can receive channels will have to provide
> diff --git a/dvbdevice.c b/dvbdevice.c
> index 9321f16..2a0dad1 100644
> --- a/dvbdevice.c
> +++ b/dvbdevice.c
> @@ -348,6 +348,8 @@ public:
>    const cPositioner *Positioner(void) const { return positioner; }
>    int GetSignalStrength(void) const;
>    int GetSignalQuality(void) const;
> +  bool IsPoweredDown(void) {return fd_frontend < 0;}
> +  void PowerDown(bool On);
>    };
>  
>  cMutex cDvbTuner::bondMutex;
> @@ -544,6 +546,8 @@ void cDvbTuner::ClearEventQueue(void) const
>  
>  bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
>  {
> +  if (fd_frontend < 0)
> +     return false;
>    ClearEventQueue();
>    while (1) {
>          if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
> @@ -559,6 +563,8 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
>  
>  int cDvbTuner::GetSignalStrength(void) const
>  {
> +  if (fd_frontend < 0)
> +     return -1;
>    ClearEventQueue();
>    uint16_t Signal;
>    while (1) {
> @@ -1001,6 +1007,26 @@ void cDvbTuner::Action(void)
>          }
>  }
>  
> +void cDvbTuner::PowerDown(bool On)
> +{
> +  cMutexLock MutexLock(&mutex);
> +  if (On && fd_frontend >= 0) {
> +     isyslog("dvb tuner: power-down - closing frontend %d/%d", adapter, frontend);
> +     tunerStatus = tsIdle;
> +     close(fd_frontend);
> +     fd_frontend = -1;
> +     }
> +  if (!On && fd_frontend < 0) {
> +     cString Filename = cString::sprintf("%s/%s%d/%s%d",
> +        DEV_DVB_BASE, DEV_DVB_ADAPTER, adapter, DEV_DVB_FRONTEND, frontend);
> +     isyslog("dvb tuner: power-up - opening frontend %d/%d", adapter, frontend);
> +     fd_frontend = open(Filename, O_RDWR | O_NONBLOCK);
> +     if (fd_frontend < 0)
> +        esyslog("ERROR: can't open DVB device frontend %d/%d", adapter, frontend);
> +     tunerStatus = tsIdle;
> +     }
> +}
> +
>  // --- cDvbSourceParam -------------------------------------------------------
>  
>  class cDvbSourceParam : public cSourceParam {
> @@ -1711,6 +1737,19 @@ void cDvbDevice::DetachAllReceivers(void)
>    needsDetachBondedReceivers = false;
>  }
>  
> +bool cDvbDevice::IsPoweredDown(void)
> +{
> +  if (dvbTuner)
> +     return dvbTuner->IsPoweredDown();
> +  return false;
> +}
> +
> +void cDvbDevice::PowerDown(bool On)
> +{
> +  if (dvbTuner)
> +     dvbTuner->PowerDown(On);
> +}
> +
>  // --- cDvbDeviceProbe -------------------------------------------------------
>  
>  cList<cDvbDeviceProbe> DvbDeviceProbes;
> diff --git a/dvbdevice.h b/dvbdevice.h
> index 0a148ce..a156de6 100644
> --- a/dvbdevice.h
> +++ b/dvbdevice.h
> @@ -289,6 +289,13 @@ protected:
>    virtual void CloseDvr(void);
>    virtual bool GetTSPacket(uchar *&Data);
>    virtual void DetachAllReceivers(void);
> +
> +// Power saving facilities
> +
> +protected:
> +  virtual bool IsPoweredDown(void);
> +  virtual void PowerDown(bool On);
> +  virtual bool SupportsPowerDown() {return true;}
>    };
>  
>  // A plugin that implements a DVB device derived from cDvbDevice needs to create
> diff --git a/eitscan.c b/eitscan.c
> index 77f15c6..3899e00 100644
> --- a/eitscan.c
> +++ b/eitscan.c
> @@ -142,7 +142,8 @@ void cEITScanner::Process(void)
>             bool AnyDeviceSwitched = false;
>             for (int i = 0; i < cDevice::NumDevices(); i++) {
>                 cDevice *Device = cDevice::GetDevice(i);
> -               if (Device && Device->ProvidesEIT()) {
> +               if (Device && Device->ProvidesEIT()
> +                     && (!Device->PoweredDown() || lastActivity == 0)) { // powered up or forced scan
>                    for (cScanData *ScanData = scanList->First(); ScanData; ScanData = scanList->Next(ScanData)) {
>                        const cChannel *Channel = ScanData->GetChannel();
>                        if (Channel) {
> @@ -159,6 +160,10 @@ void cEITScanner::Process(void)
>                                             }
>                                          }
>                                       //dsyslog("EIT scan: device %d  source  %-8s tp %5d", Device->DeviceNumber() + 1, *cSource::ToString(Channel->Source()), Channel->Transponder());
> +                                     if (lastActivity == 0)
> +                                        // forced scan - set idle timer for each channel switch;
> +                                        // this prevents powering down while scanning a transponder
> +                                        Device->SetIdleTimer(true, ScanTimeout + 5);
>                                       Device->SwitchChannel(Channel, false);
>                                       scanList->Del(ScanData);
>                                       AnyDeviceSwitched = true;
> diff --git a/menu.c b/menu.c
> index ae61c64..c469ab0 100644
> --- a/menu.c
> +++ b/menu.c
> @@ -3464,6 +3464,12 @@ void cMenuSetupLNB::Setup(void)
>       Add(new cMenuEditIntxItem(tr("Setup.LNB$Positioner speed (degrees/s)"), &data.PositionerSpeed, 1, 1800, 10));
>       }
>  
> +  Add(new cMenuEditBoolItem(tr("Setup.LNB$Enable power saving"), &data.PowerdownEnabled));
> +  if (data.PowerdownEnabled) {
> +     Add(new cMenuEditIntItem(tr("Setup.LNB$Power down an idle device after (min)"), &data.PowerdownTimeoutM));
> +     Add(new cMenuEditIntItem(tr("Setup.LNB$Wake up from power-down after (h)"), &data.PowerdownWakeupH));
> +     }
> +
>    SetCurrent(Get(current));
>    Display();
>  }
> @@ -3472,6 +3478,7 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
>  {
>    int oldDiSEqC = data.DiSEqC;
>    int oldUsePositioner = data.UsePositioner;
> +  int oldPowerdownEnabled = data.PowerdownEnabled;
>    bool DeviceBondingsChanged = false;
>    if (Key == kOk) {
>       cString NewDeviceBondings = satCableNumbers.ToString();
> @@ -3480,7 +3487,7 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
>       }
>    eOSState state = cMenuSetupBase::ProcessKey(Key);
>  
> -  if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner))
> +  if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner || data.PowerdownEnabled != oldPowerdownEnabled))
>       Setup();
>    else if (DeviceBondingsChanged)
>       cDvbDevice::BondDevices(data.DeviceBondings);
> diff --git a/vdr.c b/vdr.c
> index 71a72f2..c12ad2c 100644
> --- a/vdr.c
> +++ b/vdr.c
> @@ -1444,6 +1444,12 @@ int main(int argc, char *argv[])
>  
>          ReportEpgBugFixStats();
>  
> +        for (int i = 0; i < cDevice::NumDevices(); i++) {
> +           cDevice *d = cDevice::GetDevice(i);
> +           if (d)
> +              d->CheckIdle();
> +           }
> +
>          // Main thread hooks of plugins:
>          PluginManager.MainThreadHook();
>          }
> -- 
> 1.9.1

> #/bin/sh
> 
> # This script monitors if a dvb device's frontend is opened by VDR.
> # Can be used with a single tuner or with two tuners.
> # Output:
> #  1  - adapter 0 is on
> #  2  - adapter 1 is on
> #  3  - both are on
> #  -  - both are off
> #  .  - VDR is not running
> 
> # VDR executable. Change to vdr.bin for OpenELEC.
> VDR=vdr
> 
> # Uncomment this if you want to output timestamps of status changes
> PRINT_TIMESTAMPS=yes
> 
> old_status='*'
> old_stamp=$(date +%s)
> while true; do
>     vdrpid=$(pidof $VDR)
>     if [ -z "$vdrpid" ]; then
>         status='.'
>     else
>         mask=0
>         for adapter in 0 1; do
>             if lsof -p $vdrpid 2>/dev/null | grep -q "adapter$adapter/frontend"; then
>                 mask=$(($mask + $adapter + 1))
>             fi
>         done
>         if [ $mask -eq 0 ]; then
>             status='-'
>         else
>             status=$mask
>         fi
>     fi
>     stamp=$(date +%s)
>     if [ -n "$PRINT_TIMESTAMPS" -a "$old_status" != "$status" ]; then
>         printf '\n[%s]  %s>%s  +%ds\n' $(date +%H:%M:%S) "$old_status" $status $(($stamp - $old_stamp))
>         old_stamp=$stamp
>     fi
>     old_status=$status
>     printf "$status"
>     sleep 1
> done

> _______________________________________________
> vdr mailing list
> vdr@xxxxxxxxxxx
> https://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr


-- 
glenvt18

_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
https://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr




[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux