From 179d5b87fc39c2c4f4cb8f0e14592662aa7292d9 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Fri, 29 Mar 2024 21:46:50 +0100 Subject: [PATCH] A device is now always kept occupied if a timer is in VPS margin or needs the transponder --- CONTRIBUTORS | 2 ++ HISTORY | 4 +++- device.c | 41 +++++++++++++++++++++++++++++++---------- device.h | 21 ++++++++++++++++----- vdr.c | 16 ++++++++++------ 5 files changed, 62 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 78e8f37c..08ba4a27 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2569,6 +2569,8 @@ Markus Ehrnsperger a decoder for improving handling present/following data for VPS timers 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 for reporting a bug in handling the cPluginManager::Active() result when pressing diff --git a/HISTORY b/HISTORY index 1944dbda..85ec11ee 100644 --- a/HISTORY +++ b/HISTORY @@ -9887,7 +9887,7 @@ Video Disk Recorder Revision History - Fixed possible duplicate component entries in the info of an ongoing recording (reported by Christoph Haubrich). -2024-03-28: +2024-03-29: - Fixed the move assignment operator to check for self-assignment (suggested by Stefan Herdler). @@ -9916,3 +9916,5 @@ Video Disk Recorder Revision History transponder. - 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. +- A device is now always kept occupied if a timer is in VPS margin or needs the + transponder (thanks to Markus Ehrnsperger). diff --git a/device.c b/device.c index 72a63a65..f1aa6a98 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * 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" @@ -95,7 +95,9 @@ cDevice::cDevice(void) camSlot = NULL; + occupiedFrom = 0; occupiedTimeout = 0; + occupiedPriority = MINPRIORITY; player = NULL; 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)) continue; // CAM slot can't be used with this device 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 (cCamSlot *csi = device[i]->CamSlot()) { 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 ? !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(); // 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 <<= 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 @@ -321,6 +327,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView if (NumUsableSlots && !HasInternalCam) 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) @@ -427,7 +434,8 @@ cDevice *cDevice::GetDeviceForTransponder(const cChannel *Channel, int Priority) if (d->ProvidesTransponder(Channel)) { if (d->MaySwitchTransponder(Channel)) 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())) 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 { - 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) @@ -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); return Seconds > 0 ? Seconds : 0; } -void cDevice::SetOccupied(int Seconds) +void cDevice::SetOccupied(int Seconds, int Priority, time_t From) { - if (Seconds >= 0) - occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT); + if (Seconds < 0) + 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) @@ -1660,11 +1679,13 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly) return Played; } -int cDevice::Priority(void) const +int cDevice::Priority(bool IgnoreOccupied) const { 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 + if (!IgnoreOccupied && time(NULL) <= occupiedTimeout && occupiedPriority > priority) + priority = occupiedPriority - 1; // so timers with occupiedPriority can start cMutexLock MutexLock(&mutexReceiver); for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i]) diff --git a/device.h b/device.h index 6f5e77f4..061d43c9 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * 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 @@ -178,6 +178,9 @@ public: ///< 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. @@ -259,7 +262,9 @@ public: private: mutable cMutex mutexChannel; + time_t occupiedFrom; time_t occupiedTimeout; + int occupiedPriority; protected: static int currentChannel; public: @@ -369,9 +374,10 @@ public: ///< channel number while replaying. void ForceTransferMode(void); ///< Forces the device into transfermode for the current channel. - int Occupied(void) const; - ///< Returns the number of seconds this device is still occupied for. - void SetOccupied(int Seconds); + 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 @@ -379,6 +385,10 @@ public: ///< 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 @@ -833,9 +843,10 @@ private: mutable cMutex mutexReceiver; cReceiver *receiver[MAXRECEIVERS]; public: - int Priority(void) const; + 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 diff --git a/vdr.c b/vdr.c index 566522a0..6056e2f0 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * 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 @@ -85,7 +85,7 @@ #define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications #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 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 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 @@ -1158,8 +1158,12 @@ int main(int argc, char *argv[]) if (NeedsTransponder || InVpsMargin) { // Find a device that provides the required transponder: cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY); - if (!Device && InVpsMargin) - Device = cDevice::GetDeviceForTransponder(Timer->Channel(), LIVEPRIORITY); + if (InVpsMargin) { + 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: if (Device) { bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme(); @@ -1167,9 +1171,9 @@ int main(int argc, char *argv[]) if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice()) 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()); - if (Device->SwitchChannel(Timer->Channel(), false)) - Device->SetOccupied(TIMERDEVICETIMEOUT); + Device->SwitchChannel(Timer->Channel(), false); } + Device->SetOccupied(TIMERDEVICETIMEOUT, InVpsMargin ? Timer->Priority() : MINPRIORITY, Now); if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme()) { LastTimerChannel = Timer->Channel()->Number(); Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel