diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b9cc3824..2aa2c21d 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -678,6 +678,7 @@ Oliver Endriss for fixing the way the OSD size is determined on full featured DVB cards for his input on calculating the Aspect factor in GetOsdSize() for suggesting a better way of handling calls to realloc() + for making the cutter set the 'broken link' flag for MPEG2 TS recordings Reinhard Walter Buchner for adding some satellites to 'sources.conf' @@ -1107,6 +1108,9 @@ Rolf Ahrenberg for adding an include of VDR's 'Make.global' to libsi's Makefile for adding handling of "ANSI/SCTE 57" descriptors for some input on how to use BER and UNC values to generate a "quality" value + for fixing some crashes in subtitle display + for reporting that DELETENULL() was not thread safe + for reporting a crash in subtitle display, related to cOsd::Osds Ralf Klueber for reporting a bug in cutting a recording if there is only a single editing mark @@ -1273,6 +1277,8 @@ Reinhard Nissl for avoiding an unecessary call to Recordings.ResetResume() for debugging a problem in handling the bitmap color depth for scaled subtitles for making subtitle PIDs be decrypted + for making cEITScanner process new transponders before old ones, to make sure + transponder changes are recognized Richard Robson for reporting freezing replay if a timer starts while in Transfer Mode from the @@ -1700,6 +1706,8 @@ Udo Richter "TITLE" or "EPISODE" for a patch to "Made updating the editing marks during replay react faster in case the marks file has just been written" + for suggesting a fix for a bug in handling DiSEqC codes + for fixing handling the channelID in cMenuEditChanItem Sven Kreiensen for his help in keeping 'channels.conf.terr' up to date @@ -2147,6 +2155,7 @@ Andr recording if Setup.UseDolbyDigital is true for suggesting that the primary device should only be avoided for recording if it is an old SD full featured card + for his support in using convert/ffmpeg in the pic2mpg script of the 'pictures' plugin Jürgen Schilling for reporting that color buttons were displayed in the recording info menu if it @@ -2223,6 +2232,9 @@ Christoph Haubrich for suggesting to add a note to cTsToPes about all TS packets having to belong to the same PID for adding HD stream content identifiers to vdr.5 + for reporting that Setup.InitialChannel was dereferenced without checking for NULL + for suggesting to implement a function to determine the length of a recording's + index file Pekka Mauno for fixing cSchedule::GetFollowingEvent() in case there is currently no present @@ -2509,6 +2521,8 @@ Johan Schuring Sundararaj Reel for reporting a missing reset of maxNumber in cChannels::Renumber() + for reporting some missing 'const' in tChannelID + for suggesting to add optional case insensitive sorting to cStringList::Sort() Ales Jurik for reporting broken SI data on Czech/Slovak channels after changing the default @@ -2718,6 +2732,7 @@ Dirk Leber Marco Göbenich for reporting a problem with executing diseqc commands from different threads + for suggesting to implement command line option --filesize Johan Andersson for reporting a bug in detecting frames in case the Picture Start Code or Access Unit @@ -2726,3 +2741,22 @@ Johan Andersson Dave Pickles for adding support for "content identifier descriptor" and "default authority descriptor" to 'libsi' + +Holger Dengler + for making the isnumber() function check the given pointer for NULL + +Michael Eiler + - reporting a crash in case cSkins::Message() is called from a background thread + +Jonas Diemer + for fixing the return value of the svdrpsend.pl script in case of an error + +Jerome Lacarriere + for increasing MAXCAIDS to 12 + +Mark Hawes + for reporting a bug in handling DiSEqC codes + +Frank Niederwipper + for reporting a problem in timer handling in case a recording directory can't + be created diff --git a/HISTORY b/HISTORY index 015a1019..b5006894 100644 --- a/HISTORY +++ b/HISTORY @@ -6305,7 +6305,7 @@ Video Disk Recorder Revision History parameters, but rather the whole channel is handed down for processing. The old constructor of cReceiver is still available, but it is recommended to plugin authors that they switch to the new interface as soon as possible. - When replaying such a recording, the PCR packets are sent to PlayTsVideo() + When replaying such a recording, the PCR packets are sent to PlayTsVideo(). - The files "commands.conf" and "reccmd.conf" can now contain nested lists of commands. See vdr.5 for information about the new file format. This obsoletes the CMDSUBMENU patch. @@ -6649,3 +6649,52 @@ Video Disk Recorder Revision History Lars Bläser). - Added support for "content identifier descriptor" and "default authority descriptor" to 'libsi' (thanks to Dave Pickles). + +2011-08-15: Version 1.7.20 + +- Added some missing 'const' to tChannelID (reported by Sundararaj Reel). +- The isnumber() function now checks the given pointer for NULL (thanks to Holger + Dengler). +- Now checking Setup.InitialChannel for NULL before using it (reported by + Christoph Haubrich). +- cSkins::Message() now blocks calls from background threads (thanks to Michael + Eiler for reporting a crash in such a scenario). +- Fixed the return value of the svdrpsend.pl script in case of an error (thanks to + Jonas Diemer). +- Increased MAXCAIDS to 12 (thanks to Jerome Lacarriere). +- Fixed handling DiSEqC codes (thanks to Mark Hawes for reporting the bug, and + Udo Richter for suggesting the fix). +- Added a mechanism to defer timer handling in case of problems (reported by + Frank Niederwipper). +- Fixed distortions that happened when splitting recording into several files + (was a side effect of "Fixed detecting frames in case the Picture Start Code or + Access Unit Delimiter extends over TS packet boundaries" in version 1.7.19). + cRecorder::Action() now buffers TS packets in case the frame type is + not yet known when a new payload starts. This adds no overhead for channels + that broadcast the frame type within the first TS packet of a payload; it only + kicks in if that information is not in the first TS packet. +- Fixed handling the channelID in cMenuEditChanItem (thanks to Udo Richter). +- cStringList::Sort() can now be called with a boolean parameter that controls + case insensitive sorting (suggested by Sundararaj Reel). +- Now scanning new transponders before old ones, to make sure transponder changes + are recognized (thanks to Reinhard Nissl). +- Implemented static cIndexFile::IndexFileName(). +- The length (as number of frames) of a recording's index file can now be determined + by a call to cIndexFile::GetLength() (suggested by Christoph Haubrich). +- Fixed some crashes in subtitle display (thanks to Rolf Ahrenberg). +- Made DELETENULL() thread safe (reported by Rolf Ahrenberg). +- The pic2mpg script of the 'pictures' plugin now generates HD images (thanks to + Andre Weidemann for his support in using convert/ffmpeg). The old SD version is + still available as pic2mpg-sd. +- Added a mutex to protect cOsd::Osds from simultaneous access from different threads + (reported by Rolf Ahrenberg). +- The cutter now sets the 'broken link' flag for MPEG2 TS recordings (thanks to + Oliver Endriss). +- Fixed language code entry for Portuguese. +- The new command line options --filesize (suggested by Marco Göbenich) and --split + can be used together with --edit to set the maximum video file size and turn on + splitting edited files at the editing marks. These options must be given before + --edit to have an effect. +- cTimeMs is no longer initialized to the current time if the value given to the + constructor is negative (avoids the "cTimeMs: using monotonic clock..." log message + before VDR's starting log message). diff --git a/MANUAL b/MANUAL index 352cc10c..54e166e9 100644 --- a/MANUAL +++ b/MANUAL @@ -822,7 +822,7 @@ Version 1.6 Max. video file size = 2000 The maximum size of a single recorded video file in MB. - The valid range is 100...2000. Default is 2000, but + The valid range is 100...1048570. Default is 2000, but you may want to use smaller values if you are planning on archiving a recording to CD. diff --git a/PLUGINS/src/dvbhddevice/po/it_IT.po b/PLUGINS/src/dvbhddevice/po/it_IT.po new file mode 100644 index 00000000..a328312c --- /dev/null +++ b/PLUGINS/src/dvbhddevice/po/it_IT.po @@ -0,0 +1,93 @@ +# VDR plugin language source file +# Copyright (C) 2011 Andreas Regel +# This file is distributed under the same license as the dvbhddevice package. +# Christoph Haubrich, 2011 +# +msgid "" +msgstr "" +"Project-Id-Version: VDR\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-05-05 20:34+0200\n" +"PO-Revision-Date: 2011-07-10 00:23+0100\n" +"Last-Translator: Diego Pierotto \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Italian\n" +"X-Poedit-Country: ITALY\n" +"X-Poedit-SourceCharset: utf-8\n" + +msgid "Automatic" +msgstr "Automatica" + +msgid "Letterbox 16/9" +msgstr "Letterbox 16:9" + +msgid "Letterbox 14/9" +msgstr "Letterbox 14:9" + +msgid "Pillarbox" +msgstr "Pillarbox" + +msgid "CentreCutOut" +msgstr "CentreCutOut" + +msgid "Always 16/9" +msgstr "Sempre 16:9" + +msgid "Disabled" +msgstr "Disabilitata" + +msgid "Analogue only" +msgstr "Solo analogica" + +msgid "Always" +msgstr "Sempre" + +msgid "HDMI only" +msgstr "Solo HDMI" + +msgid "Follow resolution" +msgstr "Risoluzione seguente" + +msgid "none" +msgstr "nessuna" + +msgid "Resolution" +msgstr "Risoluzione" + +msgid "TV format" +msgstr "Formato TV" + +msgid "Video Conversion" +msgstr "Conversione video" + +msgid "Analogue Video" +msgstr "Video analogico" + +msgid "Audio Delay (ms)" +msgstr "Ritardo audio (ms)" + +msgid "Audio Downmix" +msgstr "Scala Audio" + +msgid "OSD Size" +msgstr "Dimensione OSD" + +msgid "HDMI CEC" +msgstr "HDMI CEC" + +msgid "Remote Control Protocol" +msgstr "Protocollo controllo remoto" + +msgid "Remote Control Address" +msgstr "Indirizzo controllo remoto" + +msgid "High Level OSD" +msgstr "OSD alto livello" + +msgid "Allow True Color OSD" +msgstr "Permetti OSD True Color" + diff --git a/PLUGINS/src/pictures/HISTORY b/PLUGINS/src/pictures/HISTORY index 332d77c4..964ab2e9 100644 --- a/PLUGINS/src/pictures/HISTORY +++ b/PLUGINS/src/pictures/HISTORY @@ -45,3 +45,13 @@ VDR Plugin 'pictures' Revision History 2011-02-20: Version 0.1.0 - Fixed reallocating memory (reported by Paul Menzel). + +2011-07-23: + +- Now rotating images according to the EXIF 'Orientation' parameter. + +2011-08-14: + +- The pic2mpg script now generates HD images (thanks to Andre Weidemann for his + support in using convert/ffmpeg). The old SD version is still available as + pic2mpg-sd. diff --git a/PLUGINS/src/pictures/pic2mpg b/PLUGINS/src/pictures/pic2mpg index d3175103..190ec260 100755 --- a/PLUGINS/src/pictures/pic2mpg +++ b/PLUGINS/src/pictures/pic2mpg @@ -7,62 +7,45 @@ # # See the README file for copyright information and how to reach the author. # -# $Id: pic2mpg 2.0 2008/02/29 14:34:22 kls Exp $ - -## TODO implement HDTV (1920 x 1080) +# $Id: pic2mpg 2.2 2011/08/14 13:34:15 kls Exp $ use File::Path; use File::Spec; use Getopt::Std; -use Image::Size; +use Image::ExifTool qw(:Public); $Usage = qq{ Usage: $0 [options] picture-dir mpeg-dir $0 [options] picture-file mpeg-file -Options: -a Aspect ratio 4:3 (default is 16:9) - -f Force conversion +Options: -f Force conversion -h print Help - -i Ignore unknown file types - -n NTSC (default is PAL) + -s size Screen size (WIDTHxHEIGHT, default is 1920x1080) -v num Verbose (0=none, 1=list files, 2=detailed) - -x percent X overscan in percent - -y percent Y overscan in percent }; -getopts("afhinv:x:y:") || die $Usage; +getopts("fhs:v:") || die $Usage; die $Usage if $opt_h; -$Aspect = $opt_a; $Force = $opt_f; -$Ignore = $opt_i; -$NTSC = $opt_n; +$Size = $opt_s || "1920x1080"; $Verbose = $opt_v; -$OverscanX = $opt_x; -$OverscanY = $opt_y; $ListFiles = $Verbose >= 1; $Detailed = $Verbose >= 2; -# Screen size: +# Supported picture types: -$SW = $NTSC ? 720 : 720; -$SH = $NTSC ? 480 : 576; - -$ScreenRatio = $Aspect ? 4 / 3 : 16 / 9; - -# Converter programs: - -%PNMCONV = ( - bmp => "bmptopnm", - gif => "giftopnm", - jpeg => "jpegtopnm", - jpg => "jpegtopnm", - png => "pngtopnm", - pnm => "cat", - tif => "tifftopnm", - tiff => "tifftopnm", +%PICTYPES = ( + bmp => 1, + gif => 1, + jpeg => 1, + jpg => 1, + png => 1, + pnm => 1, + tif => 1, + tiff => 1, ); # Command options: @@ -71,13 +54,6 @@ die "$0: missing parameter\n" unless $ARGV[0] && $ARGV[1]; die "$0: file or directory not found: $ARGV[0]\n" unless -e $ARGV[0]; die "$0: source and destination must be different\n" if $ARGV[0] eq $ARGV[1]; -$verbose1 = $Detailed ? "--verbose" : ""; -$verbose2 = $Detailed ? "-v 2" : "-v 0"; -$system1 = $NTSC ? "" : "--pal"; -$system2 = $NTSC ? "n" : "p"; -$framerate = $NTSC ? "30000:1001" : "25:1"; -$aspect = $Aspect ? "2" : "3"; - # Convert a single file: if (-f $ARGV[0]) { @@ -95,7 +71,7 @@ $MPGDIR = File::Spec->rel2abs($ARGV[1]); chdir($PICDIR) || die "$PICDIR: $!\n"; -@Pictures = `find -type f`; +@Pictures = `find -type f | sort`; chomp(@Pictures); for $pic (@Pictures) { @@ -145,29 +121,18 @@ sub ConvertFile { my ($Pict, $Mpeg) = @_; (my $Type) = lc($Pict) =~ /\.([^\.]*)$/; - if (!defined $PNMCONV{$Type}) { - return if ($Ignore); - die "unknown file type '$Type': '$Pict'\n"; - } - my ($w, $h) = imgsize($Pict); - print "image size is $w x $h\n" if ($Detailed); - if ($w / $h <= $ScreenRatio) { - $w = $h * $ScreenRatio; - } - else { - $h = $w / $ScreenRatio; - } - my $ScaleW = $SW / $w * (100 - 2 * $OverscanX) / 100; - my $ScaleH = $SH / $h * (100 - 2 * $OverscanY) / 100; + return if (!defined $PICTYPES{$Type}); + my $Exif = ImageInfo($Pict); + my $Orientation = $$Exif{"Orientation"}; + my ($Degrees) = $Orientation =~ /Rotate ([0-9]+) /; + my $Rotate = $Degrees ? "-rotate $Degrees" : ""; + print "orientation = '$Orientation' -> rotation = $Rotate\n" if ($Detailed); $Pict = EscapeMeta($Pict); $Mpeg = EscapeMeta($Mpeg); - print "$Pict -> $Mpeg\n" if $ListFiles; - my $Cmd = "$PNMCONV{$Type} $Pict 2> /dev/null |" - . "pnmscale $verbose1 --xscale=$ScaleW --yscale=$ScaleH |" - . "pnmpad $verbose1 --black --width $SW --height $SH |" - . "ppmntsc $verbose1 $system1 |" - . "ppmtoy4m $verbose2 -F $framerate -I p -S 420mpeg2 |" - . "mpeg2enc $verbose2 -f 3 -b 12500 -a $aspect -q 1 -n $system2 -o $Mpeg"; + print "$Pict -> $Mpeg $Rotate\n" if $ListFiles; + my $Cmd = "convert $Pict -background '#000000' $Rotate -resize $Size -gravity center -extent $Size ppm:- | " + . "ffmpeg -f image2pipe -vcodec ppm -i pipe:0 -an -vcodec libx264 -vpre baseline -s $Size -qscale 2 -f mpegts -y $Mpeg " + . ($Detailed ? "" : "2>/dev/null"); !system($Cmd) || die "$Cmd: $!\n"; $Cmd = "touch -r $Pict $Mpeg"; !system($Cmd) || die "$Cmd: $!\n"; diff --git a/PLUGINS/src/pictures/pic2mpg-sd b/PLUGINS/src/pictures/pic2mpg-sd new file mode 100755 index 00000000..6213091e --- /dev/null +++ b/PLUGINS/src/pictures/pic2mpg-sd @@ -0,0 +1,193 @@ +#!/usr/bin/perl + +# pic2mpg: Convert picture files to MPEG still frames +# +# Converts either a single picture file or all files in a +# given directory (recursively) to MPEG still frames. +# +# See the README file for copyright information and how to reach the author. +# +# $Id: pic2mpg 2.1 2011/07/23 14:23:59 kls Exp $ + +## TODO implement HDTV (1920 x 1080) + +use File::Path; +use File::Spec; +use Getopt::Std; +use Image::ExifTool qw(:Public); +use Image::Size; + +$Usage = qq{ +Usage: $0 [options] picture-dir mpeg-dir + $0 [options] picture-file mpeg-file + +Options: -a Aspect ratio 4:3 (default is 16:9) + -f Force conversion + -h print Help + -i Ignore unknown file types + -n NTSC (default is PAL) + -v num Verbose (0=none, 1=list files, 2=detailed) + -x percent X overscan in percent + -y percent Y overscan in percent +}; + +getopts("afhinv:x:y:") || die $Usage; + +die $Usage if $opt_h; + +$Aspect = $opt_a; +$Force = $opt_f; +$Ignore = $opt_i; +$NTSC = $opt_n; +$Verbose = $opt_v; +$OverscanX = $opt_x; +$OverscanY = $opt_y; + +$ListFiles = $Verbose >= 1; +$Detailed = $Verbose >= 2; + +# Screen size: + +$SW = $NTSC ? 720 : 720; +$SH = $NTSC ? 480 : 576; + +$ScreenRatio = $Aspect ? 4 / 3 : 16 / 9; + +# Converter programs: + +%PNMCONV = ( + bmp => "bmptopnm", + gif => "giftopnm", + jpeg => "jpegtopnm", + jpg => "jpegtopnm", + png => "pngtopnm", + pnm => "cat", + tif => "tifftopnm", + tiff => "tifftopnm", + ); + +# Command options: + +die "$0: missing parameter\n" unless $ARGV[0] && $ARGV[1]; +die "$0: file or directory not found: $ARGV[0]\n" unless -e $ARGV[0]; +die "$0: source and destination must be different\n" if $ARGV[0] eq $ARGV[1]; + +$verbose1 = $Detailed ? "--verbose" : ""; +$verbose2 = $Detailed ? "-v 2" : "-v 0"; +$system1 = $NTSC ? "" : "--pal"; +$system2 = $NTSC ? "n" : "p"; +$framerate = $NTSC ? "30000:1001" : "25:1"; +$aspect = $Aspect ? "2" : "3"; + +# Convert a single file: + +if (-f $ARGV[0]) { + die "$0: mixed file and directory ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -f $ARGV[1]; + ConvertFile($ARGV[0], $ARGV[1]); + exit; + } + +die "$0: mixed directory and file ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -d $ARGV[1]; + +$PICDIR = File::Spec->rel2abs($ARGV[0]); +$MPGDIR = File::Spec->rel2abs($ARGV[1]); + +# Convert pictures to mpegs: + +chdir($PICDIR) || die "$PICDIR: $!\n"; + +@Pictures = `find -type f`; +chomp(@Pictures); + +for $pic (@Pictures) { + my $mpg = "$MPGDIR/$pic.mpg"; + if ($Force || !-e $mpg || -M $mpg > -M $pic) { + (my $dir = $mpg) =~ s/\/[^\/]*$//; + mkpath($dir); + ConvertFile($pic, $mpg); + } + } + +# Remove mpegs without pictures: + +chdir($MPGDIR) || die "$MPGDIR: $!\n"; + +@Mpegs = `find -type f`; +chomp(@Mpegs); + +for $mpg (@Mpegs) { + my $pic = "$PICDIR/$mpg"; + $pic =~ s/\.mpg$//; + if (!-e $pic) { + print "removing $mpg\n"; + unlink($mpg); + } + } + +# Remove empty directories: + +chdir($MPGDIR) || die "$MPGDIR: $!\n"; + +for ($i = 0; $i < 10; $i++) { # dirs might become empty when removing empty subdirs + @Dirs = `find -type d -empty`; + chomp(@Dirs); + last unless @Dirs; + + for $dir (@Dirs) { + $dir = EscapeMeta($dir); + print "removing $dir\n"; + !system("rm -rf $dir") || die "$dir: $!\n"; + } + } + +# Actual file conversion: + +sub ConvertFile +{ + my ($Pict, $Mpeg) = @_; + (my $Type) = lc($Pict) =~ /\.([^\.]*)$/; + if (!defined $PNMCONV{$Type}) { + return if ($Ignore); + die "unknown file type '$Type': '$Pict'\n"; + } + my ($w, $h) = imgsize($Pict); + print "image size is $w x $h\n" if ($Detailed); + my $Exif = ImageInfo($Pict); + my $Orientation = $$Exif{"Orientation"}; + my ($Degrees) = $Orientation =~ /Rotate ([0-9]+) /; + my $Rotate = "-null"; + $Rotate = "-cw" if $Degrees eq "90"; + $Rotate = "-ccw" if $Degrees eq "270"; + $Rotate = "-r180" if $Degrees eq "180"; + print "orientation = '$Orientation' -> rotation = $Rotate\n" if ($Detailed); + ($w, $h) = ($h, $w) if ($Degrees eq "90" || $Degrees eq "270"); + if ($w / $h <= $ScreenRatio) { + $w = $h * $ScreenRatio; + } + else { + $h = $w / $ScreenRatio; + } + my $ScaleW = $SW / $w * (100 - 2 * $OverscanX) / 100; + my $ScaleH = $SH / $h * (100 - 2 * $OverscanY) / 100; + $Pict = EscapeMeta($Pict); + $Mpeg = EscapeMeta($Mpeg); + print "$Pict -> $Mpeg $Rotate\n" if $ListFiles; + my $Cmd = "$PNMCONV{$Type} $Pict 2> /dev/null |" + . "pamflip $verbose1 $Rotate |" + . "pnmscale $verbose1 --xscale=$ScaleW --yscale=$ScaleH |" + . "pnmpad $verbose1 --black --width $SW --height $SH |" + . "ppmntsc $verbose1 $system1 |" + . "ppmtoy4m $verbose2 -F $framerate -I p -S 420mpeg2 |" + . "mpeg2enc $verbose2 -f 3 -b 12500 -a $aspect -q 1 -n $system2 -o $Mpeg"; + !system($Cmd) || die "$Cmd: $!\n"; + $Cmd = "touch -r $Pict $Mpeg"; + !system($Cmd) || die "$Cmd: $!\n"; +} + +sub EscapeMeta +{ + my $META = ' !"#$%&\'()*;<>?[\\]`{|}~'; + my $s = shift; + $s =~ s/([$META])/\\$1/g; + return $s; +} diff --git a/channels.h b/channels.h index b8672974..51f9830e 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 2.12 2010/06/05 13:12:54 kls Exp $ + * $Id: channels.h 2.14 2011/08/06 09:56:13 kls Exp $ */ #ifndef __CHANNELS_H @@ -34,7 +34,7 @@ #define MAXAPIDS 32 // audio #define MAXDPIDS 16 // dolby (AC3 + DTS) #define MAXSPIDS 32 // subtitles -#define MAXCAIDS 8 // conditional access +#define MAXCAIDS 12 // conditional access #define MAXLANGCODE1 4 // a 3 letter language code, zero terminated #define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated @@ -61,11 +61,11 @@ public: bool Valid(void) const { 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); - int Source(void) { return source; } - int Nid(void) { return nid; } - int Tid(void) { return tid; } - int Sid(void) { return sid; } - int Rid(void) { return rid; } + int Source(void) const { return source; } + int Nid(void) const { return nid; } + int Tid(void) const { return tid; } + int Sid(void) const { return sid; } + int Rid(void) const { return rid; } static tChannelID FromString(const char *s); cString ToString(void) const; static const tChannelID InvalidID; diff --git a/config.h b/config.h index f3421f7a..19f87686 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 2.32 2011/06/13 14:24:40 kls Exp $ + * $Id: config.h 2.33 2011/06/21 21:43:01 kls Exp $ */ #ifndef __CONFIG_H @@ -22,13 +22,13 @@ // VDR's own version number: -#define VDRVERSION "1.7.19" -#define VDRVERSNUM 10719 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "1.7.20" +#define VDRVERSNUM 10720 // Version * 10000 + Major * 100 + Minor // The plugin API's version number: -#define APIVERSION "1.7.19" -#define APIVERSNUM 10719 // Version * 10000 + Major * 100 + Minor +#define APIVERSION "1.7.20" +#define APIVERSNUM 10720 // Version * 10000 + Major * 100 + Minor // When loading plugins, VDR searches them by their APIVERSION, which // may be smaller than VDRVERSION in case there have been no changes to diff --git a/diseqc.c b/diseqc.c index b65d82c6..d4462997 100644 --- a/diseqc.c +++ b/diseqc.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.c 2.4 2011/05/22 10:36:12 kls Exp $ + * $Id: diseqc.c 2.5 2011/08/06 10:32:18 kls Exp $ */ #include "diseqc.h" @@ -95,13 +95,13 @@ const char *cDiseqc::Codes(const char *s) const if (e) { int NumCodes = 0; const char *t = s; - char *p; while (t < e) { if (NumCodes < MaxDiseqcCodes) { errno = 0; + char *p; int n = strtol(t, &p, 16); if (!errno && p != t && 0 <= n && n <= 255) { - if (parsing) { + if (!parsing) { codes[NumCodes++] = uchar(n); numCodes = NumCodes; } diff --git a/dvbsubtitle.c b/dvbsubtitle.c index de1cd1c0..43d072d4 100644 --- a/dvbsubtitle.c +++ b/dvbsubtitle.c @@ -7,7 +7,7 @@ * Original author: Marco Schlüßler * With some input from the "subtitle plugin" by Pekka Virtanen * - * $Id: dvbsubtitle.c 2.17 2011/04/17 14:34:05 kls Exp $ + * $Id: dvbsubtitle.c 2.18 2011/08/13 13:33:00 kls Exp $ */ @@ -826,6 +826,7 @@ void cDvbSubtitleConverter::Action(void) while (Running()) { int WaitMs = 100; if (!frozen) { + LOCK_THREAD; if (osd) { int NewSetupLevel = setupLevel; if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) { @@ -833,7 +834,6 @@ void cDvbSubtitleConverter::Action(void) } LastSetupLevel = NewSetupLevel; } - Lock(); if (cDvbSubtitleBitmaps *sb = bitmaps->First()) { int64_t STC = cDevice::PrimaryDevice()->GetSTC(); int64_t Delta = LimitTo32Bit(sb->Pts()) - LimitTo32Bit(STC); // some devices only deliver 32 bits @@ -858,7 +858,6 @@ void cDvbSubtitleConverter::Action(void) else bitmaps->Del(sb); } - Unlock(); } cCondWait::SleepMs(WaitMs); } @@ -902,6 +901,7 @@ void cDvbSubtitleConverter::SetOsdData(void) bool cDvbSubtitleConverter::AssertOsd(void) { + LOCK_THREAD; return osd || (osd = cOsdProvider::NewOsd(int(round(osdFactorX * windowHorizontalOffset + osdDeltaX)), int(round(osdFactorY * windowVerticalOffset + osdDeltaY)) + Setup.SubtitleOffset, OSD_LEVEL_SUBTITLES)); } @@ -957,7 +957,11 @@ int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t region->SetVersion(regionVersion); bool regionFillFlag = (Data[6 + 1] & 0x08) >> 3; int regionWidth = (Data[6 + 2] << 8) | Data[6 + 3]; + if (regionWidth < 1) + regionWidth = 1; int regionHeight = (Data[6 + 4] << 8) | Data[6 + 5]; + if (regionHeight < 1) + regionHeight = 1; region->SetSize(regionWidth, regionHeight); region->SetLevel((Data[6 + 6] & 0xE0) >> 5); region->SetDepth((Data[6 + 6] & 0x1C) >> 2); @@ -1103,7 +1107,7 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page) int NumAreas = Page->regions.Count(); int Bpp = 8; bool Reduced = false; - while (osd->CanHandleAreas(Areas, NumAreas) != oeOk) { + while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) { int HalfBpp = Bpp / 2; if (HalfBpp >= 2) { for (int i = 0; i < NumAreas; i++) { @@ -1141,8 +1145,10 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page) for (cSubtitleRegion *sr = Page->regions.First(); sr; sr = Page->regions.Next(sr)) { int posX = sr->HorizontalAddress(); int posY = sr->VerticalAddress(); - cBitmap *bm = new cBitmap(sr->Width(), sr->Height(), sr->Bpp(), posX, posY); - bm->DrawBitmap(posX, posY, *sr); - Bitmaps->AddBitmap(bm); + if (sr->Width() > 0 && sr->Height() > 0) { + cBitmap *bm = new cBitmap(sr->Width(), sr->Height(), sr->Bpp(), posX, posY); + bm->DrawBitmap(posX, posY, *sr); + Bitmaps->AddBitmap(bm); + } } } diff --git a/eitscan.c b/eitscan.c index 2b43e718..25b7115c 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 2.1 2010/02/07 12:12:05 kls Exp $ + * $Id: eitscan.c 2.2 2011/08/12 14:18:04 kls Exp $ */ #include "eitscan.h" @@ -133,12 +133,12 @@ void cEITScanner::Process(void) if (Channels.Lock(false, 10)) { if (!scanList) { scanList = new cScanList; - scanList->AddTransponders(&Channels); if (transponderList) { scanList->AddTransponders(transponderList); delete transponderList; transponderList = NULL; } + scanList->AddTransponders(&Channels); } bool AnyDeviceSwitched = false; for (int i = 0; i < cDevice::NumDevices(); i++) { diff --git a/epg.data b/epg.data new file mode 100644 index 00000000..e69de29b diff --git a/i18n.c b/i18n.c index 636c4b58..bc4aaf60 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 2.3 2011/02/20 15:56:20 kls Exp $ + * $Id: i18n.c 2.4 2011/08/15 10:01:45 kls Exp $ */ /* @@ -40,7 +40,7 @@ const char *LanguageCodeList[] = { "slv,slo", "ita", "dut,nla,nld", - "por", + "prt", "fra,fre", "nor", "fin,suo", diff --git a/menu.c b/menu.c index 2ba1de8e..39785146 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 2.28 2011/02/27 12:37:48 kls Exp $ + * $Id: menu.c 2.29 2011/08/06 13:13:34 kls Exp $ */ #include "menu.h" @@ -38,6 +38,7 @@ #define NEWTIMERLIMIT 120 // seconds until the start time of a new timer created from the Schedule menu, // within which it will go directly into the "Edit timer" menu to allow // further parameter settings +#define DEFERTIMER 60 // seconds by which a timer is deferred in case of problems #define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS) #define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours @@ -4134,6 +4135,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) else DELETENULL(recorder); } + else + timer->SetDeferred(DEFERTIMER); if (!Timer) { Timers.Del(timer); Timers.SetModified(); diff --git a/menuitems.c b/menuitems.c index bc04e72c..e7797d6e 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 2.9 2011/06/13 14:48:41 kls Exp $ + * $Id: menuitems.c 2.10 2011/08/12 13:19:40 kls Exp $ */ #include "menuitems.h" @@ -736,9 +736,14 @@ void cMenuEditChanItem::Set(void) cChannel *channel = Channels.GetByNumber(*value); snprintf(buf, sizeof(buf), "%d %s", *value, channel ? channel->Name() : ""); SetValue(buf); + if (channelID) + *channelID = channel->GetChannelID().ToString(); } - else if (noneString) + else if (noneString) { SetValue(noneString); + if (channelID) + *channelID = ""; + } } eOSState cMenuEditChanItem::ProcessKey(eKeys Key) diff --git a/nit.c b/nit.c index dfd02076..0603e249 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 2.5 2010/02/16 15:37:05 kls Exp $ + * $Id: nit.c 2.6 2011/08/12 14:27:31 kls Exp $ */ #include "nit.h" @@ -150,6 +150,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (Setup.UpdateChannels >= 5) { bool found = false; + bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); @@ -164,9 +165,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('S')); + else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('S'))) + forceTransponderUpdate = true; // get us receiving this transponder } } - if (!found) { + if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); @@ -202,6 +205,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (Setup.UpdateChannels >= 5) { bool found = false; + bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); @@ -216,9 +220,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('C')); + else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('C'))) + forceTransponderUpdate = true; // get us receiving this transponder } } - if (!found) { + if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); @@ -261,6 +267,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (Setup.UpdateChannels >= 5) { bool found = false; + bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); @@ -275,9 +282,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, 0, dtp.ToString('T')); + else if (strcmp(Channel->Parameters(), dtp.ToString('T'))) + forceTransponderUpdate = true; // get us receiving this transponder } } - if (!found) { + if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); diff --git a/osd.c b/osd.c index 49b655aa..ab24e174 100644 --- a/osd.c +++ b/osd.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 2.22 2011/06/02 12:00:17 kls Exp $ + * $Id: osd.c 2.23 2011/08/15 09:27:39 kls Exp $ */ #include "osd.h" @@ -1600,9 +1600,11 @@ int cOsd::osdTop = 0; int cOsd::osdWidth = 0; int cOsd::osdHeight = 0; cVector cOsd::Osds; +cMutex cOsd::mutex; cOsd::cOsd(int Left, int Top, uint Level) { + cMutexLock MutexLock(&mutex); isTrueColor = false; savedBitmap = NULL; numBitmaps = 0; @@ -1624,6 +1626,7 @@ cOsd::cOsd(int Left, int Top, uint Level) cOsd::~cOsd() { + cMutexLock MutexLock(&mutex); for (int i = 0; i < numBitmaps; i++) delete bitmaps[i]; delete savedBitmap; @@ -1944,6 +1947,7 @@ cOsdProvider::~cOsdProvider() cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level) { + cMutexLock MutexLock(&cOsd::mutex); if (Level == OSD_LEVEL_DEFAULT && cOsd::IsOpen()) esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!"); else if (osdProvider) { diff --git a/osd.h b/osd.h index b56853a3..ab10e42b 100644 --- a/osd.h +++ b/osd.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 2.13 2011/04/17 14:24:32 kls Exp $ + * $Id: osd.h 2.14 2011/08/15 09:22:50 kls Exp $ */ #ifndef __OSD_H @@ -709,6 +709,7 @@ class cOsd { private: static int osdLeft, osdTop, osdWidth, osdHeight; static cVector Osds; + static cMutex mutex; bool isTrueColor; cBitmap *savedBitmap; cBitmap *bitmaps[MAXOSDAREAS]; diff --git a/recorder.c b/recorder.c index 2738c97c..6080deb0 100644 --- a/recorder.c +++ b/recorder.c @@ -4,13 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 2.11 2011/06/12 14:16:45 kls Exp $ + * $Id: recorder.c 2.14 2011/08/13 14:56:36 kls Exp $ */ #include "recorder.h" #include "shutdown.h" -#define RECORDERBUFSIZE MEGABYTE(5) +#define RECORDERBUFSIZE (MEGABYTE(5) / TS_SIZE * TS_SIZE) // multiple of TS_SIZE // The maximum time we wait before assuming that a recorded video data stream // is broken: @@ -88,7 +88,7 @@ bool cRecorder::RunningLowOnDiskSpace(void) bool cRecorder::NextFile(void) { - if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame + if (recordFile) { if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) { recordFile = fileName->NextFile(); fileSize = 0; @@ -119,8 +119,11 @@ void cRecorder::Action(void) time_t t = time(NULL); bool InfoWritten = false; bool FirstIframeSeen = false; - int FileNumber = 0; - off_t FrameOffset = -1; +#define BUFFERSIZE (5 * TS_SIZE) + bool Buffering = false; + int BufferIndex = 0; + int MaxBufferIndex = 0; + uchar *Buffer = NULL; while (Running()) { int r; uchar *b = ringBuffer->Get(r); @@ -141,16 +144,37 @@ void cRecorder::Action(void) } InfoWritten = true; } - if (frameDetector->NewPayload()) { - FileNumber = fileName->Number(); - FrameOffset = fileSize; + if (frameDetector->NewPayload()) { // We're at the first TS packet of a new payload... + if (Buffering) + esyslog("ERROR: encountered new payload while buffering - dropping some data!"); + if (!frameDetector->NewFrame()) { // ...but the frame type is yet unknown, so we need to buffer packets until we see the frame type + if (!Buffer) { + dsyslog("frame type not in first packet of payload - buffering"); + if (!(Buffer = MALLOC(uchar, BUFFERSIZE))) { + esyslog("ERROR: can't allocate frame type buffer"); + break; + } + } + BufferIndex = 0; + Buffering = true; + } } - if (FirstIframeSeen || frameDetector->IndependentFrame()) { + else if (frameDetector->NewFrame()) // now we know the frame type, so stop buffering + Buffering = false; + if (Buffering) { + if (BufferIndex + Count <= BUFFERSIZE) { + memcpy(Buffer + BufferIndex, b, Count); + BufferIndex += Count; + } + else + esyslog("ERROR: too many bytes for frame type buffer (%d > %d) - dropped %d bytes", BufferIndex + Count, int(BUFFERSIZE), Count); + } + else if (FirstIframeSeen || frameDetector->IndependentFrame()) { FirstIframeSeen = true; // start recording with the first I-frame - if (!NextFile()) + if (frameDetector->IndependentFrame() && !NextFile()) // every file shall start with an independent frame break; if (index && frameDetector->NewFrame()) - index->Write(frameDetector->IndependentFrame(), FileNumber, FrameOffset); + index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize); if (frameDetector->IndependentFrame()) { recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE); fileSize += TS_SIZE; @@ -160,6 +184,12 @@ void cRecorder::Action(void) fileSize += TS_SIZE; } } + if (BufferIndex) { + recordFile->Write(Buffer, BufferIndex); // if an error occurs here, the next write below will catch and report it + if (BufferIndex > MaxBufferIndex) + MaxBufferIndex = BufferIndex; + BufferIndex = 0; + } if (recordFile->Write(b, Count) < 0) { LOG_ERROR_STR(fileName->Name()); break; @@ -177,4 +207,8 @@ void cRecorder::Action(void) t = time(NULL); } } + if (Buffer) { + free(Buffer); + dsyslog("frame type buffer used %d bytes", MaxBufferIndex); + } } diff --git a/recording.c b/recording.c index a8b40f2d..4456bddb 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 2.31 2011/06/12 13:04:28 kls Exp $ + * $Id: recording.c 2.33 2011/08/13 12:37:25 kls Exp $ */ #include "recording.h" @@ -1540,84 +1540,75 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording) :resumeFile(FileName, IsPesRecording) { f = -1; - fileName = NULL; size = 0; last = -1; index = NULL; isPesRecording = IsPesRecording; indexFileGenerator = NULL; if (FileName) { - const char *Suffix = isPesRecording ? INDEXFILESUFFIX ".vdr" : INDEXFILESUFFIX; - fileName = MALLOC(char, strlen(FileName) + strlen(Suffix) + 1); - if (fileName) { - strcpy(fileName, FileName); - char *pFileExt = fileName + strlen(fileName); - strcpy(pFileExt, Suffix); - int delta = 0; - if (!Record && access(fileName, R_OK) != 0) { - // Index file doesn't exist, so try to regenerate it: - if (!isPesRecording) { // sorry, can only do this for TS recordings - resumeFile.Delete(); // just in case - indexFileGenerator = new cIndexFileGenerator(FileName); - // Wait until the index file exists: - time_t tmax = time(NULL) + MAXWAITFORINDEXFILE; - do { - cCondWait::SleepMs(INDEXFILECHECKINTERVAL); // start with a sleep, to give it a head start - } while (access(fileName, R_OK) != 0 && time(NULL) < tmax); - } - } - if (access(fileName, R_OK) == 0) { - struct stat buf; - if (stat(fileName, &buf) == 0) { - delta = int(buf.st_size % sizeof(tIndexTs)); - if (delta) { - delta = sizeof(tIndexTs) - delta; - esyslog("ERROR: invalid file size (%"PRId64") in '%s'", buf.st_size, fileName); - } - last = int((buf.st_size + delta) / sizeof(tIndexTs) - 1); - if (!Record && last >= 0) { - size = last + 1; - index = MALLOC(tIndexTs, size); - if (index) { - f = open(fileName, O_RDONLY); - if (f >= 0) { - if (safe_read(f, index, size_t(buf.st_size)) != buf.st_size) { - esyslog("ERROR: can't read from file '%s'", fileName); - free(index); - index = NULL; - close(f); - f = -1; - } - // we don't close f here, see CatchUp()! - else if (isPesRecording) - ConvertFromPes(index, size); - } - else - LOG_ERROR_STR(fileName); - } - else - esyslog("ERROR: can't allocate %zd bytes for index '%s'", size * sizeof(tIndexTs), fileName); - } - } - else - LOG_ERROR; - } - else if (!Record) - isyslog("missing index file %s", fileName); - if (Record) { - if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) { - if (delta) { - esyslog("ERROR: padding index file with %d '0' bytes", delta); - while (delta--) - writechar(f, 0); - } - } - else - LOG_ERROR_STR(fileName); + fileName = IndexFileName(FileName, isPesRecording); + int delta = 0; + if (!Record && access(fileName, R_OK) != 0) { + // Index file doesn't exist, so try to regenerate it: + if (!isPesRecording) { // sorry, can only do this for TS recordings + resumeFile.Delete(); // just in case + indexFileGenerator = new cIndexFileGenerator(FileName); + // Wait until the index file exists: + time_t tmax = time(NULL) + MAXWAITFORINDEXFILE; + do { + cCondWait::SleepMs(INDEXFILECHECKINTERVAL); // start with a sleep, to give it a head start + } while (access(fileName, R_OK) != 0 && time(NULL) < tmax); } } - else - esyslog("ERROR: can't copy file name '%s'", FileName); + if (access(fileName, R_OK) == 0) { + struct stat buf; + if (stat(fileName, &buf) == 0) { + delta = int(buf.st_size % sizeof(tIndexTs)); + if (delta) { + delta = sizeof(tIndexTs) - delta; + esyslog("ERROR: invalid file size (%"PRId64") in '%s'", buf.st_size, *fileName); + } + last = int((buf.st_size + delta) / sizeof(tIndexTs) - 1); + if (!Record && last >= 0) { + size = last + 1; + index = MALLOC(tIndexTs, size); + if (index) { + f = open(fileName, O_RDONLY); + if (f >= 0) { + if (safe_read(f, index, size_t(buf.st_size)) != buf.st_size) { + esyslog("ERROR: can't read from file '%s'", *fileName); + free(index); + index = NULL; + close(f); + f = -1; + } + // we don't close f here, see CatchUp()! + else if (isPesRecording) + ConvertFromPes(index, size); + } + else + LOG_ERROR_STR(*fileName); + } + else + esyslog("ERROR: can't allocate %zd bytes for index '%s'", size * sizeof(tIndexTs), *fileName); + } + } + else + LOG_ERROR; + } + else if (!Record) + isyslog("missing index file %s", *fileName); + if (Record) { + if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) { + if (delta) { + esyslog("ERROR: padding index file with %d '0' bytes", delta); + while (delta--) + writechar(f, 0); + } + } + else + LOG_ERROR_STR(*fileName); + } } } @@ -1625,11 +1616,15 @@ cIndexFile::~cIndexFile() { if (f >= 0) close(f); - free(fileName); free(index); delete indexFileGenerator; } +cString cIndexFile::IndexFileName(const char *FileName, bool IsPesRecording) +{ + return cString::sprintf("%s%s", FileName, IsPesRecording ? INDEXFILESUFFIX ".vdr" : INDEXFILESUFFIX); +} + void cIndexFile::ConvertFromPes(tIndexTs *IndexTs, int Count) { tIndexPes IndexPes; @@ -1696,7 +1691,7 @@ bool cIndexFile::CatchUp(int Index) last = newLast; } else - LOG_ERROR_STR(fileName); + LOG_ERROR_STR(*fileName); } else { esyslog("ERROR: can't realloc() index"); @@ -1705,7 +1700,7 @@ bool cIndexFile::CatchUp(int Index) } } else - LOG_ERROR_STR(fileName); + LOG_ERROR_STR(*fileName); if (Index < last - (i ? 2 * INDEXSAFETYLIMIT : 0) || Index > 10 * INDEXSAFETYLIMIT) // keep off the end in case of "Pause live video" break; cCondWait::SleepMs(1000); @@ -1721,7 +1716,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset) if (isPesRecording) ConvertToPes(&i, 1); if (safe_write(f, &i, sizeof(i)) < 0) { - LOG_ERROR_STR(fileName); + LOG_ERROR_STR(*fileName); close(f); f = -1; return false; @@ -1811,8 +1806,8 @@ bool cIndexFile::IsStillRecording() void cIndexFile::Delete(void) { - if (fileName) { - dsyslog("deleting index file '%s'", fileName); + if (*fileName) { + dsyslog("deleting index file '%s'", *fileName); if (f >= 0) { close(f); f = -1; @@ -1821,6 +1816,15 @@ void cIndexFile::Delete(void) } } +int cIndexFile::GetLength(const char *FileName, bool IsPesRecording) +{ + struct stat buf; + cString s = IndexFileName(FileName, IsPesRecording); + if (*s && stat(s, &buf) == 0) + return buf.st_size / (IsPesRecording ? sizeof(tIndexTs) : sizeof(tIndexPes)); + return -1; +} + bool GenerateIndex(const char *FileName) { if (DirectoryOk(FileName)) { diff --git a/recording.h b/recording.h index 8ec7f49e..578dd455 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.h 2.19 2011/04/17 13:18:04 kls Exp $ + * $Id: recording.h 2.22 2011/08/13 12:51:23 kls Exp $ */ #ifndef __RECORDING_H @@ -120,15 +120,15 @@ public: void ReadInfo(void); bool WriteInfo(void); bool Delete(void); - // Changes the file name so that it will no longer be visible in the "Recordings" menu - // Returns false in case of error + ///< Changes the file name so that it will no longer be visible in the "Recordings" menu + ///< Returns false in case of error bool Remove(void); - // Actually removes the file from the disk - // Returns false in case of error + ///< Actually removes the file from the disk + ///< Returns false in case of error bool Undelete(void); - // Changes the file name so that it will be visible in the "Recordings" menu again and - // not processed by cRemoveDeletedRecordingsThread. - // Returns false in case of error + ///< Changes the file name so that it will be visible in the "Recordings" menu again and + ///< not processed by cRemoveDeletedRecordingsThread. + ///< Returns false in case of error }; class cRecordings : public cList, public cThread { @@ -236,13 +236,14 @@ class cIndexFileGenerator; class cIndexFile { private: int f; - char *fileName; + cString fileName; int size, last; tIndexTs *index; bool isPesRecording; cResumeFile resumeFile; cIndexFileGenerator *indexFileGenerator; cMutex mutex; + static cString IndexFileName(const char *FileName, bool IsPesRecording); void ConvertFromPes(tIndexTs *IndexTs, int Count); void ConvertToPes(tIndexTs *IndexTs, int Count); bool CatchUp(int Index = -1); @@ -259,6 +260,9 @@ public: bool StoreResume(int Index) { return resumeFile.Save(Index); } bool IsStillRecording(void); void Delete(void); + static int GetLength(const char *FileName, bool IsPesRecording = false); + ///< Calculates the recording length (numer of frames) without actually reading the index file. + ///< Returns -1 in case of error. }; class cFileName { diff --git a/remote.c b/remote.c index cd3a7585..88773a5b 100644 --- a/remote.c +++ b/remote.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.c 2.3 2011/03/27 15:03:36 kls Exp $ + * $Id: remote.c 2.4 2011/08/15 13:41:40 kls Exp $ */ #include "remote.h" @@ -26,7 +26,7 @@ eKeys cRemote::keys[MaxKeys]; int cRemote::in = 0; int cRemote::out = 0; -cTimeMs cRemote::repeatTimeout; +cTimeMs cRemote::repeatTimeout(-1); cRemote *cRemote::learning = NULL; char *cRemote::unknownCode = NULL; cMutex cRemote::mutex; diff --git a/remux.c b/remux.c index c3f07fb7..f174f614 100644 --- a/remux.c +++ b/remux.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.c 2.57 2011/06/12 14:24:09 kls Exp $ + * $Id: remux.c 2.58 2011/08/15 09:50:14 kls Exp $ */ #include "remux.h" @@ -135,8 +135,11 @@ void TsSetTeiOnBrokenPackets(uchar *p, int l) if (!Processed[Pid]) { if (!TsPayloadStart(p)) p[1] |= TS_ERROR; - else + else { Processed[Pid] = true; + int offs = TsPayloadOffset(p); + cRemux::SetBrokenLink(p + offs, TS_SIZE - offs); + } } l -= TS_SIZE; p += TS_SIZE; diff --git a/skins.c b/skins.c index a0815e26..83428730 100644 --- a/skins.c +++ b/skins.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.c 2.1 2009/06/06 15:12:31 kls Exp $ + * $Id: skins.c 2.2 2011/08/06 09:41:57 kls Exp $ */ #include "skins.h" @@ -223,6 +223,10 @@ bool cSkins::SetCurrent(const char *Name) eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds) { + if (!cThread::IsMainThread()) { + dsyslog("cSkins::Message() called from background thread - ignored! (Use cSkins::QueueMessage() instead)"); + return kNone; + } switch (Type) { case mtInfo: isyslog("info: %s", s); break; case mtWarning: isyslog("warning: %s", s); break; diff --git a/svdrpsend.pl b/svdrpsend.pl index 03d78fea..caf3b41f 100755 --- a/svdrpsend.pl +++ b/svdrpsend.pl @@ -52,6 +52,6 @@ sub Error { print STDERR "@_\n"; close(SOCK); - exit 0; + exit 1; } diff --git a/timers.c b/timers.c index 246fd868..54ed1ba8 100644 --- a/timers.c +++ b/timers.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 2.4 2010/01/16 11:18:53 kls Exp $ + * $Id: timers.c 2.5 2011/08/06 13:13:54 kls Exp $ */ #include "timers.h" @@ -29,6 +29,7 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel) { startTime = stopTime = 0; lastSetEvent = 0; + deferred = 0; recording = pending = inVpsMargin = false; flags = tfNone; if (Instant) @@ -62,6 +63,7 @@ cTimer::cTimer(const cEvent *Event) { startTime = stopTime = 0; lastSetEvent = 0; + deferred = 0; recording = pending = inVpsMargin = false; flags = tfActive; if (Event->Vps() && Setup.UseVps) @@ -118,6 +120,7 @@ cTimer& cTimer::operator= (const cTimer &Timer) startTime = Timer.startTime; stopTime = Timer.stopTime; lastSetEvent = 0; + deferred = 0; recording = Timer.recording; pending = Timer.pending; inVpsMargin = Timer.inVpsMargin; @@ -422,6 +425,10 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const day = 0; } + if (t < deferred) + return false; + deferred = 0; + if (HasFlags(tfActive)) { if (HasFlags(tfVps) && event && event->Vps()) { if (Margin || !Directly) { @@ -589,6 +596,12 @@ void cTimer::SetPriority(int Priority) priority = Priority; } +void cTimer::SetDeferred(int Seconds) +{ + deferred = time(NULL) + Seconds; + isyslog("timer %s deferred for %d seconds", *ToDescr(), Seconds); +} + void cTimer::SetFlags(uint Flags) { flags |= Flags; diff --git a/timers.h b/timers.h index 8f67add6..1d733ee0 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 2.0 2008/02/16 14:33:23 kls Exp $ + * $Id: timers.h 2.1 2011/08/06 12:59:32 kls Exp $ */ #ifndef __TIMERS_H @@ -29,6 +29,7 @@ class cTimer : public cListObject { private: mutable time_t startTime, stopTime; time_t lastSetEvent; + mutable time_t deferred; ///< Matches(time_t, ...) will return false if the current time is before this value bool recording, pending, inVpsMargin; uint flags; cChannel *channel; @@ -62,6 +63,7 @@ public: const char *File(void) const { return file; } time_t FirstDay(void) const { return weekdays ? day : 0; } const char *Aux(void) const { return aux; } + time_t Deferred(void) const { return deferred; } cString ToText(bool UseChannelID = false) const; cString ToDescr(void) const; const cEvent *Event(void) const { return event; } @@ -85,6 +87,7 @@ public: void SetPending(bool Pending); void SetInVpsMargin(bool InVpsMargin); void SetPriority(int Priority); + void SetDeferred(int Seconds); void SetFlags(uint Flags); void ClrFlags(uint Flags); void InvFlags(uint Flags); diff --git a/tools.c b/tools.c index 6a98bf69..c9a3a44e 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 2.14 2011/04/29 14:51:14 kls Exp $ + * $Id: tools.c 2.17 2011/08/15 13:35:23 kls Exp $ */ #include "tools.h" @@ -261,7 +261,7 @@ int numdigits(int n) bool isnumber(const char *s) { - if (!*s) + if (!s || !*s) return false; do { if (!isdigit(*s)) @@ -270,6 +270,21 @@ bool isnumber(const char *s) return true; } +int64_t StrToNum(const char *s) +{ + char *t = NULL; + int64_t n = strtoll(s, &t, 10); + if (t) { + switch (*t) { + case 'T': n *= 1024; + case 'G': n *= 1024; + case 'M': n *= 1024; + case 'K': n *= 1024; + } + } + return n; +} + cString AddDirectory(const char *DirName, const char *FileName) { return cString::sprintf("%s/%s", DirName && *DirName ? DirName : ".", FileName); @@ -556,7 +571,10 @@ time_t LastModifiedTime(const char *FileName) cTimeMs::cTimeMs(int Ms) { - Set(Ms); + if (Ms >= 0) + Set(Ms); + else + begin = 0; } uint64_t cTimeMs::Now(void) diff --git a/tools.h b/tools.h index f17c5642..3d13c191 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 2.7 2011/02/25 15:05:58 kls Exp $ + * $Id: tools.h 2.11 2011/08/15 14:13:42 kls Exp $ */ #ifndef __TOOLS_H @@ -45,7 +45,7 @@ extern int SysLogLevel; #define MALLOC(type, size) (type *)malloc(sizeof(type) * (size)) -#define DELETENULL(p) (delete (p), p = NULL) +template inline void DELETENULL(T *&p) { T *q = p; p = NULL; delete q; } #define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls #define FATALERRNO (errno && errno != EAGAIN && errno != EINTR) @@ -205,6 +205,12 @@ bool endswith(const char *s, const char *p); bool isempty(const char *s); int numdigits(int n); bool isnumber(const char *s); +int64_t StrToNum(const char *s); + ///< Converts the given string to a number. + ///< The numerical part of the string may be followed by one of the letters + ///< K, M, G or T to abbreviate Kilo-, Mega-, Giga- or Terabyte, respectively + ///< (based on 1024). Everything after the first non-numeric character is + ///< silently ignored, as are any characters other than the ones mentionend here. cString itoa(int n); cString AddDirectory(const char *DirName, const char *FileName); bool EntriesOnSameFileSystem(const char *File1, const char *File2); @@ -266,6 +272,8 @@ private: public: cTimeMs(int Ms = 0); ///< Creates a timer with ms resolution and an initial timeout of Ms. + ///< If Ms is negative the timer is not initialized with the current + ///< time. static uint64_t Now(void); void Set(int Ms = 0); bool TimedOut(void); @@ -506,12 +514,23 @@ inline int CompareStrings(const void *a, const void *b) return strcmp(*(const char **)a, *(const char **)b); } +inline int CompareStringsIgnoreCase(const void *a, const void *b) +{ + return strcasecmp(*(const char **)a, *(const char **)b); +} + class cStringList : public cVector { public: cStringList(int Allocated = 10): cVector(Allocated) {} virtual ~cStringList(); int Find(const char *s) const; - void Sort(void) { cVector::Sort(CompareStrings); } + void Sort(bool IgnoreCase = false) + { + if (IgnoreCase) + cVector::Sort(CompareStringsIgnoreCase); + else + cVector::Sort(CompareStrings); + } virtual void Clear(void); }; diff --git a/vdr.1 b/vdr.1 index ea93bdda..4c0a9785 100644 --- a/vdr.1 +++ b/vdr.1 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.1 2.5 2010/04/02 12:22:08 kls Exp $ +.\" $Id: vdr.1 2.6 2011/08/15 12:28:54 kls Exp $ .\" .TH vdr 1 "10 Feb 2008" "1.6" "Video Disk Recorder" .SH NAME @@ -66,6 +66,15 @@ Use \fB\-E\-\fR to disable this. If \fIfile\fR is a directory, the file \fIepg.data\fR will be created in that directory. .TP +.BI \-\-filesize= size +Limit video files to \fIsize\fR bytes (default is 2000M). +This option is only useful in conjunction with --edit, and must precede that +option to have an effect. +\fIsize\fR is an integer number and may be followed by one of the letters K, M, G or T +to abbreviate Kilo-, Mega-, Giga- or Terabyte, respectively. +The given value is silently limited to the program's internal minimum and +maximum values. +.TP .BI \-\-genindex= rec Generate the index file for the given recording. \fIrec\fR must be the full path name of an existing recording. @@ -157,6 +166,11 @@ more information. Call \fIcmd\fR to shutdown the computer. See the file \fIINSTALL\fR for more information. .TP +.BI \-\-split +Split edited files at the editing marks. +This option is only useful in conjunction with --edit, and must precede that +option to have an effect. +.TP .BI \-t\ tty ,\ \-\-terminal= tty Set the controlling terminal. .TP diff --git a/vdr.c b/vdr.c index ad86f5a8..c32e45fd 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 2.21 2011/06/13 14:40:12 kls Exp $ + * $Id: vdr.c 2.23 2011/08/15 12:42:39 kls Exp $ */ #include @@ -223,6 +223,7 @@ int main(int argc, char *argv[]) { "device", required_argument, NULL, 'D' }, { "edit", required_argument, NULL, 'e' | 0x100 }, { "epgfile", required_argument, NULL, 'E' }, + { "filesize", required_argument, NULL, 'f' | 0x100 }, { "genindex", required_argument, NULL, 'g' | 0x100 }, { "grab", required_argument, NULL, 'g' }, { "help", no_argument, NULL, 'h' }, @@ -238,6 +239,7 @@ int main(int argc, char *argv[]) { "rcu", optional_argument, NULL, 'r' | 0x100 }, { "record", required_argument, NULL, 'r' }, { "shutdown", required_argument, NULL, 's' }, + { "split", no_argument, NULL, 's' | 0x100 }, { "terminal", required_argument, NULL, 't' }, { "user", required_argument, NULL, 'u' }, { "userdump", no_argument, NULL, 'u' | 0x100 }, @@ -270,6 +272,13 @@ int main(int argc, char *argv[]) return CutRecording(optarg) ? 0 : 2; case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL); break; + case 'f' | 0x100: + Setup.MaxVideoFileSize = StrToNum(optarg) / MEGABYTE(1); + if (Setup.MaxVideoFileSize < MINVIDEOFILESIZE) + Setup.MaxVideoFileSize = MINVIDEOFILESIZE; + if (Setup.MaxVideoFileSize > MAXVIDEOFILESIZETS) + Setup.MaxVideoFileSize = MAXVIDEOFILESIZETS; + break; case 'g' | 0x100: return GenerateIndex(optarg) ? 0 : 2; case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL); @@ -348,6 +357,9 @@ int main(int argc, char *argv[]) break; case 's': ShutdownHandler.SetShutdownCommand(optarg); break; + case 's' | 0x100: + Setup.SplitEditedFiles = 1; + break; case 't': Terminal = optarg; if (access(Terminal, R_OK | W_OK) < 0) { fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal); @@ -419,6 +431,8 @@ int main(int argc, char *argv[]) " '-E-' disables this\n" " if FILE is a directory, the default EPG file will be\n" " created in that directory\n" + " --filesize=SIZE limit video files to SIZE bytes (default is %dM)\n" + " only useful in conjunction with --edit\n" " --genindex=REC generate index for recording REC and exit\n" " -g DIR, --grab=DIR write images from the SVDRP command GRAB into the\n" " given DIR; DIR must be the full path name of an\n" @@ -445,6 +459,8 @@ int main(int argc, char *argv[]) " (default: %s)\n" " -r CMD, --record=CMD call CMD before and after a recording\n" " -s CMD, --shutdown=CMD call CMD to shutdown the computer\n" + " --split split edited files at the editing marks (only\n" + " useful in conjunction with --edit)\n" " -t TTY, --terminal=TTY controlling tty\n" " -u USER, --user=USER run as user USER; only applicable if started as\n" " root\n" @@ -458,6 +474,7 @@ int main(int argc, char *argv[]) "\n", DEFAULTCONFDIR, DEFAULTEPGDATAFILENAME, + MAXVIDEOFILESIZEDEFAULT, DEFAULTPLUGINDIR, LIRC_DEVICE, LOCDIR, @@ -704,12 +721,14 @@ int main(int argc, char *argv[]) if (!cDevice::WaitForAllDevicesReady(DEVICEREADYTIMEOUT)) dsyslog("not all devices ready after %d seconds", DEVICEREADYTIMEOUT); - if (isnumber(Setup.InitialChannel)) { // for compatibility with old setup.conf files - if (cChannel *Channel = Channels.GetByNumber(atoi(Setup.InitialChannel))) - Setup.InitialChannel = Channel->GetChannelID().ToString(); + if (*Setup.InitialChannel) { + if (isnumber(Setup.InitialChannel)) { // for compatibility with old setup.conf files + if (cChannel *Channel = Channels.GetByNumber(atoi(Setup.InitialChannel))) + Setup.InitialChannel = Channel->GetChannelID().ToString(); + } + if (cChannel *Channel = Channels.GetByChannelID(tChannelID::FromString(Setup.InitialChannel))) + Setup.CurrentChannel = Channel->Number(); } - if (cChannel *Channel = Channels.GetByChannelID(tChannelID::FromString(Setup.InitialChannel))) - Setup.CurrentChannel = Channel->Number(); if (Setup.InitialVolume >= 0) Setup.CurrentVolume = Setup.InitialVolume; Channels.SwitchTo(Setup.CurrentChannel);