A device is now always kept occupied if a timer is in VPS margin or needs the transponder

This commit is contained in:
Klaus Schmidinger 2024-03-29 21:46:50 +01:00
parent 51dca45a0c
commit 179d5b87fc
5 changed files with 62 additions and 22 deletions

View File

@ -2569,6 +2569,8 @@ Markus Ehrnsperger <markus.ehrnsperger@googlemail.com>
a decoder a decoder
for improving handling present/following data for VPS timers for improving handling present/following data for VPS timers
for making logging event status changes also show the previous status for making logging event status changes also show the previous status
for making a device always being kept occupied if a timer is in VPS margin or needs the
transponder
Werner Färber <w.faerber@gmx.de> Werner Färber <w.faerber@gmx.de>
for reporting a bug in handling the cPluginManager::Active() result when pressing for reporting a bug in handling the cPluginManager::Active() result when pressing

View File

@ -9887,7 +9887,7 @@ Video Disk Recorder Revision History
- Fixed possible duplicate component entries in the info of an ongoing recording - Fixed possible duplicate component entries in the info of an ongoing recording
(reported by Christoph Haubrich). (reported by Christoph Haubrich).
2024-03-28: 2024-03-29:
- Fixed the move assignment operator to check for self-assignment (suggested by - Fixed the move assignment operator to check for self-assignment (suggested by
Stefan Herdler). Stefan Herdler).
@ -9916,3 +9916,5 @@ Video Disk Recorder Revision History
transponder. transponder.
- If the current channel is no longer available because of a VPS timer entering the - If the current channel is no longer available because of a VPS timer entering the
VPS margin, live view now switches to the channel of that timer. VPS margin, live view now switches to the channel of that timer.
- A device is now always kept occupied if a timer is in VPS margin or needs the
transponder (thanks to Markus Ehrnsperger).

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: device.c 5.12 2024/03/28 13:02:42 kls Exp $ * $Id: device.c 5.13 2024/03/29 21:46:50 kls Exp $
*/ */
#include "device.h" #include "device.h"
@ -95,7 +95,9 @@ cDevice::cDevice(void)
camSlot = NULL; camSlot = NULL;
occupiedFrom = 0;
occupiedTimeout = 0; occupiedTimeout = 0;
occupiedPriority = MINPRIORITY;
player = NULL; player = NULL;
isPlayingVideo = false; isPlayingVideo = false;
@ -285,7 +287,11 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
if (NumUsableSlots && !HasInternalCam && !CamSlots.Get(j)->Assign(device[i], true)) if (NumUsableSlots && !HasInternalCam && !CamSlots.Get(j)->Assign(device[i], true))
continue; // CAM slot can't be used with this device continue; // CAM slot can't be used with this device
bool ndr = false; bool ndr = false;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job bool TunedToTransponder = device[i]->IsTunedToTransponder(Channel);
if (TunedToTransponder || device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job
bool OccupiedOtherTransponder = !TunedToTransponder && device[i]->Occupied();
if (OccupiedOtherTransponder)
ndr = true;
if (NumUsableSlots && !HasInternalCam) { if (NumUsableSlots && !HasInternalCam) {
if (cCamSlot *csi = device[i]->CamSlot()) { if (cCamSlot *csi = device[i]->CamSlot()) {
cCamSlot *csj = CamSlots.Get(j); cCamSlot *csj = CamSlots.Get(j);
@ -303,7 +309,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
imp <<= 1; imp |= (LiveView && NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlots.Get(j)->MasterSlotNumber()) || ndr : 0; // prefer CAMs that are known to decrypt this channel for live viewing, if we don't need to detach existing receivers imp <<= 1; imp |= (LiveView && NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlots.Get(j)->MasterSlotNumber()) || ndr : 0; // prefer CAMs that are known to decrypt this channel for live viewing, if we don't need to detach existing receivers
imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
imp <<= 1; imp |= !device[i]->Receiving() && (device[i] != cTransferControl::ReceiverDevice() || device[i]->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode imp <<= 1; imp |= !device[i]->Receiving() && (device[i] != cTransferControl::ReceiverDevice() || device[i]->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving imp <<= 1; imp |= device[i]->Receiving() || OccupiedOtherTransponder; // avoid devices that are receiving
imp <<= 5; imp |= GetClippedNumProvidedSystems(5, device[i]) - 1; // avoid cards which support multiple delivery systems imp <<= 5; imp |= GetClippedNumProvidedSystems(5, device[i]) - 1; // avoid cards which support multiple delivery systems
imp <<= 8; imp |= device[i]->Priority() - IDLEPRIORITY; // use the device with the lowest priority (- IDLEPRIORITY to assure that values -100..99 can be used) imp <<= 8; imp |= device[i]->Priority() - IDLEPRIORITY; // use the device with the lowest priority (- IDLEPRIORITY to assure that values -100..99 can be used)
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
@ -321,6 +327,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
if (NumUsableSlots && !HasInternalCam) if (NumUsableSlots && !HasInternalCam)
s = CamSlots.Get(j); s = CamSlots.Get(j);
} }
//dsyslog("device %d provides channel %d prio %d ndr %d imp %.8X", device[i]->DeviceNumber() + 1, Channel->Number(), Priority, ndr, imp);
} }
} }
if (!NumUsableSlots) if (!NumUsableSlots)
@ -427,7 +434,8 @@ cDevice *cDevice::GetDeviceForTransponder(const cChannel *Channel, int Priority)
if (d->ProvidesTransponder(Channel)) { if (d->ProvidesTransponder(Channel)) {
if (d->MaySwitchTransponder(Channel)) if (d->MaySwitchTransponder(Channel))
return d; // this device may switch to the transponder without disturbing any receiver or live view return d; // this device may switch to the transponder without disturbing any receiver or live view
else if (!d->Occupied() && !d->IsBonded()) { // MaySwitchTransponder() implicitly calls Occupied() else if (!d->Occupied(Priority) && !d->IsBonded() && d->Priority(true) < LIVEPRIORITY) { // MaySwitchTransponder() implicitly calls Occupied()
// we select only devices with priority < LIVEPRIORITY, so device can be switched without impact on recordings or live viewing
if (d->Priority() < Priority && (!Device || d->Priority() < Device->Priority())) if (d->Priority() < Priority && (!Device || d->Priority() < Device->Priority()))
Device = d; // use this one only if no other with less impact can be found Device = d; // use this one only if no other with less impact can be found
} }
@ -801,7 +809,7 @@ bool cDevice::IsTunedToTransponder(const cChannel *Channel) const
bool cDevice::MaySwitchTransponder(const cChannel *Channel) const bool cDevice::MaySwitchTransponder(const cChannel *Channel) const
{ {
return time(NULL) > occupiedTimeout && !Receiving() && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid); return !Occupied() && !Receiving() && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);
} }
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView) bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
@ -959,16 +967,27 @@ void cDevice::ForceTransferMode(void)
} }
} }
int cDevice::Occupied(void) const int cDevice::Occupied(int Priority) const
{ {
if (Priority > occupiedPriority)
return 0;
int Seconds = occupiedTimeout - time(NULL); int Seconds = occupiedTimeout - time(NULL);
return Seconds > 0 ? Seconds : 0; return Seconds > 0 ? Seconds : 0;
} }
void cDevice::SetOccupied(int Seconds) void cDevice::SetOccupied(int Seconds, int Priority, time_t From)
{ {
if (Seconds >= 0) if (Seconds < 0)
occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT); return;
if (From == 0)
From = time(NULL);
if (From == occupiedFrom)
occupiedPriority = max(Priority, occupiedPriority);
else {
occupiedPriority = Priority;
occupiedFrom = From;
}
occupiedTimeout = From + min(Seconds, MAXOCCUPIEDTIMEOUT);
} }
bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
@ -1660,11 +1679,13 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
return Played; return Played;
} }
int cDevice::Priority(void) const int cDevice::Priority(bool IgnoreOccupied) const
{ {
int priority = IDLEPRIORITY; int priority = IDLEPRIORITY;
if (IsPrimaryDevice() && !Replaying() && HasProgramme()) if (IsPrimaryDevice() && !Replaying() && HasProgramme())
priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing
if (!IgnoreOccupied && time(NULL) <= occupiedTimeout && occupiedPriority > priority)
priority = occupiedPriority - 1; // so timers with occupiedPriority can start
cMutexLock MutexLock(&mutexReceiver); cMutexLock MutexLock(&mutexReceiver);
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i]) if (receiver[i])

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: device.h 5.3 2024/01/22 12:10:30 kls Exp $ * $Id: device.h 5.4 2024/03/29 21:46:50 kls Exp $
*/ */
#ifndef __DEVICE_H #ifndef __DEVICE_H
@ -178,6 +178,9 @@ public:
///< the transponder of the given Channel, without disturbing any receiver ///< the transponder of the given Channel, without disturbing any receiver
///< at priorities higher or equal to Priority. ///< at priorities higher or equal to Priority.
///< If no such device is currently available, NULL will be returned. ///< 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); static void Shutdown(void);
///< Closes down all devices. ///< Closes down all devices.
///< Must be called at the end of the program. ///< Must be called at the end of the program.
@ -259,7 +262,9 @@ public:
private: private:
mutable cMutex mutexChannel; mutable cMutex mutexChannel;
time_t occupiedFrom;
time_t occupiedTimeout; time_t occupiedTimeout;
int occupiedPriority;
protected: protected:
static int currentChannel; static int currentChannel;
public: public:
@ -369,9 +374,10 @@ public:
///< channel number while replaying. ///< channel number while replaying.
void ForceTransferMode(void); void ForceTransferMode(void);
///< Forces the device into transfermode for the current channel. ///< Forces the device into transfermode for the current channel.
int Occupied(void) const; int Occupied(int Priority = MINPRIORITY) const;
///< Returns the number of seconds this device is still occupied for. ///< Returns the number of seconds this device is still occupied for
void SetOccupied(int Seconds); ///< 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 ///< 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 ///< 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 ///< and make sure it will stay there for a certain amount of time, for
@ -379,6 +385,10 @@ public:
///< after the device has been successfully tuned to the requested transponder. ///< after the device has been successfully tuned to the requested transponder.
///< Seconds will be silently limited to MAXOCCUPIEDTIMEOUT. Values less than ///< Seconds will be silently limited to MAXOCCUPIEDTIMEOUT. Values less than
///< 0 will be silently ignored. ///< 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; virtual bool HasLock(int TimeoutMs = 0) const;
///< Returns true if the device has a lock on the requested transponder. ///< Returns true if the device has a lock on the requested transponder.
///< Default is true, a specific device implementation may return false ///< Default is true, a specific device implementation may return false
@ -833,9 +843,10 @@ private:
mutable cMutex mutexReceiver; mutable cMutex mutexReceiver;
cReceiver *receiver[MAXRECEIVERS]; cReceiver *receiver[MAXRECEIVERS];
public: public:
int Priority(void) const; int Priority(bool IgnoreOccupied = false) const;
///< Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY), ///< Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),
///< or IDLEPRIORITY if no receiver is currently active. ///< or IDLEPRIORITY if no receiver is currently active.
///< If IgnoreOccupied is true, a priority set with SetOccupied() is ignored.
protected: protected:
virtual bool OpenDvr(void); virtual bool OpenDvr(void);
///< Opens the DVR of this device and prepares it to deliver a Transport ///< Opens the DVR of this device and prepares it to deliver a Transport

16
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.tvdr.de * The project's page is at http://www.tvdr.de
* *
* $Id: vdr.c 5.15 2024/03/28 13:21:42 kls Exp $ * $Id: vdr.c 5.16 2024/03/29 21:46:50 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -85,7 +85,7 @@
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications #define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready #define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed #define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
#define TIMERCHECKDELTA 10 // seconds between checks for timers that need to see their channel #define TIMERCHECKDELTA 5 // seconds between checks for timers that need to see their channel
#define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused #define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused
#define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible #define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible
#define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date #define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date
@ -1158,8 +1158,12 @@ int main(int argc, char *argv[])
if (NeedsTransponder || InVpsMargin) { if (NeedsTransponder || InVpsMargin) {
// Find a device that provides the required transponder: // Find a device that provides the required transponder:
cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY); cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY);
if (!Device && InVpsMargin) if (InVpsMargin) {
Device = cDevice::GetDeviceForTransponder(Timer->Channel(), LIVEPRIORITY); if (!Device)
Device = cDevice::GetDeviceForTransponder(Timer->Channel(), Timer->Priority() );
if (!Device)
Device = cDevice::GetDevice(Timer->Channel(), Timer->Priority(), false, false);
}
// Switch the device to the transponder: // Switch the device to the transponder:
if (Device) { if (Device) {
bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme(); bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme();
@ -1167,9 +1171,9 @@ int main(int argc, char *argv[])
if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice()) if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
dsyslog("switching device %d to channel %d %s (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), *Timer->Channel()->GetChannelID().ToString(), Timer->Channel()->Name()); dsyslog("switching device %d to channel %d %s (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), *Timer->Channel()->GetChannelID().ToString(), Timer->Channel()->Name());
if (Device->SwitchChannel(Timer->Channel(), false)) Device->SwitchChannel(Timer->Channel(), false);
Device->SetOccupied(TIMERDEVICETIMEOUT);
} }
Device->SetOccupied(TIMERDEVICETIMEOUT, InVpsMargin ? Timer->Priority() : MINPRIORITY, Now);
if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme()) { if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme()) {
LastTimerChannel = Timer->Channel()->Number(); LastTimerChannel = Timer->Channel()->Number();
Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel