Fixed VPS recording in case there is more than one timer in the VPS margin

This commit is contained in:
Klaus Schmidinger 2006-04-09 09:12:47 +02:00
parent 24b3579d14
commit bfce2b3dba
9 changed files with 121 additions and 32 deletions

View File

@ -4456,7 +4456,7 @@ Video Disk Recorder Revision History
EPG event has been explicitly set to SI::RunningStatusNotRunning.
- The check for timers to be deleted is now done only every 30 seconds.
2006-04-01: Version 1.3.46
2006-04-09: Version 1.3.46
- Fixed handling broken PMT records (thanks to Marcel Wiesweg for pointing out how
to detect these).
@ -4471,3 +4471,10 @@ Video Disk Recorder Revision History
- VPS timers now record only events that have exactly the given start time.
This fix also implements recording several subsequent events that have the
same VPS time (like a sports event with intermittent news breaks).
- When checking for timers that have entered the "VPS margin", any free devices are
now used to switch to the needed transponder. This improves cases where more than
one VPS timer is about to start.
- Fixed handling the VPS margin in case the event's duration is shorter than the
margin.
- Fixed handling VPS timers in case the primary device needs to switch to the
timer's transponder.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 1.125 2006/03/26 09:42:48 kls Exp $
* $Id: device.c 1.126 2006/04/02 13:08:08 kls Exp $
*/
#include "device.h"
@ -547,6 +547,11 @@ bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Needs
return false;
}
bool cDevice::IsTunedToTransponder(const cChannel *Channel)
{
return false;
}
bool cDevice::MaySwitchTransponder(void)
{
return !Receiving(true) && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.73 2006/03/26 09:42:40 kls Exp $
* $Id: device.h 1.74 2006/04/02 13:08:13 kls Exp $
*/
#ifndef __DEVICE_H
@ -217,6 +217,9 @@ public:
///< function itself actually returns true.
///< The default implementation always returns false, so a derived cDevice
///< class that can provide channels must implement this function.
virtual bool IsTunedToTransponder(const cChannel *Channel);
///< Returns true if this device is currently tuned to the given Channel's
///< transponder.
virtual bool MaySwitchTransponder(void);
///< Returns true if it is ok to switch the transponder on this device,
///< without disturbing any other activities.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.155 2006/03/26 09:42:54 kls Exp $
* $Id: dvbdevice.c 1.156 2006/04/01 14:19:43 kls Exp $
*/
#include "dvbdevice.h"
@ -801,6 +801,11 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
return result;
}
bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
{
return dvbTuner->IsTunedTo(Channel);
}
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
bool DoTune = !dvbTuner->IsTunedTo(Channel);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.38 2006/02/04 10:21:51 kls Exp $
* $Id: dvbdevice.h 1.39 2006/04/01 14:18:59 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -62,6 +62,7 @@ public:
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
virtual bool IsTunedToTransponder(const cChannel *Channel);
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
public:

24
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: i18n.c 1.253 2006/03/31 12:58:26 kls Exp $
* $Id: i18n.c 1.254 2006/04/08 13:52:28 kls Exp $
*
* Translations provided by:
*
@ -2246,6 +2246,28 @@ const tI18nPhrase Phrases[] = {
"*** Ugyldig kanal! ***",
"*** Neplatný kanál ***",
},
{ "Upcoming VPS recording!",
"VPS-Aufnahme beginnt in Kürze!",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "No free DVB device to record!",
"Keine freie DVB-Karte zum Aufnehmen!",
"Ni proste DVB naprave za snemanje!",

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: timers.c 1.56 2006/04/01 13:27:14 kls Exp $
* $Id: timers.c 1.57 2006/04/09 09:10:08 kls Exp $
*/
#include "timers.h"
@ -330,7 +330,7 @@ char *cTimer::SetFile(const char *File)
return file;
}
bool cTimer::Matches(time_t t, bool Directly) const
bool cTimer::Matches(time_t t, bool Directly, int Margin) const
{
startTime = stopTime = 0;
if (t == 0)
@ -370,7 +370,7 @@ bool cTimer::Matches(time_t t, bool Directly) const
stopTime = event->EndTime();
return event->IsRunning(true);
}
return startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
return startTime <= t + Margin && t < stopTime; // must stop *before* stopTime to allow adjacent timers
}
return false;
}
@ -450,19 +450,24 @@ void cTimer::SetEventFromSchedule(const cSchedules *Schedules)
if (Schedule && Schedule->Events()->First()) {
time_t now = time(NULL);
if (!lastSetEvent || Schedule->Modified() >= lastSetEvent) {
lastSetEvent = now;
const cEvent *Event = NULL;
if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
if (event && Recording())
return; // let the recording end first
// VPS timers only match if their start time exactly matches the event's VPS time:
for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
if (e->RunningStatus() == SI::RunningStatusNotRunning)
continue; // skip events that have already stopped
int overlap = 0;
Matches(e, &overlap);
if (overlap > FULLMATCH) {
Event = e;
break; // take the first matching event
if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events
int overlap = 0;
Matches(e, &overlap);
if (overlap > FULLMATCH) {
Event = e;
break; // take the first matching event
}
}
}
if (!Event && event && (now <= event->EndTime() || Matches(0, true)))
return; // stay with the old event until the timer has completely expired
}
else {
// Normal timers match the event they have the most overlap with:
@ -487,7 +492,6 @@ void cTimer::SetEventFromSchedule(const cSchedules *Schedules)
}
}
SetEvent(Event);
lastSetEvent = now;
}
}
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: timers.h 1.27 2006/03/26 14:38:46 kls Exp $
* $Id: timers.h 1.28 2006/04/08 12:41:44 kls Exp $
*/
#ifndef __TIMERS_H
@ -73,7 +73,7 @@ public:
static time_t IncDay(time_t t, int Days);
static time_t SetTime(time_t t, int SecondsFromMidnight);
char *SetFile(const char *File);
bool Matches(time_t t = 0, bool Directly = false) const;
bool Matches(time_t t = 0, bool Directly = false, int Margin = 0) const;
int Matches(const cEvent *Event, int *Overlap = NULL) const;
bool Expired(void) const;
time_t StartTime(void) const;

68
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.252 2006/04/01 12:55:33 kls Exp $
* $Id: vdr.c 1.253 2006/04/09 09:10:41 kls Exp $
*/
#include <getopt.h>
@ -72,6 +72,8 @@
#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 SHUTDOWNRETRY 300 // seconds before trying again to shut down
#define VPSCHECKDELTA 10 // seconds between checks for timers that have entered the VPS margin
#define VPSDEVICETIMEOUT 8 // seconds before a device used for VPS may be reused
#define EXIT(v) { ExitCode = (v); goto Exit; }
@ -751,21 +753,61 @@ int main(int argc, char *argv[])
LastTimerChannel = Timer->Channel()->Number();
}
// Make sure VPS timers "see" their channel early enough:
TimerInVpsMargin = false;
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
if (Timer->HasFlags(tfActive | tfVps) && !Timer->Recording() && !Timer->Pending() && Timer->Matches(Now + Setup.VpsMargin, true)) {
if (!Timer->InVpsMargin()) {
static time_t LastVpsCheck = 0;
if (Now - LastVpsCheck > VPSCHECKDELTA) { // don't do this too often
TimerInVpsMargin = false;
static time_t DeviceUsed[MAXDEVICES] = { 0 };
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
if (Timer->HasFlags(tfActive | tfVps) && !Timer->Recording() && Timer->Matches(Now, true, Setup.VpsMargin)) {
Timer->SetInVpsMargin(true);
//XXX if not primary device has TP???
LastTimerChannel = Timer->Channel()->Number();
cRecordControls::Start(Timer); // will only switch the device
// Find a device that provides the required transponder:
cDevice *Device = NULL;
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *d = cDevice::GetDevice(i);
if (d && d->ProvidesTransponder(Timer->Channel())) {
if (d->IsTunedToTransponder(Timer->Channel())) {
// if any device is tuned to the transponder, we're done
Device = d;
break;
}
else if (Now - DeviceUsed[d->DeviceNumber()] > VPSDEVICETIMEOUT) {
// only check other devices if they have been left alone for a while
if (d->MaySwitchTransponder())
// this one can be switched without disturbing anything else
Device = d;
else if (!Device && !d->Receiving() && d->ProvidesTransponderExclusively(Timer->Channel()))
// use this one only if no other with less impact can be found
Device = d;
}
}
}
if (!Device) {
cDevice *d = cDevice::ActualDevice();
if (!d->Receiving() && d->ProvidesTransponder(Timer->Channel()) && Now - DeviceUsed[d->DeviceNumber()] > VPSDEVICETIMEOUT)
Device = d; // use the actual device as a last resort
}
// Switch the device to the transponder:
if (Device) {
if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
if (!Device->IsTunedToTransponder(Timer->Channel())) {
dsyslog("switching device %d to channel %d", Device->DeviceNumber() + 1, Timer->Channel()->Number());
Device->SwitchChannel(Timer->Channel(), false);
DeviceUsed[Device->DeviceNumber()] = Now;
}
if (cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
// the previous SwitchChannel() has switched away the current live channel
Channels.SwitchTo(Timer->Channel()->Number()); // avoids toggling between old channel and black screen
Skins.Message(mtInfo, tr("Upcoming VPS recording!"));
}
}
TimerInVpsMargin = true;
}
else
Timer->SetInVpsMargin(false);
}
else
Timer->SetInVpsMargin(false);
if (Timer->InVpsMargin())
TimerInVpsMargin = true;
}
LastVpsCheck = time(NULL);
}
// Delete expired timers:
Timers.DeleteExpired();
}