summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2004-02-29 18:00:00 +0100
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2004-02-29 18:00:00 +0100
commit5a4eb3f1041b8a53826cebe570f9d201f3d35826 (patch)
tree4444f7bec812600713430e7f0570dfdd0bf9136e
parent3fc29659759abb10154b78f9e3568407e523e1fc (diff)
downloadvdr-patch-lnbsharing-vdr-1.3.5.tar.gz
vdr-patch-lnbsharing-vdr-1.3.5.tar.bz2
Version 1.3.5vdr-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 a video nor an audio PID. - Fixed editing channels (SID now range checked) and creating new channels (NID, TID and RID are now set to 0). - Fixed transponder handling to make it work with satellites that provide two transponders on the same frequency, with different polarization, like Hispasat at S30.0W (thanks to Thomas Bergwinkl for pointing this out). See man vdr(5) for details about the enhanced channel ID format. - Since there appears to be no general solution for the UPT error yet, a recording now initiates an "emergency exit" if the number of UPT errors during one recording exceeds 10 (suggested by Gregoire Favre). Since the UPT error doesn't happen on my system, this has not been explicitly tested. The "preliminary fix" for the UPT error in VDR/dvbdevice.c has been disabled by default, since it makes channel switching unpleasently slow. If you want to have that workaround back, you can uncomment the line //#define WAIT_FOR_LOCK_AFTER_TUNING 1 in VDR/dvbdevice.c. - Adapted the 'sky' plugin to use the actual channel IDs, and to fetch EPG data from www.bleb.org. - Limited automatic retuning to devices that actually provide the transponder (necessary for the 'sky' plugin). - Fixed handling receivers in the 'sky' plugin, so that a recording on the same 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 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 command LSTE is guaranteed to be sorted by start time. - Now using the 'running status' in the channel display, so that a programme that has an end time that is before the current time, but is still running, will still be shown in the display (provided the broadcasters handle the 'running status' flag correctly). This also applies to programmes that have a start time that is in the future, but are already running. - Implemented an "EPG linger time", which can be set to have older EPG information 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") 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).
-rw-r--r--CONTRIBUTORS10
-rw-r--r--HISTORY62
-rw-r--r--MANUAL32
-rw-r--r--PLUGINS/src/sky/HISTORY8
-rw-r--r--PLUGINS/src/sky/README29
-rw-r--r--PLUGINS/src/sky/channels.conf.sky38
-rwxr-xr-xPLUGINS/src/sky/getskyepg.pl136
-rw-r--r--PLUGINS/src/sky/sky.c96
-rw-r--r--README.vps130
-rw-r--r--channels.c32
-rw-r--r--channels.conf39
-rw-r--r--channels.h8
-rw-r--r--config.c15
-rw-r--r--config.h9
-rw-r--r--device.h4
-rw-r--r--dvbdevice.c6
-rw-r--r--eit.c61
-rw-r--r--eitscan.c5
-rw-r--r--eitscan.h2
-rw-r--r--epg.c159
-rw-r--r--epg.h38
-rwxr-xr-xepg2html.pl10
-rw-r--r--i18n.c78
-rw-r--r--libsi/descriptor.c23
-rw-r--r--libsi/descriptor.h14
-rw-r--r--libsi/headers.h59
-rw-r--r--libsi/section.c18
-rw-r--r--libsi/section.h15
-rw-r--r--libsi/si.c14
-rw-r--r--libsi/si.h7
-rw-r--r--menu.c163
-rw-r--r--menu.h4
-rw-r--r--menuitems.c19
-rw-r--r--menuitems.h12
-rw-r--r--nit.c4
-rw-r--r--remux.c9
-rw-r--r--remux.h3
-rw-r--r--svdrp.c76
-rw-r--r--timers.c163
-rw-r--r--timers.h35
-rw-r--r--vdr.528
-rw-r--r--vdr.c50
42 files changed, 1346 insertions, 377 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index bc4c2ee..438d827 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -279,6 +279,8 @@ Andy Grobb <Charly98@01019freenet.de>
Thomas Heiligenmann <thomas@heiligenmann.de>
for implementing the SVDRP commands LSTR and DELR
for adding MPEG1 handling to cDvbDevice::StillPicture()
+ for extending 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
Norbert Schmidt <nschmidt-nrw@t-online.de>
for filling in some missing teletext PIDs
@@ -438,6 +440,8 @@ Gregoire Favre <greg@ulima.unil.ch>
for fixing some function headers to make them compile with gcc 3.x
for reporting a bug in taking an active SVDRP connection into account when doing shutdown
for translating OSD texts to the French language
+ for suggesting to initiate an "emergency exit" if there are UPT errors during a
+ recording
Sven Grothklags <sven@uni-paderborn.de>
for fixing the cutting mechanism to make it re-sync in case a frame is larger
@@ -559,6 +563,7 @@ Jaakko Hyvätti <jaakko@hyvatti.iki.fi>
for fixing the minimum lifespan of deleted recordings
for suggesting to improve channel switching in case of numerical input by switching
as soon as the channel is unique
+ for implementing an "EPG linger time"
Dennis Noordsij <dennis.noordsij@wiral.com>
for reporting a small glitch when switching channels
@@ -933,3 +938,8 @@ Andreas Regel <andreas.regel@gmx.de>
Thomas Bergwinkl <Thomas.Bergwinkl@t-online.de>
for fixing the validity check for channel IDs, because some providers use TIDs
with value 0
+ for pointing out that transponder handling didn't work with satellites that provide
+ two transponders on the same frequency, with different polarization
+
+Stéphane Esté-Gracias <sestegra@free.fr>
+ for fixing a typo in libsi/si.h
diff --git a/HISTORY b/HISTORY
index 317be40..e995005 100644
--- a/HISTORY
+++ b/HISTORY
@@ -2651,3 +2651,65 @@ Video Disk Recorder Revision History
- Removed the now obsolete CaCaps stuff. The Setup/CICAM menu now displays the
actual CAM type as reported by the CAM. The 'ca.conf' file has been stripped
down to the values 0..4.
+
+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
+ a video nor an audio PID.
+- Fixed editing channels (SID now range checked) and creating new channels (NID,
+ TID and RID are now set to 0).
+- Fixed transponder handling to make it work with satellites that provide two
+ transponders on the same frequency, with different polarization, like Hispasat
+ at S30.0W (thanks to Thomas Bergwinkl for pointing this out). See man vdr(5)
+ for details about the enhanced channel ID format.
+- Since there appears to be no general solution for the UPT error yet, a recording
+ now initiates an "emergency exit" if the number of UPT errors during one
+ recording exceeds 10 (suggested by Gregoire Favre). Since the UPT error doesn't
+ happen on my system, this has not been explicitly tested.
+ The "preliminary fix" for the UPT error in VDR/dvbdevice.c has been disabled
+ by default, since it makes channel switching unpleasently slow. If you want
+ to have that workaround back, you can uncomment the line
+ //#define WAIT_FOR_LOCK_AFTER_TUNING 1
+ in VDR/dvbdevice.c.
+- Adapted the 'sky' plugin to use the actual channel IDs, and to fetch EPG data
+ from www.bleb.org.
+- Limited automatic retuning to devices that actually provide the transponder
+ (necessary for the 'sky' plugin).
+- Fixed handling receivers in the 'sky' plugin, so that a recording on the same
+ 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 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
+ command LSTE is guaranteed to be sorted by start time.
+- Now using the 'running status' in the channel display, so that a programme
+ that has an end time that is before the current time, but is still running,
+ will still be shown in the display (provided the broadcasters handle the
+ 'running status' flag correctly). This also applies to programmes that have
+ a start time that is in the future, but are already running.
+- Implemented an "EPG linger time", which can be set to have older EPG information
+ 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")
+ 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).
diff --git a/MANUAL b/MANUAL
index 80977a9..c4d4391 100644
--- a/MANUAL
+++ b/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
@@ -462,6 +480,9 @@ Version 1.2
be fixed accordingly. Restart VDR if you want to make sure
all data is fixed.
+ EPG linger time = 0 The time (in minutes) within which old EPG information
+ shall still be displayed in the "Schedule" menu.
+
Set system time = no Defines whether the system time will be set according to
the time received from the DVB data stream.
Note that this works only if VDR is running under a user
@@ -582,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
diff --git a/PLUGINS/src/sky/HISTORY b/PLUGINS/src/sky/HISTORY
index d3da71e..c1c5f98 100644
--- a/PLUGINS/src/sky/HISTORY
+++ b/PLUGINS/src/sky/HISTORY
@@ -16,3 +16,11 @@ VDR Plugin 'sky' Revision History
2004-01-04: Version 0.2.0
- Implemented automatic PID switching and channel detection
+
+2004-02-15: Version 0.3.0
+
+- Now using the actual channel IDs a derived from the data stream.
+- Switched EPG data retrieval to http://www.bleb.org.
+- Added automatic DST detection to getskyepg.pl.
+- Fixed handling receivers, so that a recording on the same channel
+ won't interrupt an ongoing Transfer mode.
diff --git a/PLUGINS/src/sky/README b/PLUGINS/src/sky/README
index bd089cc..185a98b 100644
--- a/PLUGINS/src/sky/README
+++ b/PLUGINS/src/sky/README
@@ -28,23 +28,18 @@ control the Digibox.
In order to access the Sky channels VDR needs to know the channel number
under which each channel is stored in the Sky Digibox. These numbers are
-used as 'frequency' parameters in the channels.conf definitions of the Sky
-channels (see the file 'channels.conf.sky'). Since these numbers are always
-less than 1000, they can be easily distinguished from normal satellite
-transponder frequencies. The VPID is 160 and the APID is 80 for all Sky
-channels. These are just fake PIDs, since the Kfir card always uses these
-fixed PIDs. The 'Ca' parameter of the Sky channels is set to 301, which
-is defined as "Videoguard, Sky Digital" in VDR's 'ca.conf' file. Again, please
-note that VDR doesn't do any decrypting here, this is just to mark these
-channels as "conditionally accessible" and have a way of setting the CICAM
-value for the Kfir device in VDR's Setup menu.
-
-The Sky EPG is available on the Internet at http://www.ananova.com.
+stored in the file 'channels.conf.sky', together with the channel IDs as
+derived from the actual channel data and the names under which the EPG
+data for each channel can be found (see below). Copy this file to your
+plugins config directory, in a subdirectory named 'sky', as in
+
+/video/plugins/sky/channels.conf.sky
+
+The Sky EPG is available on the Internet at http://www.bleb.org.
The Perl script getskyepg.pl extracts the EPG data from these pages
-and sends it to VDR via an SVDRP connection. The channel numbers Sky
-uses to generate the EPG pages are stored as the 'sid' parameter in
-the channels.conf definitions of the Sky channels. You can keep your
-EPG data up-to-date by entering a call to getskyepg.pl into your
-/etc/crontab. Call 'getskyepg.pl -h' for a list of options.
+and sends it to VDR via an SVDRP connection. The channel names as
+used on the bleb.org pages are defined in the channels.conf.sky file.
+You can keep your EPG data up-to-date by entering a call to getskyepg.pl
+into your /etc/crontab. Call 'getskyepg.pl -h' for a list of options.
The getskyepg.pl script requires the programs /usr/bin/wget and /usr/bin/logger
to be installed on your system.
diff --git a/PLUGINS/src/sky/channels.conf.sky b/PLUGINS/src/sky/channels.conf.sky
index f4a437b..ba262f8 100644
--- a/PLUGINS/src/sky/channels.conf.sky
+++ b/PLUGINS/src/sky/channels.conf.sky
@@ -1,4 +1,34 @@
-Sky One:106:h:S28.2E:0:160:80:0:301:222
-itv2:226:h:S28.2E:0:160:80:0:301:451
-sci-fi:130:h:S28.2E:0:160:80:0:301:161
-Paramount Comedy:127:h:S28.2E:0:160:80:0:301:185
+# Sky channel definitions
+#
+# Syntax:
+#
+# ChannelID:ChannelNumber:EPGname
+#
+# where
+#
+# ChannelID is the channel ID as derived from the actual channel
+# data as broadcast in the data stream (see man vdr(5)).
+#
+# ChannelNumber is the number of this channel as you have to
+# enter it on the DigiBox remote control.
+#
+# EPGname is the name of the page at www.bleb.org that has EPG
+# data for this channel (without the '.xml'). If no such
+# page exists, 'x' is entered.
+#
+S28.2E-2-2027-4705:106:sky_one
+S28.2E-2-2027-5104:107:sky_one_mix
+S28.2E-2-2054-10240:226:itv2
+S28.2E-2-2023-4905:130:scifi
+S28.2E-2-2025-5904:127:paramount
+S28.2E-2-2009-6201:551:discovery
+S28.2E-2-2020-4809:310:sky_cinema
+S28.2E-2-2007-4303:301:sky_movies1
+S28.2E-2-2007-4302:302:sky_movies2
+S28.2E-2-2007-4403:303:sky_movies3
+S28.2E-2-2011-4402:304:sky_movies4
+S28.2E-2-2011-4503:305:sky_movies5
+S28.2E-2-2011-4502:306:sky_movies6
+S28.2E-2-2020-4603:307:sky_movies7
+S28.2E-2-2007-5502:308:sky_movies8
+S28.2E-2-2020-4602:309:x
diff --git a/PLUGINS/src/sky/getskyepg.pl b/PLUGINS/src/sky/getskyepg.pl
index 46c2d7f..77768ad 100755
--- a/PLUGINS/src/sky/getskyepg.pl
+++ b/PLUGINS/src/sky/getskyepg.pl
@@ -1,14 +1,14 @@
#!/usr/bin/perl
-# getskyepg.pl: Get EPG data from Sky's web pages
+# getskyepg.pl: Get EPG data for Sky channels from the Internet
#
# Connects to a running VDR instance via SVDRP, gets the channel data
-# for the Sky channels and connects to Sky's web pages to extract the
+# for the Sky channels and connects to Internet web pages to extract the
# EPG data for these channels. The result is sent to VDR via SVDRP.
#
# See the README file for copyright information and how to reach the author.
#
-# $Id: getskyepg.pl 1.2 2003/04/02 16:21:47 kls Exp $
+# $Id: getskyepg.pl 1.3 2004/02/15 13:35:52 kls Exp $
use Getopt::Std;
use Time::Local;
@@ -16,31 +16,33 @@ use Time::Local;
$Usage = qq{
Usage: $0 [options]
-Options: -d hostname destination hostname (default: localhost)
+Options: -c filename channel config file name (default: channels.conf.sky)
+ -d hostname destination hostname (default: localhost)
-p port SVDRP port number (default: 2001)
-S source channel source (default: S28.2E)
-D days days to get EPG for (1..7, default: 2)
};
-die $Usage if (!getopts("d:D:hp:S:") || $opt_h);
+die $Usage if (!getopts("c:d:D:hp:S:") || $opt_h);
+$Conf = $opt_c || "channels.conf.sky";
$Dest = $opt_d || "localhost";
$Port = $opt_p || 2001;
$Source = $opt_S || "S28.2E";
$Days = $opt_D || 2;
-$SkyWebPage = "www.ananova.com/tv/frontpage.html";
+$SkyWebPage = "www.bleb.org/tv/data/listings";
$WGET = "/usr/bin/wget -q -O-";
$LOGGER = "/usr/bin/logger -t SKYEPG";
-$DST = -3600; ##XXX TODO find out whether DST is active!
+$DST = -3600; # Daylight Saving Time offset
$SecsInDay = 86400;
-$MaxFrequency = 1000;
-$idxName = 0;
-$idxFrequency = 1;
-$idxSource = 3;
-$idxSid = 9;
+@Channels = ();
+
+$idxSource = 0;
+$idxNumber = 1;
+$idxName = 2;
Error("days out of range: $Days") unless (1 <= $Days && $Days <= 7);
@@ -57,34 +59,52 @@ sub Error
sub GetChannels
{
- SVDRPsend("LSTC");
- my @channels = ();
- for (SVDRPreceive(250)) {
- my @a = split(':', substr($_, 4));
- if ($a[$idxSource] eq $Source && $a[$idxFrequency] < $MaxFrequency) {
- push(@channels, [@a]);
- }
- }
- return @channels;
+ open(CHANNELS, $Conf) || Error("$Conf: $!");
+ while (<CHANNELS>) {
+ chomp;
+ next if (/^#/);
+ my @a = split(":");
+ push(@Channels, [@a]) unless ($a[$idxName] eq "x");
+ }
+ close(CHANNELS);
}
+GetChannels();
+
sub GetPage
{
my $channel = shift;
my $day = shift;
- my $url = "$SkyWebPage?c=$channel&day=day$day";
+ $day--;
+ my $url = "$SkyWebPage/$day/$channel.xml";
Log("reading $url");
my @page = split("\n", `$WGET '$url'`);
Log("received " . ($#page + 1) . " lines");
return @page;
}
+sub StripWhitespace
+{
+ my $s = shift;
+ $s =~ s/\s*(.*)\s*/$1/;
+ $s =~ s/\s+/ /g;
+ return $s;
+}
+
+sub Extract
+{
+ my $s = shift;
+ my $t = shift;
+ $s =~ /<$t>([^<]*)<\/$t>/;
+ return StripWhitespace($1);
+}
+
# In order to get the duration we need to buffer the last event:
$Id = "";
$Time = 0;
$Title = "";
-$Episode = "";
-$Descr = "";
+$Subtitle = "";
+$Desc = "";
sub GetEpgData
{
@@ -94,40 +114,64 @@ sub GetEpgData
$Time = 0;
for $day (1 .. $Days) {
my $dt = 0;
- my $ap = "";
my @page = GetPage($channel, $day);
+ my $data = "";
for $line (@page) {
- if ($line =~ /^<\/tr><tr /) {
- # extract information:
- my ($time, $title, $episode, $descr) = ($line =~ /^.*?<b>(.*?)<\/b>.*?<b>(.*?)<\/b> *(<i>.*?<\/i>)? *(.*?) *<\/small>/);
- my ($h, $m, $a) = ($time =~ /([0-9]+)\.([0-9]+)(.)m/);
- # handle am/pm:
- $dt = $SecsInDay if ($ap eq "p" && $a eq "a");
- $ap = $a;
- $h += 12 if ($a eq "p" && $h < 12);
- $h -= 12 if ($a eq "a" && $h == 12);
+ chomp($line);
+ if ($line =~ /<programme>/) {
+ $data = "";
+ }
+ elsif ($line =~ /<\/programme>/) {
+ my $title = Extract($data, "title");
+ my $subtitle = Extract($data, "subtitle");
+ my $desc = Extract($data, "desc");
+ my $start = Extract($data, "start");
+ # 'end' is useless, because it is sometimes missing :-(
+ # my $end = Extract($data, "end");
+ if (!$subtitle) {
+ # They sometimes write all info into the description, as in
+ # Episode: some description.
+ # Why don't they just fill in the data correctly?
+ my ($s, $d) = ($desc =~ /([^:]*)[:](.*)/);
+ if ($s && $d) {
+ $subtitle = $s;
+ $desc = $d;
+ }
+ }
+ # 'start' and 'end' as time of day isn't of much use here, since
+ # the page for one day contains data that actually belongs to the
+ # next day (after midnight). Oh well, lets reconstruct the missing
+ # information:
+ $start = "0" . $start if (length($start) < 4);
+ my ($h, $m) = ($start =~ /(..)(..)/);
+ $dt = $SecsInDay if ($h > 12);
# convert to time_t:
my @gmt = gmtime;
$gmt[0] = 0; # seconds
$gmt[1] = $m; # minutes
$gmt[2] = $h; # hours
- $time = timegm(@gmt) + ($day - 1) * $SecsInDay + $dt + $DST;
+ $time = timegm(@gmt) + ($day - 1) * $SecsInDay + ($h < 12 ? $dt : 0);
+ # comensate for DST:
+ $time += $DST if (localtime($time))[8];
# create EPG data:
if ($Time) {
$duration = $time - $Time;
SVDRPsend("E $Id $Time $duration");
SVDRPsend("T $Title");
- SVDRPsend("S $Episode");
- SVDRPsend("D $Descr");
+ SVDRPsend("S $Subtitle");
+ SVDRPsend("D $Desc");
SVDRPsend("e");
$numEvents++;
}
# buffer the last event:
$Id = $time / 60 % 0xFFFF; # this gives us unique ids for every minute of over 6 weeks
$Time = $time;
- ($Title = $title) =~ s/<[^>]+>//g;
- ($Episode = $episode) =~ s/<[^>]+>//g;
- ($Descr = $descr) =~ s/<[^>]+>//g;
+ $Title = $title;
+ $Subtitle = $subtitle;
+ $Desc = $desc;
+ }
+ else {
+ $data .= $line;
}
}
}
@@ -137,14 +181,10 @@ sub GetEpgData
sub ProcessEpg
{
- Log("getting Sky channel definitions");
- my @channels = GetChannels();
- Error("no Sky channels found") unless @channels;
- Log("found " . ($#channels + 1) . " channels");
- for (@channels) {
- my $channel = @$_[$idxSid];
- my $channelID = "@$_[$idxSource]-0-@$_[$idxFrequency]-$channel";
- Log("processing channel @$_[0]");
+ for (@Channels) {
+ my $channel = @$_[$idxName];
+ my $channelID = @$_[$idxSource];
+ Log("processing channel $channel - $channelID");
SVDRPsend("PUTE");
SVDRPreceive(354);
GetEpgData($channel, $channelID);
diff --git a/PLUGINS/src/sky/sky.c b/PLUGINS/src/sky/sky.c
index 750d778..6196eef 100644
--- a/PLUGINS/src/sky/sky.c
+++ b/PLUGINS/src/sky/sky.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: sky.c 1.4 2004/01/04 15:29:15 kls Exp $
+ * $Id: sky.c 1.6 2004/02/15 14:59:46 kls Exp $
*/
#include <sys/socket.h>
@@ -14,16 +14,53 @@
#include <vdr/plugin.h>
#include <vdr/sources.h>
-static const char *VERSION = "0.2.0";
+static const char *VERSION = "0.3.0";
static const char *DESCRIPTION = "Sky Digibox interface";
// --- cDigiboxDevice --------------------------------------------------------
+#define DUMMYAPID 80
+#define DUMMYVPID 160
+
+class cSkyChannel : public cListObject {
+public:
+ tChannelID channelID;
+ int digiboxChannelNumber;
+ bool Parse(const char *s);
+ };
+
+bool cSkyChannel::Parse(const char *s)
+{
+ char *id = NULL;
+ if (2 == sscanf(s, "%a[^:]:%d", &id, &digiboxChannelNumber))
+ channelID = tChannelID::FromString(id);
+ free(id);
+ return digiboxChannelNumber && channelID.Valid();
+}
+
+class cSkyChannels : public cConfig<cSkyChannel> {
+public:
+ cSkyChannel *GetSkyChannel(const cChannel *Channel);
+ };
+
+cSkyChannel *cSkyChannels::GetSkyChannel(const cChannel *Channel)
+{
+ tChannelID ChannelID = Channel->GetChannelID();
+ for (cSkyChannel *sc = First(); sc; sc = Next(sc)) {
+ if (ChannelID == sc->channelID)
+ return sc;
+ }
+ return NULL;
+}
+
+cSkyChannels SkyChannels;
+
class cDigiboxDevice : public cDevice {
private:
int source;
int digiboxChannelNumber;
int fd_dvr;
+ int apid, vpid;
cTSBuffer *tsBuffer;
int fd_lirc;
void LircSend(const char *s);
@@ -47,6 +84,7 @@ cDigiboxDevice::cDigiboxDevice(void)
source = cSource::FromString("S28.2E");//XXX parameter???
digiboxChannelNumber = 0;
fd_dvr = -1;
+ apid = vpid = 0;
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strn0cpy(addr.sun_path, "/dev/lircd", sizeof(addr.sun_path));//XXX parameter???
@@ -93,7 +131,7 @@ void cDigiboxDevice::LircSend(int n)
bool cDigiboxDevice::SetPid(cPidHandle *Handle, int Type, bool On)
{
- dsyslog("SetPid %d %d", Handle->pid, On);
+ //dsyslog("SetPid %d %d", Handle->pid, On);
return true;
}
@@ -122,6 +160,16 @@ bool cDigiboxDevice::GetTSPacket(uchar *&Data)
int r = tsBuffer->Read();
if (r >= 0) {
Data = tsBuffer->Get();
+ if (Data) {
+ // insert the actual PIDs:
+ int Pid = (((uint16_t)Data[1] & PID_MASK_HI) << 8) | Data[2];
+ if (Pid == DUMMYAPID)
+ Pid = apid;
+ else if (Pid == DUMMYVPID)
+ Pid = vpid;
+ Data[1] = ((Pid >> 8) & 0xFF) | (Data[1] & ~PID_MASK_HI);
+ Data[2] = Pid & 0xFF;
+ }
return true;
}
else if (FATALERRNO) {
@@ -149,9 +197,10 @@ bool cDigiboxDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = true;
- if (ProvidesSource(Channel->Source()) && Channel->Ca() == 0x30) {//XXX
- if (Receiving()) {
- if (digiboxChannelNumber == Channel->Frequency()) {
+ cSkyChannel *SkyChannel = SkyChannels.GetSkyChannel(Channel);
+ if (SkyChannel) {
+ if (Receiving(true)) {
+ if (digiboxChannelNumber == SkyChannel->digiboxChannelNumber) {
needsDetachReceivers = false;
result = true;
}
@@ -168,16 +217,21 @@ bool cDigiboxDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool
bool cDigiboxDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
- if (fd_lirc >= 0 && !Receiving()) { // if we are receiving the channel is already set!
- digiboxChannelNumber = Channel->Frequency();
- //XXX only when recording??? -> faster channel switching!
- LircSend("SKY"); // makes sure the Digibox is "on"
- //XXX lircprint(fd_lirc, "BACKUP");
- //XXX lircprint(fd_lirc, "BACKUP");
- //XXX lircprint(fd_lirc, "BACKUP");
- LircSend(digiboxChannelNumber);
+ if (fd_lirc >= 0 && !Receiving(true)) { // if we are receiving the channel is already set!
+ cSkyChannel *SkyChannel = SkyChannels.GetSkyChannel(Channel);
+ if (SkyChannel) {
+ digiboxChannelNumber = SkyChannel->digiboxChannelNumber;
+ apid = Channel->Apid1();
+ vpid = Channel->Vpid();
+ //XXX only when recording??? -> faster channel switching!
+ LircSend("SKY"); // makes sure the Digibox is "on"
+ //XXX lircprint(fd_lirc, "BACKUP");
+ //XXX lircprint(fd_lirc, "BACKUP");
+ //XXX lircprint(fd_lirc, "BACKUP");
+ LircSend(digiboxChannelNumber);
+ }
}
-return true;
+ return true;
}
// --- cPluginSky ------------------------------------------------------------
@@ -225,8 +279,16 @@ bool cPluginSky::ProcessArgs(int argc, char *argv[])
bool cPluginSky::Initialize(void)
{
// Initialize any background activities the plugin shall perform.
- new cDigiboxDevice;
- return true;
+ const char *ConfigDir = ConfigDirectory(Name());
+ if (ConfigDir) {
+ if (SkyChannels.Load(AddDirectory(ConfigDir, "channels.conf.sky"), true)) {
+ new cDigiboxDevice;
+ return true;
+ }
+ }
+ else
+ esyslog("ERROR: can't get config directory");
+ return false;
}
void cPluginSky::Housekeeping(void)
diff --git a/README.vps b/README.vps
new file mode 100644
index 0000000..2aa19ce
--- /dev/null
+++ b/README.vps
@@ -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...
diff --git a/channels.c b/channels.c
index b5bd9bd..3b0b616 100644
--- a/channels.c
+++ b/channels.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.c 1.23 2004/02/08 11:05:22 kls Exp $
+ * $Id: channels.c 1.24 2004/02/13 15:37:42 kls Exp $
*/
#include "channels.h"
@@ -153,6 +153,13 @@ const char *tChannelID::ToString(void)
return buffer;
}
+tChannelID &tChannelID::ClrPolarization(void)
+{
+ while (tid > 100000)
+ tid -= 100000;
+ return *this;
+}
+
// -- cChannel ---------------------------------------------------------------
char *cChannel::buffer = NULL;
@@ -220,11 +227,25 @@ cChannel& cChannel::operator= (const cChannel &Channel)
return *this;
}
+int cChannel::Transponder(int Frequency, char Polarization)
+{
+ // some satellites have transponders at the same frequency, just with different polarization:
+ switch (tolower(Polarization)) {
+ case 'h': Frequency += 100000; break;
+ case 'v': Frequency += 200000; break;
+ case 'l': Frequency += 300000; break;
+ case 'r': Frequency += 400000; break;
+ }
+ return Frequency;
+}
+
int cChannel::Transponder(void) const
{
int tf = frequency;
while (tf > 20000)
tf /= 1000;
+ if (IsSat())
+ tf = Transponder(tf, polarization);
return tf;
}
@@ -803,7 +824,7 @@ cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short
return NULL;
}
-cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid)
+cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization)
{
for (cChannel *channel = First(); channel; channel = Next(channel)) {
if (!channel->GroupSep() && channel->GetChannelID() == ChannelID)
@@ -816,6 +837,13 @@ cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid)
return channel;
}
}
+ if (TryWithoutPolarization) {
+ ChannelID.ClrPolarization();
+ for (cChannel *channel = First(); channel; channel = Next(channel)) {
+ if (!channel->GroupSep() && channel->GetChannelID().ClrPolarization() == ChannelID)
+ return channel;
+ }
+ }
return NULL;
}
diff --git a/channels.conf b/channels.conf
index e7aa7f3..f36c5f3 100644
--- a/channels.conf
+++ b/channels.conf
@@ -26,8 +26,8 @@ NEUN LIVE,NEUN LIVE Television:12480:vC34:S19.2E:27500:767:768:35:0:897:133:33:0
DSF:12480:vC34:S19.2E:27500:1023:1024=deu:0:0:900:133:33:0
HSEurope,Home Shopping Europe:12480:vC34:S19.2E:27500:1279:1280:37:0:40:133:33:0
Bloomberg TV Germany:12551:vC56:S19.2E:22000:162:99=deu:0:0:12160:1:1108:0
-EURONEWS:11817:vC34:S19.2E:27500:163:92=fra,93=eng,94=ita,95=esl,91=rus,98=por,99=deu:0:500,100:8004:1:1070:0
-Sky News:11597:vC56:S19.2E:22000:305:306=eng:0:0:28707:1:1026:0
+EURONEWS:11817:vC34:S19.2E:27500:163:92=fra,93=eng,94=ita,95=esl,91=rus,98=por,99=deu:0:0:8004:1:1070:0
+Sky News Intl:11597:vC56:S19.2E:22000:305+131:306=eng:0:0:28707:1:1026:0
Veronica/FoxKids:12574:hC56:S19.2E:22000:518+8190:92=dut:38:622,602,100:5020:53:1109:0
BVN:12574:hC56:S19.2E:22000:515+8190:96=dut:36:0:5025:53:1109:0
CNBC Europe:12610:vC56:S19.2E:22000:944:945=eng:946:0:12200:1:1112:0
@@ -45,11 +45,11 @@ RBB Brandenburg:12109:hC34:S19.2E:27500:501:502=deu:504:0:28205:1:1073:0
RBB Berlin:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0
:Premiere World
START,PREMIERE START:11797:hC34:S19.2E:27500:255:256=deu:32:1702,1722,1801:8:133:2:0
-PREM 1,PREMIERE 1:11797:hC34:S19.2E:27500:511:512=deu;515=deu:0:1702,1722,1801:10:133:2:0
-PREM 2,PREMIERE 2:11797:hC34:S19.2E:27500:1791:1792=deu;1795=deu:0:1702,1722,1801:11:133:2:0
+PREM 1,PREMIERE 1:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:0:1702,1722,1801:10:133:2:0
+PREM 2,PREMIERE 2:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:0:1702,1722,1801:11:133:2:0
PREM 3,PREMIERE 3:11797:hC34:S19.2E:27500:2303:2304=deu:0:1702,1722,1801:43:133:2:0
PREM 4,PREMIERE 4:11797:hC34:S19.2E:27500:767:768=deu:0:1702,1722,1801:9:133:2:0
-PREM 5,PREMIERE 5:11797:hC34:S19.2E:27500:1279:1280=deu:0:1702,1722,1801:29:133:2:0
+PREM 5,PREMIERE 5:11797:hC34:S19.2E:27500:1279:1280=deu,1281=deu:0:1702,1722,1801:29:133:2:0
PREM 6,PREMIERE 6:11797:hC34:S19.2E:27500:1535:1536=deu:0:1702,1722,1801:41:133:2:0
PREM 7,PREMIERE 7:11797:hC34:S19.2E:27500:1023:1024=deu:0:1702,1722,1801:20:133:2:0
DISNEY,DISNEY CHANNEL:11758:hC34:S19.2E:27500:2559:2560=deu:0:1702,1722,1801:34:133:17:0
@@ -59,7 +59,7 @@ DIREKT,PREMIERE DIREKT:12031:hC34:S19.2E:27500:2815:2816=deu,2817=deu;2819=deu:0
B-UHSE,BEATE-UHSE.TV:12070:hC34:S19.2E:27500:1023:1024=deu:0:1702,1722,1801:21:133:1:0
EROTIK,PREMIERE EROTIK:12031:hC34:S19.2E:27500:1279:0:0:1702,1722,1801:513:133:4:0
:Sportsworld
-Konferenz:11719:hC34:S19.2E:27500:255:256=deu,257=deu:0:1702,1722,1801:17:133:3:0
+Konferenz:11719:hC34:S19.2E:27500:255:256=deu:0:1702,1722,1801:17:133:3:0
SPORT 2,PREMIERE SPORT 2:12031:hC34:S19.2E:27500:3839:3840=deu,3841=deu:0:1702,1722,1801:27:133:4:0
:Beta Digital
N24:12480:vC34:S19.2E:27500:2047:2048:36:0:47:133:33:0
@@ -89,20 +89,31 @@ MTV Central:11739:vC34:S19.2E:27500:3031:3032:3034:0:28653:1:1066:0
QVC GERMANY:12551:vC56:S19.2E:22000:165:166:167:0:12100:1:1108:0
TELE 5:12480:vC34:S19.2E:27500:1535:1536:38:0:51:133:33:0
:@201 Sky
-Sky One:106:h:S28.2E:0:160:80:0:30:222:0:0:0
-Sky One Mix:107:h:S28.2E:0:160:80:0:30:919:0:0:0
-itv2:226:h:S28.2E:0:160:80:0:30:451:0:0:0
-sci-fi:130:h:S28.2E:0:160:80:0:30:161:0:0:0
-Paramount Comedy:127:h:S28.2E:0:160:80:0:30:185:0:0:0
+Sky One:12226:hC23:S28.2E:27500:2305+2304:2306=eng:2307:960,961:4705:2:2027:0
+Sky One Mix:12226:hC23:S28.2E:27500:2314+2304:2315=eng:2316:960,961:5104:2:2027:0
+ITV2:10906:vC56:S28.2E:22000:2348:2349=eng,2350=eng:2351:960,961:10240:2:2054:0
+Sci-Fi:12148:hC23:S28.2E:27500:2314+2304:2315=eng:2316:960,961:4905:2:2023:0
+Paramount:12187:hC23:S28.2E:27500:2313+2304:2314=eng:2315:960,961:5904:2:2025:0
+Discovery:11875:hC23:S28.2E:27500:2304:2306=eng:2305:960,961:6201:2:2009:0
+Sky Movies 1:11836:hC23:S28.2E:27500:2309+2304:2310=eng,2311=NAR;2313=eng:2312:960,961:4303:2:2007:0
+Sky Movies 2:11836:hC23:S28.2E:27500:2305+2304:2306=eng,2307=NAR;2323=eng:2308:960,961:4302:2:2007:0
+Sky Movies 3:11836:hC23:S28.2E:27500:2314+2304:2315=eng,2316=NAR;2318=eng:2317:960,961:4403:2:2007:0
+Sky Movies 4:11914:hC23:S28.2E:27500:2305+2304:2306=eng,2307=NAR:2308:960,961:4402:2:2011:0
+Sky Movies 5:11914:hC23:S28.2E:27500:2313+2304:2314=eng,2315=NAR:2316:960,961:4503:2:2011:0
+Sky Movies 6:11914:hC23:S28.2E:27500:2309+2304:2310=eng,2311=NAR:2312:960,961:4502:2:2011:0
+Sky Movies 7:12090:vC23:S28.2E:27500:2312+2304:2313=eng,2314=NAR:2315:960,961:4603:2:2020:0
+Sky Movies 8:11836:hC23:S28.2E:27500:2319+2304:2320=eng,2321=NAR:2322:960,961:5502:2:2007:0
+Sky Movies 9:12090:vC23:S28.2E:27500:2308+2304:2309=eng,2310=NAR:2311:960,961:4602:2:2020:0
+Sky Cinema 1:12090:vC23:S28.2E:27500:2305+2304:2306=eng:2307:960,961:4809:2:2020:0
+Sky Cinema 2:12090:vC23:S28.2E:27500:2316+2304:2317=eng:2318:960,961:4802:2:2020:0
:@900 Some 'seed' channels
Chelsea TV:11778:vC23:S28.2E:27500:2308+2304:2309=eng:0:960,961:9307:2:2004:0
-Sky One:12285:vC23:S28.2E:27500:2311+2304:2312=eng,2313=NAR:2307:960,961:4703:2:2030:0
WDR Münster:12421:hC34:S19.2E:27500:101:102=deu:104:0:28310:1:1201:0
Going Places:10920:hC56:S28.2E:22000:2310+2304:2311=eng:2312:0:5008:2:2055:0
Animal Plnt+:12070:hC23:S28.2E:27500:2315+2307:2316=eng:0:960,961:50002:2:2019:0
-S1T:12285:vC23:S28.2E:27500:2311+2304:2312=eng,2313=NAR:2314:960,961:4409:2:2030:0
+S1T:12285:vC23:S28.2E:27500:2311+2304:2312=eng,2313=NAR:2307:960,961:4409:2:2030:0
CNN:12051:vC23:S28.2E:27500:2309:2311=eng:2310:0:7140:2:2018:0
BBC PARL'MNT:12129:vC23:S28.2E:27500:2306:2308=eng,2309=eng:2307:0:7300:2:2022:0
-AL HAYAT:11200:vC56:S13.0E:27500:413:414=eng:0:0:4733:318:13400:0
+AL HAYAT:11200:vC56:S13.0E:27500:413:414:0:0:4733:318:13400:0
EURO1080:12168:vC34:S19.2E:27500:308:256:0:FF:21100:1:1088:0
:@1000 New channels
diff --git a/channels.h b/channels.h
index 6d6dd3a..c797cb2 100644
--- a/channels.h
+++ b/channels.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.h 1.15 2004/02/08 12:20:22 kls Exp $
+ * $Id: channels.h 1.16 2004/02/13 15:16:36 kls Exp $
*/
#ifndef __CHANNELS_H
@@ -61,6 +61,7 @@ public:
bool operator== (const tChannelID &arg) const;
bool Valid(void) { return (nid || tid) && sid; } // rid is optional and source may be 0//XXX source may not be 0???
tChannelID &ClrRid(void) { rid = 0; return *this; }
+ tChannelID &ClrPolarization(void);
static tChannelID FromString(const char *s);
const char *ToString(void);
static const tChannelID InvalidID;
@@ -129,7 +130,8 @@ public:
bool Save(FILE *f);
const char *Name(void) const { return name; }
int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
- int Transponder(void) const; ///< Returns the transponder frequency in MHz
+ int Transponder(void) const; ///< Returns the transponder frequency in MHz, plus the polarization in case of sat
+ static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization
int Source(void) const { return source; }
int Srate(void) const { return srate; }
int Vpid(void) const { return vpid; }
@@ -187,7 +189,7 @@ public:
void ReNumber(void); // Recalculate 'number' based on channel type
cChannel *GetByNumber(int Number, int SkipGap = 0);
cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID);
- cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false);
+ cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false);
int BeingEdited(void) { return beingEdited; }
void IncBeingEdited(void) { beingEdited++; }
void DecBeingEdited(void) { beingEdited--; }
diff --git a/config.c b/config.c
index 833a2bb..6183eed 100644
--- a/config.c
+++ b/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.122 2004/02/08 15:04:41 kls Exp $
+ * $Id: config.c 1.124 2004/02/21 15:05:40 kls Exp kls $
*/
#include "config.h"
@@ -262,6 +262,7 @@ cSetup::cSetup(void)
EPGLanguages[0] = -1;
EPGScanTimeout = 5;
EPGBugfixLevel = 2;
+ EPGLinger = 0;
SVDRPTimeout = 300;
ZapTimeout = 3;
SortTimers = 1;
@@ -271,6 +272,8 @@ cSetup::cSetup(void)
PausePriority = 10;
PauseLifetime = 1;
UseSubtitle = 1;
+ UseVps = 0;
+ VpsMargin = 120;
RecordingDirs = 1;
VideoFormat = 0;
UpdateChannels = 4;
@@ -374,7 +377,9 @@ bool cSetup::ParseLanguages(const char *Value, int *Values)
{
int n = 0;
while (Value && *Value && n < I18nNumLanguages) {
- int i = I18nLanguageIndex(Value);
+ char buffer[4];
+ strn0cpy(buffer, Value, sizeof(buffer));
+ int i = I18nLanguageIndex(buffer);
if (i >= 0)
Values[n++] = i;
if ((Value = strchr(Value, ' ')) != NULL)
@@ -404,6 +409,7 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "EPGLanguages")) return ParseLanguages(Value, EPGLanguages);
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
+ else if (!strcasecmp(Name, "EPGLinger")) EPGLinger = atoi(Value);
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
else if (!strcasecmp(Name, "ZapTimeout")) ZapTimeout = atoi(Value);
else if (!strcasecmp(Name, "SortTimers")) SortTimers = atoi(Value);
@@ -413,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);
@@ -455,6 +463,7 @@ bool cSetup::Save(void)
StoreLanguages("EPGLanguages", EPGLanguages);
Store("EPGScanTimeout", EPGScanTimeout);
Store("EPGBugfixLevel", EPGBugfixLevel);
+ Store("EPGLinger", EPGLinger);
Store("SVDRPTimeout", SVDRPTimeout);
Store("ZapTimeout", ZapTimeout);
Store("SortTimers", SortTimers);
@@ -464,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);
diff --git a/config.h b/config.h
index 766126a..cc1edf2 100644
--- a/config.h
+++ b/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.186 2004/02/08 15:04:52 kls Exp $
+ * $Id: config.h 1.188 2004/02/21 15:04:53 kls Exp kls $
*/
#ifndef __CONFIG_H
@@ -20,8 +20,8 @@
#include "i18n.h"
#include "tools.h"
-#define VDRVERSION "1.3.4"
-#define VDRVERSNUM 10304 // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION "1.3.5"
+#define VDRVERSNUM 10305 // Version * 10000 + Major * 100 + Minor
#define MAXPRIORITY 99
#define MAXLIFETIME 99
@@ -220,6 +220,7 @@ public:
int EPGLanguages[I18nNumLanguages + 1];
int EPGScanTimeout;
int EPGBugfixLevel;
+ int EPGLinger;
int SVDRPTimeout;
int ZapTimeout;
int SortTimers;
@@ -227,6 +228,8 @@ public:
int DefaultPriority, DefaultLifetime;
int PausePriority, PauseLifetime;
int UseSubtitle;
+ int UseVps;
+ int VpsMargin;
int RecordingDirs;
int VideoFormat;
int UpdateChannels;
diff --git a/device.h b/device.h
index 496e702..af6c85f 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 1.39 2004/02/08 15:05:49 kls Exp $
+ * $Id: device.h 1.40 2004/02/14 11:29:57 kls Exp $
*/
#ifndef __DEVICE_H
@@ -435,7 +435,7 @@ public:
/// from the driver with each call to Read(), thus avoiding the overhead
/// of getting each TS packet separately from the driver. It also makes
/// sure the returned data points to a TS packet and automatically
-/// re-synchronizes after broken packet.
+/// re-synchronizes after broken packets.
class cTSBuffer {
private:
diff --git a/dvbdevice.c b/dvbdevice.c
index e1d5fc6..8ed62e1 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 1.81 2004/02/08 14:07:07 kls Exp $
+ * $Id: dvbdevice.c 1.82 2004/02/24 10:12:13 kls Exp $
*/
#include "dvbdevice.h"
@@ -35,6 +35,7 @@ extern "C" {
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
#define DO_MULTIPLE_RECORDINGS 1
+//#define WAIT_FOR_LOCK_AFTER_TUNING 1
#define DEV_VIDEO "/dev/video"
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@@ -751,11 +752,12 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution//XXX
+#ifdef WAIT_FOR_LOCK_AFTER_TUNING
//XXX TODO preliminary fix for the "Unknown picture type" error
time_t t0 = time(NULL);
while (!dvbTuner->Locked() && time(NULL) - t0 < 5)
usleep(100);
- //XXX
+#endif
// PID settings:
if (TurnOnLivePIDs) {
diff --git a/eit.c b/eit.c
index c24a3d0..85f051e 100644
--- a/eit.c
+++ b/eit.c
@@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
- * $Id: eit.c 1.86 2004/02/08 10:26:54 kls Exp $
+ * $Id: eit.c 1.89 2004/02/22 13:17:52 kls Exp kls $
*/
#include "eit.h"
@@ -43,18 +43,19 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
Schedules->Add(pSchedule);
}
+ bool Modified = false;
+
SI::EIT::Event SiEitEvent;
for (SI::Loop::Iterator it; eventLoop.hasNext(it); ) {
SiEitEvent = eventLoop.getNext(it);
cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime());
if (!pEvent) {
- // If we don't have that event ID yet, we create a new one.
+ // If we don't have that event yet, we create a new one.
// Otherwise we copy the information into the existing event anyway, because the data might have changed.
pEvent = pSchedule->AddEvent(new cEvent(channelID, SiEitEvent.getEventId()));
if (!pEvent)
continue;
- pEvent->SetTableID(Tid);
}
else {
// We have found an existing event, either through its event ID or its start time.
@@ -62,19 +63,9 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
// not be overwritten.
if (pEvent->TableID() == 0x00)
continue;
- // If the new event comes from a table that belongs to an "other TS" and the existing
- // one comes from an "actual TS" table, let's skip it.
- #define ISACTUALTS(tid) (tid == 0x4E || (tid & 0x50) == 0x50)
- if (!ISACTUALTS(Tid) && ISACTUALTS(pEvent->TableID()))
- continue;
- // If the new event comes from a "schedule" table and the existing one comes from
- // a "present/following" table, let's skip it (the p/f table usually contains more
- // information, like e.g. a description).
- if ((Tid & 0x50) == 0x50 && pEvent->TableID() == 0x4E || (Tid & 0x60) == 0x60 && pEvent->TableID() == 0x4F)
- continue;
- // If both events come from the same "schedule" table and the new event's table id is larger than the
- // existing one's, let's skip it (higher tids mean "farther in the future" and usually have less information).
- if (((Tid & 0x50) == 0x50 || (Tid & 0x60) == 0x60) && (pEvent->TableID() & 0xF0) == (Tid & 0xF0) && (Tid > pEvent->TableID()))
+ // If the new event has a higher table ID, let's skip it.
+ // The lower the table ID, the more "current" the information.
+ if (Tid > pEvent->TableID())
continue;
// If the new event comes from the same table and has the same version number
// as the existing one, let's skip it to avoid unnecessary work.
@@ -85,9 +76,16 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber())
continue;
}
- pEvent->SetVersion(getVersionNumber());
- pEvent->SetTableID(Tid);
+ // XXX TODO log different (non-zero) event IDs for the same event???
pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-(
+ pEvent->SetTableID(Tid);
+ pEvent->SetVersion(getVersionNumber());
+ pEvent->SetStartTime(SiEitEvent.getStartTime());
+ pEvent->SetDuration(SiEitEvent.getDuration());
+ if (isPresentFollowing()) {
+ if (SiEitEvent.getRunningStatus() > SI::RunningStatusNotRunning)
+ pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus());
+ }
int LanguagePreferenceShort = -1;
int LanguagePreferenceExt = -1;
@@ -126,6 +124,21 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
break;
case SI::ParentalRatingDescriptorTag:
break;
+ case SI::PDCDescriptorTag: {
+ SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d;
+ time_t now = time(NULL);
+ struct tm tm_r;
+ struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't'
+ t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ t.tm_mon = pd->getMonth() - 1;
+ t.tm_mday = pd->getDay();
+ t.tm_hour = pd->getHour();
+ t.tm_min = pd->getMinute();
+ t.tm_sec = 0;
+ time_t vps = mktime(&t);
+ pEvent->SetVps(vps);
+ }
+ break;
case SI::TimeShiftedEventDescriptorTag: {
SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d;
cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, 0, 0, tsed->getReferenceServiceId()));
@@ -188,20 +201,14 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
delete ExtendedEventDescriptors;
delete ShortEventDescriptor;
- pEvent->SetStartTime(SiEitEvent.getStartTime());
- pEvent->SetDuration(SiEitEvent.getDuration());
pEvent->FixEpgBugs();
- if (isPresentFollowing()) {
- if (SiEitEvent.getRunningStatus() == SI::RunningStatusPausing || SiEitEvent.getRunningStatus() == SI::RunningStatusRunning)
- pSchedule->SetPresentEvent(pEvent);
- else if (SiEitEvent.getRunningStatus() == SI::RunningStatusStartsInAFewSeconds)
- pSchedule->SetFollowingEvent(pEvent);
- }
-
if (LinkChannels)
channel->SetLinkChannels(LinkChannels);
+ Modified = true;
}
+ if (Modified)
+ pSchedule->Sort();
}
// --- cTDT ------------------------------------------------------------------
diff --git a/eitscan.c b/eitscan.c
index a2096af..54b579b 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: eitscan.c 1.20 2004/01/17 15:38:11 kls Exp $
+ * $Id: eitscan.c 1.21 2004/02/14 13:44:31 kls Exp kls $
*/
#include "eitscan.h"
@@ -146,8 +146,7 @@ void cEITScanner::Process(void)
if (!(Device->Receiving(true) || Device->Replaying())) {
const cChannel *Channel = ScanData->GetChannel();
if (Channel) {
- //XXX if (Device->ProvidesTransponder(Channel)) {
- if ((!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) && Device->ProvidesTransponder(Channel)) { //XXX temporary for the 'sky' plugin
+ if ((!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) && Device->ProvidesTransponder(Channel)) {
if (Device == cDevice::PrimaryDevice() && !currentChannel) {
currentChannel = Device->CurrentChannel();
Interface->Info("Starting EPG scan");
diff --git a/eitscan.h b/eitscan.h
index c5116cb..c2ed7fb 100644
--- a/eitscan.h
+++ b/eitscan.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: eitscan.h 1.8 2004/01/17 15:36:24 kls Exp $
+ * $Id: eitscan.h 1.8 2004/01/17 15:36:24 kls Exp kls $
*/
#ifndef __EITSCAN_H
diff --git a/epg.c b/epg.c
index 19f1c7f..88d9ee3 100644
--- a/epg.c
+++ b/epg.c
@@ -7,10 +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.4 2004/01/09 15:22:18 kls Exp $
+ * $Id: epg.c 1.13 2004/02/22 14:41:37 kls Exp kls $
*/
#include "epg.h"
+#include "libsi/si.h"
+#include "timers.h"
#include <ctype.h>
#include <time.h>
@@ -22,13 +24,13 @@ cEvent::cEvent(tChannelID ChannelID, u_int16_t EventID)
eventID = EventID;
tableID = 0;
version = 0xFF; // actual version numbers are 0..31
- isPresent = isFollowing = false;
+ runningStatus = 0;
title = NULL;
shortText = NULL;
description = NULL;
startTime = 0;
duration = 0;
- channelNumber = 0;
+ vps = 0;
}
cEvent::~cEvent()
@@ -38,6 +40,12 @@ cEvent::~cEvent()
free(description);
}
+bool cEvent::operator< (const cListObject &ListObject)
+{
+ cEvent *e = (cEvent *)&ListObject;
+ return startTime < e->startTime;
+}
+
void cEvent::SetEventID(u_int16_t EventID)
{
eventID = EventID;
@@ -53,14 +61,9 @@ void cEvent::SetVersion(uchar Version)
version = Version;
}
-void cEvent::SetIsPresent(bool IsPresent)
+void cEvent::SetRunningStatus(int RunningStatus)
{
- isPresent = IsPresent;
-}
-
-void cEvent::SetIsFollowing(bool IsFollowing)
-{
- isFollowing = IsFollowing;
+ runningStatus = RunningStatus;
}
void cEvent::SetTitle(const char *Title)
@@ -88,6 +91,20 @@ void cEvent::SetDuration(int Duration)
duration = Duration;
}
+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];
@@ -113,6 +130,14 @@ const char *cEvent::GetEndTimeString(void) const
return buf;
}
+const char *cEvent::GetVpsString(void) const
+{
+ static char buf[25];
+ struct tm tm_r;
+ strftime(buf, sizeof(buf), "%d.%m %R", localtime_r(&vps, &tm_r));
+ return buf;
+}
+
void cEvent::Dump(FILE *f, const char *Prefix) const
{
if (startTime + duration >= time(NULL)) {
@@ -123,6 +148,8 @@ void cEvent::Dump(FILE *f, const char *Prefix) const
fprintf(f, "%sS %s\n", Prefix, shortText);
if (!isempty(description))
fprintf(f, "%sD %s\n", Prefix, description);
+ if (vps)
+ fprintf(f, "%sV %ld\n", Prefix, vps);
fprintf(f, "%se\n", Prefix);
}
}
@@ -162,6 +189,9 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule)
case 'D': if (Event)
Event->SetDescription(t);
break;
+ case 'V': if (Event)
+ Event->SetVps(atoi(t));
+ break;
case 'e': Event = NULL;
break;
case 'c': // to keep things simple we react on 'c' here
@@ -175,7 +205,7 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule)
return false;
}
-#define MAXEPGBUGFIXSTATS 7
+#define MAXEPGBUGFIXSTATS 8
#define MAXEPGBUGFIXCHANS 100
struct tEpgBugFixStats {
int hits;
@@ -257,6 +287,13 @@ void cEvent::FixEpgBugs(void)
strreplace(title, '\n', ' ');
strreplace(shortText, '\n', ' ');
strreplace(description, '\n', ' ');
+ // Same for control characters:
+ strreplace(title, '\x86', ' ');
+ strreplace(title, '\x87', ' ');
+ strreplace(shortText, '\x86', ' ');
+ strreplace(shortText, '\x87', ' ');
+ strreplace(description, '\x86', ' ');
+ strreplace(description, '\x87', ' ');
if (Setup.EPGBugfixLevel == 0)
return;
@@ -397,6 +434,20 @@ void cEvent::FixEpgBugs(void)
}
}
+ // Some channels put the same information into ShortText and Description.
+ // In that case we delete one of them:
+ if (shortText && description && strcmp(shortText, description) == 0) {
+ if (strlen(shortText) > MAX_USEFUL_EPISODE_LENGTH) {
+ free(shortText);
+ shortText = NULL;
+ }
+ else {
+ free(description);
+ description = NULL;
+ }
+ EpgBugFixStat(7, ChannelID());
+ }
+
// Some channels use the ` ("backtick") character, where a ' (single quote)
// would be normally used. Actually, "backticks" in normal text don't make
// much sense, so let's replace them:
@@ -411,7 +462,6 @@ void cEvent::FixEpgBugs(void)
cSchedule::cSchedule(tChannelID ChannelID)
{
channelID = ChannelID;
- present = following = NULL;
}
cEvent *cSchedule::AddEvent(cEvent *Event)
@@ -420,26 +470,30 @@ cEvent *cSchedule::AddEvent(cEvent *Event)
return Event;
}
-const cEvent *cSchedule::GetPresentEvent(void) const
-{
- return GetEventAround(time(NULL));
-}
-
-const cEvent *cSchedule::GetFollowingEvent(void) const
+const cEvent *cSchedule::GetPresentEvent(bool CheckRunningStatus) const
{
const cEvent *pe = NULL;
time_t now = time(NULL);
- time_t delta = INT_MAX;
for (cEvent *p = events.First(); p; p = events.Next(p)) {
- time_t dt = p->StartTime() - now;
- if (dt > 0 && dt < delta) {
- delta = dt;
+ if (p->StartTime() <= now && now < p->StartTime() + p->Duration()) {
pe = p;
+ if (!CheckRunningStatus)
+ break;
}
+ if (CheckRunningStatus && p->RunningStatus() >= SI::RunningStatusPausing)
+ return p;
}
return pe;
}
+const cEvent *cSchedule::GetFollowingEvent(bool CheckRunningStatus) const
+{
+ const cEvent *p = GetPresentEvent(CheckRunningStatus);
+ if (p)
+ p = events.Next(p);
+ return p;
+}
+
const cEvent *cSchedule::GetEvent(u_int16_t EventID, time_t StartTime) const
{
// Returns either the event info with the given EventID or, if that one can't
@@ -468,22 +522,14 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
return pe;
}
-bool cSchedule::SetPresentEvent(cEvent *Event)
-{
- if (present)
- present->SetIsPresent(false);
- present = Event;
- present->SetIsPresent(true);
- return true;
-}
-
-bool cSchedule::SetFollowingEvent(cEvent *Event)
+void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus)
{
- if (following)
- following->SetIsFollowing(false);
- following = Event;
- following->SetIsFollowing(true);
- return true;
+ for (cEvent *p = events.First(); p; p = events.Next(p)) {
+ if (p == Event)
+ p->SetRunningStatus(RunningStatus);
+ else if (RunningStatus >= SI::RunningStatusPausing && p->RunningStatus() > SI::RunningStatusNotRunning)
+ p->SetRunningStatus(SI::RunningStatusNotRunning);
+ }
}
void cSchedule::ResetVersions(void)
@@ -492,6 +538,11 @@ void cSchedule::ResetVersions(void)
p->SetVersion(0xFF);
}
+void cSchedule::Sort(void)
+{
+ events.Sort();
+}
+
void cSchedule::Cleanup(void)
{
Cleanup(time(NULL));
@@ -504,20 +555,41 @@ void cSchedule::Cleanup(time_t Time)
Event = events.Get(a);
if (!Event)
break;
- if (Event->StartTime() + Event->Duration() + 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--;
}
}
}
-void cSchedule::Dump(FILE *f, const char *Prefix) const
+void cSchedule::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const
{
cChannel *channel = Channels.GetByChannelID(channelID, true);
if (channel) {
fprintf(f, "%sC %s %s\n", Prefix, channel->GetChannelID().ToString(), channel->Name());
- for (cEvent *p = events.First(); p; p = events.Next(p))
- p->Dump(f, Prefix);
+ const cEvent *p;
+ switch (DumpMode) {
+ case dmAll: {
+ for (p = events.First(); p; p = events.Next(p))
+ p->Dump(f, Prefix);
+ }
+ break;
+ case dmPresent: {
+ if ((p = GetPresentEvent()) != NULL)
+ p->Dump(f, Prefix);
+ }
+ break;
+ case dmFollowing: {
+ if ((p = GetFollowingEvent()) != NULL)
+ p->Dump(f, Prefix);
+ }
+ break;
+ case dmAtTime: {
+ if ((p = GetEventAround(AtTime)) != NULL)
+ p->Dump(f, Prefix);
+ }
+ break;
+ }
fprintf(f, "%sc\n", Prefix);
}
}
@@ -539,6 +611,7 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules)
if (p) {
if (!cEvent::Read(f, p))
return false;
+ p->Sort();
}
}
else {
@@ -640,13 +713,13 @@ bool cSchedules::ClearAll(void)
return false;
}
-bool cSchedules::Dump(FILE *f, const char *Prefix)
+bool cSchedules::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime)
{
cSchedulesLock SchedulesLock;
cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
if (s) {
for (cSchedule *p = s->First(); p; p = s->Next(p))
- p->Dump(f, Prefix);
+ p->Dump(f, Prefix, DumpMode, AtTime);
return true;
}
return false;
diff --git a/epg.h b/epg.h
index 409968a..4b209b5 100644
--- a/epg.h
+++ b/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.4 2004/01/09 15:21:05 kls Exp $
+ * $Id: epg.h 1.10 2004/02/22 14:34:04 kls Exp kls $
*/
#ifndef __EPG_H
@@ -19,6 +19,8 @@
#define MAXEPGBUGFIXLEVEL 2
+enum eDumpMode { dmAll, dmPresent, dmFollowing, dmAtTime };
+
class cSchedule;
class cEvent : public cListObject {
@@ -27,45 +29,43 @@ private:
u_int16_t eventID; // Event ID of this event
uchar tableID; // Table ID this event came from
uchar version; // Version number of section this event came from
- //XXX present/following obsolete???
- bool isPresent; // true if this is the present event running
- bool isFollowing; // true if this is the next event on this channel
+ int runningStatus; // 0=undefined, 1=not running, 2=starts in a few seconds, 3=pausing, 4=running
char *title; // Title of this event
char *shortText; // Short description of this event (typically the episode name in case of a series)
char *description; // Description of this event
time_t startTime; // Start time of this event
int duration; // Duration of this event in seconds
- //XXX find an other solution, avoiding channelNumber???
- int channelNumber; // the actual channel number from VDR's channel list (used in cMenuSchedule for sorting by channel number)
+ time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
public:
cEvent(tChannelID ChannelID, u_int16_t EventID);
~cEvent();
+ virtual bool operator< (const cListObject &ListObject);
tChannelID ChannelID(void) const { return channelID; }
u_int16_t EventID(void) const { return eventID; }
uchar TableID(void) const { return tableID; }
uchar Version(void) const { return version; }
- bool IsPresent(void) const { return isPresent; }
- bool IsFollowing(void) const { return isFollowing; }
+ int RunningStatus(void) const { return runningStatus; }
const char *Title(void) const { return title; }
const char *ShortText(void) const { return shortText; }
const char *Description(void) const { return description; }
time_t StartTime(void) const { return startTime; }
int Duration(void) const { return duration; }
- int ChannelNumber(void) const { return channelNumber; }
+ 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;
+ const char *GetVpsString(void) const;
void SetEventID(u_int16_t EventID);
void SetTableID(uchar TableID);
void SetVersion(uchar Version);
- void SetIsPresent(bool IsPresent);
- void SetIsFollowing(bool IsFollowing);
+ void SetRunningStatus(int RunningStatus);
void SetTitle(const char *Title);
void SetShortText(const char *ShortText);
void SetDescription(const char *Description);
void SetStartTime(time_t StartTime);
void SetDuration(int Duration);
- void SetChannelNumber(int ChannelNumber) const { ((cEvent *)this)->channelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const' //XXX
+ void SetVps(time_t Vps);
void Dump(FILE *f, const char *Prefix = "") const;
static bool Read(FILE *f, cSchedule *Schedule);
void FixEpgBugs(void);
@@ -77,24 +77,22 @@ class cSchedule : public cListObject {
private:
tChannelID channelID;
cList<cEvent> events;
- cEvent *present;
- cEvent *following;
public:
cSchedule(tChannelID ChannelID);
tChannelID ChannelID(void) const { return channelID; }
- bool SetPresentEvent(cEvent *Event);
- bool SetFollowingEvent(cEvent *Event);
+ void SetRunningStatus(cEvent *Event, int RunningStatus);
void ResetVersions(void);
+ void Sort(void);
void Cleanup(time_t Time);
void Cleanup(void);
cEvent *AddEvent(cEvent *Event);
- const cEvent *GetPresentEvent(void) const;
- const cEvent *GetFollowingEvent(void) const;
+ const cEvent *GetPresentEvent(bool CheckRunningStatus = false) const;
+ const cEvent *GetFollowingEvent(bool CheckRunningStatus = false) const;
const cEvent *GetEvent(u_int16_t EventID, time_t StartTime = 0) const;
const cEvent *GetEventAround(time_t Time) const;
const cEvent *GetEventNumber(int n) const { return events.Get(n); }
int NumEvents(void) const { return events.Count(); }
- void Dump(FILE *f, const char *Prefix = "") const;
+ void Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0) const;
static bool Read(FILE *f, cSchedules *Schedules);
};
@@ -125,7 +123,7 @@ public:
static void Cleanup(bool Force = false);
static void ResetVersions(void);
static bool ClearAll(void);
- static bool Dump(FILE *f, const char *Prefix = "");
+ static bool Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0);
static bool Read(FILE *f = NULL);
cSchedule *AddSchedule(tChannelID ChannelID);
const cSchedule *GetSchedule(tChannelID ChannelID) const;
diff --git a/epg2html.pl b/epg2html.pl
index 6b7b8c9..c6e6f9c 100755
--- a/epg2html.pl
+++ b/epg2html.pl
@@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: epg2html.pl 1.4 2002/05/30 09:46:46 kls Exp $
+# $Id: epg2html.pl 1.5 2004/02/22 13:18:27 kls Exp $
@Index = ();
@@ -47,13 +47,14 @@ while (<>) {
while (<>) {
if (/^E (.*?) (.*?) ([^ ]*)/) {
(my $Time, $Duration) = ($2, $3);
- my $Title = "", $Subtitle = "", $Description = "";
+ my $Title = "", $Subtitle = "", $Description = "", $Vps = 0;
while (<>) {
if (/^T (.*)/) { $Title = Tags($1); }
elsif (/^S (.*)/) { $Subtitle = Tags($1); }
elsif (/^D (.*)/) { $Description = Tags($1); }
+ elsif (/^V (.*)/) { $Vps = $1; }
elsif (/^e/) {
- $Events{$Time} = [($Duration, $Title, $Subtitle, $Description)];
+ $Events{$Time} = [($Duration, $Title, $Subtitle, $Description, $Vps)];
last;
}
}
@@ -62,7 +63,7 @@ while (<>) {
my @Schedule = ();
my $Day = "";
for $t (sort keys %Events) {
- (my $Duration, $Title, $Subtitle, $Description) = @{$Events{$t}};
+ (my $Duration, $Title, $Subtitle, $Description, $Vps) = @{$Events{$t}};
my $d = GetDay($t);
if ($d ne $Day) {
push(@Schedule, "</table>\n") if ($Day && @Schedule);
@@ -73,6 +74,7 @@ while (<>) {
my $Entry = $Title;
$Entry .= "<br><i>$Subtitle</i>" if $Subtitle;
$Entry .= "<br>$Description" if $Description;
+ $Entry .= "<br>(VPS = " . scalar localtime($Vps) . ")" if $Vps && $Vps != $t;
push(@Schedule, "<tr><td valign=top>" . GetTime($t) . "</td><td>$Entry</td></tr>\n");
}
push(@Schedule, "</table>\n") if (@Schedule);
diff --git a/i18n.c b/i18n.c
index f87827b..8983d6f 100644
--- a/i18n.c
+++ b/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.146 2004/01/25 14:41:02 kls Exp $
+ * $Id: i18n.c 1.148 2004/02/21 15:14:36 kls Exp kls $
*
* Translations provided by:
*
@@ -835,7 +835,7 @@ const tI18nPhrase Phrases[] = {
"",//TODO
"",//TODO
"",//TODO
- "",//TODO
+ "ÁÚÐÝØàÞÒÐâì",
},
// Confirmations:
{ "Delete channel?",
@@ -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",
@@ -2355,6 +2373,24 @@ const tI18nPhrase Phrases[] = {
"Nivell de correcció de la Guia",
"ÃàÞÒÕÝì ÚÞààÕÚæØØ ÞèØÑÞÚ",
},
+ { "Setup.EPG$EPG linger time (min)",
+ "Alte EPG-Daten anzeigen (min)",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "Vanha tieto näkyy (min)",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "Visa gammal info (min)",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
{ "Setup.EPG$Set system time",
"Systemzeit stellen",
"Nastavi sistemski cas",
@@ -2787,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",
@@ -4140,7 +4212,7 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"Ennek a plugin-nak nincs setup-parametere!",
"Aquest plugin no admet configuració!",
- "¼ÞÔãÛì àÐáèØàÕÝØï ÝÕ ØÜÕÕâ ßÐàÐÜÕâàÞÒ ÝÐáâàÞÙÚØ!",
+ "¼ÞÔãÛì ÝÕ ØÜÕÕâ ßÐàÐÜÕâàÞÒ ÝÐáâàÞÙÚØ!",
},
{ NULL }
};
diff --git a/libsi/descriptor.c b/libsi/descriptor.c
index 55a6a9c..0b0019f 100644
--- a/libsi/descriptor.c
+++ b/libsi/descriptor.c
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: descriptor.c 1.5 2004/01/24 14:52:41 kls Exp $
+ * $Id: descriptor.c 1.6 2004/02/22 11:11:36 kls Exp $
* *
***************************************************************************/
@@ -529,6 +529,27 @@ void ISO639LanguageDescriptor::Parse() {
languageCode[3]=0;
}
+void PDCDescriptor::Parse() {
+ unsigned int offset=0;
+ data.setPointerAndOffset<const descr_pdc>(s, offset);
+}
+
+int PDCDescriptor::getDay() const {
+ return ((s->pil0 & 0x0F) << 1) | ((s->pil1 & 0x80) >> 7);
+}
+
+int PDCDescriptor::getMonth() const {
+ return (s->pil1 >> 3) & 0x0F;
+}
+
+int PDCDescriptor::getHour() const {
+ return ((s->pil1 & 0x07) << 2) | ((s->pil2 & 0xC0) >> 6);
+}
+
+int PDCDescriptor::getMinute() const {
+ return s->pil2 & 0x3F;
+}
+
void ApplicationSignallingDescriptor::Parse() {
entryLoop.setData(data+sizeof(descr_application_signalling), getLength()-sizeof(descr_application_signalling));
}
diff --git a/libsi/descriptor.h b/libsi/descriptor.h
index 5541cc4..17c81eb 100644
--- a/libsi/descriptor.h
+++ b/libsi/descriptor.h
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: descriptor.h 1.5 2004/01/24 14:52:05 kls Exp $
+ * $Id: descriptor.h 1.6 2004/02/22 10:16:47 kls Exp $
* *
***************************************************************************/
@@ -383,6 +383,18 @@ private:
const descr_iso_639_language *s;
};
+class PDCDescriptor : public Descriptor {
+public:
+ int getDay() const;
+ int getMonth() const;
+ int getHour() const;
+ int getMinute() const;
+protected:
+ virtual void Parse();
+private:
+ const descr_pdc *s;
+};
+
//a descriptor currently unimplemented in this library
class UnimplementedDescriptor : public Descriptor {
protected:
diff --git a/libsi/headers.h b/libsi/headers.h
index d5c1199..ff3e0f9 100644
--- a/libsi/headers.h
+++ b/libsi/headers.h
@@ -10,7 +10,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: headers.h 1.2 2003/12/13 10:43:26 kls Exp $
+ * $Id: headers.h 1.4 2004/02/22 11:12:46 kls Exp $
* *
***************************************************************************/
@@ -257,7 +257,48 @@ struct pmt_info {
/*
*
- * 4) Network Information Table (NIT):
+ * 4) Transport Stream Description Table (TSDT):
+ *
+ * - The TSDT carries a loop of descriptors that apply to
+ * the whole transport stream. The syntax and semantics
+ * of the TSDT are defined in newer versions of ISO/IEC 13818-1.
+ *
+ */
+
+#define TSDT_LEN 8
+
+struct tsdt {
+ u_char table_id :8;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char section_syntax_indicator :1;
+ u_char dummy :1; // has to be 0
+ u_char :2;
+ u_char section_length_hi :4;
+#else
+ u_char section_length_hi :4;
+ u_char :2;
+ u_char dummy :1; // has to be 0
+ u_char section_syntax_indicator :1;
+#endif
+ u_char section_length_lo :8;
+ u_char :8;
+ u_char :8;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char :2;
+ u_char version_number :5;
+ u_char current_next_indicator :1;
+#else
+ u_char current_next_indicator :1;
+ u_char version_number :5;
+ u_char :2;
+#endif
+ u_char section_number :8;
+ u_char last_section_number :8;
+};
+
+/*
+ *
+ * 5) Network Information Table (NIT):
*
* - the NIT is intended to provide information about the physical
* network. The syntax and semantics of the NIT are defined in
@@ -468,7 +509,7 @@ struct eit {
u_char original_network_id_hi :8;
u_char original_network_id_lo :8;
u_char segment_last_section_number :8;
- u_char segment_last_table_id :8;
+ u_char last_table_id :8;
};
#define EIT_EVENT_LEN 12
@@ -1416,11 +1457,19 @@ struct descr_dsng {
/* 0x69 pdc_descriptor */
-#define DESCR_PDC_LEN XX
+#define DESCR_PDC_LEN 5
struct descr_pdc {
u_char descriptor_tag :8;
u_char descriptor_length :8;
- /* TBD */
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char pil2 :8;
+ u_char pil1 :8;
+ u_char pil0 :8;
+#else
+ u_char pil0 :8;
+ u_char pil1 :8;
+ u_char pil2 :8;
+#endif
};
/* 0x6A ac3_descriptor */
diff --git a/libsi/section.c b/libsi/section.c
index 0de2a57..2cac809 100644
--- a/libsi/section.c
+++ b/libsi/section.c
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: section.c 1.2 2003/12/13 10:42:14 kls Exp $
+ * $Id: section.c 1.3 2004/02/20 13:44:59 kls Exp $
* *
***************************************************************************/
@@ -76,6 +76,14 @@ void PMT::Stream::Parse() {
streamDescriptors.setData(data+offset, HILO(s->ES_info_length));
}
+/*********************** TSDT ***********************/
+
+void TSDT::Parse() {
+ unsigned int offset=0;
+ data.setPointerAndOffset<const tsdt>(s, offset);
+ transportStreamDescriptors.setDataAndOffset(data+offset, getLength()-offset-4, offset);
+}
+
/*********************** NIT ***********************/
int NIT::getNetworkId() const {
@@ -161,6 +169,14 @@ int EIT::getOriginalNetworkId() const {
return HILO(s->original_network_id);
}
+int EIT::getSegmentLastSectionNumber() const {
+ return s->segment_last_section_number;
+}
+
+int EIT::getLastTableId() const {
+ return s->last_table_id;
+}
+
bool EIT::isPresentFollowing() const {
return getTableId() == TableIdEIT_presentFollowing || getTableId() == TableIdEIT_presentFollowing_other;
}
diff --git a/libsi/section.h b/libsi/section.h
index efca9bb..5b43f05 100644
--- a/libsi/section.h
+++ b/libsi/section.h
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: section.h 1.2 2003/12/13 10:42:15 kls Exp $
+ * $Id: section.h 1.3 2004/02/20 13:45:45 kls Exp $
* *
***************************************************************************/
@@ -77,6 +77,17 @@ private:
const pmt *s;
};
+class TSDT : public NumberedSection {
+public:
+ TSDT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+ TSDT() {}
+ DescriptorLoop transportStreamDescriptors;
+protected:
+ virtual void Parse();
+private:
+ const tsdt *s;
+};
+
class NIT : public NumberedSection {
public:
NIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
@@ -166,6 +177,8 @@ public:
int getServiceId() const;
int getTransportStreamId() const;
int getOriginalNetworkId() const;
+ int getSegmentLastSectionNumber() const;
+ int getLastTableId() const;
StructureLoop<Event> eventLoop;
//true if table conveys present/following information, false if it conveys schedule information
diff --git a/libsi/si.c b/libsi/si.c
index 4264feb..ee64802 100644
--- a/libsi/si.c
+++ b/libsi/si.c
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: si.c 1.6 2004/01/23 14:27:45 kls Exp $
+ * $Id: si.c 1.8 2004/02/22 10:14:12 kls Exp $
* *
***************************************************************************/
@@ -61,6 +61,14 @@ bool CRCSection::CheckCRCAndParse() {
return true;
}
+int NumberedSection::getTableIdExtension() const {
+ return getTableIdExtension(data.getData());
+}
+
+int NumberedSection::getTableIdExtension(const unsigned char *d) {
+ return HILO(((const ExtendedSectionHeader *)d)->table_id_extension);
+}
+
bool NumberedSection::getCurrentNextIndicator() const {
return data.getData<ExtendedSectionHeader>()->current_next_indicator;
}
@@ -326,6 +334,9 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain)
case ISO639LanguageDescriptorTag:
d=new ISO639LanguageDescriptor();
break;
+ case PDCDescriptorTag:
+ d=new PDCDescriptor();
+ break;
//note that it is no problem to implement one
//of the unimplemented descriptors.
@@ -367,7 +378,6 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain)
case CaSystemDescriptorTag:
case AC3DescriptorTag:
case DSNGDescriptorTag:
- case PDCDescriptorTag:
case AncillaryDataDescriptorTag:
case AnnouncementSupportDescriptorTag:
case AdaptationFieldDataDescriptorTag:
diff --git a/libsi/si.h b/libsi/si.h
index 4666c2d..befff85 100644
--- a/libsi/si.h
+++ b/libsi/si.h
@@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: si.h 1.6 2004/01/12 16:19:11 kls Exp $
+ * $Id: si.h 1.8 2004/02/23 17:02:33 kls Exp $
* *
***************************************************************************/
@@ -36,7 +36,7 @@ enum TableId { TableIdPAT = 0x00, //program association section
TableIdEIT_schedule_last = 0x5F,
//range from 0x60 to 0x6F
TableIdEIT_schedule_Other_first = 0x60,
- TableIdEIT_schedule_Other_fast = 0x6F,
+ TableIdEIT_schedule_Other_last = 0x6F,
TableIdTDT = 0x70, //time date section
TableIdRST = 0x71, //running status section
TableIdST = 0x72, //stuffing section
@@ -216,11 +216,14 @@ class NumberedSection : public CRCSection {
public:
NumberedSection(const unsigned char *data, bool doCopy=true) : CRCSection(data, doCopy) {}
NumberedSection() {}
+ int getTableIdExtension() const;
bool getCurrentNextIndicator() const;
int getVersionNumber() const;
int getSectionNumber() const;
int getLastSectionNumber() const;
bool moreThanOneSection() const { return getLastSectionNumber()>0; }
+
+ static int getTableIdExtension(const unsigned char *d);
};
class VariableLengthPart : public Object {
diff --git a/menu.c b/menu.c
index 6a7907f..c153151 100644
--- a/menu.c
+++ b/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.284 2004/02/08 15:06:42 kls Exp $
+ * $Id: menu.c 1.292 2004/02/22 14:14:55 kls Exp kls $
*/
#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"
@@ -557,8 +558,12 @@ cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New)
channel = Channel;
if (channel) {
data = *channel;
- if (New)
+ if (New) {
channel = NULL;
+ data.nid = 0;
+ data.tid = 0;
+ data.rid = 0;
+ }
Setup();
}
}
@@ -583,7 +588,7 @@ void cMenuEditChannel::Setup(void)
Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpids[1], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
Add(new cMenuEditCaItem( tr("CA"), &data.caids[0], true));//XXX
- Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 0));
+ Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF));
/* XXX not yet used
Add(new cMenuEditIntItem( tr("Nid"), &data.nid, 0));
Add(new cMenuEditIntItem( tr("Tid"), &data.tid, 0));
@@ -867,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)));
@@ -922,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;
}
}
@@ -972,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,
@@ -1037,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;
@@ -1164,6 +1170,13 @@ cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch)
Add(item = new cMenuTextItem(Subtitle, 1, Line, Setup.OSDwidth - 2, -1, clrYellow));
Line += item->Height() + 1;
}
+ if (Event->Vps() && Event->Vps() != Event->StartTime()) {
+ char *buffer;
+ asprintf(&buffer, "VPS: %s", Event->GetVpsString());
+ Add(item = new cMenuTextItem(buffer, 1, Line, Setup.OSDwidth - 2, -1, clrYellow));
+ free(buffer);
+ Line += item->Height() + 1;
+ }
if (!isempty(ExtendedDescription))
Add(new cMenuTextItem(ExtendedDescription, 1, Line, Setup.OSDwidth - 2, Height() - Line - 2, clrCyan), true);
SetHelp(tr("Record"), NULL, NULL, CanSwitch ? tr("Switch") : NULL);
@@ -1191,15 +1204,20 @@ eOSState cMenuEvent::ProcessKey(eKeys Key)
class cMenuWhatsOnItem : public cOsdItem {
public:
const cEvent *event;
- cMenuWhatsOnItem(const cEvent *Event);
+ const cChannel *channel;
+ cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel);
};
-cMenuWhatsOnItem::cMenuWhatsOnItem(const cEvent *Event)
+cMenuWhatsOnItem::cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel)
{
event = Event;
+ channel = Channel;
char *buffer = NULL;
- cChannel *channel = Channels.GetByNumber(event->ChannelNumber());
- asprintf(&buffer, "%d\t%.*s\t%.*s\t%s", event->ChannelNumber(), 6, channel ? 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);
}
@@ -1222,39 +1240,20 @@ public:
int cMenuWhatsOn::currentChannel = 0;
const cEvent *cMenuWhatsOn::scheduleEvent = NULL;
-static int CompareEventChannel(const void *p1, const void *p2)
-{
- return (int)( (*(const cEvent **)p1)->ChannelNumber() - (*(const cEvent **)p2)->ChannelNumber());
-}
-
cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
-:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6)
-{
- const cSchedule *Schedule = Schedules->First();
- const cEvent **pArray = NULL;
- int num = 0;
-
- while (Schedule) {
- pArray = (const cEvent **)realloc(pArray, (num + 1) * sizeof(cEvent *));
-
- pArray[num] = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
- if (pArray[num]) {
- cChannel *channel = Channels.GetByChannelID(pArray[num]->ChannelID(), true);
- if (channel) {
- pArray[num]->SetChannelNumber(channel->Number());
- num++;
- }
- }
- Schedule = (const cSchedule *)Schedules->Next(Schedule);
- }
-
- qsort(pArray, num, sizeof(cEvent *), CompareEventChannel);
-
- for (int a = 0; a < num; a++)
- Add(new cMenuWhatsOnItem(pArray[a]), pArray[a]->ChannelNumber() == CurrentChannelNr);
-
+: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()) {
+ const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
+ if (Schedule) {
+ const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
+ if (Event)
+ Add(new cMenuWhatsOnItem(Event, Channel), Channel->Number() == CurrentChannelNr);
+ }
+ }
+ }
currentChannel = CurrentChannelNr;
- free(pArray);
SetHelp(Count() ? tr("Record") : NULL, Now ? tr("Next") : tr("Now"), tr("Button$Schedule"), tr("Switch"));
}
@@ -1306,7 +1305,7 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
cMenuWhatsOnItem *mi = (cMenuWhatsOnItem *)Get(Current());
if (mi) {
scheduleEvent = mi->event;
- currentChannel = mi->event->ChannelNumber();
+ currentChannel = mi->channel->Number();
}
}
break;
@@ -1332,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);
}
@@ -1354,7 +1357,7 @@ public:
};
cMenuSchedule::cMenuSchedule(void)
-:cOsdMenu("", 6, 6)
+:cOsdMenu("", 6, 6, 4)
{
now = next = false;
otherChannel = 0;
@@ -1372,11 +1375,6 @@ cMenuSchedule::~cMenuSchedule()
cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
}
-static int CompareEventTime(const void *p1, const void *p2)
-{
- return (int)((*(cEvent **)p1)->StartTime() - (*(cEvent **)p2)->StartTime());
-}
-
void cMenuSchedule::PrepareSchedule(cChannel *Channel)
{
Clear();
@@ -1387,23 +1385,14 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel)
if (schedules) {
const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());
if (Schedule) {
+ const cEvent *PresentEvent = Schedule->GetPresentEvent(Channel->Number() == cDevice::CurrentChannel());
int num = Schedule->NumEvents();
- const cEvent **pArray = MALLOC(const cEvent *, num);
- if (pArray) {
- time_t now = time(NULL);
- int numreal = 0;
- for (int a = 0; a < num; a++) {
- const cEvent *Event = Schedule->GetEventNumber(a);
- if (Event->StartTime() + Event->Duration() > now)
- pArray[numreal++] = Event;
- }
-
- qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime);
-
- for (int a = 0; a < numreal; a++)
- Add(new cMenuScheduleItem(pArray[a]));
- free(pArray);
- }
+ time_t now = time(NULL) - Setup.EPGLinger * 60;
+ for (int a = 0; a < num; a++) {
+ const cEvent *Event = Schedule->GetEventNumber(a);
+ if (Event->StartTime() + Event->Duration() > now || Event == PresentEvent)
+ Add(new cMenuScheduleItem(Event), Event == PresentEvent);
+ }
}
}
}
@@ -2057,6 +2046,7 @@ void cMenuSetupEPG::Setup(void)
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"), &data.EPGScanTimeout));
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"), &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
+ Add(new cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"), &data.EPGLinger, 0));
Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
if (data.SetSystemTime)
Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder));
@@ -2314,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));
@@ -2784,7 +2776,7 @@ void cDisplayChannel::DisplayInfo(void)
if (Schedule) {
const char *PresentTitle = NULL, *PresentSubtitle = NULL, *FollowingTitle = NULL, *FollowingSubtitle = NULL;
int Lines = 0;
- if ((Present = Schedule->GetPresentEvent()) != NULL) {
+ if ((Present = Schedule->GetPresentEvent(true)) != NULL) {
PresentTitle = Present->Title();
if (!isempty(PresentTitle))
Lines++;
@@ -2792,7 +2784,7 @@ void cDisplayChannel::DisplayInfo(void)
if (!isempty(PresentSubtitle))
Lines++;
}
- if ((Following = Schedule->GetFollowingEvent()) != NULL) {
+ if ((Following = Schedule->GetFollowingEvent(true)) != NULL) {
FollowingTitle = Following->Title();
if (!isempty(FollowingTitle))
Lines++;
@@ -3076,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();
@@ -3135,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;
@@ -3207,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);
@@ -3237,7 +3232,7 @@ void cRecordControls::Stop(cDevice *Device)
{
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
- if (RecordControls[i]->Uses(Device)) {
+ if (RecordControls[i]->Device() == Device) {
isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
RecordControls[i]->Stop(true);
}
@@ -3316,10 +3311,12 @@ void cRecordControls::ChannelDataModified(cChannel *Channel)
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (RecordControls[i]->Timer() && RecordControls[i]->Timer()->Channel() == Channel) {
- isyslog("stopping recording due to modification of channel %d", Channel->Number());
- RecordControls[i]->Stop(true);
- // This will restart the recording, maybe even from a different
- // device in case conditional access has changed.
+ if (RecordControls[i]->Device()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
+ isyslog("stopping recording due to modification of channel %d", Channel->Number());
+ RecordControls[i]->Stop(true);
+ // This will restart the recording, maybe even from a different
+ // device in case conditional access has changed.
+ }
}
}
}
diff --git a/menu.h b/menu.h
index 041d6f7..a03d3a0 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.59 2004/01/04 11:01:13 kls Exp $
+ * $Id: menu.h 1.60 2004/02/15 14:11:28 kls Exp kls $
*/
#ifndef __MENU_H
@@ -123,7 +123,7 @@ public:
cRecordControl(cDevice *Device, cTimer *Timer = NULL, bool Pause = false);
virtual ~cRecordControl();
bool Process(time_t t);
- bool Uses(cDevice *Device) { return Device == device; }
+ cDevice *Device(void) { return device; }
void Stop(bool KeepInstant = false);
bool IsInstant(void) { return instantId; }
const char *InstantId(void) { return instantId; }
diff --git a/menuitems.c b/menuitems.c
index e664154..e3c99a5 100644
--- a/menuitems.c
+++ b/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.14 2004/01/25 15:40:55 kls Exp kls $
*/
#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)
diff --git a/menuitems.h b/menuitems.h
index f9b2ff3..72a2418 100644
--- a/menuitems.h
+++ b/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.5 2003/01/12 15:06:23 kls Exp kls $
*/
#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;
diff --git a/nit.c b/nit.c
index f388716..92c02f1 100644
--- a/nit.c
+++ b/nit.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: nit.c 1.5 2004/01/18 16:32:45 kls Exp $
+ * $Id: nit.c 1.6 2004/02/13 14:41:36 kls Exp $
*/
#include "nit.h"
@@ -106,7 +106,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
int CodeRate = CodeRates[sd->getFecInner()];
int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10;
if (ThisNIT >= 0) {
- if (ISTRANSPONDER(Frequency, Transponder())) {
+ if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) {
nits[ThisNIT].hasTransponder = true;
//printf("has transponder %d\n", Transponder());
}
diff --git a/remux.c b/remux.c
index 5ea24bf..65544b5 100644
--- a/remux.c
+++ b/remux.c
@@ -8,7 +8,7 @@
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
* VDR's needs.
*
- * $Id: remux.c 1.17 2003/09/14 10:34:39 kls Exp $
+ * $Id: remux.c 1.18 2004/02/14 10:40:37 kls Exp $
*/
/* The calling interface of the 'cRemux::Process()' function is defined
@@ -114,6 +114,7 @@
#define SC_PICTURE 0x00 // "picture header"
#define MAXNONUSEFULDATA (10*1024*1024)
+#define MAXNUMUPTERRORS 10
class cTS2PES {
private:
@@ -459,6 +460,7 @@ cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOn
dPid1 = DPid1;
dPid2 = DPid2;
exitOnFailure = ExitOnFailure;
+ numUPTerrors = 0;
synced = false;
skipped = 0;
resultCount = resultDelivered = 0;
@@ -618,8 +620,11 @@ XXX*/
if (l < 0)
return NULL; // no useful data found, wait for more
if (pt != NO_PICTURE) {
- if (pt < I_FRAME || B_FRAME < pt)
+ if (pt < I_FRAME || B_FRAME < pt) {
esyslog("ERROR: unknown picture type '%d'", pt);
+ if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
+ cThread::EmergencyExit(true);
+ }
else if (!synced) {
if (pt == I_FRAME) {
resultDelivered = i; // will drop everything before this position
diff --git a/remux.h b/remux.h
index e953001..31e64df 100644
--- a/remux.h
+++ b/remux.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.h 1.10 2003/04/26 14:13:11 kls Exp $
+ * $Id: remux.h 1.11 2004/02/14 10:40:41 kls Exp $
*/
#ifndef __REMUX_H
@@ -30,6 +30,7 @@ class cTS2PES;
class cRemux {
private:
bool exitOnFailure;
+ int numUPTerrors;
bool synced;
int skipped;
int vPid, aPid1, aPid2, dPid1, dPid2;
diff --git a/svdrp.c b/svdrp.c
index 4ade5ec..b73d211 100644
--- a/svdrp.c
+++ b/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.59 2004/01/31 10:13:50 kls Exp $
+ * $Id: svdrp.c 1.60 2004/02/22 15:31:23 kls Exp kls $
*/
#include "svdrp.h"
@@ -205,8 +205,12 @@ const char *HelpPages[] = {
" List channels. Without option, all channels are listed. Otherwise\n"
" only the given channel is listed. If a name is given, all channels\n"
" containing the given string as part of their name are listed.",
- "LSTE\n"
- " List EPG data.",
+ "LSTE [ <channel> ] [ now | next | at <time> ]\n"
+ " List EPG data. Without any parameters all data of all channels is\n"
+ " listed. If a channel is given (either by number of by channel ID),\n",
+ " only data for that channel is listed. 'now', 'next', or 'at <time>'\n"
+ " restricts the returned data to present events, following events, or\n"
+ " events at the given time (which must be in time_t form)."
"LSTR [ <number> ]\n"
" List recordings. Without option, all recordings are listed. Otherwise\n"
" the summary for the given recording is listed.",
@@ -714,9 +718,65 @@ void cSVDRP::CmdLSTE(const char *Option)
cSchedulesLock SchedulesLock;
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
if (Schedules) {
+ const cSchedule* Schedule = NULL;
+ eDumpMode DumpMode = dmAll;
+ time_t AtTime = 0;
+ if (*Option) {
+ char buf[strlen(Option) + 1];
+ strcpy(buf, Option);
+ const char *delim = " \t";
+ char *p = strtok(buf, delim);
+ while (p && DumpMode == dmAll) {
+ if (strcasecmp(p, "NOW") == 0)
+ DumpMode = dmPresent;
+ else if (strcasecmp(p, "NEXT") == 0)
+ DumpMode = dmFollowing;
+ else if (strcasecmp(p, "AT") == 0) {
+ DumpMode = dmAtTime;
+ if ((p = strtok(NULL, delim)) != NULL) {
+ if (isnumber(p))
+ AtTime = strtol(p, NULL, 10);
+ else {
+ Reply(501, "Invalid time");
+ return;
+ }
+ }
+ else {
+ Reply(501, "Missing time");
+ return;
+ }
+ }
+ else if (!Schedule) {
+ cChannel* Channel = NULL;
+ if (isnumber(p))
+ Channel = Channels.GetByNumber(strtol(Option, NULL, 10));
+ else
+ Channel = Channels.GetByChannelID(tChannelID::FromString(Option));
+ if (Channel) {
+ Schedule = Schedules->GetSchedule(Channel->GetChannelID());
+ if (!Schedule) {
+ Reply(550, "No schedule found");
+ return;
+ }
+ }
+ else {
+ Reply(550, "Channel \"%s\" not defined", p);
+ return;
+ }
+ }
+ else {
+ Reply(501, "Unknown option: \"%s\"", p);
+ return;
+ }
+ p = strtok(NULL, delim);
+ }
+ }
FILE *f = fdopen(file, "w");
if (f) {
- Schedules->Dump(f, "215-");
+ if (Schedule)
+ Schedule->Dump(f, "215-", DumpMode, AtTime);
+ else
+ Schedules->Dump(f, "215-", DumpMode, AtTime);
fflush(f);
Reply(215, "End of EPG data");
// don't 'fclose(f)' here!
@@ -741,7 +801,7 @@ void cSVDRP::CmdLSTR(const char *Option)
free(summary);
}
else
- Reply(550, "No summary availabe");
+ Reply(550, "No summary available");
}
else
Reply(550, "Recording \"%s\" not found", Option);
@@ -845,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
diff --git a/timers.c b/timers.c
index 1db88a0..b29d6c8 100644
--- a/timers.c
+++ b/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.8 2003/12/27 13:10:04 kls Exp $
+ * $Id: timers.c 1.9 2004/02/13 15:37:49 kls Exp kls $
*/
#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;
@@ -219,7 +230,7 @@ bool cTimer::Parse(const char *s)
if (isnumber(channelbuffer))
channel = Channels.GetByNumber(atoi(channelbuffer));
else
- channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true);
+ channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true, true);
if (!channel) {
esyslog("ERROR: channel %s not defined", channelbuffer);
result = false;
@@ -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)
+{
+ 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)
{
- active = Active;
+ 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);
+ }
+ }
+}
diff --git a/timers.h b/timers.h
index b70983c..11fc0b2 100644
--- a/timers.h
+++ b/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.6 2003/12/13 13:04:21 kls Exp kls $
*/
#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;
diff --git a/vdr.5 b/vdr.5
index 7f8626e..2947e94 100644
--- a/vdr.5
+++ b/vdr.5
@@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
-.\" $Id: vdr.5 1.23 2004/01/25 14:44:59 kls Exp $
+.\" $Id: vdr.5 1.25 2004/02/22 13:18:48 kls Exp kls $
.\"
.TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files"
.SH NAME
@@ -178,6 +178,13 @@ so the above example could also be written as S19.2E-1-1089-12003).
.br
The \fBchannel\ ID\fR is used in the \fItimers.conf\fR and \fIepg.data\fR
files to properly identify the channels.
+
+If a channel has both \fBNID\fR and \fBTID\fR set to 0, the \fBchannel\ ID\fR
+will use the \fBFrequency\fR instead of the \fBTID\fR. For satellite channels
+an additional offset of 100000, 200000, 300000 or 400000 is added to that
+number, depending on the \fBPolarization\fR (\fBH\fR, \fBV\fR, \fBL\fR or \fBR\fR,
+respectively). This is necessary because on some satellites the same frequency is
+used for two different transponders, with opposite polarization.
.SS TIMERS
The file \fItimers.conf\fR contains the timer setup.
Each line contains one timer definition, with individual fields
@@ -189,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
@@ -554,6 +566,7 @@ l l.
\fBT\fR@<title>
\fBS\fR@<short text>
\fBD\fR@<description>
+\fBV\fR@<vps time>
\fBe\fR@
\fBc\fR@
.TE
@@ -576,6 +589,7 @@ l l.
<title> @is the title of the event
<short text> @is the short text of the event (typically the name of the episode etc.)
<description> @is the description of the event (any '|' characters will be interpreted as newlines)
+<vps time> @is the Video Programming Service time of this event
.TE
This file will be read at program startup in order to restore the results of
diff --git a/vdr.c b/vdr.c
index 9167307..5d43874 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
- * $Id: vdr.c 1.175 2004/02/08 11:23:29 kls Exp $
+ * $Id: vdr.c 1.177 2004/02/15 14:29:30 kls Exp kls $
*/
#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:
@@ -496,11 +497,15 @@ int main(int argc, char *argv[])
if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
static time_t lastTime = 0;
if (time(NULL) - lastTime > MINCHANNELWAIT) {
- if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
- && !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer...
- && !cDevice::SwitchChannel(1) // ...or the next higher available one...
- && !cDevice::SwitchChannel(-1)) // ...or the next lower available one
- lastTime = time(NULL); // don't do this too often
+ cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+ if (Channel && (Channel->Vpid() || Channel->Apid1())) {
+ if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
+ && !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer...
+ && !cDevice::SwitchChannel(1) // ...or the next higher available one...
+ && !cDevice::SwitchChannel(-1)) // ...or the next lower available one
+ ;
+ }
+ lastTime = time(NULL); // don't do this too often
LastTimerChannel = -1;
}
}
@@ -522,8 +527,10 @@ int main(int argc, char *argv[])
cRecordControls::ChannelDataModified(Channel);
if (Channel->Number() == cDevice::CurrentChannel()) {
if (!cDevice::PrimaryDevice()->Replaying() || cTransferControl::ReceiverDevice()) {
- isyslog("retuning due to modification of channel %d", Channel->Number());
- Channels.SwitchTo(Channel->Number());
+ if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
+ isyslog("retuning due to modification of channel %d", Channel->Number());
+ Channels.SwitchTo(Channel->Number());
+ }
}
}
}
@@ -542,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))
@@ -551,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())
@@ -752,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!"));