mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented VPS controlled timers
This commit is contained in:
parent
4063d72760
commit
198fcf437b
26
HISTORY
26
HISTORY
@ -2652,7 +2652,7 @@ Video Disk Recorder Revision History
|
||||
actual CAM type as reported by the CAM. The 'ca.conf' file has been stripped
|
||||
down to the values 0..4.
|
||||
|
||||
2004-02-23: Version 1.3.5
|
||||
2004-02-29: Version 1.3.5
|
||||
|
||||
- Fixed reading the EPG preferred language parameter from 'setup.conf'.
|
||||
- Fixed switching to a visible programme in case the current channel has neither
|
||||
@ -2680,11 +2680,8 @@ Video Disk Recorder Revision History
|
||||
channel won't interrupt an ongoing Transfer Mode.
|
||||
- Added subtable ID and TSDT handling to 'libsi' (thanks to Marcel Wiesweg).
|
||||
- Fixed some Russian OSD texts (thanks to Vyacheslav Dikonov).
|
||||
- Added the 'running status' to the EPG events. This might lead to a VPS like
|
||||
function for recording, but unfortunately not all stations handle this flag
|
||||
correctly - and some (like RTL, for instance) even change the ID of the same
|
||||
event randomly, making it impossible for a timer to be programmed on a ceartain
|
||||
event rather than a specific time. Well, let's see where this leads us...
|
||||
- Added the 'running status' to the EPG events. This is necessary for implementing
|
||||
the VPS function for recording.
|
||||
- Removed the obsolete 'present' and 'following' handling from the EPG data.
|
||||
- The EPG data is now always kept sorted chronologically in the internal data
|
||||
structures. This also means that any EPG data retrieved through the SVRDP
|
||||
@ -2698,10 +2695,21 @@ Video Disk Recorder Revision History
|
||||
still displayed in the "Schedule" menu (thanks to Jaakko Hyvätti).
|
||||
- Added PDCDescriptor handling to 'libsi'.
|
||||
- Implemented handling the VPS timestamps (aka "Programme Identification Label")
|
||||
in preparation for full VPS support for timers (provided the tv stations
|
||||
actually broadcast this information). Currently these are just displayed in
|
||||
the event page if they exist and are different than the event's start time.
|
||||
for full VPS support for timers (provided the tv stations actually broadcast
|
||||
this information). The VPS time is displayed in the event info page if it exists
|
||||
and is different than the event's start time.
|
||||
- Extended the SVDRP command LSTE to allow limiting the listed data to a given
|
||||
channel, the present or following events, or events at a given time (thanks to
|
||||
Thomas Heiligenmann).
|
||||
- Fixed a typo in libsi/si.h (thanks to Stéphane Esté-Gracias).
|
||||
- Timers can now be set to use the VPS information to control recording a programme.
|
||||
The new setup options "Recording/Use VPS" and "Recording/VPS margin", as well as
|
||||
the "VPS" option in the individual timers, can be used to control this feature
|
||||
(see MANUAL for details).
|
||||
Note that this feature will certainly need a lot of testing before it can be
|
||||
called "safe"!
|
||||
- The "Schedule" and "What's on now/next?" menus now have an additional column
|
||||
which displays information on whether there is a timer defined for an event,
|
||||
whether an event has a VPS time that's different than its start time, and
|
||||
whether an event is currently running (see MANUAL under "The "Schedule" Menu"
|
||||
for details).
|
||||
|
29
MANUAL
29
MANUAL
@ -138,6 +138,16 @@ Version 1.2
|
||||
The "Blue" button can be pressed to switch to the channel with the selected
|
||||
programme.
|
||||
|
||||
The following markers in these menus give additional information about the
|
||||
status of the events:
|
||||
|
||||
t there is a timer defined for this event which covers only part of the event
|
||||
T there is a timer defined for this event which covers the entire event
|
||||
V this event has a VPS time that's different than its start time
|
||||
* this event is currently running (the validity of this marker depends on
|
||||
whether there is currently a DVB card receiving the transponder this channel
|
||||
is on).
|
||||
|
||||
* Selecting a Channel
|
||||
|
||||
There are four ways to select a channel:
|
||||
@ -351,6 +361,14 @@ Version 1.2
|
||||
would have a Day setting of "M-W----".
|
||||
Start: The start time of the timer in hh:mm as 24 hour ("military") time.
|
||||
Stop: The stop time of the timer.
|
||||
VPS: Defines whether the timer shall use VPS (if available). If this
|
||||
option is set to 'yes', the start time must exactly match the
|
||||
programme's VPS time, otherwise nothing will be recorded. If VPS
|
||||
is used, the stop time has no real meaning. However, it must be
|
||||
different than the start time, and should correspond to the actual
|
||||
stop time of the programme, just in case there is no real VPS data
|
||||
available at the time of recording, so VDR has to fall back to
|
||||
normal timer recording.
|
||||
Priority: The Priority (0..99) is used to decide which timer shall be
|
||||
started in case there are two or more timers with the exact same
|
||||
start time. The first timer in the list with the highest Priority
|
||||
@ -585,6 +603,17 @@ Version 1.2
|
||||
no = don't use the 'Episode name'
|
||||
yes = use it (and create subdirectories)
|
||||
|
||||
Use VPS = 0 Defines whether a timer that is created from an EPG entry
|
||||
(by pressing the "Record" (red) button in the "Schedules"
|
||||
or "What's on now/next?" menu) will automatically use VPS
|
||||
if the event it is created for has a VPS time.
|
||||
|
||||
VPS margin = 120 Defines how many seconds before a VPS controlled timer is
|
||||
scheduled to start, VDR will make sure that one of the DVB
|
||||
devices is tuned to the transponder that timer shall record
|
||||
from. This is necessary for the "Running Status" information
|
||||
that is broadcast in the EPG data to be seen by VDR.
|
||||
|
||||
Mark instant recording = yes
|
||||
Defines whether an "instant recording" (started by
|
||||
pressing the "Red" button in the "VDR" menu) will be
|
||||
|
130
README.vps
Normal file
130
README.vps
Normal file
@ -0,0 +1,130 @@
|
||||
VPS (Video Programming Service)
|
||||
===============================
|
||||
|
||||
Beginning with version 1.3.5 VDR supports the VPS method
|
||||
of identifying programmes to record, and making sure they
|
||||
are recorded in full length, even if they run longer than
|
||||
initially specified or are shifted in time.
|
||||
|
||||
Of course, the main prerequisite for this to work is that
|
||||
the broadcasters actually provide the necessary data. In
|
||||
particular these are
|
||||
|
||||
- EPG data (well, obviously)
|
||||
- the data for each event must contain the "Programme Identification Label"
|
||||
descriptor, which contains the VPS timestamp for this programme
|
||||
- the event data must provide and maintain the "Running Status" flag,
|
||||
which indicates whether this programme is currently running or not.
|
||||
|
||||
Currently only the German "Öffentlich Rechtliche" tv stations provide
|
||||
the necessary VPS data, so this will work only for stations like "Das Erste",
|
||||
"ZDF" and the like.
|
||||
|
||||
Following is a step by step description of what happens for a VPS controlled
|
||||
timer recording. First let's take a look at what the VDR user needs to do.
|
||||
|
||||
VPS as seen by the VDR user:
|
||||
----------------------------
|
||||
|
||||
When the VDR user sets up a timer that shall be under VPS control, there
|
||||
are only two things that need to be done:
|
||||
|
||||
1. Set the "Start" time to the actual VPS time as published in tv magazines.
|
||||
Typically the VPS time is the same as the printed start time, unless
|
||||
expliciltly specified otherwise. For instance, a tv magazine might print
|
||||
|
||||
20:15 Wetten, dass...?
|
||||
(VPS = 20:14)
|
||||
|
||||
In this case the timer would need to be set to 20:14.
|
||||
|
||||
2. Set the "VPS" flag in the timer definition to "yes" in order to tell VDR
|
||||
that this timer is to be handled under VPS control. This is no different
|
||||
to old analog video recorders, where each timer has also had a separate
|
||||
VPS flag.
|
||||
|
||||
If the user sets up a timer from the "Schedule" menu, things are even simpler.
|
||||
If the setup option "Recording/Use VPS" is set to "yes", any timer that is
|
||||
programmed from an event that has VPS information will automatically be set
|
||||
up as a VPS timer.
|
||||
|
||||
IMPORTANT: In order for a recording to work under VPS control it is of
|
||||
========== paramount importance that the start time is set to the actual
|
||||
VPS time of that event, NOT some time a few minutes before the
|
||||
event starts! If a timer is set to use VPS, and the time doesn't
|
||||
match any event's VPS time, nothing will be recorded!
|
||||
|
||||
VPS as seen by VDR:
|
||||
-------------------
|
||||
|
||||
The following things happen when VDR processes timers:
|
||||
|
||||
- VDR regularly scans the EPG data and assigns events to the timers (see
|
||||
cTimers::SetEvents() in VDR/timers.c).
|
||||
This can be seen in the log file as
|
||||
|
||||
timer 1 (15 1830-1900 'Neues') set to event 28.02.2004 18:30-18:59 (VPS: 28.02 18:30) 'neues'
|
||||
|
||||
- When a VPS timer is asked whether it matches (i.e. whether a recording shall
|
||||
be started), it checks whether it has an event assigned to it, and whether
|
||||
that event has a running status of "starts in a few seconds" or "running"
|
||||
(see cTimer::Matches(time_t t, bool Directly) in VDR/timers.c). This allows
|
||||
the recording process to catch the entire programme, even if it runs longer
|
||||
than initially advertised. It also works if it runs shorter or gets shifted.
|
||||
|
||||
- When a VPS timer event is coming up (i.e. there are only a few minutes until
|
||||
it starts, according to the related event data's start time - which may be
|
||||
different than the VPS time!), VDR tunes a free DVB device to that transponder
|
||||
(unless there is already a device tuned to that one) in order to make sure
|
||||
that the event data (especially the "Running Status") will be up to date and
|
||||
a change in the "Running status" flag will be seen immediately. This may
|
||||
lead to the primary device being switched to a different channel if there
|
||||
is no other free DVB device available. See the main program loop in VDR/vdr.c,
|
||||
"Make sure VPS timers "see" their channel early enough:".
|
||||
|
||||
Problems:
|
||||
---------
|
||||
|
||||
- In order for a VPS controlled timer to function properly, it needs to "see"
|
||||
any changes in the running status of its event. This means that one of the
|
||||
DVB devices needs to be tuned to the proper transponder some time before
|
||||
the actual start time of the event. However, this may result in an other
|
||||
timer (with lower priority) to be stopped, because it occupies the DVB device
|
||||
and has it tuned to a different transponder.
|
||||
See "// Make sure VPS timers "see" their channel early enough:" in VDR/vdr.c.
|
||||
TODO:
|
||||
Something needs to be done to prevent two timers from repeatedly switching
|
||||
the device between channels in such a situation.
|
||||
|
||||
- If, for some reason, the driver doesn't deliver any more section data, a
|
||||
VPS controlled timer will never see that the programme has started (or ended).
|
||||
TODO:
|
||||
Therefore some mechanism needs to be implemented that makes absolutely sure
|
||||
we continuously receive at least the event data for the present event.
|
||||
|
||||
Caveats:
|
||||
--------
|
||||
|
||||
Apparently VPS in digital broadcasting is still in an early state. Only few
|
||||
tv stations actually support it, and other tv stations don't even handle the
|
||||
"Running Status" correctly (which, by itself, would already be helpful, even
|
||||
without VPS).
|
||||
|
||||
Here's a list of things that are apparently done wrong by the individual
|
||||
stations:
|
||||
|
||||
- The German "Öffentlich Rechtliche" tv stations, although supporting VPS,
|
||||
don't switch the "Running Status" of an upcoming broadcast to "starts in
|
||||
a few seconds", but rather go directly from "unknown" or "not running" to
|
||||
"running". This may result in a recording that misses the first few seconds
|
||||
of the programme.
|
||||
- The RTL group handles EPG events in a rather random way. They change event
|
||||
IDs randomly, and switch the "Running Status" flag at times that are only
|
||||
losely related to the actual events. For instance, if the "RTL aktuell"
|
||||
programme starts at 18:45, they switch that event to "running" at about
|
||||
18:43. Or, even worse, if "Wer wird Millionär?" runs until 21:15, they
|
||||
switch the _next_ programme to running (which implicitly set "Wer wird
|
||||
Millionär?" to "not running) at around 21:11 - so anybody using that
|
||||
information to control recording would not see the end of that programme.
|
||||
|
||||
... more following as it comes up...
|
8
config.c
8
config.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.c 1.124 2004/02/21 15:05:40 kls Exp $
|
||||
* $Id: config.c 1.125 2004/02/28 11:12:20 kls Exp $
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@ -272,6 +272,8 @@ cSetup::cSetup(void)
|
||||
PausePriority = 10;
|
||||
PauseLifetime = 1;
|
||||
UseSubtitle = 1;
|
||||
UseVps = 0;
|
||||
VpsMargin = 120;
|
||||
RecordingDirs = 1;
|
||||
VideoFormat = 0;
|
||||
UpdateChannels = 4;
|
||||
@ -417,6 +419,8 @@ bool cSetup::Parse(const char *Name, const char *Value)
|
||||
else if (!strcasecmp(Name, "PausePriority")) PausePriority = atoi(Value);
|
||||
else if (!strcasecmp(Name, "PauseLifetime")) PauseLifetime = atoi(Value);
|
||||
else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value);
|
||||
else if (!strcasecmp(Name, "UseVps")) UseVps = atoi(Value);
|
||||
else if (!strcasecmp(Name, "VpsMargin")) VpsMargin = atoi(Value);
|
||||
else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value);
|
||||
else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
|
||||
else if (!strcasecmp(Name, "UpdateChannels")) UpdateChannels = atoi(Value);
|
||||
@ -469,6 +473,8 @@ bool cSetup::Save(void)
|
||||
Store("PausePriority", PausePriority);
|
||||
Store("PauseLifetime", PauseLifetime);
|
||||
Store("UseSubtitle", UseSubtitle);
|
||||
Store("UseVps", UseVps);
|
||||
Store("VpsMargin", VpsMargin);
|
||||
Store("RecordingDirs", RecordingDirs);
|
||||
Store("VideoFormat", VideoFormat);
|
||||
Store("UpdateChannels", UpdateChannels);
|
||||
|
4
config.h
4
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 1.188 2004/02/21 15:04:53 kls Exp $
|
||||
* $Id: config.h 1.189 2004/02/28 11:11:35 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
@ -228,6 +228,8 @@ public:
|
||||
int DefaultPriority, DefaultLifetime;
|
||||
int PausePriority, PauseLifetime;
|
||||
int UseSubtitle;
|
||||
int UseVps;
|
||||
int VpsMargin;
|
||||
int RecordingDirs;
|
||||
int VideoFormat;
|
||||
int UpdateChannels;
|
||||
|
14
epg.c
14
epg.c
@ -7,11 +7,12 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.c 1.13 2004/02/22 14:41:37 kls Exp $
|
||||
* $Id: epg.c 1.14 2004/02/29 13:48:34 kls Exp $
|
||||
*/
|
||||
|
||||
#include "epg.h"
|
||||
#include "libsi/si.h"
|
||||
#include "timers.h"
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
@ -95,6 +96,15 @@ void cEvent::SetVps(time_t Vps)
|
||||
vps = Vps;
|
||||
}
|
||||
|
||||
bool cEvent::HasTimer(void) const
|
||||
{
|
||||
for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
|
||||
if (t->Event() == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *cEvent::GetDateString(void) const
|
||||
{
|
||||
static char buf[25];
|
||||
@ -545,7 +555,7 @@ void cSchedule::Cleanup(time_t Time)
|
||||
Event = events.Get(a);
|
||||
if (!Event)
|
||||
break;
|
||||
if (Event->StartTime() + Event->Duration() + Setup.EPGLinger * 60 + 3600 < Time) { // adding one hour for safety
|
||||
if (!Event->HasTimer() && Event->StartTime() + Event->Duration() + Setup.EPGLinger * 60 + 3600 < Time) { // adding one hour for safety
|
||||
events.Del(Event);
|
||||
a--;
|
||||
}
|
||||
|
3
epg.h
3
epg.h
@ -7,7 +7,7 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.h 1.10 2004/02/22 14:34:04 kls Exp $
|
||||
* $Id: epg.h 1.11 2004/02/29 14:10:06 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __EPG_H
|
||||
@ -51,6 +51,7 @@ public:
|
||||
time_t StartTime(void) const { return startTime; }
|
||||
int Duration(void) const { return duration; }
|
||||
time_t Vps(void) const { return vps; }
|
||||
bool HasTimer(void) const;
|
||||
const char *GetDateString(void) const;
|
||||
const char *GetTimeString(void) const;
|
||||
const char *GetEndTimeString(void) const;
|
||||
|
56
i18n.c
56
i18n.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: i18n.c 1.148 2004/02/21 15:14:36 kls Exp $
|
||||
* $Id: i18n.c 1.149 2004/02/28 11:13:27 kls Exp $
|
||||
*
|
||||
* Translations provided by:
|
||||
*
|
||||
@ -1542,6 +1542,24 @@ const tI18nPhrase Phrases[] = {
|
||||
"Fi",
|
||||
"ºÞÝÕæ",
|
||||
},
|
||||
{ "VPS",
|
||||
"VPS",
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
},
|
||||
{ "Priority",
|
||||
"Priorität",
|
||||
"Prioriteta",
|
||||
@ -2805,6 +2823,42 @@ const tI18nPhrase Phrases[] = {
|
||||
"Utilitzar el nom de l'episodi",
|
||||
"³àãßßØàÞÒÐâì äÐÙÛë ßÞ íßØ×ÞÔÐÜ",
|
||||
},
|
||||
{ "Setup.Recording$Use VPS",
|
||||
"VPS benutzen",
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
},
|
||||
{ "Setup.Recording$VPS margin (s)",
|
||||
"Zeitpuffer bei VPS (s)",
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
"",// TODO
|
||||
},
|
||||
{ "Setup.Recording$Mark instant recording",
|
||||
"Direktaufzeichnung markieren",
|
||||
"Oznaci direktno snemanje",
|
||||
|
53
menu.c
53
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.292 2004/02/22 14:14:55 kls Exp $
|
||||
* $Id: menu.c 1.293 2004/02/29 14:11:16 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -18,6 +18,7 @@
|
||||
#include "cutter.h"
|
||||
#include "eitscan.h"
|
||||
#include "i18n.h"
|
||||
#include "libsi/si.h"
|
||||
#include "menuitems.h"
|
||||
#include "plugin.h"
|
||||
#include "recording.h"
|
||||
@ -871,13 +872,14 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
|
||||
if (timer) {
|
||||
data = *timer;
|
||||
if (New)
|
||||
data.active = 1;
|
||||
data.SetFlags(tfActive);
|
||||
channel = data.Channel()->Number();
|
||||
Add(new cMenuEditBoolItem(tr("Active"), &data.active));
|
||||
Add(new cMenuEditBitItem( tr("Active"), &data.flags, tfActive));
|
||||
Add(new cMenuEditChanItem(tr("Channel"), &channel));
|
||||
Add(new cMenuEditDayItem( tr("Day"), &data.day));
|
||||
Add(new cMenuEditTimeItem(tr("Start"), &data.start));
|
||||
Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
|
||||
Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
|
||||
Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
|
||||
Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars)));
|
||||
@ -926,13 +928,13 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
|
||||
if (timer) {
|
||||
if (memcmp(timer, &data, sizeof(data)) != 0) {
|
||||
*timer = data;
|
||||
if (timer->active)
|
||||
timer->active = 1; // allows external programs to mark active timers with values > 1 and recognize if the user has modified them
|
||||
if (timer->HasFlags(tfActive))
|
||||
timer->ClrFlags(~tfAll); // allows external programs to mark active timers with values > 0xFFFF and recognize if the user has modified them
|
||||
}
|
||||
if (addIfConfirmed)
|
||||
Timers.Add(timer);
|
||||
Timers.Save();
|
||||
isyslog("timer %d %s (%s)", timer->Index() + 1, addIfConfirmed ? "added" : "modified", timer->active ? "active" : "inactive");
|
||||
isyslog("timer %d %s (%s)", timer->Index() + 1, addIfConfirmed ? "added" : "modified", timer->HasFlags(tfActive) ? "active" : "inactive");
|
||||
addIfConfirmed = false;
|
||||
}
|
||||
}
|
||||
@ -976,7 +978,7 @@ void cMenuTimerItem::Set(void)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
|
||||
!timer->Active() ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
|
||||
!(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
|
||||
timer->Channel()->Number(),
|
||||
timer->PrintDay(timer->Day()),
|
||||
timer->Start() / 100,
|
||||
@ -1041,7 +1043,7 @@ eOSState cMenuTimers::OnOff(void)
|
||||
if (timer->FirstDay())
|
||||
isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
|
||||
else
|
||||
isyslog("timer %d %sactivated", timer->Index() + 1, timer->Active() ? "" : "de");
|
||||
isyslog("timer %d %sactivated", timer->Index() + 1, timer->HasFlags(tfActive) ? "" : "de");
|
||||
Timers.Save();
|
||||
}
|
||||
return osContinue;
|
||||
@ -1211,7 +1213,11 @@ cMenuWhatsOnItem::cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel)
|
||||
event = Event;
|
||||
channel = Channel;
|
||||
char *buffer = NULL;
|
||||
asprintf(&buffer, "%d\t%.*s\t%.*s\t%s", channel->Number(), 6, channel->Name(), 5, event->GetTimeString(), event->Title());
|
||||
int TimerMatch;
|
||||
char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' ';
|
||||
char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
|
||||
char r = event->RunningStatus() > SI::RunningStatusNotRunning ? '*' : ' ';
|
||||
asprintf(&buffer, "%d\t%.*s\t%.*s\t%c%c%c\t%s", channel->Number(), 6, channel->Name(), 5, event->GetTimeString(), t, v, r, event->Title());
|
||||
SetText(buffer, false);
|
||||
}
|
||||
|
||||
@ -1235,7 +1241,7 @@ int cMenuWhatsOn::currentChannel = 0;
|
||||
const cEvent *cMenuWhatsOn::scheduleEvent = NULL;
|
||||
|
||||
cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
|
||||
:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6)
|
||||
:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4)
|
||||
{
|
||||
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
|
||||
if (!Channel->GroupSep()) {
|
||||
@ -1325,7 +1331,11 @@ cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event)
|
||||
{
|
||||
event = Event;
|
||||
char *buffer = NULL;
|
||||
asprintf(&buffer, "%.*s\t%.*s\t%s", 5, event->GetDateString(), 5, event->GetTimeString(), event->Title());
|
||||
int TimerMatch;
|
||||
char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' ';
|
||||
char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
|
||||
char r = event->RunningStatus() > SI::RunningStatusNotRunning ? '*' : ' ';
|
||||
asprintf(&buffer, "%.*s\t%.*s\t%c%c%c\t%s", 5, event->GetDateString(), 5, event->GetTimeString(), t, v, r, event->Title());
|
||||
SetText(buffer, false);
|
||||
}
|
||||
|
||||
@ -1347,7 +1357,7 @@ public:
|
||||
};
|
||||
|
||||
cMenuSchedule::cMenuSchedule(void)
|
||||
:cOsdMenu("", 6, 6)
|
||||
:cOsdMenu("", 6, 6, 4)
|
||||
{
|
||||
now = next = false;
|
||||
otherChannel = 0;
|
||||
@ -2294,6 +2304,8 @@ cMenuSetupRecord::cMenuSetupRecord(void)
|
||||
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
|
||||
Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
|
||||
Add(new cMenuEditBoolItem(tr("Setup.Recording$Use VPS"), &data.UseVps));
|
||||
Add(new cMenuEditIntItem( tr("Setup.Recording$VPS margin (s)"), &data.VpsMargin, 0));
|
||||
Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"), &data.MarkInstantRecord));
|
||||
Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"), data.NameInstantRecord, sizeof(data.NameInstantRecord), tr(FileNameChars)));
|
||||
Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
|
||||
@ -3056,11 +3068,12 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
|
||||
}
|
||||
timer->SetPending(true);
|
||||
timer->SetRecording(true);
|
||||
event = timer->Event();
|
||||
|
||||
const char *Title = NULL;
|
||||
const char *Subtitle = NULL;
|
||||
const char *Summary = NULL;
|
||||
if (GetEvent()) {
|
||||
if (event || GetEvent()) {
|
||||
Title = event->Title();
|
||||
Subtitle = event->ShortText();
|
||||
Summary = event->Description();
|
||||
@ -3115,7 +3128,7 @@ cRecordControl::~cRecordControl()
|
||||
bool cRecordControl::GetEvent(void)
|
||||
{
|
||||
const cChannel *channel = timer->Channel();
|
||||
time_t Time = timer->Active() == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
|
||||
time_t Time = timer->HasFlags(tfInstant) ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
|
||||
for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
|
||||
{
|
||||
cSchedulesLock SchedulesLock;
|
||||
@ -3187,12 +3200,14 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
|
||||
cThread::EmergencyExit(true);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
|
||||
if (!RecordControls[i]) {
|
||||
RecordControls[i] = new cRecordControl(device, Timer, Pause);
|
||||
return true;
|
||||
if (!Timer || Timer->Matches()) {
|
||||
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
|
||||
if (!RecordControls[i]) {
|
||||
RecordControls[i] = new cRecordControl(device, Timer, Pause);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!Timer || (Timer->Priority() >= Setup.PrimaryLimit && !Timer->Pending()))
|
||||
isyslog("no free DVB device to record channel %d!", ch);
|
||||
|
19
menuitems.c
19
menuitems.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menuitems.c 1.14 2004/01/25 15:40:55 kls Exp $
|
||||
* $Id: menuitems.c 1.15 2004/02/24 12:38:43 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menuitems.h"
|
||||
@ -113,6 +113,23 @@ void cMenuEditBoolItem::Set(void)
|
||||
SetValue(buf);
|
||||
}
|
||||
|
||||
// --- cMenuEditBitItem ------------------------------------------------------
|
||||
|
||||
cMenuEditBitItem::cMenuEditBitItem(const char *Name, int *Value, int Mask, const char *FalseString, const char *TrueString)
|
||||
:cMenuEditBoolItem(Name, &bit, FalseString, TrueString)
|
||||
{
|
||||
value = Value;
|
||||
bit = (*value & Mask) != 0;
|
||||
mask = Mask;
|
||||
Set();
|
||||
}
|
||||
|
||||
void cMenuEditBitItem::Set(void)
|
||||
{
|
||||
*value = bit ? *value | mask : *value & ~mask;
|
||||
cMenuEditBoolItem::Set();
|
||||
}
|
||||
|
||||
// --- cMenuEditNumItem ------------------------------------------------------
|
||||
|
||||
cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
|
||||
|
12
menuitems.h
12
menuitems.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menuitems.h 1.5 2003/01/12 15:06:23 kls Exp $
|
||||
* $Id: menuitems.h 1.6 2004/02/24 11:55:14 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MENUITEMS_H
|
||||
@ -42,6 +42,16 @@ public:
|
||||
cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
|
||||
};
|
||||
|
||||
class cMenuEditBitItem : public cMenuEditBoolItem {
|
||||
protected:
|
||||
int *value;
|
||||
int bit;
|
||||
int mask;
|
||||
virtual void Set(void);
|
||||
public:
|
||||
cMenuEditBitItem(const char *Name, int *Value, int Mask, const char *FalseString = NULL, const char *TrueString = NULL);
|
||||
};
|
||||
|
||||
class cMenuEditNumItem : public cMenuEditItem {
|
||||
protected:
|
||||
char *value;
|
||||
|
8
svdrp.c
8
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 1.60 2004/02/22 15:31:23 kls Exp $
|
||||
* $Id: svdrp.c 1.61 2004/02/24 12:24:43 kls Exp $
|
||||
*/
|
||||
|
||||
#include "svdrp.h"
|
||||
@ -905,16 +905,16 @@ void cSVDRP::CmdMODT(const char *Option)
|
||||
if (timer) {
|
||||
cTimer t = *timer;
|
||||
if (strcasecmp(tail, "ON") == 0)
|
||||
t.SetActive(taActive);
|
||||
t.SetFlags(tfActive);
|
||||
else if (strcasecmp(tail, "OFF") == 0)
|
||||
t.SetActive(taInactive);
|
||||
t.ClrFlags(tfActive);
|
||||
else if (!t.Parse(tail)) {
|
||||
Reply(501, "Error in timer settings");
|
||||
return;
|
||||
}
|
||||
*timer = t;
|
||||
Timers.Save();
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1, timer->Active() ? "active" : "inactive");
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1, timer->HasFlags(tfActive) ? "active" : "inactive");
|
||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
|
||||
}
|
||||
else
|
||||
|
161
timers.c
161
timers.c
@ -4,13 +4,14 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.c 1.9 2004/02/13 15:37:49 kls Exp $
|
||||
* $Id: timers.c 1.10 2004/02/29 14:20:48 kls Exp $
|
||||
*/
|
||||
|
||||
#include "timers.h"
|
||||
#include <ctype.h>
|
||||
#include "channels.h"
|
||||
#include "i18n.h"
|
||||
#include "libsi/si.h"
|
||||
|
||||
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
|
||||
// format characters in order to allow any number of blanks after a numeric
|
||||
@ -23,8 +24,10 @@ char *cTimer::buffer = NULL;
|
||||
cTimer::cTimer(bool Instant, bool Pause)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = Instant ? taActInst : taInactive;
|
||||
recording = pending = inVpsMargin = false;
|
||||
flags = tfNone;
|
||||
if (Instant)
|
||||
SetFlags(tfActive | tfInstant);
|
||||
channel = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
time_t t = time(NULL);
|
||||
struct tm tm_r;
|
||||
@ -40,6 +43,7 @@ cTimer::cTimer(bool Instant, bool Pause)
|
||||
*file = 0;
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
event = NULL;
|
||||
if (Instant && channel)
|
||||
snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
|
||||
}
|
||||
@ -47,12 +51,17 @@ cTimer::cTimer(bool Instant, bool Pause)
|
||||
cTimer::cTimer(const cEvent *Event)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = true;
|
||||
recording = pending = inVpsMargin = false;
|
||||
flags = tfActive;
|
||||
if (Event->Vps() && Setup.UseVps)
|
||||
SetFlags(tfVps);
|
||||
channel = Channels.GetByChannelID(Event->ChannelID(), true);
|
||||
time_t tstart = Event->StartTime();
|
||||
time_t tstop = tstart + Event->Duration() + Setup.MarginStop * 60;
|
||||
tstart -= Setup.MarginStart * 60;
|
||||
time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime();
|
||||
time_t tstop = tstart + Event->Duration();
|
||||
if (!(HasFlags(tfVps))) {
|
||||
tstop += Setup.MarginStop * 60;
|
||||
tstart -= Setup.MarginStart * 60;
|
||||
}
|
||||
struct tm tm_r;
|
||||
struct tm *time = localtime_r(&tstart, &tm_r);
|
||||
day = time->tm_mday;
|
||||
@ -69,6 +78,7 @@ cTimer::cTimer(const cEvent *Event)
|
||||
strn0cpy(file, Event->Title(), sizeof(file));
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
event = Event;
|
||||
}
|
||||
|
||||
cTimer::~cTimer()
|
||||
@ -81,6 +91,7 @@ cTimer& cTimer::operator= (const cTimer &Timer)
|
||||
memcpy(this, &Timer, sizeof(*this));
|
||||
if (summary)
|
||||
summary = strdup(summary);
|
||||
event = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -97,7 +108,7 @@ const char *cTimer::ToText(bool UseChannelID)
|
||||
free(buffer);
|
||||
strreplace(file, ':', '|');
|
||||
strreplace(summary, '\n', '|');
|
||||
asprintf(&buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s\n", active, UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number()), PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
|
||||
asprintf(&buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number()), PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
|
||||
strreplace(summary, '|', '\n');
|
||||
strreplace(file, '|', ':');
|
||||
return buffer;
|
||||
@ -205,7 +216,7 @@ bool cTimer::Parse(const char *s)
|
||||
s = s2;
|
||||
}
|
||||
bool result = false;
|
||||
if (8 <= sscanf(s, "%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) {
|
||||
if (8 <= sscanf(s, "%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) {
|
||||
if (summary && !*skipspace(summary)) {
|
||||
free(summary);
|
||||
summary = NULL;
|
||||
@ -290,7 +301,7 @@ char *cTimer::SetFile(const char *File)
|
||||
return file;
|
||||
}
|
||||
|
||||
bool cTimer::Matches(time_t t)
|
||||
bool cTimer::Matches(time_t t, bool Directly)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
if (t == 0)
|
||||
@ -316,9 +327,35 @@ bool cTimer::Matches(time_t t)
|
||||
}
|
||||
if (!startTime)
|
||||
startTime = firstday; // just to have something that's more than a week in the future
|
||||
else if (t > startTime || t > firstday + SECSINDAY + 3600) // +3600 in case of DST change
|
||||
else if (!Directly && (t > startTime || t > firstday + SECSINDAY + 3600)) // +3600 in case of DST change
|
||||
firstday = 0;
|
||||
return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
|
||||
|
||||
if (HasFlags(tfActive)) {
|
||||
if (HasFlags(tfVps) && !Directly && event && event->Vps()) {
|
||||
startTime = event->StartTime();
|
||||
stopTime = startTime + event->Duration();
|
||||
return event->RunningStatus() > SI::RunningStatusNotRunning;
|
||||
}
|
||||
return startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int cTimer::Matches(const cEvent *Event)
|
||||
{
|
||||
if (channel->GetChannelID() == Event->ChannelID()) {
|
||||
bool UseVps = HasFlags(tfVps) && Event->Vps();
|
||||
time_t t1 = UseVps ? Event->Vps() : Event->StartTime();
|
||||
time_t t2 = t1 + Event->Duration();
|
||||
bool m1 = Matches(t1, true);
|
||||
bool m2 = UseVps ? m1 : Matches(t2, true);
|
||||
startTime = stopTime = 0;
|
||||
if (m1 && m2)
|
||||
return tmFull;
|
||||
if (m1 || m2)
|
||||
return tmPartial;
|
||||
}
|
||||
return tmNone;
|
||||
}
|
||||
|
||||
time_t cTimer::StartTime(void)
|
||||
@ -335,6 +372,19 @@ time_t cTimer::StopTime(void)
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
void cTimer::SetEvent(const cEvent *Event)
|
||||
{
|
||||
if (event != Event) { //XXX TODO check event data, too???
|
||||
if (Event) {
|
||||
char vpsbuf[64] = "";
|
||||
if (Event->Vps())
|
||||
sprintf(vpsbuf, "(VPS: %s) ", Event->GetVpsString());
|
||||
isyslog("timer %d (%d %04d-%04d '%s') set to event %s %s-%s %s'%s'", Index() + 1, Channel()->Number(), start, stop, file, Event->GetDateString(), Event->GetTimeString(), Event->GetEndTimeString(), vpsbuf, Event->Title());
|
||||
}
|
||||
event = Event;
|
||||
}
|
||||
}
|
||||
|
||||
void cTimer::SetRecording(bool Recording)
|
||||
{
|
||||
recording = Recording;
|
||||
@ -346,28 +396,52 @@ void cTimer::SetPending(bool Pending)
|
||||
pending = Pending;
|
||||
}
|
||||
|
||||
void cTimer::SetActive(int Active)
|
||||
void cTimer::SetInVpsMargin(bool InVpsMargin)
|
||||
{
|
||||
active = Active;
|
||||
if (InVpsMargin && !inVpsMargin)
|
||||
isyslog("timer %d (%d %04d-%04d '%s') entered VPS margin", Index() + 1, Channel()->Number(), start, stop, file);
|
||||
inVpsMargin = InVpsMargin;
|
||||
}
|
||||
|
||||
void cTimer::SetFlags(int Flags)
|
||||
{
|
||||
flags |= Flags;
|
||||
}
|
||||
|
||||
void cTimer::ClrFlags(int Flags)
|
||||
{
|
||||
flags &= ~Flags;
|
||||
}
|
||||
|
||||
void cTimer::InvFlags(int Flags)
|
||||
{
|
||||
flags ^= Flags;
|
||||
}
|
||||
|
||||
bool cTimer::HasFlags(int Flags)
|
||||
{
|
||||
return (flags & Flags) == Flags;
|
||||
}
|
||||
|
||||
void cTimer::Skip(void)
|
||||
{
|
||||
firstday = IncDay(SetTime(StartTime(), 0), 1);
|
||||
event = NULL;
|
||||
}
|
||||
|
||||
void cTimer::OnOff(void)
|
||||
{
|
||||
if (IsSingleEvent())
|
||||
active = !active;
|
||||
InvFlags(tfActive);
|
||||
else if (firstday) {
|
||||
firstday = 0;
|
||||
active = false;
|
||||
ClrFlags(tfActive);
|
||||
}
|
||||
else if (active)
|
||||
else if (HasFlags(tfActive))
|
||||
Skip();
|
||||
else
|
||||
active = true;
|
||||
SetFlags(tfActive);
|
||||
event = NULL;
|
||||
Matches(); // refresh start and end time
|
||||
}
|
||||
|
||||
@ -396,12 +470,59 @@ cTimer *cTimers::GetMatch(time_t t)
|
||||
return t0;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetMatch(const cEvent *Event, int *Match)
|
||||
{
|
||||
cTimer *t = NULL;
|
||||
int m = tmNone;
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
int tm = ti->Matches(Event);
|
||||
if (tm > m) {
|
||||
t = ti;
|
||||
m = tm;
|
||||
if (m == tmFull)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Match)
|
||||
*Match = m;
|
||||
return t;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetNextActiveTimer(void)
|
||||
{
|
||||
cTimer *t0 = NULL;
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (ti->Active() && (!t0 || *ti < *t0))
|
||||
if ((ti->HasFlags(tfActive)) && (!t0 || *ti < *t0))
|
||||
t0 = ti;
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
void cTimers::SetEvents(void)
|
||||
{
|
||||
cSchedulesLock SchedulesLock;
|
||||
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
|
||||
if (Schedules) {
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
const cSchedule *Schedule = Schedules->GetSchedule(ti->Channel()->GetChannelID());
|
||||
const cEvent *Event = NULL;
|
||||
if (Schedule) {
|
||||
//XXX what if the Schedule doesn't have any VPS???
|
||||
const cEvent *e;
|
||||
int Match = tmNone;
|
||||
int i = 0;
|
||||
while ((e = Schedule->GetEventNumber(i++)) != NULL) {
|
||||
int m = ti->Matches(e);
|
||||
if (m > Match) {
|
||||
Match = m;
|
||||
Event = e;
|
||||
if (Match == tmFull)
|
||||
break;
|
||||
//XXX what if there's another event with the same VPS time???
|
||||
}
|
||||
}
|
||||
}
|
||||
ti->SetEvent(Event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
timers.h
35
timers.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.h 1.6 2003/12/13 13:04:21 kls Exp $
|
||||
* $Id: timers.h 1.7 2004/02/29 14:18:17 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TIMERS_H
|
||||
@ -15,19 +15,21 @@
|
||||
#include "epg.h"
|
||||
#include "tools.h"
|
||||
|
||||
enum eTimerActive { taInactive = 0,
|
||||
taActive = 1,
|
||||
taInstant = 2,
|
||||
taActInst = (taActive | taInstant)
|
||||
};
|
||||
enum eTimerFlags { tfNone = 0x0000,
|
||||
tfActive = 0x0001,
|
||||
tfInstant = 0x0002,
|
||||
tfVps = 0x0004,
|
||||
tfAll = 0xFFFF,
|
||||
};
|
||||
enum eTimerMatch { tmNone, tmPartial, tmFull };
|
||||
|
||||
class cTimer : public cListObject {
|
||||
friend class cMenuEditTimer;
|
||||
private:
|
||||
time_t startTime, stopTime;
|
||||
static char *buffer;
|
||||
bool recording, pending;
|
||||
int active;
|
||||
bool recording, pending, inVpsMargin;
|
||||
int flags;
|
||||
cChannel *channel;
|
||||
int day;
|
||||
int start;
|
||||
@ -37,6 +39,7 @@ private:
|
||||
char file[MaxFileName];
|
||||
time_t firstday;
|
||||
char *summary;
|
||||
const cEvent *event;
|
||||
public:
|
||||
cTimer(bool Instant = false, bool Pause = false);
|
||||
cTimer(const cEvent *Event);
|
||||
@ -45,7 +48,8 @@ public:
|
||||
virtual bool operator< (const cListObject &ListObject);
|
||||
bool Recording(void) { return recording; }
|
||||
bool Pending(void) { return pending; }
|
||||
int Active(void) { return active; }
|
||||
bool InVpsMargin(void) { return inVpsMargin; }
|
||||
int Flags(void) { return flags; }
|
||||
const cChannel *Channel(void) { return channel; }
|
||||
int Day(void) { return day; }
|
||||
int Start(void) { return start; }
|
||||
@ -56,6 +60,7 @@ public:
|
||||
time_t FirstDay(void) { return firstday; }
|
||||
const char *Summary(void) { return summary; }
|
||||
const char *ToText(bool UseChannelID = false);
|
||||
const cEvent *Event(void) { return event; }
|
||||
bool Parse(const char *s);
|
||||
bool Save(FILE *f);
|
||||
bool IsSingleEvent(void);
|
||||
@ -65,12 +70,18 @@ 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 Matches(time_t t = 0, bool Directly = false);
|
||||
int Matches(const cEvent *Event);
|
||||
time_t StartTime(void);
|
||||
time_t StopTime(void);
|
||||
void SetEvent(const cEvent *Event);
|
||||
void SetRecording(bool Recording);
|
||||
void SetPending(bool Pending);
|
||||
void SetActive(int Active);
|
||||
void SetInVpsMargin(bool InVpsMargin);
|
||||
void SetFlags(int Flags);
|
||||
void ClrFlags(int Flags);
|
||||
void InvFlags(int Flags);
|
||||
bool HasFlags(int Flags);
|
||||
void Skip(void);
|
||||
void OnOff(void);
|
||||
const char *PrintFirstDay(void);
|
||||
@ -85,10 +96,12 @@ private:
|
||||
public:
|
||||
cTimer *GetTimer(cTimer *Timer);
|
||||
cTimer *GetMatch(time_t t);
|
||||
cTimer *GetMatch(const cEvent *Event, int *Match = NULL);
|
||||
cTimer *GetNextActiveTimer(void);
|
||||
int BeingEdited(void) { return beingEdited; }
|
||||
void IncBeingEdited(void) { beingEdited++; }
|
||||
void DecBeingEdited(void) { beingEdited--; }
|
||||
void SetEvents(void);
|
||||
};
|
||||
|
||||
extern cTimers Timers;
|
||||
|
19
vdr.5
19
vdr.5
@ -8,7 +8,7 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" vdr distribution.
|
||||
.\"
|
||||
.\" $Id: vdr.5 1.25 2004/02/22 13:18:48 kls Exp $
|
||||
.\" $Id: vdr.5 1.26 2004/02/24 12:36:35 kls Exp $
|
||||
.\"
|
||||
.TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files"
|
||||
.SH NAME
|
||||
@ -196,12 +196,17 @@ The fields in a timer definition have the following meaning (from left
|
||||
to right):
|
||||
.TP
|
||||
.B Status
|
||||
Defines whether this timer is \fBinactive\fR (0) or \fBactive\fR (1).
|
||||
The value 3 is used for instant recordings.
|
||||
Values other than these can be used by external programs to mark active timers
|
||||
and recognize if the user has modified them. When a user modifes an active
|
||||
timer the \fBstatus\fR field will be explicitly set to '1' (or '0', respectively,
|
||||
if the user deactivates the timer).
|
||||
The individual bits in this field have the following meaning:
|
||||
.TS
|
||||
tab (@);
|
||||
l l.
|
||||
\fB1\fR@the timer is active (and will record if it hits)
|
||||
\fB2\fR@this is an instant recording timer
|
||||
\fB4\fR@this timer uses VPS
|
||||
.TE
|
||||
Bits other than these can be used by external programs to mark active timers
|
||||
and recognize if the user has modified them. When a user modifies an active
|
||||
timer, the upper 16 bits of this 32 bit parameter will be explicitly set to 0.
|
||||
|
||||
Note: in order to allow future extensibility, external programs using the
|
||||
\fBstatus\fR parameter should only use the upper 16 bit of this 32 bit parameter
|
||||
|
30
vdr.c
30
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* The project's page is at http://www.cadsoft.de/vdr
|
||||
*
|
||||
* $Id: vdr.c 1.177 2004/02/15 14:29:30 kls Exp $
|
||||
* $Id: vdr.c 1.178 2004/02/29 14:21:22 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -483,6 +483,7 @@ int main(int argc, char *argv[])
|
||||
int MaxLatencyTime = 0;
|
||||
bool ForceShutdown = false;
|
||||
bool UserShutdown = false;
|
||||
bool TimerInVpsMargin = false;
|
||||
|
||||
while (!Interrupted) {
|
||||
// Handle emergency exits:
|
||||
@ -548,8 +549,15 @@ int main(int argc, char *argv[])
|
||||
PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel;
|
||||
// Timers and Recordings:
|
||||
if (!Timers.BeingEdited()) {
|
||||
time_t Now = time(NULL); // must do both following calls with the exact same time!
|
||||
static time_t LastSetEvents = 0;//XXX trigger by actual EPG data modification???
|
||||
if (!Menu && time(NULL) - LastSetEvents > 5) {
|
||||
Timers.SetEvents();
|
||||
LastSetEvents = time(NULL);
|
||||
}
|
||||
time_t Now = time(NULL); // must do all following calls with the exact same time!
|
||||
// Process ongoing recordings:
|
||||
cRecordControls::Process(Now);
|
||||
// Start new recordings:
|
||||
cTimer *Timer = Timers.GetMatch(Now);
|
||||
if (Timer) {
|
||||
if (!cRecordControls::Start(Timer))
|
||||
@ -557,6 +565,21 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
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()) {
|
||||
Timer->SetInVpsMargin(true);
|
||||
TimerInVpsMargin = true;
|
||||
//XXX if not primary device has TP???
|
||||
LastTimerChannel = Timer->Channel()->Number();
|
||||
cRecordControls::Start(Timer); // will only switch the device
|
||||
}
|
||||
}
|
||||
else
|
||||
Timer->SetInVpsMargin(false);
|
||||
}
|
||||
}
|
||||
// CAM control:
|
||||
if (!Menu && !Interface->IsOpen())
|
||||
@ -758,7 +781,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
if (!Menu) {
|
||||
EITScanner.Process();
|
||||
if (!TimerInVpsMargin)
|
||||
EITScanner.Process();
|
||||
if (!cCutter::Active() && cCutter::Ended()) {
|
||||
if (cCutter::Error())
|
||||
Interface->Error(tr("Editing process failed!"));
|
||||
|
Loading…
Reference in New Issue
Block a user