diff options
Diffstat (limited to 'obsolete-patch')
-rw-r--r-- | obsolete-patch/HISTORY | 42 | ||||
-rw-r--r-- | obsolete-patch/README | 33 | ||||
-rw-r--r-- | obsolete-patch/README - series | 24 | ||||
-rw-r--r-- | obsolete-patch/README - vps | 18 | ||||
-rw-r--r-- | obsolete-patch/TODO | 9 | ||||
-rwxr-xr-x | obsolete-patch/series.pl | 543 | ||||
-rw-r--r-- | obsolete-patch/vdrtva-1.6.0-2.diff | 534 | ||||
-rw-r--r-- | obsolete-patch/vdrtva-1.7.18.diff | 482 | ||||
-rw-r--r-- | obsolete-patch/vdrtva-1.7.20.diff | 361 |
9 files changed, 2046 insertions, 0 deletions
diff --git a/obsolete-patch/HISTORY b/obsolete-patch/HISTORY new file mode 100644 index 0000000..9af5ce2 --- /dev/null +++ b/obsolete-patch/HISTORY @@ -0,0 +1,42 @@ +2008/2/2 - Version 0.0.1 (vdr version 1.5.13) + +- Initial preview. + +2008/3/30 - Version 0.0.2 (vdr version 1.6.0) + +- sdt.c - minor code tidy. +- eit.c - don't populate CRIDs if DefaultAuthority hasn't been set yet. + - change tags in epg.data to match vdr standard. + - check CRID is not indirectly referenced via a CIT. +- vdr.5 - document changes made to files. + +2008/9/30 - Version 0.0.3 (vdr version 1.6.0-2) + +- Patch against latest VDR version +- Sample Perl program included to implement Series Link. +- Freeview Playback is now Freeview Plus. + +2008/12/01 - Version 0.0.4 (vdr version 1.6.0-2) +- eit.c - fixed memory leak (thanks Dominic Morris). +- series.pl - links file could be corrupted if a new timer was manually added to a series. Timer clash detection added. General clean-up. + +2009/8/23 - Version 0.0.5 (vdr versions 1.6.0-2 and 1.7.9) +- New patch for development version of vdr. + +2009/11/22 - Version 0.0.6 (vdr versions 1.6.0-2 and 1.7.10) +- timers.c - Apply an old VPS patch by Anssi Hannula to use Running Status for VPS. +- series.pl - Auto-created timers use Running Status to set start/stop times. +- README-vps New file. + +2010/1/6 - Version 0.0.7 (vdr versions 1.6.0-2 and 1.7.11) +- config.c +- menu.c +- timers.c - Modify VPS patch to use a new config parameter 'VpsFallback' to determine whether to use Running Status for accurate recording. Config changes based on the Mandriva source for vdr. +- epg.c - Change the ID letters used for the CRIDs in epg.data to X and Y, because vdr now uses R for Parental Rating. Note that this will cause error messages in the log when viewing old recordings but these are harmless. +- vdr.5 - Updated man page with new CRID IDs. + +2010/5/9 - Version 0.0.8 (vdr versions 1.6.0-2 and 1.7.14) +series.pl - Ensure that all parts of split events (eg a long film with a news summary in the middle) are recorded. Warn if the title of an event in the EPG has changed since a timer was set - maybe it is no longer the same programme... + +2010/6/6- Version 0.0.9 (vdr versions 1.6.0-2 and 1.7.15) +series.pl - Don't try to process timers if there aren't any. Change default SVRDP port to 6419 in line with VDR 1.7.15. diff --git a/obsolete-patch/README b/obsolete-patch/README new file mode 100644 index 0000000..af7b373 --- /dev/null +++ b/obsolete-patch/README @@ -0,0 +1,33 @@ +TV-Anytime is the name given to a set of technologies which aim to simplify the +process of recording and replaying broadcast content. The standards are published +by ETSI and are available without cost from www.etsi.org (registration required). The main standard is ETSI TS 102 323. + +In the UK a subset of the TV-Anytime specification is broadcast on the DTV service +under the trade name "FreeView Plus". This patch is written for the UK version but +should work with the full specification (untested). + +TV-Anytime data is contained in Content Reference Identifiers (CRIDs). The syntax +of a CRID is described in RFC 4078; it is a URI-compliant string of the form: + + crid://<DNS name>/<data> + +in which <DNS name> is a registered internet domain name (RFC 1034) and <data> is +a free-format string. The <DNS Name> section relates to the content provider (TV +channel or company), and the <data> section to the programme. + +CRIDs are transmitted in the EIT as Content Identifier Descriptors, with descriptor +ID 0x76. To save bandwith only the <data> section is sent, the <DNS Name> part is +taken from the Default Authority Descriptor in the SDT, and the crid:// is assumed. + +A programme may have up to two CRIDs in its EPG entry. One identifies the specific +item of content which is being broadcast, while the other identifies a series of +programmes which this item belongs to. In FreeView Plus these CRIDs have crid_type +values 0x31 and 0x32 respectively (TV-Anytime uses values 0x01 and 0x02). + +To give an example, the programme "Torchwood" broadcast on channel BBC2 at 21:00 on +2008-01-16 had item CRID '54BXLC' and series CRID 'KCJ00C'. When the same programme +was repeated the following day on channel BBC3, the item CRID remained the same but +the series CRID was 'KCJ12C'. Meanwhile the episode broadcast on BBC2 one week +later on 2008-01-24 had CRID '54BXLD' but the same series as the previous week. Hence it is possible for a PVR to record an entire series by using the series CRID, or to find an alternative broadcast for an individual item if there is a clash with +another recording. + diff --git a/obsolete-patch/README - series b/obsolete-patch/README - series new file mode 100644 index 0000000..6e0956b --- /dev/null +++ b/obsolete-patch/README - series @@ -0,0 +1,24 @@ +This is a very simple script to demonstrate the 'series link' concept. Run it every day as a cron job and never miss your favourite series again! Just create a timer for the first programme in a series and the script will automatically record the rest for you. + +Configuration parameters at the start of the file must be set to match your vdr +settings. The 'padding' values must match those used when you manually set timers (eg when using vdradmin). + +If you set the 'VPS' config parameter to '1', and also set 'UseVps = 1' and 'VpsFallack=1' in VDR's setup.conf, new timers will be created to use the EIT Running Status to set start and end times. An accurate Running Status is a requirement of the Freeview Plus specification. However if the programme starts earlier than (scheduled time - VpsMargin) the beginning will be missed. + +The script detects split events, eg a film with a news summary in the middle, and ensures that if a timer is set for one part, all parts of the programme are recorded (as separate timers). The script also checks for timer clashes, though it doesn't try to resolve the clash, and also warns if the title of an event in the EPG has changed since the timer was set (so perhaps a different programme is being broadcast). + +The script creates a file "links.data" in the vdr directory when run. This file contains series CRIDs of all of the timers which have been set, and the item CRIDs of the individual programmes which have had recordings scheduled. A timestamp against each entry gives the date of the last timer set, so that old series can be automatically purged. + +Points to remember: + +- Not all channels on UK Freeview have CRIDs in the EPG (at present just the BBC, ITV, C4 and C5 stables plus Sky 3 and Virgin 1). Some radio channels have item CRIDs but none have series CRIDs. + +- Different programme providers have different ideas of what constitutes a 'series'. + +- The timer creation process is very simplistic; it doesn't check for timer clashes, and selects the first physical entry in the EPG (which may not be the prime broadcast of the programme). + +- A series link is created for every timer whether you want one or not. + +- If you run this script overnight, a timer set one day which fires on the same day will not create a series link (because the timer no longer exists). + +- This script has not been tested with multiple tuner cards or with mixed DVB-T and DVB-S setups. diff --git a/obsolete-patch/README - vps b/obsolete-patch/README - vps new file mode 100644 index 0000000..5f4f38a --- /dev/null +++ b/obsolete-patch/README - vps @@ -0,0 +1,18 @@ +One of the important features of a video recorder is the ability to deal with programme schedule changes and to ensure that the wanted programme is always recorded. TVAnytime has its own set of data descriptors to handle late-running or cancelled programmes but these are not used by Freeview Plus. Instead the Running Status from the now/next section of the EIT is used to start and stop the recording. + +VDR can control timers from the running status by using 'VPS' functions in conjunction with this patch. Note however that the normal VPS operation will no longer work. + +To use VPS for accurate recording, some conditions must be met: + +- Parameters 'UseVps' and 'VpsFallback' in VDR's setup.conf must both be set to 1 +- Timers must have the 'use VPS' flag set in timers.conf +- The start time of a timer must be set to exactly the scheduled start time of the programme (ie no padding). + +With these conditions met, the sequence of events is: + +- Shortly before the scheduled time of the recording (set by 'VpsMargin' in VDR's setup.conf) VDR switches to the multiplex containing the channel to be recorded +- VDR monitors the running status of the programme to be recorded +- When the running status changes to 'running', VDR starts recording +- When the running status changes to 'not running', VDR stops recording and deletes the timer. + +CAUTION - VPS recordings seem to have a lower priority than live viewing. VDR will not interrupt live viewing to start looking for the start of a recording.
\ No newline at end of file diff --git a/obsolete-patch/TODO b/obsolete-patch/TODO new file mode 100644 index 0000000..ac4653d --- /dev/null +++ b/obsolete-patch/TODO @@ -0,0 +1,9 @@ +- We should allow for Content Identifier Descriptors to include more than one CRID, as allowed in ETSI 102 323. Freeview has each CRID in a separate descriptor. + +- The Authority portion of the CRID is always taken from the SDT. ETSI 102 323 provides several other locations for this information which should be searched before the SDT. + +- ETSI 102 323 allows CRID data to be referenced indirectly via a Content Identifier Table (though this use is deprecated). Indirection is not supported as Freeview does not broadcast CITs. + +- series.pl should be smarter; it should choose the 'best' broadcast of an event, should workaround schedule clashes, and only record a series if the user requests it. + +- When using the accurate recording function, the event will not be recorded if it starts before (scheduled_start_time - VpsMargin).
\ No newline at end of file diff --git a/obsolete-patch/series.pl b/obsolete-patch/series.pl new file mode 100755 index 0000000..8423da9 --- /dev/null +++ b/obsolete-patch/series.pl @@ -0,0 +1,543 @@ +#!/usr/bin/perl +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# Or, point your browser to http://www.gnu.org/copyleft/gpl.html +# +# Parts of this code are taken from VDRAdmin-AM +# (C) 2005 - 2008 by Andreas Mair <mail@andreas.vdr-developer.org> + + + +use Socket; +use POSIX; + +my (%CONFIG); +$CONFIG{VDR_HOST} = "localhost"; # Name or IP address of VDR server +$CONFIG{VDR_PORT} = 6419; # SVDRP port on VDR server (use 2001 pre - 1.7.15) +$CONFIG{SERIES_TIMEOUT} = 30; # Expiry time of a series (days) +$CONFIG{START_PADDING} = 1; # Padding when creating new timers without VPS +$CONFIG{STOP_PADDING} = 3; +$CONFIG{PRIORITY} = 99; # Recording priority and lifetime +$CONFIG{LIFETIME} = 99; +$CONFIG{LINKSDIR} = "/video/video"; # Directory holding links file +$CONFIG{VPS} = 1; # 1 = control timer from EIT running status + +if ($CONFIG{VPS}) { + $CONFIG{START_PADDING} = 0; + $CONFIG{VPS} = 1; +} + +my (@timers, @chans, @epg); +my %links = {}; +my $updates = 0; + +open_SVDRP(); +get_channels(); +get_epg(); +get_timers(); +get_links(); +if (scalar (@timers)) { + $updates = check_timers(); +} +$updates += check_links(); +if (scalar (@timers)) { + $updates += check_split_recordings(); + if ($updates) { + put_links(); + @timers = (); + get_timers(); + } +#show_links(); + check_timer_clashes(); + check_changed_events(); +} +close_SVDRP(); + +# Examine each timer and update the links file if necessary (manually-added timers) + +sub check_timers { + + my $count = 0; + foreach my $timer (@timers) { + my $channelid = $chans[$timer->{chan}] -> {id}; + my $start_t = $timer->{tstart}; + my $stop_t = $timer->{tstop}; + foreach my $prog (@epg) { + my $scrid = $prog->{scrid}; + my $icrid = $prog->{icrid}; + next if $scrid eq 'NULL'; + if (($prog->{sid} eq $channelid) && ($start_t <= $prog->{st}) && ($stop_t >= $prog->{et})) { + if (exists $links{$scrid}) { # Existing series + if ($links{$scrid} !~ /$icrid/) { + my ($last_t,$val) = split(';', $links{$scrid}); + $links{$scrid} = "$start_t;$val:$icrid"; # New episode already added + $count++; + } + } + else { # New series added to timer + $links{$scrid} = "$start_t;$icrid"; + $count++; + } + } + } + } + return $count; +} + +# Check for new EPG entries for each series in the links file and create timers. +# FIXME This algorithm fails if an item is part of two series and both are being +# recorded (how can that happen?). + +sub check_links { + my $count = 0; + foreach my $prog (@epg) { + my $scrid = $prog->{scrid}; + my $icrid = $prog->{icrid}; + next if $scrid eq 'NULL'; + if (exists $links{$scrid}) { + if ($links{$scrid} !~ /$icrid/) { +# Have we already recorded this programme on a diferent series? + my $title = get_title($prog->{sid},$prog->{st}); + my $done = 0; + for (values %links) { + if (/$icrid/) { + print STDOUT "Item $icrid ($title) already recorded!\n"; + $done = 1; + } + } + if (!$done) { + my $fstart = strftime("%Y-%m-%d:%H%M", localtime($prog->{st}-$CONFIG{START_PADDING}*60)); + my $fend = strftime("%H%M", localtime($prog->{et}+$CONFIG{STOP_PADDING}*60)); + my $flag = 1 + 4*$CONFIG{VPS}; + print STDOUT "New timer set for $scrid, \"$title\" at $fstart\n"; + set_timer ("$flag:$prog->{sid}:$fstart:$fend:$CONFIG{PRIORITY}:$CONFIG{LIFETIME}:$title:"); + my ($last_t,$val) = split(';', $links{$scrid}); + $links{$scrid} = "$prog->{st};$val:$icrid"; + $count++; + } + } + } + } + return $count; +} + +# Review the timer list for clashes. FIXME: What happens when there are multiple cards? + +sub check_timer_clashes { + my ($ii, $jj); + my (@tstart, @tstop); + print STDOUT "Checking for timer clashes\n"; + for ($ii = 0 ; $ii < @timers ; $ii++) { + push @tstart, $timers[$ii]->{tstart}; + push @tstop, $timers[$ii]->{tstop}; + + for ($jj = 0 ; $jj < $ii ; $jj++) { + if (is_clash($ii, $jj)) { + # What to do?? For now just report the collision + my $start1 = $tstart[$ii]+$CONFIG{START_PADDING}*60; + my $start2 = $tstart[$jj]+$CONFIG{START_PADDING}*60; + my $ttl1 = get_title($chans[$timers[$ii]->{chan}]->{id}, $start1); + my $ttl2 = get_title($chans[$timers[$jj]->{chan}]->{id}, $start2); + my $crid1 = get_crid_for_timer($chans[$timers[$ii]->{chan}]->{id}, $start1); + my $crid2 = get_crid_for_timer($chans[$timers[$jj]->{chan}]->{id}, $start2); + my $when = localtime ($timers[$ii]->{tstart}); + print STDOUT "Collision! $when\n$ttl1 ($crid1) <-> $ttl2 ($crid2)\n"; + list_alternatives($crid1, $ttl1, $start1); + list_alternatives($crid2, $ttl2, $start2); + } + } + } + + sub is_clash { + my ($i, $j) = @_; + if (($tstart[$i] >= $tstart[$j] && $tstart[$i] < $tstop[$j]) + || ($tstart[$j] >= $tstart[$i] && $tstart[$j] < $tstop[$i])) { + # Timers collide in time. Check if the + # Timers are on the same transponder + my $t1 = $chans[$timers[$i]->{chan}] -> {transponder}; + my $t2 = $chans[$timers[$j]->{chan}] -> {transponder}; + if ($t1 eq $t2) { +# print STDOUT "Multiple recordings on same transponder - OK\n"; + return 0; + } + else { + return 1; + } + } + return 0; + } + + sub list_alternatives { + my ($crid, $ttl, $start) = @_; + + if (my @prog = events_for_crid($crid)) { + print STDOUT "Alternatives for $ttl:\n"; + foreach my $evt (@prog) { + if ($evt->{st} != $start) { + my $w = localtime($evt->{st}); + my ($c) = channel_name_from_id($evt->{sid}) =~ /(.*?);/; + print STDOUT "\t$c - $w\n"; + } + } + } + } +} + +# Look for split events (eg a film with a news summary in the middle), and ensure +# that all parts of the event are being recorded. +# A split event has a # in the item CRID, for example /19778232#1 +# FIXME this will likely fail if a split event is repeated or is part of a series. + +sub check_split_recordings { + + my $count = 0; + my @splits; + print STDOUT "Checking for split recordings\n"; + foreach my $timer (@timers) { + my $channelid = $chans[$timer->{chan}] -> {id}; + my $start_t = $timer->{tstart}; + my $stop_t = $timer->{tstop}; + foreach my $prog (@epg) { + if (($prog->{sid} eq $channelid) && ($start_t <= $prog->{st}) && ($stop_t >= $prog->{et})) { + if ($prog->{icrid} =~ /\#/) { + push @splits, "$prog->{icrid}:$prog->{st}:$channelid"; + } + } + } + } + foreach $split (@splits) { + my ($tcrid, $tstart, $tchan) = split(':', $split); + foreach my $prog (@epg) { + if (($prog->{icrid} eq $tcrid) && ($tchan eq $prog->{sid}) && ($prog->{st} != $tstart)) { + my $got = 0; + foreach $split2 (@splits) { + my ($tcrid2, $tstart2, $tchan2) = split(':', $split2); + if (($prog->{icrid} eq $tcrid2) && ($tchan2 eq $prog->{sid}) && ($prog->{st} == $tstart2)) { + $got = 1; + } + } + if (!$got) { + print STDOUT "Unset timer for split event $tcrid found!\n"; + my $fstart = strftime("%Y-%m-%d:%H%M", localtime($prog->{st}-$CONFIG{START_PADDING}*60)); + my $fend = strftime("%H%M", localtime($prog->{et}+$CONFIG{STOP_PADDING}*60)); + my $title = get_title($prog->{sid},$prog->{st}); + my $flag = 1 + 4*$CONFIG{VPS}; + print STDOUT "New timer set for \"$title\" at $fstart\n"; + set_timer ("$flag:$prog->{sid}:$fstart:$fend:$CONFIG{PRIORITY}:$CONFIG{LIFETIME}:$title:"); + $count++; + } + } + } + } + return $count; +} + +# Scan the timers list for events which have changed since the timer was +# set. For now we just report the error. TODO: if the event has moved maybe +# we should try to find the new one? + +sub check_changed_events { + + print STDOUT "Checking for changed timer events\n"; + foreach my $timer (@timers) { + my $channelid = $chans[$timer->{chan}] -> {id}; + my $start_t = $timer->{tstart}; + my $timer_title = $timer ->{title}; + my $event_title = get_title ($channelid, $start_t); + if ($timer_title ne $event_title) { + print STDOUT "Event: $event_title <=> Timer: $timer_title\n"; + } + } +} + +# Get a list of all occurrences of an event CRID. Return an array of EPG objects. + +sub events_for_crid { + + my ($crid) = @_; + my @elist; + + foreach my $prog (@epg) { + if ($prog->{icrid} eq $crid) { + push (@elist, $prog); + } + } + return @elist; +} + +# Get the name of a channel given its ID. + +sub channel_name_from_id { + + my ($id) = @_; + foreach my $chan (@chans) { + if ($chan->{id} eq $id) { + return $chan->{name}; + } + } + return ('Unknown'); +} + +# Read the timers from VDR + +sub get_timers { + + Send("LSTT"); + while (<SOCK>) { + chomp; + if (/^\d*([- ])\d* (.*)/) { + my ($flag,$chan,$day,$start,$stop,$prio,$life,$title) = split(':', $2); + my ($yy,$mm,$dd) = split('-', $day); + my $starth = $start / 100; + my $startm = $start % 100; + my $stoph = $stop / 100; + my $stopm = $stop % 100; + my $tstart = mktime(0, $startm, $starth, $dd, $mm-1, $yy-1900, 0, 0, -1); + if ($stoph < $starth) { # prog over midnight + $dd++; + } + my $tstop = mktime(0, $stopm, $stoph, $dd, $mm-1, $yy-1900, 0, 0, -1); + push (@timers, { + flag => $flag, + chan => $chan, + tstart => $tstart, + tstop => $tstop, + title => $title + }); + } + last if $1 ne "-"; + } + print STDOUT "Read ",scalar(@timers)," Timers\n"; +} + +# Read the EPG from VDR (TVAnytime events only) + +sub get_epg { + + my ($sid,$id,$st,$et,$d); + my $icrid = "NULL"; + my $scrid = "NULL"; + Send("LSTE"); + while (<SOCK>) { + chomp; + if (my ($flag,$type,$data) = /^215(.)(.) *(.*)$/) { + if ($type eq 'C') { + ($sid) = ($data =~ /^(.*?) /); + } + elsif ($type eq 'Y') { + $icrid = $data; + } + elsif ($type eq 'Z') { + $scrid = $data; + } + elsif ($type eq 'E') { + ($id,$st,$d) = split(" ",$data); + $et = $st + $d; + } + elsif ($type eq 'e') { + if ($icrid ne "NULL") { + push (@epg, { + sid => $sid, + st => $st, + et => $et, + id => $id, + icrid => $icrid, + scrid => $scrid + }); + $icrid = "NULL"; + } + $scrid = "NULL"; + } + last if $flag ne "-"; +} + } + print STDOUT "Read ",scalar(@epg)," EPG lines\n"; +} + +# Read the channels list from VDR. Channel numbers are 1-based not 0-based... + +sub get_channels { + + push (@chans, { + id => '0-0-0-0', + transponder => '0-0', + name => 'Dummy' + }); + Send("LSTC"); + while (<SOCK>) { + chomp; + if (/^\d*([- ])\d* (.*)/) { + my ($name,$f,$p,$t,$d4,$d5,$d6,$d7,$d8,$id1,$id2,$id3) = split(':', $2); + push (@chans, { + id => join('-', $t, $id2, $id3, $id1), + transponder => join('-', $t, $f), + name => $name + }); + } + last if $1 ne "-"; + } + print STDOUT "Read ",scalar(@chans)," Channels\n"; +} + +# Read the links file. + +sub get_links { + + if (open (LINKS,'<',"$CONFIG{LINKSDIR}/links.data")) { + while (<LINKS>) { + chomp; + my ($scrid,$icrids) = split(','); + $links{$scrid} = $icrids; + } + close (LINKS); + print STDOUT "Read ",scalar(keys(%links))," Links\n"; + } + else { + print STDOUT "No links file found\n"; + } +} + +# Save the links file + +sub put_links { + + print STDOUT "Rewriting Links file\n"; + open (LINKS,'>',"$CONFIG{LINKSDIR}/links.data.new") or die "Cannot open new links file\n"; + while (my($link,$val) = each %links){ + if ($val ne '') { + my ($last_t,$entries) = split(';',$val); + if (($last_t + ($CONFIG{SERIES_TIMEOUT} * 86640)) > time()) { + print LINKS $link,',',$val,"\n"; + } + else { + print STDOUT "Expiring series $link\n"; + } + } + } + close (LINKS); + if (-e "$CONFIG{LINKSDIR}/links.data.old") { + unlink "$CONFIG{LINKSDIR}/links.data.old"; + } + if (-e "$CONFIG{LINKSDIR}/links.data") { + rename "$CONFIG{LINKSDIR}/links.data", "$CONFIG{LINKSDIR}/links.data.old"; + } + rename "$CONFIG{LINKSDIR}/links.data.new", "$CONFIG{LINKSDIR}/links.data"; + print STDOUT "Wrote ",scalar(keys(%links))," Links\n"; +} + +# Display the links + +sub show_links { + + while (my($link,$val) = each %links){ + if ($val ne '') { + my ($last_t,$entries) = split(';',$val); + my $last = strftime("%Y-%m-%d:%H%M", localtime($last_t)); + print STDOUT "$link\t$last\n"; + } + } +} + +# Get the program title from EPG, given channel and start time. + +sub get_title { + + my $title = "TITLE"; + my ($chan,$time) = @_; + Send ("LSTE $chan at $time"); + while (<SOCK>) { + chomp; + if (/^215-T (.*)/) { + $title = $1; + } + last if substr($_, 3, 1) ne "-"; + } + $title =~ s/:/|/g; + return ($title); +} + +# Get the item CRID for a timer entry. + +sub get_crid_for_timer { + + my $crid = "CRID"; + my ($chan,$time) = @_; + Send ("LSTE $chan at $time"); + while (<SOCK>) { + chomp; + if (/^215-Y (.*)/) { + $crid = $1; + } + last if substr($_, 3, 1) ne "-"; + } + $crid =~ s/:/|/g; + return ($crid); +} + + +# Set a new timer + +sub set_timer { + + my $string = shift; + Send ("NEWT $string"); + Receive(); +} + +# SVDRP handling + +sub open_SVDRP { + + $SIG{ALRM} = sub { Error("timeout"); }; + alarm($Timeout); + + $iaddr = inet_aton($CONFIG{VDR_HOST}) || Error("no host: $Dest"); + $paddr = sockaddr_in($CONFIG{VDR_PORT}, $iaddr); + + $proto = getprotobyname('tcp'); + socket(SOCK, PF_INET, SOCK_STREAM, $proto) || Error("socket: $!"); + connect(SOCK, $paddr) || Error("connect: $!"); + select(SOCK); $| = 1; + Receive(); +} + +sub close_SVDRP { + print STDOUT "Closing connection\n"; + Send("quit"); + Receive(); + close(SOCK) || Error("close: $!"); +} + +sub Send +{ + my $cmd = shift || Error("no command to send"); + print SOCK "$cmd\r\n"; +} + +sub Receive +{ + while (<SOCK>) { +# print STDOUT $_; + last if substr($_, 3, 1) ne "-"; + } +} + +sub Error +{ + print STDERR "@_\n"; + close(SOCK); + exit 0; +} + diff --git a/obsolete-patch/vdrtva-1.6.0-2.diff b/obsolete-patch/vdrtva-1.6.0-2.diff new file mode 100644 index 0000000..a8032f4 --- /dev/null +++ b/obsolete-patch/vdrtva-1.6.0-2.diff @@ -0,0 +1,534 @@ +diff -ur vdr-1.6.0/channels.c vdr-1.5/channels.c +--- vdr-1.6.0/channels.c 2008-03-05 16:42:50.000000000 +0000 ++++ vdr-1.5/channels.c 2008-03-16 12:55:17.000000000 +0000 +@@ -166,6 +166,7 @@ + shortName = strdup(""); + provider = strdup(""); + portalName = strdup(""); ++ defaultAuthority = strdup(""); + memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__); + inversion = INVERSION_AUTO; + bandwidth = BANDWIDTH_AUTO; +@@ -187,6 +188,7 @@ + shortName = NULL; + provider = NULL; + portalName = NULL; ++ defaultAuthority = NULL; + schedule = NULL; + linkChannels = NULL; + refChannel = NULL; +@@ -215,6 +217,7 @@ + free(shortName); + free(provider); + free(portalName); ++ free(defaultAuthority); + } + + cChannel& cChannel::operator= (const cChannel &Channel) +@@ -223,6 +226,7 @@ + shortName = strcpyrealloc(shortName, Channel.shortName); + provider = strcpyrealloc(provider, Channel.provider); + portalName = strcpyrealloc(portalName, Channel.portalName); ++ defaultAuthority = strcpyrealloc(defaultAuthority, Channel.defaultAuthority); + memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__); + return *this; + } +@@ -407,6 +411,13 @@ + } + } + ++void cChannel::SetDefaultAuthority(const char *DefaultAuthority) ++{ ++ if (!isempty(DefaultAuthority) && strcmp(defaultAuthority, DefaultAuthority) != 0) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, DefaultAuthority); ++ } ++} ++ + #define STRDIFF 0x01 + #define VALDIFF 0x02 + +@@ -681,11 +692,11 @@ + q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs); + } + *q = 0; +- char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia ++ char caidbuf[MAXCAIDS * 5 + 10 + 256]; // 5: 4 digits plus delimiting ',', 10 + max DNS domain length: paranoia + q = caidbuf; + q += IntArrayToString(q, Channel->caids, 16); + *q = 0; +- buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->ParametersToString(), *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); ++ buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d:%s\n", FullName, Channel->frequency, *Channel->ParametersToString(), *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid, Channel->defaultAuthority); + } + return buffer; + } +@@ -720,13 +731,16 @@ + char *vpidbuf = NULL; + char *apidbuf = NULL; + char *caidbuf = NULL; +- int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid); ++ char *dabuf = NULL; ++ int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d :%a[^:]", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid, &dabuf); + if (fields >= 9) { + if (fields == 9) { + // allow reading of old format + sid = atoi(caidbuf); + delete caidbuf; + caidbuf = NULL; ++ delete dabuf; ++ dabuf = NULL; + caids[0] = tpid; + caids[1] = 0; + tpid = 0; +@@ -828,12 +842,17 @@ + } + name = strcpyrealloc(name, namebuf); + ++ if (dabuf) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, dabuf); ++ } ++ + free(parambuf); + free(sourcebuf); + free(vpidbuf); + free(apidbuf); + free(caidbuf); + free(namebuf); ++ free(dabuf); + if (!GetChannelID().Valid()) { + esyslog("ERROR: channel data results in invalid ID!"); + return false; +diff -ur vdr-1.6.0/channels.h vdr-1.5/channels.h +--- vdr-1.6.0/channels.h 2008-02-08 13:48:31.000000000 +0000 ++++ vdr-1.5/channels.h 2008-02-17 15:13:46.000000000 +0000 +@@ -114,6 +114,7 @@ + char *shortName; + char *provider; + char *portalName; ++ char *defaultAuthority; + int __BeginData__; + int frequency; // MHz + int source; +@@ -162,6 +163,7 @@ + const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; } + const char *Provider(void) const { return provider; } + const char *PortalName(void) const { return portalName; } ++ const char *DefaultAuthority(void) const { return defaultAuthority; } + 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, plus the polarization in case of sat + static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization +@@ -212,6 +214,7 @@ + void SetId(int Nid, int Tid, int Sid, int Rid = 0); + void SetName(const char *Name, const char *ShortName, const char *Provider); + void SetPortalName(const char *PortalName); ++ void SetDefaultAuthority(const char *DefaultAuthority); + void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); + void SetCaIds(const int *CaIds); // list must be zero-terminated + void SetCaDescriptors(int Level); +diff -ur vdr-1.6.0/config.c vdr-1.5/config.c +--- vdr-1.6.0/config.c 2008-02-17 13:39:00.000000000 +0000 ++++ vdr-1.5/config.c 2009-12-31 15:11:46.000000000 +0000 +@@ -255,6 +255,7 @@ + UseSubtitle = 1; + UseVps = 0; + VpsMargin = 120; ++ VpsFallback = 0; + RecordingDirs = 1; + VideoDisplayFormat = 1; + VideoFormat = 0; +@@ -428,6 +429,7 @@ + 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, "VpsFallback")) VpsFallback = atoi(Value); + else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); + else if (!strcasecmp(Name, "VideoDisplayFormat")) VideoDisplayFormat = atoi(Value); + else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value); +@@ -511,6 +513,7 @@ + Store("UseSubtitle", UseSubtitle); + Store("UseVps", UseVps); + Store("VpsMargin", VpsMargin); ++ Store("VpsFallback", VpsFallback); + Store("RecordingDirs", RecordingDirs); + Store("VideoDisplayFormat", VideoDisplayFormat); + Store("VideoFormat", VideoFormat); +diff -ur vdr-1.6.0/config.h vdr-1.5/config.h +--- vdr-1.6.0/config.h 2010-01-03 19:44:47.000000000 +0000 ++++ vdr-1.5/config.h 2009-12-31 15:09:37.000000000 +0000 +@@ -237,6 +237,7 @@ + int UseSubtitle; + int UseVps; + int VpsMargin; ++ int VpsFallback; + int RecordingDirs; + int VideoDisplayFormat; + int VideoFormat; +diff -ur vdr-1.6.0/dvbdevice.h vdr-1.5/dvbdevice.h +--- vdr-1.6.0/dvbdevice.h 2008-02-08 13:48:31.000000000 +0000 ++++ vdr-1.5/dvbdevice.h 2009-05-31 16:28:58.000000000 +0100 +@@ -15,8 +15,8 @@ + #include "device.h" + #include "dvbspu.h" + +-#if DVB_API_VERSION != 3 +-#error VDR requires Linux DVB driver API version 3! ++#if (DVB_API_VERSION != 3) && (DVB_API_VERSION != 5) ++#error VDR requires Linux DVB driver API version 3 or 5! + #endif + + #define MAXDVBDEVICES 8 +diff -ur vdr-1.6.0/eit.c vdr-1.5/eit.c +--- vdr-1.6.0/eit.c 2010-01-03 19:44:41.000000000 +0000 ++++ vdr-1.5/eit.c 2008-10-14 20:10:48.000000000 +0100 +@@ -110,6 +110,8 @@ + SI::Descriptor *d; + SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; + SI::ShortEventDescriptor *ShortEventDescriptor = NULL; ++ SI::ContentIdentifierDescriptor *itemCrid = NULL; ++ SI::ContentIdentifierDescriptor *seriesCrid = NULL; + cLinkChannels *LinkChannels = NULL; + cComponents *Components = NULL; + for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { +@@ -227,6 +229,23 @@ + } + } + break; ++ case SI::ContentIdentifierDescriptorTag: { ++ SI::ContentIdentifierDescriptor *cd = (SI::ContentIdentifierDescriptor *)d; ++ switch (cd->getCridType()) { ++ case 0x01: ++ case 0x31: { ++ itemCrid = cd; ++ d = NULL; // so that it is not deleted ++ break; ++ } ++ case 0x02: ++ case 0x32: { ++ seriesCrid = cd; ++ d = NULL; // so that it is not deleted ++ break; ++ } ++ } ++ } + default: ; + } + delete d; +@@ -248,9 +267,27 @@ + } + else if (!HasExternalData) + pEvent->SetDescription(NULL); ++ if (itemCrid && (itemCrid->getCridLocation() == 0)) { ++ char buffer[Utf8BufSize(256)]; ++ char buff2[Utf8BufSize(256)]; ++ strcpy (buffer, channel->DefaultAuthority()); ++ itemCrid->entry.getText(buff2, sizeof(buff2)); ++ strcat(buffer, buff2); ++ pEvent->SetItemCRID(buffer); + } ++ if (seriesCrid && (seriesCrid->getCridLocation() == 0)) { ++ char buffer[Utf8BufSize(256)]; ++ char buff2[Utf8BufSize(256)]; ++ strcpy (buffer, channel->DefaultAuthority()); ++ seriesCrid->entry.getText(buff2, sizeof(buff2)); ++ strcat(buffer, buff2); ++ pEvent->SetSeriesCRID(buffer); ++ } ++ } + delete ExtendedEventDescriptors; + delete ShortEventDescriptor; ++ delete itemCrid; ++ delete seriesCrid; + + pEvent->SetComponents(Components); + +diff -ur vdr-1.6.0/epg.c vdr-1.5/epg.c +--- vdr-1.6.0/epg.c 2008-02-16 16:09:12.000000000 +0000 ++++ vdr-1.5/epg.c 2008-02-23 12:28:13.000000000 +0000 +@@ -113,6 +113,8 @@ + startTime = 0; + duration = 0; + vps = 0; ++ itemCRID = NULL; ++ seriesCRID = NULL; + SetSeen(); + } + +@@ -121,6 +123,8 @@ + free(title); + free(shortText); + free(description); ++ free(itemCRID); ++ free(seriesCRID); + delete components; + } + +@@ -205,6 +209,16 @@ + vps = Vps; + } + ++void cEvent::SetItemCRID(const char *CRID) ++{ ++ itemCRID = strcpyrealloc(itemCRID, CRID); ++} ++ ++void cEvent::SetSeriesCRID(const char *CRID) ++{ ++ seriesCRID = strcpyrealloc(seriesCRID, CRID); ++} ++ + void cEvent::SetSeen(void) + { + seen = time(NULL); +@@ -278,6 +292,10 @@ + } + if (vps) + fprintf(f, "%sV %ld\n", Prefix, vps); ++ if (!isempty(itemCRID)) ++ fprintf(f, "%sI %s\n", Prefix, itemCRID); ++ if (!isempty(seriesCRID)) ++ fprintf(f, "%sR %s\n", Prefix, seriesCRID); + if (!InfoOnly) + fprintf(f, "%se\n", Prefix); + } +@@ -300,6 +318,10 @@ + break; + case 'V': SetVps(atoi(t)); + break; ++ case 'I': SetItemCRID(t); ++ break; ++ case 'R': SetSeriesCRID(t); ++ break; + default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s); + return false; + } +diff -ur vdr-1.6.0/epg.h vdr-1.5/epg.h +--- vdr-1.6.0/epg.h 2006-10-07 14:47:19.000000000 +0100 ++++ vdr-1.5/epg.h 2008-02-17 15:13:46.000000000 +0000 +@@ -66,6 +66,8 @@ + int duration; // Duration of this event in seconds + time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL) + time_t seen; // When this event was last seen in the data stream ++ char *itemCRID; // Item CRID ++ char *seriesCRID; // Series CRID + public: + cEvent(tEventID EventID); + ~cEvent(); +@@ -84,6 +86,8 @@ + time_t EndTime(void) const { return startTime + duration; } + int Duration(void) const { return duration; } + time_t Vps(void) const { return vps; } ++ const char *ItemCRID(void) const { return itemCRID; } ++ const char *SeriesCRID(void) const { return seriesCRID; } + time_t Seen(void) const { return seen; } + bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; } + bool HasTimer(void) const; +@@ -103,6 +107,8 @@ + void SetStartTime(time_t StartTime); + void SetDuration(int Duration); + void SetVps(time_t Vps); ++ void SetItemCRID(const char *CRID); ++ void SetSeriesCRID(const char *CRID); + void SetSeen(void); + cString ToDescr(void) const; + void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const; +diff -ur vdr-1.6.0/libsi/descriptor.c vdr-1.5/libsi/descriptor.c +--- vdr-1.6.0/libsi/descriptor.c 2007-02-03 11:45:58.000000000 +0000 ++++ vdr-1.5/libsi/descriptor.c 2008-02-17 15:13:46.000000000 +0000 +@@ -643,6 +643,29 @@ + textualServiceIdentifier.setData(data+sizeof(descr_service_identifier), getLength()-sizeof(descr_service_identifier)); + } + ++void ContentIdentifierDescriptor::Parse() { ++ int offset=0; ++ data.setPointerAndOffset<const descr_content_identifier>(s, offset); ++ if (s->crid_location == 0) { ++ entry.setData(data+(offset-1), s->crid_length); ++ } ++ else { ++ entry.setData(data+(offset-1), 2); ++ } ++} ++ ++int ContentIdentifierDescriptor::getCridType() const { ++ return s->crid_type; ++} ++ ++int ContentIdentifierDescriptor::getCridLocation() const { ++ return s->crid_location; ++} ++ ++void DefaultAuthorityDescriptor::Parse() { ++ DefaultAuthority.setData(data+sizeof(descr_default_authority), getLength()-sizeof(descr_default_authority)); ++} ++ + void MultilingualNameDescriptor::Parse() { + nameLoop.setData(data+sizeof(descr_multilingual_network_name), getLength()-sizeof(descr_multilingual_network_name)); + } +diff -ur vdr-1.6.0/libsi/descriptor.h vdr-1.5/libsi/descriptor.h +--- vdr-1.6.0/libsi/descriptor.h 2007-02-03 11:45:58.000000000 +0000 ++++ vdr-1.5/libsi/descriptor.h 2008-02-17 15:13:46.000000000 +0000 +@@ -361,6 +361,25 @@ + virtual void Parse(); + }; + ++class ContentIdentifierDescriptor : public Descriptor { ++public: ++ String entry; ++ int getCridType() const; ++ int getCridLocation() const; ++// virtual int getLength() { return sizeof(descr_content_identifier)+entry.getLength(); } ++ private: ++ const descr_content_identifier *s; ++protected: ++ virtual void Parse(); ++}; ++ ++class DefaultAuthorityDescriptor : public Descriptor { ++public: ++ String DefaultAuthority; //ID ++protected: ++ virtual void Parse(); ++}; ++ + //abstract base class + class MultilingualNameDescriptor : public Descriptor { + public: +diff -ur vdr-1.6.0/libsi/headers.h vdr-1.5/libsi/headers.h +--- vdr-1.6.0/libsi/headers.h 2007-02-03 11:45:58.000000000 +0000 ++++ vdr-1.5/libsi/headers.h 2008-02-17 15:13:46.000000000 +0000 +@@ -1673,11 +1673,39 @@ + u_char descriptor_length :8; + }; + ++struct entry_tva_id { ++ u_char tva_id_hi :8; ++ u_char tva_id_lo :8; ++#if BYTE_ORDER == BIG_ENDIAN ++ u_char reserved :5; ++ u_char running_status :3; ++#else ++ u_char running_status :3; ++ u_char reserved :5; ++#endif ++}; ++ ++ + /* 0x76 content_identifier_descriptor (ETSI TS 102 323) */ + + struct descr_content_identifier { + u_char descriptor_tag :8; + u_char descriptor_length :8; ++#if BYTE_ORDER == BIG_ENDIAN ++ u_char crid_type :6; ++ u_char crid_location :2; ++#else ++ u_char crid_location :2; ++ u_char crid_type :6; ++#endif ++ union { ++ u_char crid_length :8; ++ u_char crid_ref_hi :8; ++ }; ++ union { ++ u_char crid_byte :8; ++ u_char crid_ref_lo :8; ++ }; + }; + + /* 0x77 time_slice_fec_identifier_descriptor (ETSI EN 301 192) */ +diff -ur vdr-1.6.0/libsi/si.c vdr-1.5/libsi/si.c +--- vdr-1.6.0/libsi/si.c 2008-03-05 17:00:55.000000000 +0000 ++++ vdr-1.5/libsi/si.c 2008-03-16 12:55:17.000000000 +0000 +@@ -605,6 +605,12 @@ + case ExtensionDescriptorTag: + d=new ExtensionDescriptor(); + break; ++ case ContentIdentifierDescriptorTag: ++ d=new ContentIdentifierDescriptor(); ++ break; ++ case DefaultAuthorityDescriptorTag: ++ d=new DefaultAuthorityDescriptor(); ++ break; + + //note that it is no problem to implement one + //of the unimplemented descriptors. +@@ -647,10 +653,8 @@ + case TransportStreamDescriptorTag: + + //defined in ETSI EN 300 468 v 1.7.1 +- case DefaultAuthorityDescriptorTag: + case RelatedContentDescriptorTag: + case TVAIdDescriptorTag: +- case ContentIdentifierDescriptorTag: + case TimeSliceFecIdentifierDescriptorTag: + case ECMRepetitionRateDescriptorTag: + case EnhancedAC3DescriptorTag: +diff -ur vdr-1.6.0/menu.c vdr-1.5/menu.c +--- vdr-1.6.0/menu.c 2008-03-16 11:15:28.000000000 +0000 ++++ vdr-1.5/menu.c 2009-12-31 15:14:34.000000000 +0000 +@@ -2753,6 +2753,7 @@ + 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$Use running status as VPS fallback"), &data.VpsFallback)); + 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))); + Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME)); +diff -ur vdr-1.6.0/sdt.c vdr-1.5/sdt.c +--- vdr-1.6.0/sdt.c 2008-02-08 13:48:31.000000000 +0000 ++++ vdr-1.5/sdt.c 2008-02-17 15:13:46.000000000 +0000 +@@ -123,6 +123,12 @@ + } + } + break; ++ case SI::DefaultAuthorityDescriptorTag: { ++ SI::DefaultAuthorityDescriptor *da = (SI::DefaultAuthorityDescriptor *)d; ++ char DaBuf[Utf8BufSize(1024)]; ++ da->DefaultAuthority.getText(DaBuf, sizeof(DaBuf)); ++ channel->SetDefaultAuthority(DaBuf); ++ } + default: ; + } + delete d; +diff -ur vdr-1.6.0/timers.c vdr-1.5/timers.c +--- vdr-1.6.0/timers.c 2010-01-03 19:44:41.000000000 +0000 ++++ vdr-1.5/timers.c 2009-12-31 15:16:18.000000000 +0000 +@@ -413,7 +413,7 @@ + } + + if (HasFlags(tfActive)) { +- if (HasFlags(tfVps) && event && event->Vps()) { ++ if (HasFlags(tfVps) && event && (Setup.VpsFallback || event->Vps())) { + if (Margin || !Directly) { + startTime = event->StartTime(); + stopTime = event->EndTime(); +diff -ur vdr-1.6.0/vdr.5 vdr-1.5/vdr.5 +--- vdr-1.6.0/vdr.5 2008-03-09 15:46:57.000000000 +0000 ++++ vdr-1.5/vdr.5 2008-03-16 12:55:17.000000000 +0000 +@@ -177,6 +177,9 @@ + .B RID + The Radio ID of this channel (typically 0, may be used to distinguish channels where + NID, TID and SID are all equal). ++.TP ++.B Default Authority ++The Default Authority for CRIDs on this channel (TVAnytime). + .PP + A particular channel can be uniquely identified by its \fBchannel\ ID\fR, + which is a string that looks like this: +@@ -620,6 +623,8 @@ + \fBD\fR@<description> + \fBX\fR@<stream> <type> <language> <descr> + \fBV\fR@<vps time> ++\fBI\fR@<item CRID> ++\fBR\fR@<series CRID> + \fBe\fR@ + \fBc\fR@ + .TE +@@ -653,6 +658,8 @@ + <language> @is the three letter language code (optionally two codes, separated by '+') + <descr> @is the description of this stream component + <vps time> @is the Video Programming Service time of this event ++<item CRID> @is the CRID of this event (TVAnytime) ++<series CRID> @is the CRID of the series which this event is part of (TVAnytime) + .TE + + This file will be read at program startup in order to restore the results of diff --git a/obsolete-patch/vdrtva-1.7.18.diff b/obsolete-patch/vdrtva-1.7.18.diff new file mode 100644 index 0000000..775ebaa --- /dev/null +++ b/obsolete-patch/vdrtva-1.7.18.diff @@ -0,0 +1,482 @@ +diff -ur vdr-1.7.18/channels.c vdr-1.7/channels.c +--- vdr-1.7.18/channels.c 2010-11-07 12:24:59.000000000 +0000 ++++ vdr-1.7/channels.c 2011-04-17 16:04:26.000000000 +0100 +@@ -61,6 +61,7 @@ + shortName = strdup(""); + provider = strdup(""); + portalName = strdup(""); ++ defaultAuthority = strdup(""); + memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__); + parameters = ""; + modification = CHANNELMOD_NONE; +@@ -75,6 +76,7 @@ + shortName = NULL; + provider = NULL; + portalName = NULL; ++ defaultAuthority = NULL; + schedule = NULL; + linkChannels = NULL; + refChannel = NULL; +@@ -103,6 +105,7 @@ + free(shortName); + free(provider); + free(portalName); ++ free(defaultAuthority); + } + + cChannel& cChannel::operator= (const cChannel &Channel) +@@ -111,6 +114,7 @@ + shortName = strcpyrealloc(shortName, Channel.shortName); + provider = strcpyrealloc(provider, Channel.provider); + portalName = strcpyrealloc(portalName, Channel.portalName); ++ defaultAuthority = strcpyrealloc(defaultAuthority, Channel.defaultAuthority); + memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__); + parameters = Channel.parameters; + return *this; +@@ -255,6 +259,13 @@ + } + } + ++void cChannel::SetDefaultAuthority(const char *DefaultAuthority) ++{ ++ if (!isempty(DefaultAuthority) && strcmp(defaultAuthority, DefaultAuthority) != 0) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, DefaultAuthority); ++ } ++} ++ + #define STRDIFF 0x01 + #define VALDIFF 0x02 + +@@ -506,11 +517,11 @@ + q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes); + } + *q = 0; +- char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia ++ char caidbuf[MAXCAIDS * 5 + 10 + 256]; // 5: 4 digits plus delimiting ',', 10 + max DNS domain length: paranoia + q = caidbuf; + q += IntArrayToString(q, Channel->caids, 16); + *q = 0; +- buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); ++ buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d:%s\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid, Channel->defaultAuthority); + } + return buffer; + } +@@ -545,13 +556,16 @@ + char *vpidbuf = NULL; + char *apidbuf = NULL; + char *caidbuf = NULL; +- int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid); ++ char *dabuf = NULL; ++ int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d :%a[^:]", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid, &dabuf); + if (fields >= 9) { + if (fields == 9) { + // allow reading of old format + sid = atoi(caidbuf); + delete caidbuf; + caidbuf = NULL; ++ delete dabuf; ++ dabuf = NULL; + caids[0] = tpid; + caids[1] = 0; + tpid = 0; +@@ -677,12 +691,17 @@ + } + name = strcpyrealloc(name, namebuf); + ++ if (dabuf) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, dabuf); ++ } ++ + free(parambuf); + free(sourcebuf); + free(vpidbuf); + free(apidbuf); + free(caidbuf); + free(namebuf); ++ free(dabuf); + if (!GetChannelID().Valid()) { + esyslog("ERROR: channel data results in invalid ID!"); + return false; +diff -ur vdr-1.7.18/channels.h vdr-1.7/channels.h +--- vdr-1.7.18/channels.h 2010-06-05 14:12:54.000000000 +0100 ++++ vdr-1.7/channels.h 2011-04-17 16:04:26.000000000 +0100 +@@ -96,6 +96,7 @@ + char *shortName; + char *provider; + char *portalName; ++ char *defaultAuthority; + int __BeginData__; + int frequency; // MHz + int source; +@@ -141,6 +142,7 @@ + const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; } + const char *Provider(void) const { return provider; } + const char *PortalName(void) const { return portalName; } ++ const char *DefaultAuthority(void) const { return defaultAuthority; } + 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, plus the polarization in case of sat + static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization +@@ -189,6 +191,7 @@ + void SetId(int Nid, int Tid, int Sid, int Rid = 0); + void SetName(const char *Name, const char *ShortName, const char *Provider); + void SetPortalName(const char *PortalName); ++ void SetDefaultAuthority(const char *DefaultAuthority); + void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); + void SetCaIds(const int *CaIds); // list must be zero-terminated + void SetCaDescriptors(int Level); +diff -ur vdr-1.7.18/config.c vdr-1.7/config.c +--- vdr-1.7.18/config.c 2010-06-06 11:06:43.000000000 +0100 ++++ vdr-1.7/config.c 2011-04-17 16:04:26.000000000 +0100 +@@ -353,6 +353,7 @@ + UseSubtitle = 1; + UseVps = 0; + VpsMargin = 120; ++ VpsFallback = 0; + RecordingDirs = 1; + FoldersInTimerMenu = 1; + NumberKeysForChars = 1; +@@ -544,6 +545,7 @@ + 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, "VpsFallback")) VpsFallback = atoi(Value); + else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); + else if (!strcasecmp(Name, "FoldersInTimerMenu")) FoldersInTimerMenu = atoi(Value); + else if (!strcasecmp(Name, "NumberKeysForChars")) NumberKeysForChars = atoi(Value); +@@ -640,6 +642,7 @@ + Store("UseSubtitle", UseSubtitle); + Store("UseVps", UseVps); + Store("VpsMargin", VpsMargin); ++ Store("VpsFallback", VpsFallback); + Store("RecordingDirs", RecordingDirs); + Store("FoldersInTimerMenu", FoldersInTimerMenu); + Store("NumberKeysForChars", NumberKeysForChars); +diff -ur vdr-1.7.18/config.h vdr-1.7/config.h +--- vdr-1.7.18/config.h 2011-03-13 12:02:31.000000000 +0000 ++++ vdr-1.7/config.h 2011-04-17 16:04:26.000000000 +0100 +@@ -252,6 +252,7 @@ + int UseSubtitle; + int UseVps; + int VpsMargin; ++ int VpsFallback; + int RecordingDirs; + int FoldersInTimerMenu; + int NumberKeysForChars; +diff -ur vdr-1.7.18/eit.c vdr-1.7/eit.c +--- vdr-1.7.18/eit.c 2010-05-14 15:08:35.000000000 +0100 ++++ vdr-1.7/eit.c 2011-04-17 16:04:26.000000000 +0100 +@@ -265,6 +265,27 @@ + } + } + break; ++ case SI::ContentIdentifierDescriptorTag: { ++ SI::ContentIdentifierDescriptor *cd = (SI::ContentIdentifierDescriptor *)d; ++ SI::ContentIdentifierDescriptor::Identifier cde; ++ for (SI::Loop::Iterator ite; (cd->identifierLoop.getNext(cde,ite)); ) { ++ if (cde.getCridLocation() == 0) { ++ char buffer[Utf8BufSize(256)]; ++ strcpy (buffer, channel->DefaultAuthority()); ++ strcat(buffer, cde.identifier.getText()); ++ switch (cde.getCridType()) { ++ case 0x01: ++ case 0x31: ++ pEvent->SetItemCRID(buffer); ++ break; ++ case 0x02: ++ case 0x32: ++ pEvent->SetSeriesCRID(buffer); ++ break; ++ } ++ } ++ } ++ } + default: ; + } + delete d; +diff -ur vdr-1.7.18/epg.c vdr-1.7/epg.c +--- vdr-1.7.18/epg.c 2011-02-25 15:16:05.000000000 +0000 ++++ vdr-1.7/epg.c 2011-04-17 16:04:26.000000000 +0100 +@@ -126,6 +126,8 @@ + startTime = 0; + duration = 0; + vps = 0; ++ itemCRID = NULL; ++ seriesCRID = NULL; + SetSeen(); + } + +@@ -134,6 +136,8 @@ + free(title); + free(shortText); + free(description); ++ free(itemCRID); ++ free(seriesCRID); + delete components; + } + +@@ -229,6 +233,16 @@ + vps = Vps; + } + ++void cEvent::SetItemCRID(const char *CRID) ++{ ++ itemCRID = strcpyrealloc(itemCRID, CRID); ++} ++ ++void cEvent::SetSeriesCRID(const char *CRID) ++{ ++ seriesCRID = strcpyrealloc(seriesCRID, CRID); ++} ++ + void cEvent::SetSeen(void) + { + seen = time(NULL); +@@ -457,6 +471,10 @@ + } + if (vps) + fprintf(f, "%sV %ld\n", Prefix, vps); ++ if (!isempty(itemCRID)) ++ fprintf(f, "%sY %s\n", Prefix, itemCRID); ++ if (!isempty(seriesCRID)) ++ fprintf(f, "%sZ %s\n", Prefix, seriesCRID); + if (!InfoOnly) + fprintf(f, "%se\n", Prefix); + } +@@ -495,6 +513,10 @@ + break; + case 'V': SetVps(atoi(t)); + break; ++ case 'Y': SetItemCRID(t); ++ break; ++ case 'Z': SetSeriesCRID(t); ++ break; + default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s); + return false; + } +diff -ur vdr-1.7.18/epg.h vdr-1.7/epg.h +--- vdr-1.7.18/epg.h 2011-02-25 14:14:38.000000000 +0000 ++++ vdr-1.7/epg.h 2011-04-17 16:04:26.000000000 +0100 +@@ -86,6 +86,8 @@ + int duration; // Duration of this event in seconds + time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL) + time_t seen; // When this event was last seen in the data stream ++ char *itemCRID; // Item CRID ++ char *seriesCRID; // Series CRID + public: + cEvent(tEventID EventID); + ~cEvent(); +@@ -106,6 +108,8 @@ + time_t EndTime(void) const { return startTime + duration; } + int Duration(void) const { return duration; } + time_t Vps(void) const { return vps; } ++ const char *ItemCRID(void) const { return itemCRID; } ++ const char *SeriesCRID(void) const { return seriesCRID; } + time_t Seen(void) const { return seen; } + bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; } + bool HasTimer(void) const; +@@ -129,6 +133,8 @@ + void SetStartTime(time_t StartTime); + void SetDuration(int Duration); + void SetVps(time_t Vps); ++ void SetItemCRID(const char *CRID); ++ void SetSeriesCRID(const char *CRID); + void SetSeen(void); + cString ToDescr(void) const; + void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const; +diff -ur vdr-1.7.18/libsi/descriptor.c vdr-1.7/libsi/descriptor.c +--- vdr-1.7.18/libsi/descriptor.c 2010-11-01 15:24:31.000000000 +0000 ++++ vdr-1.7/libsi/descriptor.c 2011-04-17 16:04:26.000000000 +0100 +@@ -643,6 +643,33 @@ + textualServiceIdentifier.setData(data+sizeof(descr_service_identifier), getLength()-sizeof(descr_service_identifier)); + } + ++void ContentIdentifierDescriptor::Parse() { ++ identifierLoop.setData(data+sizeof(descr_content_identifier), getLength()-sizeof(descr_content_identifier)); ++} ++ ++void ContentIdentifierDescriptor::Identifier::Parse() { ++ int offset=0; ++ data.setPointerAndOffset<const content_identifier_entry>(s, offset); ++ if (s->crid_location == 0) { ++ identifier.setData(data+(offset-1), s->crid_length); ++ } ++ else { ++ identifier.setData(data+(offset-1), 2); ++ } ++} ++ ++int ContentIdentifierDescriptor::Identifier::getCridType() const { ++ return s->crid_type; ++} ++ ++int ContentIdentifierDescriptor::Identifier::getCridLocation() const { ++ return s->crid_location; ++} ++ ++void DefaultAuthorityDescriptor::Parse() { ++ DefaultAuthority.setData(data+sizeof(descr_default_authority), getLength()-sizeof(descr_default_authority)); ++} ++ + void MultilingualNameDescriptor::Parse() { + nameLoop.setData(data+sizeof(descr_multilingual_network_name), getLength()-sizeof(descr_multilingual_network_name)); + } +diff -ur vdr-1.7.18/libsi/descriptor.h vdr-1.7/libsi/descriptor.h +--- vdr-1.7.18/libsi/descriptor.h 2010-11-01 15:24:32.000000000 +0000 ++++ vdr-1.7/libsi/descriptor.h 2011-04-17 16:04:26.000000000 +0100 +@@ -361,6 +361,31 @@ + virtual void Parse(); + }; + ++class ContentIdentifierDescriptor : public Descriptor { ++public: ++ class Identifier : public LoopElement { ++ public: ++ String identifier; ++ int getCridType() const; ++ int getCridLocation() const; ++ virtual int getLength() { return sizeof(content_identifier_entry)+identifier.getLength(); } ++ protected: ++ virtual void Parse(); ++ private: ++ const content_identifier_entry *s; ++ }; ++ StructureLoop<Identifier> identifierLoop; ++protected: ++ virtual void Parse(); ++}; ++ ++class DefaultAuthorityDescriptor : public Descriptor { ++public: ++ String DefaultAuthority; //ID ++protected: ++ virtual void Parse(); ++}; ++ + //abstract base class + class MultilingualNameDescriptor : public Descriptor { + public: +diff -ur vdr-1.7.18/libsi/headers.h vdr-1.7/libsi/headers.h +--- vdr-1.7.18/libsi/headers.h 2010-11-01 15:24:32.000000000 +0000 ++++ vdr-1.7/libsi/headers.h 2011-04-17 16:04:26.000000000 +0100 +@@ -1680,6 +1680,24 @@ + u_char descriptor_length :8; + }; + ++struct content_identifier_entry { ++#if BYTE_ORDER == BIG_ENDIAN ++ u_char crid_type :6; ++ u_char crid_location :2; ++#else ++ u_char crid_location :2; ++ u_char crid_type :6; ++#endif ++ union { ++ u_char crid_length :8; ++ u_char crid_ref_hi :8; ++ }; ++ union { ++ u_char crid_byte :8; ++ u_char crid_ref_lo :8; ++ }; ++}; ++ + /* 0x77 time_slice_fec_identifier_descriptor (ETSI EN 301 192) */ + + struct descr_time_slice_fec_identifier { +diff -ur vdr-1.7.18/libsi/si.c vdr-1.7/libsi/si.c +--- vdr-1.7.18/libsi/si.c 2010-11-01 15:24:32.000000000 +0000 ++++ vdr-1.7/libsi/si.c 2011-04-17 16:04:26.000000000 +0100 +@@ -609,6 +609,12 @@ + case RegistrationDescriptorTag: + d=new RegistrationDescriptor(); + break; ++ case ContentIdentifierDescriptorTag: ++ d=new ContentIdentifierDescriptor(); ++ break; ++ case DefaultAuthorityDescriptorTag: ++ d=new DefaultAuthorityDescriptor(); ++ break; + + //note that it is no problem to implement one + //of the unimplemented descriptors. +@@ -650,10 +656,8 @@ + case TransportStreamDescriptorTag: + + //defined in ETSI EN 300 468 v 1.7.1 +- case DefaultAuthorityDescriptorTag: + case RelatedContentDescriptorTag: + case TVAIdDescriptorTag: +- case ContentIdentifierDescriptorTag: + case TimeSliceFecIdentifierDescriptorTag: + case ECMRepetitionRateDescriptorTag: + case EnhancedAC3DescriptorTag: +diff -ur vdr-1.7.18/menu.c vdr-1.7/menu.c +--- vdr-1.7.18/menu.c 2011-02-27 12:37:48.000000000 +0000 ++++ vdr-1.7/menu.c 2011-04-17 16:04:26.000000000 +0100 +@@ -3057,6 +3057,7 @@ + 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$Use running status as VPS fallback"), &data.VpsFallback)); + 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))); + Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME)); +diff -ur vdr-1.7.18/sdt.c vdr-1.7/sdt.c +--- vdr-1.7.18/sdt.c 2010-05-16 15:23:21.000000000 +0100 ++++ vdr-1.7/sdt.c 2011-04-17 16:04:26.000000000 +0100 +@@ -128,6 +128,12 @@ + } + } + break; ++ case SI::DefaultAuthorityDescriptorTag: { ++ SI::DefaultAuthorityDescriptor *da = (SI::DefaultAuthorityDescriptor *)d; ++ char DaBuf[Utf8BufSize(1024)]; ++ da->DefaultAuthority.getText(DaBuf, sizeof(DaBuf)); ++ channel->SetDefaultAuthority(DaBuf); ++ } + default: ; + } + delete d; +diff -ur vdr-1.7.18/timers.c vdr-1.7/timers.c +--- vdr-1.7.18/timers.c 2010-01-16 11:18:53.000000000 +0000 ++++ vdr-1.7/timers.c 2011-04-17 16:04:26.000000000 +0100 +@@ -423,7 +423,7 @@ + } + + if (HasFlags(tfActive)) { +- if (HasFlags(tfVps) && event && event->Vps()) { ++ if (HasFlags(tfVps) && event && (Setup.VpsFallback || event->Vps())) { + if (Margin || !Directly) { + startTime = event->StartTime(); + stopTime = event->EndTime(); +diff -ur vdr-1.7.18/vdr.5 vdr-1.7/vdr.5 +--- vdr-1.7.18/vdr.5 2011-04-03 11:21:36.000000000 +0100 ++++ vdr-1.7/vdr.5 2011-04-17 16:04:26.000000000 +0100 +@@ -247,6 +247,9 @@ + .B RID + The Radio ID of this channel (typically 0, may be used to distinguish channels where + NID, TID and SID are all equal). ++.TP ++.B Default Authority ++The Default Authority for CRIDs on this channel (TVAnytime). + .PP + A particular channel can be uniquely identified by its \fBchannel\ ID\fR, + which is a string that looks like this: +@@ -778,6 +781,8 @@ + \fBR\fR@<parental rating> + \fBX\fR@<stream> <type> <language> <descr> + \fBV\fR@<vps time> ++\fBY\fR@<item CRID> ++\fBZ\fR@<series CRID> + \fBe\fR@ + \fBc\fR@ + .TE +@@ -812,6 +817,8 @@ + <language> @is the three letter language code (optionally two codes, separated by '+') + <descr> @is the description of this stream component + <vps time> @is the Video Programming Service time of this event ++<item CRID> @is the CRID of this event (TVAnytime) ++<series CRID> @is the CRID of the series which this event is part of (TVAnytime) + .TE + + This file will be read at program startup in order to restore the results of diff --git a/obsolete-patch/vdrtva-1.7.20.diff b/obsolete-patch/vdrtva-1.7.20.diff new file mode 100644 index 0000000..438d104 --- /dev/null +++ b/obsolete-patch/vdrtva-1.7.20.diff @@ -0,0 +1,361 @@ +diff -ur vdr-1.7.20/channels.c vdr-1.7.20.new/channels.c +--- vdr-1.7.20/channels.c 2010-11-07 12:24:59.000000000 +0000 ++++ vdr-1.7.20.new/channels.c 2011-08-16 18:08:26.000000000 +0100 +@@ -61,6 +61,7 @@ + shortName = strdup(""); + provider = strdup(""); + portalName = strdup(""); ++ defaultAuthority = strdup(""); + memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__); + parameters = ""; + modification = CHANNELMOD_NONE; +@@ -75,6 +76,7 @@ + shortName = NULL; + provider = NULL; + portalName = NULL; ++ defaultAuthority = NULL; + schedule = NULL; + linkChannels = NULL; + refChannel = NULL; +@@ -103,6 +105,7 @@ + free(shortName); + free(provider); + free(portalName); ++ free(defaultAuthority); + } + + cChannel& cChannel::operator= (const cChannel &Channel) +@@ -111,6 +114,7 @@ + shortName = strcpyrealloc(shortName, Channel.shortName); + provider = strcpyrealloc(provider, Channel.provider); + portalName = strcpyrealloc(portalName, Channel.portalName); ++ defaultAuthority = strcpyrealloc(defaultAuthority, Channel.defaultAuthority); + memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__); + parameters = Channel.parameters; + return *this; +@@ -255,6 +259,13 @@ + } + } + ++void cChannel::SetDefaultAuthority(const char *DefaultAuthority) ++{ ++ if (!isempty(DefaultAuthority) && strcmp(defaultAuthority, DefaultAuthority) != 0) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, DefaultAuthority); ++ } ++} ++ + #define STRDIFF 0x01 + #define VALDIFF 0x02 + +@@ -506,11 +517,11 @@ + q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes); + } + *q = 0; +- char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia ++ char caidbuf[MAXCAIDS * 5 + 10 + 256]; // 5: 4 digits plus delimiting ',', 10 + max DNS domain length: paranoia + q = caidbuf; + q += IntArrayToString(q, Channel->caids, 16); + *q = 0; +- buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); ++ buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d:%s\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid, Channel->defaultAuthority); + } + return buffer; + } +@@ -545,13 +556,16 @@ + char *vpidbuf = NULL; + char *apidbuf = NULL; + char *caidbuf = NULL; +- int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid); ++ char *dabuf = NULL; ++ int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d :%a[^:]", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid, &dabuf); + if (fields >= 9) { + if (fields == 9) { + // allow reading of old format + sid = atoi(caidbuf); + delete caidbuf; + caidbuf = NULL; ++ delete dabuf; ++ dabuf = NULL; + caids[0] = tpid; + caids[1] = 0; + tpid = 0; +@@ -677,12 +691,17 @@ + } + name = strcpyrealloc(name, namebuf); + ++ if (dabuf) { ++ defaultAuthority = strcpyrealloc(defaultAuthority, dabuf); ++ } ++ + free(parambuf); + free(sourcebuf); + free(vpidbuf); + free(apidbuf); + free(caidbuf); + free(namebuf); ++ free(dabuf); + if (!GetChannelID().Valid()) { + esyslog("ERROR: channel data results in invalid ID!"); + return false; +diff -ur vdr-1.7.20/channels.h vdr-1.7.20.new/channels.h +--- vdr-1.7.20/channels.h 2011-08-06 10:56:13.000000000 +0100 ++++ vdr-1.7.20.new/channels.h 2011-08-16 18:08:26.000000000 +0100 +@@ -96,6 +96,7 @@ + char *shortName; + char *provider; + char *portalName; ++ char *defaultAuthority; + int __BeginData__; + int frequency; // MHz + int source; +@@ -141,6 +142,7 @@ + const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; } + const char *Provider(void) const { return provider; } + const char *PortalName(void) const { return portalName; } ++ const char *DefaultAuthority(void) const { return defaultAuthority; } + 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, plus the polarization in case of sat + static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization +@@ -189,6 +191,7 @@ + void SetId(int Nid, int Tid, int Sid, int Rid = 0); + void SetName(const char *Name, const char *ShortName, const char *Provider); + void SetPortalName(const char *PortalName); ++ void SetDefaultAuthority(const char *DefaultAuthority); + void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); + void SetCaIds(const int *CaIds); // list must be zero-terminated + void SetCaDescriptors(int Level); +diff -ur vdr-1.7.20/config.c vdr-1.7.20.new/config.c +--- vdr-1.7.20/config.c 2011-06-13 15:41:01.000000000 +0100 ++++ vdr-1.7.20.new/config.c 2011-08-16 18:08:26.000000000 +0100 +@@ -353,6 +353,7 @@ + UseSubtitle = 1; + UseVps = 0; + VpsMargin = 120; ++ VpsFallback = 0; + RecordingDirs = 1; + FoldersInTimerMenu = 1; + NumberKeysForChars = 1; +@@ -545,6 +546,7 @@ + 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, "VpsFallback")) VpsFallback = atoi(Value); + else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); + else if (!strcasecmp(Name, "FoldersInTimerMenu")) FoldersInTimerMenu = atoi(Value); + else if (!strcasecmp(Name, "NumberKeysForChars")) NumberKeysForChars = atoi(Value); +@@ -641,6 +643,7 @@ + Store("UseSubtitle", UseSubtitle); + Store("UseVps", UseVps); + Store("VpsMargin", VpsMargin); ++ Store("VpsFallback", VpsFallback); + Store("RecordingDirs", RecordingDirs); + Store("FoldersInTimerMenu", FoldersInTimerMenu); + Store("NumberKeysForChars", NumberKeysForChars); +diff -ur vdr-1.7.20/config.h vdr-1.7.20.new/config.h +--- vdr-1.7.20/config.h 2011-06-21 22:43:01.000000000 +0100 ++++ vdr-1.7.20.new/config.h 2011-08-16 18:08:26.000000000 +0100 +@@ -252,6 +252,7 @@ + int UseSubtitle; + int UseVps; + int VpsMargin; ++ int VpsFallback; + int RecordingDirs; + int FoldersInTimerMenu; + int NumberKeysForChars; +diff -ur vdr-1.7.20/eit.c vdr-1.7.20.new/eit.c +--- vdr-1.7.20/eit.c 2010-05-14 15:08:35.000000000 +0100 ++++ vdr-1.7.20.new/eit.c 2011-08-16 18:08:26.000000000 +0100 +@@ -265,6 +265,33 @@ + } + } + break; ++ case SI::ContentIdentifierDescriptorTag: { ++ SI::ContentIdentifierDescriptor *cd = (SI::ContentIdentifierDescriptor *)d; ++ SI::ContentIdentifierDescriptor::Identifier cde; ++ for (SI::Loop::Iterator ite; (cd->identifierLoop.getNext(cde,ite)); ) { ++ if (cde.getCridLocation() == 0) { ++ char buffer[Utf8BufSize(256)]; ++ strcpy (buffer, channel->DefaultAuthority()); ++ int i = strlen(buffer); ++ if (i) { ++ cde.identifier.getText(buffer+i, Utf8BufSize(256)-i); ++ switch (cde.getCridType()) { ++ case 0x01: // ETSI 102 363 code ++ case 0x31: // UK Freeview private code ++ pEvent->SetItemCRID(buffer); ++ break; ++ case 0x02: // ETSI 102 363 code ++ case 0x32: // UK Freeview private code ++ pEvent->SetSeriesCRID(buffer); ++ break; ++ // ETSI 102 323 defines CRID type 0x03, which describes 'related' or 'suggested' events. ++ // Freeview broadcasts these as CRID type 0x33. ++ // There can be more than one type 0x33 descriptor per event (each with one CRID). ++ } ++ } ++ } ++ } ++ } + default: ; + } + delete d; +diff -ur vdr-1.7.20/epg.c vdr-1.7.20.new/epg.c +--- vdr-1.7.20/epg.c 2011-02-25 15:16:05.000000000 +0000 ++++ vdr-1.7.20.new/epg.c 2011-08-16 18:08:26.000000000 +0100 +@@ -126,6 +126,8 @@ + startTime = 0; + duration = 0; + vps = 0; ++ itemCRID = NULL; ++ seriesCRID = NULL; + SetSeen(); + } + +@@ -134,6 +136,8 @@ + free(title); + free(shortText); + free(description); ++ free(itemCRID); ++ free(seriesCRID); + delete components; + } + +@@ -229,6 +233,16 @@ + vps = Vps; + } + ++void cEvent::SetItemCRID(const char *CRID) ++{ ++ itemCRID = strcpyrealloc(itemCRID, CRID); ++} ++ ++void cEvent::SetSeriesCRID(const char *CRID) ++{ ++ seriesCRID = strcpyrealloc(seriesCRID, CRID); ++} ++ + void cEvent::SetSeen(void) + { + seen = time(NULL); +@@ -457,6 +471,10 @@ + } + if (vps) + fprintf(f, "%sV %ld\n", Prefix, vps); ++ if (!isempty(itemCRID)) ++ fprintf(f, "%sY %s\n", Prefix, itemCRID); ++ if (!isempty(seriesCRID)) ++ fprintf(f, "%sZ %s\n", Prefix, seriesCRID); + if (!InfoOnly) + fprintf(f, "%se\n", Prefix); + } +@@ -495,6 +513,10 @@ + break; + case 'V': SetVps(atoi(t)); + break; ++ case 'Y': SetItemCRID(t); ++ break; ++ case 'Z': SetSeriesCRID(t); ++ break; + default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s); + return false; + } +diff -ur vdr-1.7.20/epg.h vdr-1.7.20.new/epg.h +--- vdr-1.7.20/epg.h 2011-02-25 14:14:38.000000000 +0000 ++++ vdr-1.7.20.new/epg.h 2011-08-16 18:08:26.000000000 +0100 +@@ -86,6 +86,8 @@ + int duration; // Duration of this event in seconds + time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL) + time_t seen; // When this event was last seen in the data stream ++ char *itemCRID; // Item CRID ++ char *seriesCRID; // Series CRID + public: + cEvent(tEventID EventID); + ~cEvent(); +@@ -106,6 +108,8 @@ + time_t EndTime(void) const { return startTime + duration; } + int Duration(void) const { return duration; } + time_t Vps(void) const { return vps; } ++ const char *ItemCRID(void) const { return itemCRID; } ++ const char *SeriesCRID(void) const { return seriesCRID; } + time_t Seen(void) const { return seen; } + bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; } + bool HasTimer(void) const; +@@ -129,6 +133,8 @@ + void SetStartTime(time_t StartTime); + void SetDuration(int Duration); + void SetVps(time_t Vps); ++ void SetItemCRID(const char *CRID); ++ void SetSeriesCRID(const char *CRID); + void SetSeen(void); + cString ToDescr(void) const; + void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const; +diff -ur vdr-1.7.20/menu.c vdr-1.7.20.new/menu.c +--- vdr-1.7.20/menu.c 2011-08-06 14:13:34.000000000 +0100 ++++ vdr-1.7.20.new/menu.c 2011-08-16 18:08:26.000000000 +0100 +@@ -3058,6 +3058,7 @@ + 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$Use running status as VPS fallback"), &data.VpsFallback)); + 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))); + Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME)); +diff -ur vdr-1.7.20/sdt.c vdr-1.7.20.new/sdt.c +--- vdr-1.7.20/sdt.c 2010-05-16 15:23:21.000000000 +0100 ++++ vdr-1.7.20.new/sdt.c 2011-08-16 18:08:26.000000000 +0100 +@@ -128,6 +128,12 @@ + } + } + break; ++ case SI::DefaultAuthorityDescriptorTag: { ++ SI::DefaultAuthorityDescriptor *da = (SI::DefaultAuthorityDescriptor *)d; ++ char DaBuf[Utf8BufSize(1024)]; ++ da->DefaultAuthority.getText(DaBuf, sizeof(DaBuf)); ++ channel->SetDefaultAuthority(DaBuf); ++ } + default: ; + } + delete d; +diff -ur vdr-1.7.20/timers.c vdr-1.7.20.new/timers.c +--- vdr-1.7.20/timers.c 2011-08-06 14:13:54.000000000 +0100 ++++ vdr-1.7.20.new/timers.c 2011-08-16 18:08:26.000000000 +0100 +@@ -430,7 +430,7 @@ + deferred = 0; + + if (HasFlags(tfActive)) { +- if (HasFlags(tfVps) && event && event->Vps()) { ++ if (HasFlags(tfVps) && event && (Setup.VpsFallback || event->Vps())) { + if (Margin || !Directly) { + startTime = event->StartTime(); + stopTime = event->EndTime(); +diff -ur vdr-1.7.20/vdr.5 vdr-1.7.20.new/vdr.5 +--- vdr-1.7.20/vdr.5 2011-04-03 11:21:36.000000000 +0100 ++++ vdr-1.7.20.new/vdr.5 2011-08-16 18:08:26.000000000 +0100 +@@ -247,6 +247,9 @@ + .B RID + The Radio ID of this channel (typically 0, may be used to distinguish channels where + NID, TID and SID are all equal). ++.TP ++.B Default Authority ++The Default Authority for CRIDs on this channel (TVAnytime). + .PP + A particular channel can be uniquely identified by its \fBchannel\ ID\fR, + which is a string that looks like this: +@@ -778,6 +781,8 @@ + \fBR\fR@<parental rating> + \fBX\fR@<stream> <type> <language> <descr> + \fBV\fR@<vps time> ++\fBY\fR@<item CRID> ++\fBZ\fR@<series CRID> + \fBe\fR@ + \fBc\fR@ + .TE +@@ -812,6 +817,8 @@ + <language> @is the three letter language code (optionally two codes, separated by '+') + <descr> @is the description of this stream component + <vps time> @is the Video Programming Service time of this event ++<item CRID> @is the CRID of this event (TVAnytime) ++<series CRID> @is the CRID of the series which this event is part of (TVAnytime) + .TE + + This file will be read at program startup in order to restore the results of |