diff -ruN vdr-2.0.0/CONTRIBUTORS vdr-2.2.0/CONTRIBUTORS
--- vdr-2.0.0/CONTRIBUTORS	2013-03-29 16:37:16.000000000 +0100
+++ vdr-2.2.0/CONTRIBUTORS	2015-02-19 09:20:08.000000000 +0100
@@ -232,6 +232,7 @@
  featured DVB cards
  for pointing out a bug in handling lowercase polarization characters in channel
  definitions if no DiSEqC is used
+ for fixing a bug in the Makefile when installing plugins with LCLBLD=1
 
 Ulrich R�der <roeder@efr-net.de>
  for pointing out that there are channels that have a symbol rate higher than 27500
@@ -537,7 +538,7 @@
 Onno Kreuzinger <ok@solutas.net>
  for reporting leftover references to the file FORMATS in MANUAL and svdrp.c
 
-Rudi Hofer (Rudi.Hofer@gmx.de)
+Rudi Hofer <Rudi.Hofer@gmx.de>
  for his help in keeping 'channels.conf' up to date
  for reporting a problem with overlapping tab positions in skins when using wide fonts
 
@@ -618,6 +619,12 @@
  for suggesting to read the epg.data file in a separate thread
  for some improvements to allowing the parameters PATH and NAME to the --dirnames
  command line option to be left empty to use the default values if only ENC shall be set
+ for reporting an inconsistent behavior between opening the Recordings menu manually
+ via the main menu and by pressing the Recordings key
+ for helping to debug a problem with frame detection in MPEG-2 streams that have "bottom fields"
+ or varying GOP structures
+ for a patch that was used to implement the command line option --updindex
+ for modifying the "binary skip" patch to move editing marks
 
 Jeremy Hall <jhall@UU.NET>
  for fixing an incomplete initialization of the filter parameters in eit.c
@@ -700,6 +707,13 @@
  for helping to debug a problem with reduced number of retries in Transfer Mode on
  SD-FF cards
  for reporting a problem with resuming replay of PES recordings
+ for suggesting to make all bonded devices (except for the master) turn off their LNB
+ power completely to avoid problems when receiving vertically polarized transponders
+ for suggesting to eliminate MAXDVBDEVICES
+ for reporting that there are channels that need even more than 10 TS packets in order
+ to detect the frame type
+ for suggesting to ignore channels with an RID that is not 0 when checking for obsolete
+ channels
 
 Reinhard Walter Buchner <rw.buchner@freenet.de>
  for adding some satellites to 'sources.conf'
@@ -773,9 +787,10 @@
 Stefan Schluenss <dxr3_osd@schluenss.de>
  for reporting a bug where PID handles were not closed correctly
 
-R�gis Bossut <rbossut@auchan.com>
+R�gis Bossut <famille.bossut@wanadoo.fr>
  for pointing out that with some providers the channels can only be distinguished
  through the RID
+ for translating OSD texts to the French language
 
 Andreas Kool <akool@akool.de>
  for his help in keeping 'channels.conf.cable' up to date
@@ -1013,9 +1028,11 @@
  for fixing the type of MBperMinute in cVideoDiskUsage::HasChanged()
  for reporting a bug in sorting recordings in case two folders have the same name,
  but one of them ends in an additional digit, as in "abc" and "abc2"
- for reporting multiple occurrences of the same directory in the recordings list ini
+ for reporting multiple occurrences of the same directory in the recordings list in
  case there are directories that only differ in non-alphanumeric characters
  for reporting a problem with reduced number of retries in Transfer Mode on SD-FF cards
+ for fixing multiple occurrences of the same directory in the recordings list in case
+ there are directories that only differ in non-alphanumeric characters
 
 Olivier Jacques <jacquesolivier@hotmail.com>)
  for translating OSD texts to the French language
@@ -1165,6 +1182,21 @@
  for fixing the call to ChannelString() in cSkinLCARSDisplayChannel::SetChannel()
  for a patch that was used to rename the "plp id" to a more general "stream id"
  and add support for DVB-S2 "Input Stream Identifier" (ISI)
+ for helping to debug and understand subtitle page refreshes
+ for a patch that was used to implement the SVDRP command RENR
+ for fixing some compiler warnings with gcc-4.6.3
+ for suggesting to prompt the user for confirmation before overwriting an already
+ existing edited version of a recording
+ for adding code for parsing LCN and AVC descriptors to libsi
+ for fixing clearing non-editable members in the channel editor
+ for reporting a problem with adding new source types in case they are already
+ registered
+ for adding support for "Pilot", "T2-System-Id" and "SISO/MISO" parameters
+ for fixing a problem with subtitles not being displayed because the broadcaster
+ doesn't set the data's version numbers as required by the DVB standard
+ for the "binary skip" patch
+ for adding support for LCN (Logical Channel Numbers)
+ for suggesting to change the naming of "binary skip mode" to "adaptive skip mode"
 
 Ralf Klueber <ralf.klueber@vodafone.com>
  for reporting a bug in cutting a recording if there is only a single editing mark
@@ -1487,6 +1519,8 @@
  for fixing handling the '0' key for switching between the last two channels
  for making cPatFilter::Process() check whether the channel exists before setting
  the PMT filter
+ for the Rotor plugin, from which code was used by a patch that was used as a base for
+ implementing support for positioners
 
 St�phane Est�-Gracias <sestegra@free.fr>
  for fixing a typo in libsi/si.h
@@ -1670,6 +1704,7 @@
  cDevice::TrickSpeed()
  for renaming 'runvdr' to 'runvdr.template' and no longer copying it to the BINDIR
  in 'make install'
+ for suggesting to add "#REMOTE=LIRC" to Make.config.template
 
 Maynard Cedric <maynard.cedric@wanadoo.fr>
  for reporting a problem in handling the color button texts in cMenuEditStrItem
@@ -1695,6 +1730,9 @@
  for reporting references to old *.vdr file names in MANUAL
  for reporting that the video stream type was set to 2 even if the vpid was 0
  for updates to 'sources.conf'
+ for reporting a wrong initialization of Setup.PositionerSwing
+ for updating sources.conf to reflect the fact that Astra 4A and SES5 are actually in
+ two separate positions
 
 Milos Kapoun <m.kapoun@cra.cz>
  for suggesting to skip code table info in SI data
@@ -1788,6 +1826,7 @@
  for requesting to keep using relative paths when building plugins locally
  for fixing a problem with detecting user inactivity in case the system time is
  changed after VDR has been started
+ for a patch that was used to add definitions for older DVB API versions, back until 5.0
 
 Sven Kreiensen <svenk@kammer.uni-hannover.de>
  for his help in keeping 'channels.conf.terr' up to date
@@ -2015,6 +2054,13 @@
  be escaped
  for changing the template for PLGCFG to $(CONFDIR)/plugins.mk
  for updating the help and man page entry about the location of the epg.data file
+ for reporting a possible crash when shutting down VDR while subtitles are being
+ displayed
+ for fixing some spellings in positioner.h and Doxyfile
+ for changing '%a' to the POSIX compliant '%m' in all scanf() calls
+ for reporting a possible NULL pointer dereference in cCiSession::SendData()
+ for reporting a superfluous assignment in cPipe::Open()
+ for avoiding unnecessary pkg-config warnings in plugin Makefiles
 
 Steffen Beyer <cpunk@reactor.de>
  for fixing setting the colored button help after deleting a recording in case the next
@@ -2100,6 +2146,8 @@
  for suggesting to make the 'Allowed' parameter in cMenuEditStrItem() NULL by default,
  which results in using tr(FileNameChars)
  for fixing handling "none" color entries in XPM files
+ for fixing displaying the frame number when setting an editing mark
+ for the "jumpplay" patch
 
 David Woodhouse <dwmw2@infradead.org>
  for his help in replacing the get/put_unaligned() macros from asm/unaligned.h with
@@ -2127,6 +2175,10 @@
  pressed in string input fields
  for fixing missing ',' in the Italian and Polish OSD texts
  for pointing out that "Menu button closes" should actually be "Menu key closes"
+ for fixing a missing initialization in the c'tor of cSkinLCARSDisplayChannel
+ for suggesting to simplify some conditional expressions in skinlcars.c and skinsttng.c
+ for reporting some uninitialized item area coordinates in cSkinLCARSDisplayMenu
+ for reporting a problem with the video directory not being set correctly with --edit
 
 Patrick Rother <krd-vdr@gulu.net>
  for reporting a bug in defining timers that only differ in the day of week
@@ -2360,6 +2412,17 @@
  display gets hidden
  for reporting a wrong type ('int' vs. 'eTimerEvent') in the declaration of
  cSkinDisplayMenu::SetItemEvent()
+ for reporting that the source recording was not deleted after moving it to a different
+ volume
+ for suggesting to replace "Schnitt" with "Bearbeitung" in the German OSD texts
+ for reporting a superfluous call to the skin's SetRecording() function after renaming
+ a recording
+ for suggesting to add a function to remove the name of a recording and replace it
+ with the last element of the recording's folder path name
+ for reporting a bug in setting an empty recording name or folder to a blank in the
+ "Edit recording" menu
+ for suggesting to add a confirmation before renaming a recording to its folder name
+ for reporting a problem with data loss in case renaming a recording fails
 
 Pekka Mauno <pekka.mauno@iki.fi>
  for fixing cSchedule::GetFollowingEvent() in case there is currently no present
@@ -2475,6 +2538,8 @@
 Antti Hartikainen <ami+vdr@ah.fi>
  for updating 'S13E' in 'sources.conf'
  for adding maximum SNR value for PCTV Systems nanoStick T2 290e
+ for updating 'sources.conf'
+ for translating OSD texts to the Finnish language
 
 Bernd Melcher <bernd@bernd-melcher.de>
  for reporting a problem with the 'servicedemo' plugin having no PLUGIN macro
@@ -2500,6 +2565,7 @@
  for suggestions used in revising priority handling to allow receivers with a priority
  that is lower than that of live viewing
  for fixing handling IDLEPRIORITY in cDvbDevice::ProvidesChannel()
+ for suggesting to add functions to set and retrieve the priority of a cReceiver
 
 J�rn Reder <joern@zyn.de>
  for reporting that a recording may unnecessarily block a device with a CAM, while
@@ -2537,11 +2603,14 @@
  for suggesting to add user defined key kUser0
  for suggesting to perform absolute jumps when replaying a recording (via the Red key)
  only if an actual value has been entered
+ for suggesting to make the Yellow button in the main menu not act as "Pause" if
+ "Pause key handling" is set to "do not pause live video"
 
 J�rg Wendel <vdr-ml@jwendel.de>
  for reporting that cPlugin::Active() was called too often
  for adding HandledExternally() to the EPG handler interface
  for adding IsUpdate() to the EPG handler interface
+ for adding Begin/EndSegmentTransfer() to the EPG handler interface
 
 Peter Pinnau <vdr@unterbrecher.de>
  for reporting that 'uint32_t' requires including stdint.h in font.h on some systems
@@ -2566,6 +2635,7 @@
  for a patch that fixed part of a crash in i18n character set conversion
  for fixing cDvbPlayer::NextFile() to handle files larger than 2GB
  for implementing full handling of the stream types of Dolby Digital pids
+ for adding subsystem id support for DVB devices connected via USB
 
 Patrice Staudt <staudt@engsystem.net>
  for adding full weekday names to i18n.c for plugins to use
@@ -2598,6 +2668,8 @@
  for suggesting to make the "Source" item in the "Edit channel" menu wrap around the
  list of sources
  for reporting a crash when creating a new channel if the channel list is empty
+ for reporting that editing marks were generated even if the edited recording resulted
+ in just one single sequence
 
 Denis Knauf <denis.knauf@gmail.com>
  for reporting a missing '-' at the next to last line of SVDRP help texts
@@ -2620,6 +2692,7 @@
  for pointing out that it was not obvious how to initiate internationalization
  support for a plugin
  for suggesting to add a section about "Logging" to PLUGINS.html
+ for the "jumpplay" patch
 
 Michael Nival <mnival@club-internet.fr>
  for translating OSD texts to the French language
@@ -2690,6 +2763,7 @@
  for reporting broken SI data on Czech/Slovak channels after changing the default
  character set to ISO-8859-9
  for adding MPEG 1 handling to remux.c
+ for a patch that was used as a base for implementing support for positioners
 
 Magnus Andersson <svankan@bahnhof.se>
  for translating OSD texts to the Swedish language
@@ -2747,7 +2821,7 @@
 Timo Helkio <timolavi@mbnet.fi>
  for reporting a hangup when replaying a TS recording with subtitles activated
 
-Derek Kelly (user.vdr@gmail.com)
+Derek Kelly <user.vdr@gmail.com>
  for fixing handling the 'new' indicator in the recordings menu for TS recordings
  for reporting a problem with HD NTSC broadcasts that split frames over several payload
  units
@@ -2766,6 +2840,8 @@
  are no short names at all
  for reporting an incompatible change from DTV_DVBT2_PLP_ID to DTV_STREAM_ID in DVB API 5.8
  for reporting a missing template for DVBDIR in Make.config.template
+ for suggesting to add ARGSDIR to the ONEDIR section of Make.config.template
+ for suggesting to change the naming of "binary skip mode" to "adaptive skip mode"
 
 Marcel Unbehaun <frostworks@gmx.de>
  for adding cRecordingInfo::GetEvent()
@@ -2832,6 +2908,20 @@
  for fixing a typo in skins.h
  for fixing some #include statements in plugins to use <vdr/...> instead of "vdr/..."
  for reporting an invalid line in channels.conf.terr
+ for fixing handling '/' and '~' in recording file names in case DirectoryEncoding is
+ used
+ for making the LIRC remote control connect to the socket even if it doesn't yet exist
+ when VDR is started
+ for reporting a possible crash if the recordings list is updated externally while the
+ Recordings menu is open
+ for reporting a missing closing ')' in the help entry of the --vfat option
+ for making the Recordings menu able to be called with a cRecordingFilter, which allows
+ the caller to have it display only a certain subset of the recordings
+ for adding handling UTF-8 'umlaut' characters to cKbdRemote
+ for fixing learning keyboard remote control codes
+ for making VDR read command line options from *.conf files in /etc/vdr/conf.d
+ for adding a missing backslash to the help text of the SVDRP command MOVR
+ for fixing a memory leak in case of broken Extended Event Descriptors
 
 Alex Lasnier <alex@fepg.org>
  for adding tuning support for ATSC devices
@@ -2851,6 +2941,7 @@
  for reporting a possible memory leak in the "pictures" plugin
  for removing an obsolete local variable in dvbsdffosd.c
  for reporting a possible NULL pointer dereference in osddemo.c
+ for reporting some compiler warnings with Clang 3.4.1
 
 Radek Stastny <dedkus@gmail.com>
  for translating OSD texts to the Czech language
@@ -2859,7 +2950,7 @@
  for suggesting to add handling MPEG audio type "ISO/IEC 14496-3 Audio with LATM
  transport syntax"
 
-Christopher Reimer <reimer.christopher@freenet.de>
+Christopher Reimer <vdr@creimer.net>
  for reporting a problem with external Dolby Digital processing via the '-a' option
  in live mode and with TS recordings
  for contributing to a patch that implements FHS support
@@ -2869,6 +2960,10 @@
  for making plugin Makefiles use DESTDIR and the 'install' program
  for suggesting to make sure that plugins include the VDR header files from the actual
  VDR source directory when doing "make plugins"
+ for reverting the change from version 1.5.7 that made all logging go to LOG_ERR
+ for reporting a possible crash in the OSD demo
+ for adding support for systemd
+ for suggesting to replace VDR_CHARSET_OVERRIDE with a command line option
 
 Stefan Huskamp <coca_cola1@gmx.de>
  for suggesting to make entering characters via the number keys
@@ -2889,10 +2984,12 @@
 
 Dominik Strasser <dominik@die-strassers.de>
  for making a cRemote be removed from the Remotes list in case its initialization failed
+ for reporting a possible access to uninitialized data in cEIT::cEIT()
 
 Joerg Bornkessel <hd_brummy@gentoo.org>
  for adding LDFLAGS to the linker calls in the Makefiles
  for fixing font handling with fontconfig 2.9.0 or newer
+ for fixing "warning: invalid suffix on literal" with GCC 4.8 and C++11
 
 Andreas Oberritter <obi@opendreambox.org>
  for suggesting to retrieve the include path to the freetype2 header files
@@ -2905,6 +3002,7 @@
  for suggesting to reduce the thread and I/O priority cCuttingThread::Action()
  for reporting a problem with tuning timeouts when using SCR with multiple tuners
  for fixing the German translation of "VDR will shut down in %s minutes"
+ for adding support for "Satellite Channel Routing" (SCR) according to EN50607 ("JESS")
 
 Gerald Dachs <vdr@dachsweb.de>
  for reporting a problem with checking for minimum line length of 21 characters in
@@ -2946,6 +3044,7 @@
 Dave Pickles <dave@pickles.me.uk>
  for adding support for "content identifier descriptor" and "default authority
  descriptor" to 'libsi'
+ for reporting that old EPG events are not cleaned up in case no epg data file is given
 
 Holger Dengler <holger.dengler@gmx.de>
  for making the isnumber() function check the given pointer for NULL
@@ -2968,6 +3067,7 @@
 
 Chris Mayo <aklhfex@gmail.com>
  for reporting a problem with detecting frames on radio channels
+ for fixing the link to "svdrpsend (1)" in the vdr.1 man page
 
 Dominic Evans <oldmanuk@gmail.com>
  for making the SVDRP command LSTC accepts channel IDs
@@ -2994,6 +3094,11 @@
  for suggesting to increase the size of the TS buffer to 5MB and that of the Recorder
  buffer to 20MB to better handle HD recordings
  for fixing setting the video format in the dvbhdffdevice
+ for reporting a problem with setting the system time from the TDT in case devices
+ are tuned to the same transponder on different sources, and these broadcast different
+ time data
+ for reporting a problem with unjustified "video data stream broken" errors in case
+ the system time is changed while a recording is active
 
 Christian Ruppert <idl0r@gentoo.org>
  for some improvements to the Makefiles
@@ -3027,9 +3132,12 @@
 Christian Richter <cr@crichter.net>
  for extending the interface to the script that gets called for recordings, so that in
  the "edited" case it also provides the name of the original recording
+ for suggesting to add SDNOTIFY to Make.config.template
 
-Christian Kaiser <christian.kaiser@teleservice.com>
+Christian Kaiser <chr-kaiser@arcor.de>
  for adding DeleteEvent() to the EPG handler interface
+ for making the script given to VDR with the '-r' option also be called after the
+ recording process has actually started
 
 Dirk Heiser <dirk-vdr@gmx.de>
  for adding SetComponents() to the EPG handler interface
@@ -3072,6 +3180,12 @@
  for pointing out that FindHeader() can also be used in cMpeg2Fixer::AdjTref()
  for reporting a problem with detecting user inactivity in case the system time is
  changed after VDR has been started
+ for reporting that the change "Fixed some compiler warnings with Clang 3.4.1" caused
+ ci.c to no longer compile with older versions of gcc
+ for suggesting to make the "Select folder" menu add the folder names of all existing
+ recordings to any names that have been predefined in "folders.conf"
+ for adding an empty target to the Makefile to make sure the sub-make for libsi is
+ always called
 
 Peter M�nster <pmlists@free.fr>
  for fixing 'make install' to not overwrite existing configuration files
@@ -3090,6 +3204,7 @@
  recording is started
  for suggesting that floating point numbers presented to the user shall be displayed
  in the way defined by the current locale
+ for changing the German weekday names from "MonDieMitDonFreSamSon" to "Mo.Di.Mi.Do.Fr.Sa.So."
 
 Cedric Dewijs <cedric.dewijs@telfort.nl>
  for adding maximum SNR value for PCTV Systems PCTV 73ESE
@@ -3117,6 +3232,10 @@
  for reporting a problem with switching back to live viewing after replay in a setup
  with device bonding
  for reporting a problem with handling overlapping pending timers
+ for fixing the German translation of "Binary skip timeout (s)"
+ for reporting a bug in switching channels in the Schedule menu after going through
+ various Now and Schedule menus for different channels
+ for the "jumpingseconds" patch
 
 Marek Nazarko <mnazarko@gmail.com>
  for translating OSD texts to the Polish language
@@ -3135,3 +3254,149 @@
  for translating OSD texts to the Serbian language
  for adding maximum SNR and signal strength value for TechniSat SkyStar HD2
  for pointing out that the language file sr_SR.po should be renamed to sr_RS.po
+
+Stefan Braun <louis.braun@gmx.de>
+ for reporting an endless loop in cTextWrapper::Set() in case the given Width is smaller
+ than one character
+ for reporting an endless loop in the DrawEllipse() functions for very small ellipses
+ for suggesting to add the menu category mcRecordingEdit for marking menus that edit
+ recording properties
+ for suggesting to make cRecording::GetResume() public
+
+Jochen Dolze <vdr@dolze.de>
+ for changing cThread::SetIOPriority() from "best effort class" to "idle class" in order
+ to improve overall performance when an editing process is running
+
+Dominique Dumont <domi.dumont@free.fr>
+ for reporting a crash in the LCARS skin's main menu in case there is no current channel
+
+Seppo Ingalsuo <seppo.ingalsuo@iki.fi>
+ for a patch that was used as a base for implementing support for positioners
+
+Manfred V�lkel <mvoelkel@digitaldevices.de>
+ for suggesting to make all bonded devices (except for the master) turn off their LNB
+ power completely to avoid problems when receiving vertically polarized transponders
+ for adding support for "Satellite Channel Routing" (SCR) according to EN50607 ("JESS")
+
+Thomas Maass <mase@setho.org>
+ for reporting a difference in the internal sequence of actions when pressing the Blue
+ and the Back key, respectively, during replay
+
+Martin Prochnow <nordlicht@martins-kabuff.de>
+ for writing the "extrecmenu" plugin, which inspired the implementation of editing
+ recording properties
+
+Eike Edener <eike@edener.de>
+ for reporting a bug in writing group separators to channels.conf that contain a comma
+
+Harald Koenig <koenig@tat.physik.uni-tuebingen.de>
+ for making the function cRecordings::MBperMinute() only take into account recordings
+ with less than 5 seconds per megabyte, to filter out radio recordings
+
+Guido Cordaro <guido.cordaro@tiscali.it>
+ for adding maximum signal strength value for TechniSat SkyStar 2 DVB-S rev 2.3P
+
+Thomas Reufer <thomas@reufer.ch>
+ for making it clear that the Data parameter in cDevice::StillPicture() may point to a
+ series of packets, not just a single one
+ for suggesting to add an additional parameter named Forward to cDevice::TrickSpeed()
+ for suggesting to add a note to ePlayMode in device.h that VDR itself always uses
+ pmAudioVideo when replaying a recording
+ for fixing a possible crash in the LCARS skin
+ for implementing cOsd::DrawScaledBitmap()
+ for adding handling for DTS audio tracks to cPatPmtParser::ParsePmt()
+ for adding support for PGS subtitles
+ for adding cOsdProvider::OsdSizeChanged()
+ for suggesting to change the German translations if the texts related to "binary
+ skipping"
+ for suggesting to change the return value of cOsd::RenderPixmaps() from cPixmapMemory
+ to cPixmap
+
+Eike Sauer <EikeSauer@t-online.de>
+ for reporting a problem with channels that need more than 5 TS packets for detecting
+ frame borders
+ for reporting a problem in handling the frame detection buffer length
+ for suggesting to add a comment to cRecorder::Activate() about the need to call
+ Detach() in the destructor
+
+Christian Paulick <cpaulick@xeatre.tv>
+ for reporting a problem with frame detection in MPEG-2 streams that have "bottom fields"
+ or varying GOP structures
+
+Mariusz Bialonczyk <manio@skyboo.net>
+ for reporting a problem with live streaming of encrypted channels, when there are no
+ CA descriptors, yet, on initial tuning
+ for reporting that acquiring the CA descriptors takes way too long on transponders
+ with many PAT entries, and his help in debugging this
+
+Tony Houghton <h@realh.co.uk>
+ for suggesting to add LinkageTypePremiere to libsi/si.h and eit.c to avoid a compiler
+ warning with Clang 3.4.1
+ for suggesting to replace the NULL pointer assignment in ~cReceiver() to force a
+ segfault with a call to abort()
+
+Christian Winkler <winkler_chr@yahoo.de>
+ for reporting a problem with transfer mode on full featured DVB cards for encrypted
+ channels that have no audio pid
+
+Dietmar Spingler <d_spingler@gmx.de>
+ for reporting a problem that led to a fix in detaching receivers from devices in case
+ a CAM needs to receive the TS
+ for reporting a problem that led to a fix with EMM pids not being properly reset for
+ CAMs that need to receive the TS
+ for suggesting to add the channel name to log messages that reference a channel
+
+Stefan Schallenberg <infos@nafets.de>
+ for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement()
+ to the cVector class
+
+Claus Muus <email@clausmuus.de>
+ for adding the new parameters "Setup/Miscellaneous/Volume steps" and
+ ".../Volume linearize"
+
+Dieter Ferdinand <dieter.ferdinand@gmx.de>
+ for reporting a problem with jumping to an absolute position via the Red key in
+ case replay was paused
+ for reporting a problem with the system getting unresponsive when removing a huge
+ number of files in the thread that removes deleted recordings
+
+Jasmin Jessich <jasmin@anw.at>
+ for modifying the CAM API so that it is possible to implement CAMs that can be freely
+ assigned to any devices
+
+Martin Schirrmacher <schirrmie@gmail.com>
+ for suggesting to provide a way for skin plugins to get informed about the currently
+ used sort mode of a menu
+
+Clemens Brauers <vdr@admin-cb.de>
+ for modifying runvdr.template to improve compatibility with the "bash" and "dash" shells
+
+Stefan Herdler <herdler@gmx.de>
+ for fixing cMarks::GetNextBegin() and cMarks::GetNextEnd()
+ for reporting that pausing replay at the last editing mark actually paused one I-frame
+ too early
+ for reporting a bug in using the default sort mode in a video directory without a
+ ".sort" file
+
+Tobias Faust <tobias.faust@gmx.de>
+ for the original "jumpingseconds" patch
+
+Erik Oomen <oomen.e@gmail.com>
+ for translating OSD texts to the Dutch language
+
+Magnus Sirwi� <sirwio@hotmail.com>
+ for translating OSD texts to the Swedish language
+
+Albert Danis <a.danis@gmx.de>
+ for developing and improving several formulas used in controlling positioners
+ for translating OSD texts to the Hungarian language
+ for fixing the German translation of "Zap timeout"
+ for unifying the German translations of "StreamId" and "T2SystemId"
+ for improving the German translations of "EPG bugfix level"", "StreamId" and
+ "T2SystemId"
+
+Tomasz Maciej Nowak <tmn505@gmail.com>
+ for translating OSD texts to the Polish language
+
+Gabriel Bonich <gbonich@gmail.com>
+ for translating OSD texts to the Spanish language
diff -ruN vdr-2.0.0/Doxyfile vdr-2.2.0/Doxyfile
--- vdr-2.0.0/Doxyfile	2013-02-17 12:49:39.000000000 +0100
+++ vdr-2.2.0/Doxyfile	2013-12-28 12:15:55.000000000 +0100
@@ -1658,7 +1658,7 @@
 # the class node. If there are many fields or methods and many nodes the
 # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
 # threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
+# manageable. Set this to 0 for no limit. Note that the threshold may be
 # exceeded by 50% before the limit is enforced.
 
 UML_LIMIT_NUM_FIELDS   = 10
diff -ruN vdr-2.0.0/Doxyfile.filter vdr-2.2.0/Doxyfile.filter
--- vdr-2.0.0/Doxyfile.filter	2013-02-17 11:54:05.000000000 +0100
+++ vdr-2.2.0/Doxyfile.filter	2013-02-17 11:54:05.000000000 +0100
@@ -9,7 +9,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Doxyfile.filter 2.1 2013/02/17 10:54:05 kls Exp $
+# $Id: Doxyfile.filter 3.0 2013/02/17 10:54:05 kls Exp $
 
 $TAG = "///<";
 
diff -ruN vdr-2.0.0/HISTORY vdr-2.2.0/HISTORY
--- vdr-2.0.0/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/HISTORY	2015-02-19 10:17:46.000000000 +0100
@@ -7144,7 +7144,7 @@
   Christian Richter).
 - Added DeleteEvent() to the EPG handler interface, so that an EPG handler can trigger
   deleting of an event (thanks to Christian Kaiser).
-- Speeded up opening menus on systems with many (several thousands) of recordings, by
+- Speeded up opening menus on systems with many (several thousands of) recordings, by
   caching the information whether a recording is stored on the video directory file
   system within the cRecording data (based on a patch from Torsten Lang).
 
@@ -7328,7 +7328,7 @@
   + no longer generating an editing mark at the "end" of the edited recording (this
     was actually generated at the beginning of the last GOP, so that a subsequent
     edit would have cut off the last GOP)
-  + no longer generating any editing marks if the edited recording results on just
+  + no longer generating any editing marks if the edited recording results in just
     one single sequence
   + ignoring pairs of editing marks that are placed at exactly the same position of
     a recording when actually cutting the recording
@@ -7773,7 +7773,6 @@
   Dominic Evans).
 - Updated the default channels.conf file.
 
-
 2013-03-31: Version 2.0.0
 
 - Updated the Lithuanian OSD texts (thanks to Valdemaras Pipiras).
@@ -7782,3 +7781,817 @@
 - Fixed handling overlapping pending timers (reported by Matthias Senzel).
 - Bumped all version numbers to 2.0.0.
 - Official release.
+
+2013-04-11: Version 2.0.1
+
+- Fixed initializing cDevice::keepTracks.
+- Fixed an endless loop in cTextWrapper::Set() in case the given Width is smaller than
+  one character (reported by Stefan Braun).
+- Added definitions for older DVB API versions, back until 5.0 (based on a patch from
+  Udo Richter).
+- Fixed handling '/' and '~' in recording file names in case DirectoryEncoding is
+  used (thanks to Lars Hanisch).
+- Changed cThread::SetIOPriority() from "best effort class" to "idle class" in order to
+  improve overall performance when an editing process is running (thanks to Jochen
+  Dolze).
+
+2013-05-19: Version 2.0.2
+
+- Fixed multiple occurrences of the same directory in the recordings list in case there
+  are directories that only differ in non-alphanumeric characters (was broken by
+  "Fixed selecting the last replayed recording in the Recordings menu in case there
+  are folders and plain recordings with names that differ only in non-alphanumeric
+  characters" in version 1.7.36).
+- Fixed displaying the frame number when setting an editing mark (thanks to Thomas
+  G�nther).
+- Fixed no longer generating any editing marks if the edited recording results in just
+  one single sequence (reported by Halim Sahin).
+- Fixed an error message when parsing SCR values in diseqc.conf.
+- Fixed an unexpected RCS version tag in the newplugin script.
+- Fixed an endless loop in the DrawEllipse() functions for very small ellipses (reported
+  by Stefan Braun).
+- Fixed a crash in the LCARS skin's main menu in case there is no current channel
+  (reported by Dominique Dumont).
+
+2013-08-25: Version 2.1.1
+
+- Fixed initializing cDevice::keepTracks.
+- Fixed an endless loop in cTextWrapper::Set() in case the given Width is smaller than
+  one character (reported by Stefan Braun).
+- Removed all "modified since version 1.6" markers from PLUGINS.html.
+- Added definitions for older DVB API versions, back until 5.0 (based on a patch from
+  Udo Richter).
+- Changed cThread::SetIOPriority() from "best effort class" to "idle class" in order to
+  improve overall performance when an editing process is running (thanks to Jochen
+  Dolze).
+- Fixed handling '/' and '~' in recording file names in case DirectoryEncoding is
+  used (thanks to Lars Hanisch).
+- Changed the sign of the satellite position value in cSource to reflect the standard
+  of western values being negative. The new member function cSource::Position() can be
+  used to retrieve the orbital position of a satellite.
+- Fixed multiple occurrences of the same directory in the recordings list in case there
+  are directories that only differ in non-alphanumeric characters (was broken by
+  "Fixed selecting the last replayed recording in the Recordings menu in case there
+  are folders and plain recordings with names that differ only in non-alphanumeric
+  characters" in version 1.7.36).
+- Fixed displaying the frame number when setting an editing mark (thanks to Thomas
+  G�nther).
+- Fixed no longer generating any editing marks if the edited recording results in just
+  one single sequence (reported by Halim Sahin).
+- Fixed an error message when parsing SCR values in diseqc.conf.
+- Fixed an unexpected RCS version tag in the newplugin script.
+- Fixed an endless loop in the DrawEllipse() functions for very small ellipses (reported
+  by Stefan Braun).
+- Fixed a crash in the LCARS skin's main menu in case there is no current channel
+  (reported by Dominique Dumont).
+- Added basic support for positioners to control steerable satellite dishes (based on
+  a patch from Seppo Ingalsuo and Ales Jurik, which in turn used code from Thomas
+  Bergwinkl's Rotor plugin; with lots of help from Albert Danis).
+  + Supports GotoN (aka "DiSEqC 1.2") and GotoX (aka "USALS").
+  + The new DiSEqC command code 'P' can be used to instruct a positioner to move the
+    dish to the required satellite position. When a 'P' code is processed, further
+    execution of the remaining DiSEqC sequence (if any) is postponed until the positioner
+    has reached the new satellite position.
+  + The new special source value of "S360E" can be used in diseqc.conf to indicate that
+    an entry using a positioner can move the dish to any requested position within its
+    range. Think of it as "full circle".
+  + The devices a particular cDiseqc or cScr applies to are now stored directly in each
+    cDiseqc or cScr, respectively.
+  + A plugin can implement a custom positioner control (see PLUGINS.html, section "Positioners").
+  + The new function cSkinDisplayChannel::SetPositioner() can be implemented by skins to
+    show the user a progress display when the dish is being moved. The default implementation
+    calls SetMessage() with a string indicating the new position the dish is being moved to.
+    The LCARS skin shows a progress bar indicating the movement of the dish.
+  + The new parameters "Site latitude", "Site longitude", "Positioner speed", and
+    "Positioner swing" in the "Setup/LNB" menu can be used to configure the necessary
+    values for a steerable dish.
+  + The cDvbTuner now has a new status tsPositioning, in which it waits until a steerable
+    dish has reached its target position. Parsing SI data is paused until the target
+    position has been reached.
+- The LCARS skin now shows the source value of the current channel in its channel display.
+- Fixed asserting free disk space in the cutter.
+- No longer trying to delete old recordings in AssertFreeDiskSpace() if the given
+  Priority is less than 1.
+- Fixed handling LIRC events in case repeated events are lost.
+- Fixed a possible crash when shutting down VDR while subtitles are being displayed
+  (reported by Ville Skytt�).
+- cDevice::IsPrimaryDevice() now also checks whether the primary device actually has
+  a decoder and returns false otherwise. This should improve device allocation on
+  systems that are only used as a receiver and don't actually display anything.
+- Increased the value of MAXRETRIES to 20 to reduce the probability of disturbances
+  in transfer mode.
+- All bonded devices (except for the master) now turn off their LNB power completely
+  to avoid problems when receiving vertically polarized transponders (suggested by
+  Manfred V�lkel and Oliver Endriss).
+- Reverted the change from version 1.5.7 that made all logging go to LOG_ERR (thanks
+  to Christopher Reimer).
+- Added Begin/EndSegmentTransfer() to the EPG handler interface (thanks to J�rg Wendel).
+- The code for distributing recordings over several video directories is now
+  deprecated and disabled by default.
+  You can re-enable this feature by removing the comment sign ('//') from the beginning
+  of the line
+  //#define DEPRECATED_DISTRIBUTED_VIDEODIR // Code enclosed with this macro is ...
+  in the file videodir.c. Note, though, that this can only be a temporary workaround.
+  This feature will be completely removed in one of the next developer versions.
+  Distributing the video directory over several disks was a useful feature in times
+  when disks were still relatively small, but it also caused serious problems in case
+  one of the disks failed. Nowadays hard disks come in sizes measured in terabytes,
+  and tools like "mhddfs" can be used to combine several disks to form one large volume.
+  A recommended method for a relatively safe disk setup in a VDR system is to use two
+  1TB (or larger) disks and use them as a RAID-1 (mirrored). That way, if one disk
+  fails, you can replace it without data loss.
+
+2013-09-01: Version 2.0.3
+
+- Fixed asserting free disk space in the cutter.
+- No longer trying to delete old recordings in AssertFreeDiskSpace() if the given
+  Priority is less than 1.
+- Fixed handling LIRC events in case repeated events are lost.
+- Fixed a possible crash when shutting down VDR while subtitles are being displayed
+  (reported by Ville Skytt�).
+- cDevice::IsPrimaryDevice() now also checks whether the primary device actually has
+  a decoder and returns false otherwise. This should improve device allocation on
+  systems that are only used as a receiver and don't actually display anything.
+- Increased the value of MAXRETRIES to 20 to reduce the probability of disturbances
+  in transfer mode.
+- All bonded devices (except for the master) now turn off their LNB power completely
+  to avoid problems when receiving vertically polarized transponders (suggested by
+  Manfred V�lkel and Oliver Endriss).
+- Fixed cleaning up old EPG events in case no epg data file is given (reported by
+  Dave Pickles).
+
+2013-10-19: Version 2.1.2
+
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Fixed displaying DVB subtitles (thanks to Rolf Ahrenberg for helping to debug and
+  understand subtitle page refreshes):
+  + Fixed handling DVB subtitle fill region codes for 2 and 8 bpp.
+  + Fixed handling pages without an explicit END_OF_DISPLAY_SET_SEGMENT.
+    The FINISHPAGE_HACK is no longer necessary.
+  + Fixed handling "page refreshes". The data is now parsed and stored closer to the
+    DVB standard specs, introducing "object refs" and "region refs".
+  + The debug output now goes into an HTML file named dbg-log.htm and shows the actual
+    bitmaps (dbg-nnn.jpg) used to display the subtitles. That way it is much easier to
+    see what's actually going on.
+  + Fixed handling subtitles encoded as a string of characters (the very first
+    character was always skipped).
+- Fixed wrong initialization of Setup.PositionerSwing (reported by Arthur Konovalov).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Fixed cleaning up old EPG events in case no epg data file is given (reported by
+  Dave Pickles).
+- Unified the internal sequence of actions when pressing the Blue and the Back key,
+  respectively, during replay (reported by Thomas Maass).
+- The Yellow button in the main menu no longer acts as "Pause" if "Pause key handling"
+  is set to "do not pause live video" (suggested by Ulf Kiener).
+- The code for distributing recordings over several video directories has been
+  removed. VDR now by default assumes that the video directory is one big disk.
+  If you absolutely need to use several separate disks to store recordings, you can
+  write a plugin that uses the new cVideoDirectory API to implement the necessary
+  functionality (see PLUGINS.html, section "The video directory"). You can copy the
+  respective code from previous versions of videodir.c.
+  IMPORTANT NOTE: If you write a plugin that implements a distributed video directory,
+  =============== be sure to make cVideoDirectory::Rename() follow symbolic links!
+                  This functionality was never implemented in VDR and it therefore
+                  used a workaround in cutter.c. See the section marked with
+                  // XXX this can be removed once RenameVideoFile() follows symlinks
+                  in previous versions of cutter.c.
+  + CloseVideoFile() is obsolete and has been removed.
+  + The functions OpenVideoFile(), RenameVideoFile(), RemoveVideoFile(), VideoFileSpaceAvailable(),
+    VideoDiskSpace(), RemoveEmptyVideoDirectories(), IsOnVideoDirectoryFileSystem() and
+    PrefixVideoFileName() are now static members of cVideoDirectory and need to be called
+    with the proper prefix.
+  + The name of the video directory is now available through cVideoDirectory::Name().
+- Added renaming and moving recordings and folders, editing a recording's priority and
+  lifetime, and queueing cutting jobs (inspired by the "extrecmenu" plugin from Martin
+  Prochnow).
+  + The "Recording info" menu now has a new Blue button named "Edit", which opens a
+    dialog in which several properties of the selected recording can be changed. It can
+    be renamed or moved into another folder and its priority and lifetime can be
+    modified (inspired by the "extrecmenu" plugin from Martin Prochnow).
+    The new blue "Edit" button in the "Recordings" menu opens a dialog in which a folder
+    can be renamed or moved. See MANUAL, section "Managing folders".
+  + In the "Edit recording" menu the Yellow button ("Delete marks") allows you to delete
+    all editing marks of the selected recording.
+  + cCutter is no longer a static class. Cutting requests should now be invoked by
+    calling RecordingsHandler.Add(ruCut, FileName). See the new cRecordingsHandler
+    class in recording.h.
+  + Cutting jobs are now placed in a queue (together with any move or copy jobs) and
+    are processed one by one.
+  + The new SVDRP command RENR can be used to rename a recording (suggested by Rolf
+    Ahrenberg).
+  + Note that in several places in the source code a "copy" operation is mentioned,
+    however there is no user interface for this, yet.
+- Changed some variable names in positioner.c to match the names used in the page with
+  the explanation on vdr-portal.de (suggested by Albert Danis).
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- Fixed writing group separators to channels.conf that contain a comma (reported by
+  Eike Edener).
+- Now also checking the source (in addition to the transponder) when setting the
+  system time from the TDT, which avoids problems in case devices are tuned to the
+  same transponder on different sources, and these broadcast different time data
+  (reported by Torsten Lang).
+- Changed cRecorder::Action() to use cTimeMs instead of time() to avoid problems with
+  unjustified "video data stream broken" errors in case the system time is changed
+  while a recording is active (reported by Torsten Lang).
+- Revised the section on "Learning the remote control keys" in the INSTALL file to
+  avoid the impression that there actually is a default remote.conf file, and to
+  not use any alphabetic keys for special functions, so that they remain available
+  for textual input.
+- The function cRecordings::MBperMinute() now only takes into account recordings with
+  less than 5 seconds per megabyte, in an attempt to filter out radio recordings
+  (thanks to Harald Koenig). The result of this function was way off any realistic
+  value in case there are many radio recordings in the video directory.
+- Added maximum signal strength value for TechniSat SkyStar 2 DVB-S rev 2.3P (thanks
+  to Guido Cordaro).
+- Fixed an inconsistent behavior between opening the Recordings menu manually via the
+  main menu and by pressing the Recordings key. In the latter case it automatically
+  opened all sub folders to position the cursor to the last replayed recording, which
+  is unexpected at this point (reported by Helmut Auer). You can still navigate to
+  the last replayed recording (if any) by pressing Ok repeatedly in the Recordings
+  menu.
+
+2013-10-23: Version 2.0.4
+
+- Unified the internal sequence of actions when pressing the Blue and the Back key,
+  respectively, during replay (reported by Thomas Maass).
+- The Yellow button in the main menu no longer acts as "Pause" if "Pause key handling"
+  is set to "do not pause live video" (suggested by Ulf Kiener).
+- Fixed writing group separators to channels.conf that contain a comma (reported by
+  Eike Edener).
+- Now also checking the source (in addition to the transponder) when setting the
+  system time from the TDT, which avoids problems in case devices are tuned to the
+  same transponder on different sources, and these broadcast different time data
+  (reported by Torsten Lang).
+- Changed cRecorder::Action() to use cTimeMs instead of time() to avoid problems with
+  unjustified "video data stream broken" errors in case the system time is changed
+  while a recording is active (reported by Torsten Lang).
+- Fixed an inconsistent behavior between opening the Recordings menu manually via the
+  main menu and by pressing the Recordings key. In the latter case it automatically
+  opened all sub folders to position the cursor to the last replayed recording, which
+  is unexpected at this point (reported by Helmut Auer). You can still navigate to
+  the last replayed recording (if any) by pressing Ok repeatedly in the Recordings
+  menu.
+
+2014-01-05: Version 2.1.3
+
+- Changed the return value of cPositioner::HorizonLongitude() to 0 in case the
+  latitude of the antenna location is beyond +/-81 degrees.
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Fixed some compiler warnings with gcc-4.6.3 (thanks to Rolf Ahrenberg).
+- Changed the name of the SVDRP command RENR to MOVR (suggested by Rolf Ahrenberg).
+- When cutting a recording it is now checked whether there is already an edited
+  version of this recording (with the same name, but starting with '%'), and the
+  user is prompted for confirmation to overwrite it (suggested by Rolf Ahrenberg).
+- Revoked "Added maximum signal strength value for TechniSat SkyStar 2 DVB-S rev 2.3P"
+  because it broke things for the "TechniSat AirStar 2" DVB-T card.
+- The LIRC remote control now connects to the socket even if it doesn't yet exist when
+  VDR is started (thanks to Lars Hanisch).
+- Changed the absolute latitude limit for visible satellites to 81.2 degrees.
+- Added code for parsing LCN and AVC descriptors to libsi (thanks to Rolf Ahrenberg).
+- In the "Select folder" menu pressing Ok now selects the folder, even if this is a
+  folder that contains sub folders (marked with "..."). To open such a folder you
+  can press the Red key.
+- Fixed a possible access to uninitialized data in cEIT::cEIT() (reported by Dominik
+  Strasser).
+- The new menu category mcRecordingEdit is now used to mark menus that edit recording
+  properties (suggested by Stefan Braun).
+- Changes in the teletext PID no longer cause retuning (and thus interrupting a
+  recording).
+- Removed '_' from the FileNameChars and CharMap translations in uk_UA.po.
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- Fixed a missing initialization in the c'tor of cSkinLCARSDisplayChannel (thanks to
+  Marko M�kel�).
+- Simplified some conditional expressions in skinlcars.c and skinsttng.c (suggested
+  by Marko M�kel�).
+- Fixed uninitialized item area coordinates in cSkinLCARSDisplayMenu (reported by
+  Marko M�kel�).
+- Fixed a possible crash if the recordings list is updated externally while the
+  Recordings menu is open (reported by Lars Hanisch).
+- Added a missing closing ')' in the help and man page entry of the --vfat option
+  (reported by Lars Hanisch).
+- Fixed setting the name of the video directory to avoid a crash when using --genindex,
+  and also to use the correct directory with --edit (the latter reported by Marko
+  M�kel�).
+- The Recordings menu can now be called with a cRecordingFilter, which allows the
+  caller to have it display only a certain subset of the recordings (thanks to Lars
+  Hanisch).
+- Added handling UTF-8 'umlaut' characters to cKbdRemote (thanks to Lars Hanisch).
+- Made it clear that the Data parameter in cDevice::StillPicture() may point to a
+  series of packets, not just a single one (thanks to Thomas Reufer).
+- cDevice::TrickSpeed() now has an additional parameter named Forward, which indicates
+  the direction in which replay is being done (suggested by Thomas Reufer). This
+  information may be necessary for some output devices in order to properly implement
+  trick modes. Authors of plugins that implement output devices will need to add this
+  parameter to their derived cDevice class, regardless of whether they will make use
+  of it or not.
+- Added a note to ePlayMode in device.h that VDR itself always uses pmAudioVideo when
+  replaying a recording (suggested by Thomas Reufer).
+- Fixed some spellings in positioner.h and Doxyfile (thanks to Ville Skytt�).
+- Changed '%a' to the POSIX compliant '%m' in all scanf() calls (thanks to Ville
+  Skytt�).
+- The new function cCamSlot::Decrypt() can be used by derived classes to implement a
+  CAM slot that can be freely assigned to any device, without being directly inserted
+  into the full TS data stream in hardware. A derived class that implements Decrypt()
+  will also need to set the new parameter ReceiveCaPids in the call to the cCamSlot
+  base class constructor to true, in order to receive the CA pid TS packets that
+  contain data necessary for decrypting.
+- Many member functions of cCamSlot have been made virtual to allow for easier
+  implementation of derived classes.
+- cTSBuffer now provides the number of available bytes in its Get() function.
+- cDvbDevice::GetTSPacket() now calls CamSlot()->Decrypt() in order to allow CAM slots
+  that can be freely assigned to any device access to the TS data stream.
+- Added a check to avoid a possible NULL pointer dereference in cCiSession::SendData()
+  (reported by Ville Skytt�).
+- Deleted a superfluous assignment in cPipe::Open() (reported by Ville Skytt�).
+- The script given to VDR with the '-r' option is now also called after the recording
+  process has actually started (thanks to Christian Kaiser).
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles (thanks to Ville Skytt�).
+  Plugin authors may want to apply the following change to their Makefile:
+  -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+  +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
+- Eliminated MAXDVBDEVICES (suggested by Oliver Endriss).
+- Channels that are no longer contained in the current SDT of a transponder are now
+  marked with the keyword OBSOLETE in their name and provider fields. That way you can
+  identify obsolete channels when you switch to them, and you can get the complete
+  overview of all obsolete channels by sorting the Channels list by provider (by
+  pressing the 0 key twice). Automatic deletion of obsolete channels may follow later.
+
+2014-01-07: Version 2.0.5
+
+- The LIRC remote control now connects to the socket even if it doesn't yet exist when
+  VDR is started (thanks to Lars Hanisch).
+- Fixed a missing initialization in the c'tor of cSkinLCARSDisplayChannel (thanks to
+  Marko M�kel�).
+- Fixed uninitialized item area coordinates in cSkinLCARSDisplayMenu (reported by
+  Marko M�kel�).
+- Fixed a possible crash if the recordings list is updated externally while the
+  Recordings menu is open (reported by Lars Hanisch).
+- Added a missing closing ')' in the help and man page entry of the --vfat option
+  (reported by Lars Hanisch).
+- Fixed setting the name of the video directory to avoid a crash when using --genindex,
+  and also to use the correct directory with --edit (the latter reported by Marko
+  M�kel�).
+
+2014-01-26: Version 2.1.4
+
+- Updated 'sources.conf' (thanks to Antti Hartikainen).
+- cFont::CreateFont() now returns a dummy font in case there are no fonts installed.
+  This prevents a crash with the LCARS skin on a system that has no fonts.
+- Improved locking for CAM slots and made the pure functions of cCiAdapter have
+  default implementations, to fix a possible crash with CI adapters and CAM slots
+  that are implemented in a plugin.
+- Added logging the supported system ids of a CAM.
+- Increased MIN_TS_PACKETS_FOR_FRAME_DETECTOR to 10 in order to be able to record
+  channels that need more than 5 TS packets for detecting frame borders (reported by
+  Eike Sauer).
+- Fixed deleting the source recording after moving it to a different volume (reported
+  by Christoph Haubrich).
+- Now waiting explicitly until all CAM slots are ready before switching to the
+  initial channel when VDR is started. This is necessary in case CI adapters are
+  used that are not physically connected to a dedicated device. The respective checks
+  in cDvbDevice have been removed to avoid redundancy.
+- Fixed detecting frame borders in MPEG-2 streams that have "bottom fields" or varying
+  GOP structures (reported by Christian Paulick, with help from Helmut Auer).
+- Now unassigning CAMs from their devices when they are no longer used.
+- Now making sure the primary device goes into transfer mode for live viewing if the
+  CAM wants to receive the TS data.
+- Fixed a wrong alignment in cCiDateTime::SendDateTime().
+- Since the new cRecordingsHandler that was introduced in version 2.1.2 not only
+  handles "cutting", but also "moving" and "copying" recordings, the German word
+  "Schnitt" has been replaced with the more generic "Bearbeitung", which covers all
+  three variations of "editing" a recording (suggested by Christoph Haubrich).
+  Maintainers of translations for other languages may want to change their *.po files
+  accordingly.
+- The new function cStatus::ChannelChange() can be implemented by plugins to be
+  informed about changes to the parameters of a channel that may require a retune.
+  This may, for instance, be useful for plugins that implement live streaming, so that
+  they can react on changes to a channel's PIDs or CA descriptors (problem reported
+  by Mariusz Bialonczyk).
+- Fixed a superfluous call to the skin's SetRecording() function after renaming a
+  recording (reported by Christoph Haubrich).
+
+2014-02-23: Version 2.1.5
+
+- Now checking whether the primary device actually has a decoder before retuning the
+  current channel after a change in its parameters. This fixes broken recordings on
+  the primary device on "headless" systems.
+- Increased MIN_TS_PACKETS_FOR_FRAME_DETECTOR to 100 and introduced counting the number
+  of actual video TS packets in cTsPayload in order to be able to record channels that
+  sometimes need even more than 10 TS packets for detecting frame borders (reported by
+  Oliver Endriss).
+- Fixed sorting recordings by time in the Recordings menu if "Setup/OSD/Recording
+  directories" is set to "no".
+- Fixed clearing non-editable members in the channel editor (thanks to Rolf Ahrenberg).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Further clarified the semantics of cCamSlot::Decrypt().
+- Fixed flickering if subtitles are active while the OSD demo is running.
+- Fixed numbering frames. Previously they were numbered starting from 1, while it
+  is apparently standard to number them from 0. Any existing recordings with editing
+  marks (which will now be off by one) can still be cut with all VDR versions from
+  1.7.32, because these will automatically adjust editing marks to I-frames.
+  Users of stable releases shouldn't notice any problems.
+- Fixed a possible crash in the OSD demo (reported by Christopher Reimer).
+- Fixed some compiler warnings with Clang 3.4.1 (reported by Paul Menzel).
+- Added LinkageTypePremiere to libsi/si.h and eit.c to avoid a compiler warning with
+  Clang 3.4.1 (suggested by Tony Houghten).
+- Replaced the NULL pointer assignment in ~cReceiver() to force a segfault with
+  a call to abort() (suggested by Tony Houghten).
+- Fixed learning keyboard remote control codes (thanks to Lars Hanisch).
+- Improved PAT/PMT scanning to speed up initial tuning to encrypted channels on
+  transponders with many PAT entries (reported by Mariusz Bialonczyk).
+- Fixed the replay progress display for very long recordings.
+- Fixed detecting broken video data streams when recording.
+- Fixed handling frame detection buffer length (reported by Eike Sauer).
+
+2014-03-16: Version 2.1.6
+
+- Revoked "Fixed some compiler warnings with Clang 3.4.1" from ci.c, because this
+  did not compile with older versions of gcc (thanks to S�ren Moch).
+- Fixed keeping the current position in the Recordings menu if a recording was
+  deleted in a sub folder.
+- Fixed handling transfer mode on full featured DVB cards for encrypted channels
+  that have no audio pid (reported by Christian Winkler).
+- Fixed a possible endless loop in cH264Parser::GetGolombUe(), which caused recordings
+  on some HD channels to get stuck and resulted in buffer overflows.
+- Fixed handling PAT packets when detecting frames, so that they can be properly
+  taken into account when regenerating the index of a recording.
+- Fixed adding new source types in case they are already registered (reported by Rolf
+  Ahrenberg).
+- Removed an unnecessary assignment from cMenuRecordings::~cMenuRecordings().
+- The Recordings menu now remembers the last recording the cursor was positioned on,
+  independent of the last replayed recording. When a replay ends, however, the cursor
+  will initially be positioned to the last replayed recording again when the menu
+  is opened.
+- Updated the Finnish OSD texts (thanks to Antti Hartikainen).
+- Fixed drawing the live indicator in the LCARS skin in case there are no devices.
+- When checking for obsolete channels, those with an RID that is not 0 are now
+  ignored (suggested by Oliver Endriss).
+- The SDT is now only parsed *after* the NIT has been read, and it explicitly uses
+  the source value derived from the NIT. This should prevent new channels from being
+  created with the wrong source.
+- Added a log message in case a receiver is detached from its device because the
+  assigned CAM can't decrypt the channel.
+- Refactored setup parameter handling for output devices:
+  + The function cDevice::GetVideoSystem() has been deprecated and will be removed
+    in a future version. In order to check whether a particular plugin needs to be
+    modified if this function is removed, you can comment out the line
+    #define DEPRECATED_VIDEOSYSTEM
+    in device.h.
+  + Handling the "video (display) format" (things like 16:9, 4:3, pan&scan, letterbox
+    etc) shall now be done by the individual output devices, because the types and
+    numbers of parameters are too device specific. The Setup/DVB parameters
+    "Video format" and "Video display format" are still there for now and can be used
+    by SD devices. HD devices, however, shall not use these parameters (any more),
+    but rather implement their own setup menu with the necessary parameters for
+    controlling output.
+  + The dvbhdffdevice plugin has been modified accordingly.
+  + Made it clear that cDevice::SetDigitalAudioDevice() merely tells the output device
+    that the current audio track is Dolby Digital. This function was only used by the
+    original "full featured" DVB cards - do not use it for new developments!
+    If an output device has several ways of replaying audio (like HDMI or analog jack)
+    it shall implement the proper options in its plugin's SetupMenu() function.
+- Added support for "Pilot", "T2-System-Id" and "SISO/MISO" parameters (thanks to
+  Rolf Ahrenberg).
+- Now initializing the isOnVideoDirectoryFileSystem member of cRecording when
+  scanning the video directory, so that it won't cause a delay when opening the menu
+  on a system with a large number of recordings.
+- Now resetting the isOnVideoDirectoryFileSystem member of a cRecording to -1 after
+  renaming it, so that it will be re-checked upon the next call to
+  IsOnVideoDirectoryFileSystem().
+- Added support for systemd (thanks to Christopher Reimer). To activate this you
+  need to add "SDNOTIFY=1" to the 'make' call.
+
+2014-03-22: Version 2.0.6
+
+- Updated 'sources.conf' (thanks to Antti Hartikainen).
+- cFont::CreateFont() now returns a dummy font in case there are no fonts installed.
+  This prevents a crash with the LCARS skin on a system that has no fonts.
+- Fixed detecting frame borders in MPEG-2 streams that have "bottom fields" or varying
+  GOP structures (reported by Christian Paulick, with help from Helmut Auer).
+- Fixed a wrong alignment in cCiDateTime::SendDateTime().
+- Now checking whether the primary device actually has a decoder before retuning the
+  current channel after a change in its parameters. This fixes broken recordings on
+  the primary device on "headless" systems.
+- Increased MIN_TS_PACKETS_FOR_FRAME_DETECTOR to 100 and introduced counting the number
+  of actual video TS packets in cTsPayload in order to be able to record channels that
+  sometimes need even more than 10 TS packets for detecting frame borders (reported by
+  Eike Sauer and Oliver Endriss).
+- Fixed sorting recordings by time in the Recordings menu if "Setup/OSD/Recording
+  directories" is set to "no".
+- Fixed clearing non-editable members in the channel editor (thanks to Rolf Ahrenberg).
+- Fixed flickering if subtitles are active while the OSD demo is running.
+- Fixed a possible crash in the OSD demo (reported by Christopher Reimer).
+- Fixed learning keyboard remote control codes (thanks to Lars Hanisch).
+- Fixed the replay progress display for very long recordings.
+- Improved PAT/PMT scanning to speed up initial tuning to encrypted channels on
+  transponders with many PAT entries (reported by Mariusz Bialonczyk).
+- Fixed detecting broken video data streams when recording.
+- Fixed handling frame detection buffer length (reported by Eike Sauer).
+- Fixed keeping the current position in the Recordings menu if a recording was
+  deleted in a sub folder.
+- Fixed handling transfer mode on full featured DVB cards for encrypted channels
+  that have no audio pid (reported by Christian Winkler).
+- Fixed a possible endless loop in cH264Parser::GetGolombUe(), which caused recordings
+  on some HD channels to get stuck and resulted in buffer overflows.
+- Fixed handling PAT packets when detecting frames, so that they can be properly
+  taken into account when regenerating the index of a recording.
+- Fixed adding new source types in case they are already registered (reported by Rolf
+  Ahrenberg).
+- Fixed drawing the live indicator in the LCARS skin in case there are no devices.
+- The SDT is now only parsed *after* the NIT has been read, and it explicitly uses
+  the source value derived from the NIT. This should prevent new channels from being
+  created with the wrong source.
+- Now initializing the isOnVideoDirectoryFileSystem member of cRecording when
+  scanning the video directory, so that it won't cause a delay when opening the menu
+  on a system with a large number of recordings.
+- The APIVERSION has been increased to 2.0.6 due to the changes to pat.h, sdt.h and
+  the functional modification to cFont::CreateFont().
+
+2015-01-18: Version 2.1.7
+
+- No longer logging an error message in DirSizeMB() if the given directory doesn't
+  exist. This avoids lots of log entries in case several VDRs use the same video
+  directory and one of them has already physically removed a recording directory,
+  while the others still have it in their list of deleted recordings.
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- A cCamSlot that has WantsTsData set to true in its constructor now also gets
+  the CAT and EMM PIDs data.
+- Fixed a possible division by zero in frame rate detection.
+- VDR now reads command line options from *.conf files in /etc/vdr/conf.d (thanks
+  to Lars Hanisch). See vdr.1 and vdr.5 for details.
+- Fixed a possible crash in the LCARS skin (thanks to Thomas Reufer).
+- Updated the dvbhddevice plugin source.
+- Fixed a bug in the Makefile when installing plugins with LCLBLD=1 (thanks to
+  Stefan Huelswitt).
+- The pid of the PMT in which the CA descriptors of a given channel are broadcast
+  is now stored together with the CA descriptors and can be retrieved by calling
+  GetPmtPid() (this information is only required to receive encrypted channels
+  with the OctopusNet receiver via the 'satip' plugin).
+- Channels that are not listed in the SDT are now only marked as OBSOLETE if
+  "Setup/DVB/Update channels" is set to a value other than "no" or "PIDs only".
+- Fixed multiple OBSOLETE marks in channels that are not listed in the SDT in case
+  "Setup/Miscellaneous/Show channel names with source" is set to "yes".
+- The new function cOsd::DrawScaledBitmap() is now used for drawing subtitles.
+  This function can be reimplemented by high level OSDs which may be able to do
+  the scaling in hardware or otherwise more efficiently (thanks to Thomas Reufer).
+- Fixed detaching receivers from devices in case a CAM needs to receive the TS
+  (reported by Dietmar Spingler).
+- Fixed resetting the receiver for EMM pids for CAMs that need to receive the TS
+  (reported by Dietmar Spingler).
+- Fixed (well, actually worked around) a problem with subtitles not being displayed
+  because the broadcaster doesn't set the data's version numbers as required by the
+  DVB standard (thanks to Rolf Ahrenberg).
+- Fixed support for systemd (thanks to Christopher Reimer).
+- Added a missing backslash to the help text of the SVDRP command MOVR (thanks to
+  Lars Hanisch).
+- Added subsystem id support for DVB devices connected via USB (thanks to Jose
+  Alberto Reguero).
+- Added the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement()
+  to the cVector class (thanks to Stefan Schallenberg).
+- Fixed a possible out-of-bounds access in cVector::Remove().
+- Added functions to set and retrieve the priority of a cReceiver (suggested by
+  Frank Schmirler).
+- Added the new parameters "Setup/Miscellaneous/Volume steps" and
+  ".../Volume linearize" (thanks to Claus Muus). See the MANUAL for details.
+- Fixed jumping to an absolute position via the Red key in case replay was paused
+  (reported by Dieter Ferdinand).
+- Changed the German weekday names from "MonDieMitDonFreSamSon" to
+  "Mo.Di.Mi.Do.Fr.Sa.So." (thanks to Stefan Blochberger).
+- Now handling CAT sections that consist of more than one TS packet.
+- Added handling for DTS audio tracks to cPatPmtParser::ParsePmt() (thanks to
+  Thomas Reufer).
+- Added support for PGS subtitles (thanks to Thomas Reufer).
+- Use of the function cOsd::GetBitmap() outside of derived classes is now deprecated,
+  and it may be made 'protected' in a future version, since it doesn't work with
+  TrueColor OSDs. Plugin authors may want to modify their code so that it
+  works without this function.
+- Modified the descriptions of several threads, so that the important information
+  (like device or frontend numbers) is within the first 15 characters of the
+  string, because only these are displayed in thread listings. Plugin authors may
+  want to do the same.
+- Added the channel name to log messages that reference a channel (suggested by
+  Dietmar Spingler).
+- Modified the CAM API so that it is possible to implement CAMs that can be freely
+  assigned to any devices (thanks to Jasmin Jessich).
+- Plugins can now implement the function SetMenuSortMode() in their skin objects
+  derived from cSkinDisplayMenu, to get informed about the currently used sort
+  mode, if applicable (suggested by Martin Schirrmacher).
+- Added cOsdProvider::OsdSizeChanged(), which plugins that implement an output device
+  can call to signal a change in the OSD that requires a redraw of the currently
+  displayed object (thanks to Thomas Reufer).
+- Added a comment to cRecorder::Activate() about the need to call Detach() in the
+  destructor (suggested by Eike Sauer).
+- Now returning from removing deleted recordings after at most 10 seconds, or if the
+  user presses a remote control key, to keep the system from getting unresponsive
+  when removing a huge number of files (reported by Dieter Ferdinand).
+- Fixed generating the index file of an existing recording in case at the end of a TS file
+  there is less data in the buffer than needed by the frame detector. In such a case
+  it was possible that frames were missed, and there was most likely a distortion
+  when replaying that part of a recording. This is mostly a problem for recordings that
+  consist of more than one *.ts file. Single file recordings could only lose some
+  frames at their very end, which probably doesn't matter. At any rate, if you have
+  generated an index file with VDR version 2.0.6, 2.1.5 or 2.1.6, you may want to
+  do so again with this version to make sure the index is OK.
+- Added the new command line option --updindex, which can be used to update an
+  incomplete index of a recording (based on a patch from Helmut Auer).
+
+2015-01-20: Version 2.0.7
+
+- Fixed a possible division by zero in frame rate detection.
+- Fixed a bug in the Makefile when installing plugins with LCLBLD=1 (thanks to
+  Stefan Huelswitt).
+- Fixed jumping to an absolute position via the Red key in case replay was paused
+  (reported by Dieter Ferdinand).
+- Now returning from removing deleted recordings after at most 10 seconds, or if the
+  user presses a remote control key, to keep the system from getting unresponsive
+  when removing a huge number of files (reported by Dieter Ferdinand).
+- Fixed generating the index file of an existing recording in case at the end of a TS file
+  there is less data in the buffer than needed by the frame detector. In such a case
+  it was possible that frames were missed, and there was most likely a distortion
+  when replaying that part of a recording. This is mostly a problem for recordings that
+  consist of more than one *.ts file. Single file recordings could only lose some
+  frames at their very end, which probably doesn't matter. At any rate, if you have
+  generated an index file with VDR version 2.0.6 you may want to do so again with this
+  version to make sure the index is OK.
+
+2015-02-01: Version 2.1.8
+
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- Fixed "warning: invalid suffix on literal" with GCC 4.8 and C++11 (thanks to Joerg
+  Bornkessel).
+- Fixed the link to "svdrpsend (1)" in the vdr.1 man page (thanks to Chris Mayo).
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Updated the Romanian OSD texts (thanks to Lucian Muresan).
+- Added functionality based on the "jumpplay" patch from Torsten Kunkel and Thomas
+  G�nther:
+  + The new option "Setup/Replay/Pause replay when jumping to a mark" can be used to
+    turn off pausing replay when jumping to an editing mark with the '9' key.
+  + The new option "Setup/Replay/Skip edited parts" can be used to automatically skip
+    the edited parts of a recording during replay, without the need to actually cut
+    the recording.
+  + The new option "Setup/Replay/Pause replay at last mark" can be used to make replay
+    go into Pause mode when it has reached the last "end" mark.
+  + The '8' key for testing an edited sequence now also jumps to the next *end*
+    mark if "Setup/Replay/Skip edited parts" is active. This allows for testing edits
+    in recordings that have actually been cut, as well as recordings that have not
+    been cut, in case "Skip edited parts" is enabled.
+- Added support for "Satellite Channel Routing" (SCR) according to EN50607, also
+  known as "JESS" (thanks to Manfred V�lkel and Frank Neumann).
+- The keys '1' and '3' can now be used in replay mode to position an editing mark
+  in "binary" mode (based on a patch from Rolf Ahrenberg, with modifications by Helmut
+  Auer). See MANUAL, section "Editing a Recording".
+- The Yellow button in the "Setup/CAM" menu can now be used to put the selected
+  CAM into a mode where it remains assigned to a device that is tuned to the current
+  channel until the smart card it contains is activated and the CAM thus starts to
+  descramble (see MANUAL, section "Setup/CAM" for details).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Added ARGSDIR to the ONEDIR section of Make.config.template (suggested by Derek
+  Kelly).
+- Made cRecording::GetResume() public (suggested by Stefan Braun).
+- Fixed setting the read index in cDvbPlayer::Goto() in case Still is false.
+- The function cDvbPlayer::Goto() now automatically calls Play() if Still is false.
+- Added support for LCN (Logical Channel Numbers), which plugins may use to sort
+  channels (thanks to Rolf Ahrenberg).
+
+2015-02-08: Version 2.1.9
+
+- Fixed a memory leak in case of broken Extended Event Descriptors (thanks to Lars
+  Hanisch).
+- Fixed the German translation of "Binary skip timeout (s)" (thanks to Matthias
+  Senzel).
+- Fixed the German translation of "VDR will shut down later - press Power to force".
+- Fixed the Finnish translation of "Binary skip timeout (s)" (thanks to Rolf
+  Ahrenberg).
+- Updated the Lithuanian OSD texts (thanks to Valdemaras Pipiras).
+- Added SDNOTIFY to Make.config.template (suggested by Christian Richter). Also
+  added NO_KBD and BIDI.
+- Added code from the "jumpplay" patch that makes the recording still be considered
+  unviewed when stopping replay within RESUMEBACKUP seconds of the first mark.
+- The new option "Setup/Replay/Alternate behavior for adaptive skipping" can be used
+  to make adaptive skipping only halve the skip distance when the direction changes.
+  That way you can reach the desired point in a recording even if you make one too
+  many skips in a certain direction (see MANUAL for details).
+- Fixed cCamSlot::Assign(), so that it actually ignores the value of Query if Device
+  is NULL (as described in the header file).
+- Added a missing VDRDIR="$(CWD)" to the clean-plugins target of the Makefile, to
+  avoid error messages regarding the missing vdr.pc file.
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Updated the Ukrainian OSD texts (thanks to Yarema Aka Knedlyk).
+- Updated the Romanian OSD texts (thanks to Lucian Muresan).
+- Updated the Hungarian OSD texts (thanks to Istv�n F�ley).
+- Fixed switching channels in the Schedule menu after going through various Now and
+  Schedule menus for different channels (reported by Matthias Senzel).
+- Fixed setting the Blue button in the Schedule/Now/Next menus, so that it only shows
+  "Switch" if the selected event is on a different channel.
+- Added "NORDIG" to the list of "DVB/Standard compliance" options and using it to
+  restrict the LCN (Logical Channel Numbers) parsing to networks that actually use
+  this non-standard feature (thanks to Rolf Ahrenberg).
+- In the "Edit recording" menu the '0' key can now be used on the "Name:" field to
+  remove the name of the recording and replace it with the last element of the
+  recording's folder path name (suggested by Christoph Haubrich). See MANUAL, section
+  "Managing folders" for details.
+- Updated the Italian OSD texts (thanks to Nino Gerbino).
+- The "Select folder" menu now adds the folder names of all existing recordings to
+  any names that have been predefined in "folders.conf" (suggested by S�ren Moch).
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- Fixed the German translations of "latitude" and "longitude" (they were swapped).
+- Modified runvdr.template to improve compatibility with the "bash" and "dash" shells
+  (thanks to Clemens Brauers).
+- Changed the German translations if the texts related to "binary skipping" (based
+  on a suggestion by Thomas Reufer).
+- Updated sources.conf to reflect the fact that Astra 4A and SES5 are actually in
+  two separate positions (thanks to Arthur Konovalov).
+- Fixed cMarks::GetNextBegin() and cMarks::GetNextEnd() (thanks to Stefan Herdler).
+  The behavior of these two functions is now exacly as described in the header file.
+  Editing marks that are placed at exactly the same offset in a recording are now
+  preserved in the cutting process.
+- Changed the naming of "binary skip mode" to "adaptive skip mode" (suggested by
+  Rolf Ahrenberg and Derek Kelly).
+- cDvbPlayer and cReplayControl now use the same list of editing marks. This avoids
+  inconsistent behavior with the "Skip edited parts" or "Pause replay at last mark"
+  functions when the editing marks are manipulated during replay.
+- Fixed setting an empty recording name or folder to a blank in the "Edit recording"
+  menu (reported by Christoph Haubrich).
+- Added a confirmation before renaming a recording to its folder name (suggested
+  by Christoph Haubrich).
+- Modified EntriesOnSameFileSystem(), so that it returns 'true' if either of the given
+  files doesn't exist (to avoid any actions that might be triggered if files are on
+  different file system), and changed handling the 'error' variable in cDirCopier, so
+  that it is initialized to 'true' and will only be set to 'false' if the entire
+  copy process has been successful (problem reported by Christoph Haubrich).
+- Added the UPDATE-2.2.0 file.
+
+2015-02-11: Version 2.1.10
+
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Updated the Macedonian OSD texts (thanks to Dimitar Petrovski).
+- Updated the Romanian OSD texts (thanks to Lucian Muresan).
+- Updated the Ukrainian OSD texts (thanks to Yarema Aka Knedlyk).
+- Added "#REMOTE=LIRC" to Make.config.template (suggested by Martin Dauskardt).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Increased the width of the left text column in the Setup menus to avoid cutting
+  off German texts.
+- Added an empty target to the Makefile to make sure the sub-make for libsi is always
+  called (thanks to S�ren Moch).
+- Updated the Italian OSD texts (thanks to Diego Pierotto and Nino Gerbino).
+- Updated the Lithuanian OSD texts (thanks to Valdemaras Pipiras).
+- The new options "Setup/Replay/Skip distance with Green/Yellow keys" and
+  "Setup/Replay/Skip distance with Green/Yellow keys in repeat" can be used to
+  configure the number of seconds to skip when pressing these keys once or pressing
+  and holding them (based on a patch from Matthias Senzel, originally from Tobias
+  Faust).
+- The new option "Setup/Replay/Use Prev/Next keys for adaptive skipping" can be used
+  to change the behavior of these keys during replay. They normally jump between
+  editing marks, but with this option set to 'yes' they will do adaptive skipping.
+- The new command line option --chartab can be used to set the default character
+  table to use for strings in the DVB data stream that don't begin with a proper
+  character table indicator (suggested by Christopher Reimer). The old mechanism
+  of using the environment variable VDR_CHARSET_OVERRIDE still works, but is now
+  deprecated and may be removed in a future version. The value given in the --chartab
+  option takes precedence over that in VDR_CHARSET_OVERRIDE.
+- cOsd::RenderPixmaps() now returns a pointer to cPixmap instead of cPixmapMemory
+  (suggested by Thomas Reufer). This is necessary to allow plugins with derived
+  cPixmap implementations to use this function. Plugins that use this function
+  with cPixmapMemory now need to add a dynamic cast to the call, as in
+
+    cPixmapMemory *pm = dynamic_cast<cPixmapMemory *>(RenderPixmaps()));
+
+  They also need to call DestroyPixmap(pm) instead of "delete pm" to properly release
+  the resulting pixmap after use.
+  The dvbhddevice plugin has been modified accordingly.
+- A cPixmap with a negative layer no longer marks any portion of the OSD's view port
+  as "dirty" when drawing on it. This may improve performance when drawing on a
+  hidden pixmap, because it avoids unnecessary refreshes of the OSD.
+- Added a missing initialization of "panning" to the constructor of cPixmapMemory.
+- Updated the Dutch OSD texts (thanks to Erik Oomen).
+- Updated the French OSD texts (thanks to R�gis Bossut).
+- Fixed the German translation of "Zap timeout" (thanks to Albert Danis).
+
+2015-02-19: Version 2.2.0
+
+- Updated the Lithuanian OSD texts (thanks to Valdemaras Pipiras).
+- Updated the Macedonian OSD texts (thanks to Dimitar Petrovski).
+- Updated the Romanian OSD texts (thanks to Lucian Muresan).
+- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
+- Updated the Italian OSD texts (thanks to Diego Pierotto and Nino Gerbino).
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Updated the Swedish OSD texts (thanks to Magnus Sirwi�).
+- Updated the Hungarian OSD texts (thanks to Istv�n F�ley and Albert Danis).
+- Modified the German translations of the OSD texts regarding "adaptive skipping".
+- Fixed pausing replay at the last editing mark (reported by Stefan Herdler).
+- Updated the Polish OSD texts (thanks to Tomasz Maciej Nowak).
+- Updated the Ukrainian OSD texts (thanks to Yarema Aka Knedlyk).
+- Fixed using the default sort mode in a video directory without a ".sort" file
+  (reported by Stefan Herdler).
+- Improved the German translations of "EPG bugfix level"", "StreamId" and "T2SystemId"
+  (thanks to Albert Danis).
+- Updated the Czech OSD texts (thanks to Ales Jurik).
+- Updated the Slovak OSD texts (thanks to Milan Hrala).
+- Updated sources.conf to reflect the fact that Thor 5/6 and Intelsat 10-02 are
+  actually in two separate positions.
+- Updated the French OSD texts (thanks to R�gis Bossut).
+- Updated the Spanish OSD texts (thanks to Gabriel Bonich).
+- Fixed leading/trailing/multiple blanks in the translation files.
+- Bumped all version numbers to 2.2.0.
+- Official release.
diff -ruN vdr-2.0.0/INSTALL vdr-2.2.0/INSTALL
--- vdr-2.0.0/INSTALL	2013-03-14 10:17:20.000000000 +0100
+++ vdr-2.2.0/INSTALL	2015-02-17 14:47:52.000000000 +0100
@@ -1,7 +1,7 @@
 Installation of the Video Disk Recorder
 ---------------------------------------
 
-Version 2.0
+Version 2.2
 -----------
 
 Compiling and running the program:
@@ -141,7 +141,7 @@
 ISO6937. But unfortunately some broadcasters actually use ISO-8859-9 or
 other encodings, but fail to correctly announce that.
 Users who want to set the default character set to something different can
-do this by setting the environment variable VDR_CHARSET_OVERRIDE to something
+do this by using the command line option --chartab with something
 like ISO-8859-9.
 
 Start script with automatic restart in case of hangups:
@@ -254,6 +254,7 @@
 parameters. The first parameter is one of
 
   before      if this is *before* a recording starts
+  started     if this is after a recording has *started*
   after       if this is *after* a recording has finished
   edited      if this is after a recording has been *edited*
   deleted     if this is after a recording has been *deleted*
@@ -279,6 +280,9 @@
      before)
             echo "Before recording $2"
             ;;
+     started)
+            echo "Started recording $2"
+            ;;
      after)
             echo "After recording $2"
             ;;
@@ -325,38 +329,9 @@
 program splits video files into chunks of about 2GB. You should use
 a disk with several gigabytes of free space. One GB can store roughly
 half an hour of SD video data, or 10 minutes of HD video.
-
-If you have more than one disk and don't want to combine them to form
-one large logical volume, you can set up several video directories as
-mount points for these disks. All of these directories must have the
-same basic name and must end with a numeric part, which starts at 0 for
-the main directory and has increasing values for the rest of the
-directories. For example
-
-   /srv/vdr/video0
-   /srv/vdr/video1
-   /srv/vdr/video2
-
-would be a setup with three directories. You can use more than one
-numeric digit:
-
-   /mnt/MyVideos/vdr.00
-   /mnt/MyVideos/vdr.01
-   /mnt/MyVideos/vdr.02
-   ...
-   /mnt/MyVideos/vdr.11
-
-would set up twelve disks (wow, what a machine that would be!).
-
-To use such a multi directory setup, you need to add the '-v' option
-with the name of the basic directory when running 'vdr':
-
-   vdr -v /srv/vdr/video0
-
-WARNING: Using multiple disks to form one large video directory this way
-is deprecated and will be removed from VDR in a future version! Either
-use one of today's large terabyte disks (preferably with a backup disk
-in a RAID-1 array), or use something like "mhddfs".
+Either use one of today's large terabyte disks (preferably with a backup disk
+in a RAID-1 array), or use something like "mhddfs" to group several disks
+into one large volume.
 
 Note that you should not copy any non-VDR files into the video directory,
 since this might cause a lot of unnecessary disk access when VDR cleans up those
@@ -416,8 +391,9 @@
 A special form of DiSEqC is used to connect several receivers to one signal
 source using only a single cable. This method, known as "Satellite Channel Routing"
 according to EN50494 (aka "Unicable(TM)", "OLT(TM)", "SatCR", "Single Cable
-Distribution", "Channel Stacking System" or "Single Cable Interface") uses
-the file "scr.conf" to specify which SCR channels use which user band frequency.
+Distribution", "Channel Stacking System" or "Single Cable Interface") or
+EN50607 (aka "JESS") uses the file "scr.conf" to specify which SCR channels
+use which user band frequency.
 
 If DVB-S devices need to be connected to the same satellite cable, but no
 "Satellite Channel Routing" is available, they can be set to be "bonded" in
@@ -468,19 +444,17 @@
 
 The recommended PC key assignments are:
 
-  Up, Down, Left, Right     Crsr keys in numeric block
-  Menu                      'Home' in numeric block
+  Up, Down, Left, Right     Cursor keys
+  Menu                      'Home'
   Ok                        'Enter'
-  Back                      'End' in numeric block
+  Back                      'Backspace'
   Red, Green, Yellow, Blue  'F1'..'F4'
-  0..9                      '0'..'9' in top row
-  Power                     'P'
-  Volume+/-                 '+', '-'
-  Mute                      'm'
-
-If you prefer different key assignments, or if the default doesn't work for
-your keyboard, simply delete the file 'remote.conf' and restart 'vdr' to get
-into learning mode.
+  0..9                      '0'..'9'
+  Volume+/-                 'PgUp', 'PgDn'
+  Mute                      'F10'
+
+If you want to change your key assignments later, simply delete the file
+'remote.conf' and restart 'vdr' to get into learning mode.
 
 Generating source code documentation:
 -------------------------------------
diff -ruN vdr-2.0.0/MANUAL vdr-2.2.0/MANUAL
--- vdr-2.0.0/MANUAL	2013-03-11 13:01:11.000000000 +0100
+++ vdr-2.2.0/MANUAL	2015-02-17 14:48:38.000000000 +0100
@@ -1,7 +1,7 @@
 Video Disk Recorder User's Manual
 ---------------------------------
 
-Version 2.0
+Version 2.2
 -----------
 
 * Remote Control Keys
@@ -363,15 +363,30 @@
   - 0       Toggles an editing mark. If the mark indicator shows a red triangle,
             the current mark is deleted. Otherwise a new mark is set at the
             current position.
-  - 4, 6    Move an editing mark back and forward. You need to first jump to
-            an editing mark for this to work.
+  - 1, 3    Move an editing mark back and forward in "adaptive" mode. Pressing
+            either of these keys for the first time moves the mark 120 seconds
+            in the given direction (configurable via "Setup/Replay/Initial
+            duration for adaptive skipping"). Further presses of the same key
+            keep moving the mark by the same value. Once the other key is
+            pressed, the value is divided by 2 (hence the name "adaptive") with
+            every further press of either key. Pressing '1' and '3'
+            alternatingly divides the distance all the way down to a single
+            I-frame. That way a particular place in a recording (for instance
+            the beginning or end of a commercial break) can be found very
+            quickly. If none of these two keys is pressed for a while
+            (configurable via "Setup/Replay/Reset timeout for adaptive
+            skipping") the distance falls back to the initial value.
+            If replay is not in Pause mode, or if there is no mark at the
+            current position, the skip is performed without moving any mark.
+  - 4, 6    Move an editing mark back and forward by one I-frame. You need to
+            first jump to an editing mark for this to work.
   - 7, 9    Jump back and forward between editing marks. Replay goes into still
             mode after jumping to a mark. If the current position is at the
             first or last mark, or if there are no marks at all, these keys
             jump to the very beginning or end, respectively, of the recording.
   - 8       Positions replay at a point 3 seconds before the current or next
             "begin" mark and starts replay.
-  - 2       Start the actual cutting process.
+  - 2       Starts the actual cutting process.
 
   Editing marks are represented by black, vertical lines in the progress display.
   A small black triangle at the top of the mark means that this is a "begin"
@@ -500,9 +515,42 @@
   folder name in the list). The "Yellow" key deletes the current folder (note
   that this will merely delete the folder definition stored in 'folders.conf'
   and has no effect on existing timers or recordings). The "Blue" key can be
-  used to edit an existing folder definition. The "Red" key selects the current
-  folder, or enters a sub folder. Once a folder has been selected, the entire
-  path of the timer's file name will be replaced with the selected folder.
+  used to edit an existing folder definition. The "Red" key opens a folder that
+  contains sub folders, while pressing Ok selects the current folder. Once a
+  folder has been selected, the entire path of the timer's file name will be
+  replaced with the selected folder.
+
+  In the "Recordings" menu the folders of existing recordings can be renamed or
+  moved by pressing the "Blue" key ("Edit") while the cursor is positioned on
+  a folder. This will open a menu in which the folder's name and location (the
+  "parent" folder) can be edited. If such an operation will result in moving
+  more than one recording, you will be asked for confirmation.
+  The name, folder, priority and lifetime of an individual recording can be
+  changed by pressing the "Blue" key ("Info") while the cursor is positioned
+  on a recording, and in the resulting Info menu pressing the "Blue" key again
+  to bring up the "Edit recording" menu.
+  In the "Edit recording" menu the Red button ("Folder") allows you to select one
+  of your predefined folders. The Green button has multiple functions, depending
+  on what is currently going on with the recording. It can either stop or cancel
+  a cut, move or copy operation. If the button reads "Stop..." it means that the
+  respective operation is already happening, while "Cancel..." means that the
+  operation is still pending execution. If no operation is currently happening
+  and the recording has editing marks, the Button will read "Cut" and triggers
+  cutting the recording (same as pressing '2' while replaying the recording).
+  The Yellow button ("Delete marks") allows you to delete all editing marks from
+  the selected recording (if there are any and the recording is not currently
+  being cut). To directly edit the folder or name of the recording, position the
+  cursor to the respective line and press the Right key to start editing (press
+  Ok to confirm the edit, or Back to return to the previous value). If you want
+  to remove the name of the recording and make the folder name the actual
+  recording's name, you can position the cursor to the "Name:" field and press
+  the '0' key. This will take the last element of the recording's folder path
+  and make it the actual name of the recording. You can do this in turn until
+  the recording is moved all the way up to the root of the video directory.
+  Note that, in case you inadvertently pressed the '0' key, you can leave the
+  "Edit recording" menu with the "Back" key and any changes you have made so far
+  will not be applied. Once you are finished with editing the recording
+  properties, press Ok to confirm the changes.
 
 * Parameters in the "Setup" menu
 
@@ -704,15 +752,16 @@
                          Defines the standard compliance mode:
                          0 = DVB
                          1 = ANSI/SCTE
+                         2 = NORDIG
 
   Video format = 4:3     The video format (or aspect ratio) of the tv set in use
-                         (4:3 or 16:9).
+                         (4:3 or 16:9). Applies only to SD output devices.
 
   Video display format = letterbox
                          The display format to use for playing wide screen video on
                          a 4:3 tv set ("pan & scan", "letterbox" or "center cut out").
                          This option is only available if "Video format" is set to
-                         4:3.
+                         4:3. Applies only to SD output devices.
 
   Use Dolby Digital = yes
                          Controls whether Dolby Digital tracks appear in the "Audio"
@@ -776,6 +825,28 @@
                          are connected to the same sat cable must be set to the same
                          number here.
 
+  Use dish positioner = no
+                         By default, the 'P' command code in DiSEqC command sequences
+                         is ignored. Set this parameter to 'yes' if you are using a
+                         satellite dish positioner.
+
+  Site latitude (degrees) = 0
+  Site longitude (degrees) = 0
+                         Set these to the latitude and longitude of your dish's
+                         location if you use a satellite dish positioner. Use the
+                         "Green" key to switch between north/south and east/west,
+                         respectively.
+
+  Max. positioner swing (degrees) = 65
+                         Defines the maximum angle by which the positioner can move
+                         the dish away from due south (or north) in either direction.
+                         The valid range is 0...90.
+
+  Positioner speed (degrees/s) = 1.5
+                         Defines the speed at which the positioner moves the dish.
+                         The valid range is 0.1...180. This value is used to calculate
+                         how long it takes the positioner to reach the target position.
+
   CAM:
 
   n CAM Name             Shows the CAM slots that are present in this system, where
@@ -784,7 +855,19 @@
                          if it is in the process of being reset, its current status
                          is displayed.  The "Red" key can be pressed to enter the CAM
                          menu, and the "Green" key triggers a reset of the selected
-                         slot. The "Ok" key also opens the CAM menu.
+                         slot. The "Ok" key also opens the CAM menu. The "Yellow" key
+                         assigns the selected CAM to a device and switches it to the
+                         current channel. The CAM/device combination remains tuned to
+                         the current channel until the smart card in the CAM has been
+                         activated and thus starts to descramble, or until a recording
+                         needs this device. Pressing the "Yellow" key while a CAM is
+                         in activation mode cancels the activation. The activation mode
+                         remains in effect even if you switch to a different channel
+                         (provided there is more than one device in the system) or
+                         watch a recording. To activate your smart card simply switch
+                         to the channel you want to watch, open the "Setup/CAM" menu,
+                         select the CAM that contains the smart card (in case you
+                         have more than one CAM) and press the "Yellow" key.
 
   Recording:
 
@@ -896,6 +979,70 @@
                          Defines whether the player automatically goes into Pause
                          mode when setting an editing mark.
 
+  Pause replay when jumping to a mark = yes
+                         By default replay is automatically paused whenever you jump
+                         to an editing mark with the '7' or '9' key in order to allow
+                         you to easily adjust those marks. If this option is set to
+                         'no', the '9' key will not pause if you are in Play mode and
+                         the mark you jump to is not within 3 seconds of the end of
+                         the recording.
+
+  Skip edited parts = no Defines whether the edited parts of a recording are
+                         automatically skipped during replay. This includes jumping
+                         to the first mark if replay starts at the beginning of the
+                         recording, and stopping at the last mark.
+
+  Pause replay at last mark = no
+                         If enabled, replay of a recording will go into Pause mode
+                         when it has reached the last "end" mark (if any). Note that
+                         the actual position at which the pause occurs may be a couple
+                         of frames before the last "end" mark, depending on how much
+                         data is buffered by your output device.
+
+  Initial duration for adaptive skipping (s) = 120
+                         Defines the number of seconds to jump from the current replay
+                         position in either direction, when pressing the '1' or '3'
+                         key for the first time after the "Reset timeout for adaptive
+                         skipping".
+                         The valid range is 10...600.
+
+  Reset timeout for adaptive skipping (s) = 3
+                         Defines the number of seconds after which pressing the
+                         '1' or '3' key falls back to the "Initial duration for adaptive
+                         skipping".
+                         The valid range is 0...10. Setting the timeout to 0 disables
+                         the adaptive mode and makes '1' and '3' always skip the number
+                         of seconds configured as the initial duration.
+
+  Alternate behavior for adaptive skipping = no
+                         When skipping in adaptive mode with the '1' and '3' keys, the
+                         distance of the skip is halved with every key press after the
+                         first change of direction. While this allows for locating a
+                         particular position in a recording very fast, once you make
+                         one step too many in the current direction you have no chance
+                         of ever reaching the desired point any more. You will have to
+                         wait for the timeout to occur and start adaptive skipping anew.
+                         If this option is set to 'yes', the skip distance will only be
+                         halved if the direction actually changes. That way, even if
+                         you missed the target point, you can still back up to it.
+
+  Use Prev/Next keys for adaptive skipping = no
+                         Normally the Prev/Next keys jump between editing marks (or
+                         the beginning/end of the recording). You can set this option
+                         to 'yes' if you want to use these keys for adaptive skipping
+                         instead.
+
+  Skip distance with Green/Yellow keys (s) = 60
+                         Defines the number of seconds to skip in either direction
+                         when pressing the "Green" or "Yellow" key, respectively.
+                         The valid range is 5...600.
+
+  Skip distance with Green/Yellow keys in repeat (s) = 60
+                         Defines the number of seconds to skip in either direction
+                         when pressing and holding the "Green" or "Yellow" key,
+                         respectively.
+                         The valid range is 5...600.
+
   Resume ID = 0          Defines an additional ID that can be used in a multi user
                          environment, so that every user has his/her own resume
                          files for each recording. The valid range is 0...99, with
@@ -952,6 +1099,17 @@
                          VDR was stopped will be used. The valid range is from
                          0 (silent) to 255 (loudest).
 
+  Volume steps = 51      The number of steps the volume will use when moving from
+                         the lowest to the highest value. The valid range is from
+                         5 to 255.
+
+  Volume linearize = 0   How to linearize the volume control. The valid range is
+                         from -20 to 20. A value of 0 results in no linearization.
+                         The higher this value is, the more fine grained the control
+                         of the volume is for low sound levels. Lower values do the
+                         same for high sound levels. This allows you to adjust the
+                         more or less linear volume control of your sound card.
+
   Channels wrap = no     During zapping with the "Up" and "Down" keys (or the
                          "Channel+" and "Channel-" keys) the current channel will
                          wrap around the beginning or end of the channel list if
diff -ruN vdr-2.0.0/Make.config.template vdr-2.2.0/Make.config.template
--- vdr-2.0.0/Make.config.template	2013-02-18 11:55:39.000000000 +0100
+++ vdr-2.2.0/Make.config.template	2015-02-09 10:58:45.000000000 +0100
@@ -6,7 +6,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Make.config.template 2.19 2013/02/18 10:55:39 kls Exp $
+# $Id: Make.config.template 3.4 2015/02/09 09:58:45 kls Exp $
 
 ### The C compiler and options:
 
@@ -38,6 +38,7 @@
 
 #VIDEODIR  = /srv/vdr/video
 #CONFDIR   = /var/lib/vdr
+#ARGSDIR   = /etc/vdr/conf.d
 #CACHEDIR  = /var/cache/vdr
 
 # Overrides for preset/legacy configurations:
@@ -57,6 +58,7 @@
 VIDEODIR     = /video
 CACHEDIR     = $(VIDEODIR)
 CONFDIR      = $(VIDEODIR)
+ARGSDIR      = $(VIDEODIR)/conf.d
 RESDIR       = $(VIDEODIR)
 endif
 
@@ -65,12 +67,23 @@
 #PLGCFG = $(CONFDIR)/plugins.mk
 
 ### The remote control:
-
 LIRC_DEVICE = /var/run/lirc/lircd
 
+### Define if you always want to use LIRC, independent of the --lirc option:
+#REMOTE=LIRC
+
+### Define if you don't want keyboard remote control:
+#NO_KBD = 1
+
 ### Define if you want vdr to not run as root:
 #VDR_USER = vdr
 
+### Define if you want to compile in 'bidi' support:
+#BIDI = 1
+
+### Define if you want 'systemd' notification:
+#SDNOTIFY = 1
+
 ### Fallback for plugins with old makefiles:
 ifdef PLUGIN
 CFLAGS   += -fPIC
diff -ruN vdr-2.0.0/Make.global vdr-2.2.0/Make.global
--- vdr-2.0.0/Make.global	2012-12-29 13:03:01.000000000 +0100
+++ vdr-2.2.0/Make.global	2012-12-29 13:03:01.000000000 +0100
@@ -4,7 +4,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Make.global 1.2 2012/12/29 12:03:01 kls Exp $
+# $Id: Make.global 3.0 2012/12/29 12:03:01 kls Exp $
 
 # This is just a dummy file for plugins that use old style (version 1.7.33
 # or earlier) Makefiles.
diff -ruN vdr-2.0.0/Makefile vdr-2.2.0/Makefile
--- vdr-2.0.0/Makefile	2013-03-11 16:01:01.000000000 +0100
+++ vdr-2.2.0/Makefile	2015-02-09 13:28:24.000000000 +0100
@@ -4,7 +4,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Makefile 2.54 2013/03/11 15:01:01 kls Exp $
+# $Id: Makefile 3.6 2015/02/09 12:28:24 kls Exp $
 
 .DELETE_ON_ERROR:
 
@@ -31,6 +31,7 @@
 DESTDIR   ?=
 VIDEODIR  ?= /srv/vdr/video
 CONFDIR   ?= /var/lib/vdr
+ARGSDIR   ?= /etc/vdr/conf.d
 CACHEDIR  ?= /var/cache/vdr
 
 PREFIX    ?= /usr/local
@@ -66,9 +67,9 @@
 
 SILIB    = $(LSIDIR)/libsi.a
 
-OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o\
+OBJS = args.o audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o\
        dvbplayer.o dvbspu.o dvbsubtitle.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
-       lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o\
+       lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o positioner.o\
        receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
        skinclassic.o skinlcars.o skins.o skinsttng.o sourceparams.o sources.o spu.o status.o svdrp.o themes.o thread.o\
        timers.o tools.o transfer.o vdr.o videodir.o
@@ -93,12 +94,18 @@
 DEFINES += -DBIDI
 LIBS += $(shell pkg-config --libs fribidi)
 endif
+ifdef SDNOTIFY
+INCLUDES += $(shell pkg-config --cflags libsystemd-daemon)
+DEFINES += -DSDNOTIFY
+LIBS += $(shell pkg-config --libs libsystemd-daemon)
+endif
 
 LIRC_DEVICE ?= /var/run/lirc/lircd
 
 DEFINES += -DLIRC_DEVICE=\"$(LIRC_DEVICE)\"
 DEFINES += -DVIDEODIR=\"$(VIDEODIR)\"
 DEFINES += -DCONFDIR=\"$(CONFDIR)\"
+DEFINES += -DARGSDIR=\"$(ARGSDIR)\"
 DEFINES += -DCACHEDIR=\"$(CACHEDIR)\"
 DEFINES += -DRESDIR=\"$(RESDIR)\"
 DEFINES += -DPLUGINDIR=\"$(LIBDIR)\"
@@ -132,8 +139,9 @@
 
 # The libsi library:
 
-$(SILIB):
-	$(MAKE) --no-print-directory -C $(LSIDIR) CXXFLAGS="$(CXXFLAGS)" DEFINES="$(CDEFINES)" all
+$(SILIB): make-libsi
+	@$(MAKE) --no-print-directory -C $(LSIDIR) CXXFLAGS="$(CXXFLAGS)" DEFINES="$(CDEFINES)" all
+make-libsi: # empty rule makes sure the sub-make for libsi is always called
 
 # pkg-config file:
 
@@ -141,8 +149,9 @@
 vdr.pc:
 	@echo "bindir=$(BINDIR)" > $@
 	@echo "mandir=$(MANDIR)" >> $@
-	@echo "configdir=$(CONFDIR)" >> $@
 	@echo "videodir=$(VIDEODIR)" >> $@
+	@echo "configdir=$(CONFDIR)" >> $@
+	@echo "argsdir=$(ARGSDIR)" >> $@
 	@echo "cachedir=$(CACHEDIR)" >> $@
 	@echo "resdir=$(RESDIR)" >> $@
 	@echo "libdir=$(LIBDIR)" >> $@
@@ -220,7 +229,7 @@
 	    INCLUDES="-I$(CWD)/include"\
 	    $(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" VDRDIR="$(CWD)" || failed="$$failed $$i";\
 	    if [ -n "$(LCLBLD)" ] ; then\
-	       (cd $(PLUGINDIR)/src/$$i; for l in `find -name 'libvdr-*.so' -o -name 'lib$$i-*.so'`; do install $$l $(LIBDIR)/`basename $$l`.$(APIVERSION); done);\
+	       (cd $(PLUGINDIR)/src/$$i; for l in `find -name "libvdr-*.so" -o -name "lib$$i-*.so"`; do install $$l $(LIBDIR)/`basename $$l`.$(APIVERSION); done);\
 	       if [ -d $(PLUGINDIR)/src/$$i/po ]; then\
 	          for l in `ls $(PLUGINDIR)/src/$$i/po/*.mo`; do\
 	              install -D -m644 $$l $(LOCDIR)/`basename $$l | cut -d. -f1`/LC_MESSAGES/vdr-$$i.mo;\
@@ -242,7 +251,7 @@
 	if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; exit 1; fi
 
 clean-plugins:
-	@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" clean; done
+	@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" VDRDIR="$(CWD)" clean; done
 	@-rm -f $(PLUGINDIR)/lib/lib*-*.so.$(APIVERSION)
 
 # Install the files (note that 'install-pc' must be first!):
@@ -260,6 +269,7 @@
 install-dirs:
 	@mkdir -p $(DESTDIR)$(VIDEODIR)
 	@mkdir -p $(DESTDIR)$(CONFDIR)
+	@mkdir -p $(DESTDIR)$(ARGSDIR)
 	@mkdir -p $(DESTDIR)$(CACHEDIR)
 	@mkdir -p $(DESTDIR)$(RESDIR)
 
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/HISTORY vdr-2.2.0/PLUGINS/src/dvbhddevice/HISTORY
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/HISTORY	2015-02-17 14:49:42.000000000 +0100
@@ -71,3 +71,38 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2013-04-11: Version 2.0.1
+
+- Fixed aspect ratio and position of scaled video.
+
+2013-08-23: Version 2.1.1
+
+- Fixed aspect ratio and position of scaled video.
+- Added yellow button in main menu to send CEC TV-Off command.
+
+2013-08-26: Version 2.1.2
+
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+
+2014-01-01: Version 2.1.3
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+- cDevice::TrickSpeed() now has an additional parameter named Forward.
+
+2014-01-17: Version 2.1.4
+
+- Using PCR based clock recovery in transfer mode.
+
+2014-03-15: Version 2.1.6
+
+- The function cDevice::GetVideoSystem() has been deprecated.
+- Removed old-style video format setting functions.
+
+2015-02-11: Version 2.1.7
+
+- Adapted to the new return value of cOsd::RenderPixmaps().
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/Makefile vdr-2.2.0/PLUGINS/src/dvbhddevice/Makefile
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/Makefile	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -15,7 +15,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 LOCDIR = $(call PKGCFG,locdir)
 PLGCFG = $(call PKGCFG,plgcfg)
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhddevice.c vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhddevice.c
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhddevice.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhddevice.c	2015-02-17 14:11:20.000000000 +0100
@@ -10,7 +10,7 @@
 #include "menu.h"
 #include "setup.h"
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = trNOOP("HD Full Featured DVB device");
 static const char *MAINMENUENTRY  = "dvbhddevice";
 
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.c vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.c	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.c	2014-11-16 11:19:03.000000000 +0100
@@ -51,6 +51,12 @@
      isHdffPrimary = true;
      mHdffCmdIf = new HDFF::cHdffCmdIf(fd_osd);
 
+     uint32_t firmwareVersion = mHdffCmdIf->CmdGetFirmwareVersion(NULL, 0);
+     if (firmwareVersion < 0x401)
+        supportsPcrInTransferMode = false;
+     else
+        supportsPcrInTransferMode = true;
+
      /* reset some stuff in case the VDR was killed before and had no chance
         to clean up. */
      mHdffCmdIf->CmdOsdReset();
@@ -102,9 +108,12 @@
 
 void cDvbHdFfDevice::MakePrimaryDevice(bool On)
 {
-  if (On)
-     new cHdffOsdProvider(mHdffCmdIf);
-  cDvbDevice::MakePrimaryDevice(On);
+    if (On) {
+        new cHdffOsdProvider(mHdffCmdIf);
+
+        gHdffSetup.SetVideoFormat(mHdffCmdIf);
+    }
+    cDvbDevice::MakePrimaryDevice(On);
 }
 
 bool cDvbHdFfDevice::HasDecoder(void) const
@@ -231,33 +240,22 @@
 
 void cDvbHdFfDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
 {
-  //TODO???
-  cDevice::SetVideoDisplayFormat(VideoDisplayFormat);
-}
-
-void cDvbHdFfDevice::SetVideoFormat(bool VideoFormat16_9)
-{
-  HdffVideoFormat_t videoFormat;
-  videoFormat.AutomaticEnabled = true;
-  videoFormat.AfdEnabled = false;
-  videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat;
-  videoFormat.VideoConversion = (HdffVideoConversion_t) gHdffSetup.VideoConversion;
-  mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
-}
-
-eVideoSystem cDvbHdFfDevice::GetVideoSystem(void)
-{
-  eVideoSystem VideoSystem = vsPAL;
-  if (fd_video >= 0) {
-     video_size_t vs;
-     if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
-        if (vs.h == 480 || vs.h == 240)
-           VideoSystem = vsNTSC;
+    if (gHdffSetup.TvFormat == HDFF_TV_FORMAT_4_BY_3)
+    {
+        switch (VideoDisplayFormat)
+        {
+            case vdfPanAndScan:
+            case vdfCenterCutOut:
+                gHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT;
+                break;
+
+            case vdfLetterBox:
+                gHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9;
+                break;
         }
-     else
-        LOG_ERROR;
-     }
-  return VideoSystem;
+        gHdffSetup.SetVideoFormat(mHdffCmdIf);
+    }
+    cDevice::SetVideoDisplayFormat(VideoDisplayFormat);
 }
 
 void cDvbHdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
@@ -377,8 +375,8 @@
 
   bool DoTune = !IsTunedToTransponder(Channel);
 
-  bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
-  bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
+  bool pidHandlesVideo = vpid && pidHandles[ptVideo].pid == vpid;
+  bool pidHandlesAudio = apid && pidHandles[ptAudio].pid == apid;
 
   bool TurnOffLivePIDs = DoTune
                          || !IsPrimaryDevice()
@@ -437,11 +435,6 @@
   mHdffCmdIf->CmdMuxSetVolume(Volume * 100 / 255);
 }
 
-void cDvbHdFfDevice::SetDigitalAudioDevice(bool On)
-{
-  // not needed
-}
-
 void cDvbHdFfDevice::SetAudioTrackDevice(eTrackType Type)
 {
     //printf("SetAudioTrackDevice %d\n", Type);
@@ -511,13 +504,15 @@
         }
         else
         {
-            mHdffCmdIf->CmdAvSetPlayMode(1, Transferring() || (cTransferControl::ReceiverDevice() == this));
+            isTransferMode = Transferring() || (cTransferControl::ReceiverDevice() == this);
+            mHdffCmdIf->CmdAvSetPlayMode(1, isTransferMode);
             mHdffCmdIf->CmdAvSetStc(0, 100000);
             mHdffCmdIf->CmdAvEnableSync(0, false);
             mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true);
 
             playVideoPid = -1;
             playAudioPid = -1;
+            playPcrPid = -1;
             audioCounter = 0;
             videoCounter = 0;
             freezed = false;
@@ -574,18 +569,43 @@
     }
     else
     {
+        //printf("ScaleVideo: Rect = %d %d %d %d\n", Rect.X(), Rect.Y(), Rect.Width(), Rect.Height());
+
         int osdWidth;
         int osdHeight;
         double osdPixelAspect;
 
         GetOsdSize(osdWidth, osdHeight, osdPixelAspect);
-        mHdffCmdIf->CmdAvSetVideoWindow(0, true,
-            Rect.X() * 1000 / osdWidth, Rect.Y() * 1000 / osdHeight,
-            Rect.Width() * 1000 / osdWidth, Rect.Height() * 1000 / osdHeight);
+        //printf("ScaleVideo: OsdSize = %d %d %g\n", osdWidth, osdHeight, osdPixelAspect);
+
+        // Convert the video window coordinates in 1/10 percent of the display
+        // resolution.
+        int x = (Rect.X() * 1000 + osdWidth / 2) / osdWidth;
+        int y = (Rect.Y() * 1000 + osdHeight / 2) / osdHeight;
+        int w = (Rect.Width() * 1000 + osdWidth / 2) / osdWidth;
+        int h = (Rect.Height() * 1000 + osdHeight / 2) / osdHeight;
+        //printf("ScaleVideo: Win1 = %d %d %d %d\n", x, y, w, h);
+
+        // fix aspect ratio, reposition video
+        if (w > h) {
+            x += (w - h) / 2;
+            w = h;
+        }
+        else if (w < h) {
+            y += (h - w) / 2;
+            h = w;
+        }
+
+        //printf("ScaleVideo: Win2 = %d %d %d %d\n", x, y, w, h);
+        mHdffCmdIf->CmdAvSetVideoWindow(0, true, x, y, w, h);
     }
 }
 
+#if (APIVERSNUM >= 20103)
+void cDvbHdFfDevice::TrickSpeed(int Speed, bool Forward)
+#else
 void cDvbHdFfDevice::TrickSpeed(int Speed)
+#endif
 {
   freezed = false;
   mHdffCmdIf->CmdAvEnableSync(0, false);
@@ -790,6 +810,11 @@
         mHdffCmdIf->CmdAvEnableSync(0, true);
         isPlayingVideo = true;
     }
+
+    // ignore padding PES packets
+    if (Data[3] == 0xBE)
+        return Length;
+
     //TODO: support greater Length
     uint8_t tsBuffer[188 * 16];
     uint32_t tsLength;
@@ -875,6 +900,14 @@
             mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, MapVideoStreamTypes(PatPmtParser()->Vtype()), true);
         }
     }
+    if (isTransferMode && supportsPcrInTransferMode) {
+        if (pid != playPcrPid) {
+            if (pid == PatPmtParser()->Ppid()) {
+                playPcrPid = pid;
+                mHdffCmdIf->CmdAvSetPcrPid(0, playPcrPid);
+            }
+        }
+    }
     return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
 }
 
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.h vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.h	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/dvbhdffdevice.h	2014-11-16 11:19:03.000000000 +0100
@@ -52,8 +52,6 @@
 
 public:
   virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat);
-  virtual void SetVideoFormat(bool VideoFormat16_9);
-  virtual eVideoSystem GetVideoSystem(void);
   virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect);
   virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect);
 
@@ -70,16 +68,18 @@
   virtual int GetAudioChannelDevice(void);
   virtual void SetAudioChannelDevice(int AudioChannel);
   virtual void SetVolumeDevice(int Volume);
-  virtual void SetDigitalAudioDevice(bool On);
 
 // Player facilities
 
 private:
   int playVideoPid;
   int playAudioPid;
+  int playPcrPid;
   bool freezed;
   bool trickMode;
   bool isPlayingVideo;
+  bool isTransferMode;
+  bool supportsPcrInTransferMode;
 
   // Pes2Ts conversion stuff
   uint8_t videoCounter;
@@ -99,7 +99,11 @@
   virtual int64_t GetSTC(void);
   virtual cRect CanScaleVideo(const cRect &Rect, int Alignment = taCenter);
   virtual void ScaleVideo(const cRect &Rect = cRect::Null);
+#if (APIVERSNUM >= 20103)
+  virtual void TrickSpeed(int Speed, bool Forward);
+#else
   virtual void TrickSpeed(int Speed);
+#endif
   virtual void Clear(void);
   virtual void Play(void);
   virtual void Freeze(void);
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/hdffosd.c vdr-2.2.0/PLUGINS/src/dvbhddevice/hdffosd.c
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/hdffosd.c	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/hdffosd.c	2015-02-11 10:48:02.000000000 +0100
@@ -42,7 +42,6 @@
     tFont mFonts[MAX_NUM_FONTS];
     uint32_t mBitmapPalette;
     uint32_t mBitmapColors[256];
-    uint32_t mBitmapNumColors;
 
     bool mSupportsUtf8Text;
 
@@ -555,7 +554,6 @@
     uint32_t mDisplay;
     uint32_t mBitmapPalette;
     uint32_t mBitmapColors[256];
-    uint32_t mBitmapNumColors;
 
 protected:
     virtual void SetActive(bool On);
@@ -665,16 +663,25 @@
 {
     if (!Active() || (mDisplay == HDFF_INVALID_HANDLE))
         return;
-    //struct timeval start;
-    //struct timeval end;
-    //struct timezone timeZone;
-    //gettimeofday(&start, &timeZone);
+#ifdef MEASURE_OSD_TIME
+    struct timeval start;
+    struct timeval end;
+    struct timezone timeZone;
+    gettimeofday(&start, &timeZone);
+#endif
 
     bool render = false;
     if (IsTrueColor())
     {
+        uint8_t * buffer = 0;
+        if (gHdffSetup.TrueColorFormat != 0)
+        {
+            buffer = new uint8_t[MAX_BITMAP_SIZE];
+            if (!buffer)
+                return;
+        }
         LOCK_PIXMAPS;
-        while (cPixmapMemory *pm = RenderPixmaps())
+        while (cPixmapMemory *pm = dynamic_cast<cPixmapMemory *>(RenderPixmaps()))
         {
             int w = pm->ViewPort().Width();
             int h = pm->ViewPort().Height();
@@ -684,17 +691,56 @@
                 Chunk = h;
             for (int y = 0; y < h; y += Chunk)
             {
-                 int hc = Chunk;
-                 if (y + hc > h)
-                     hc = h - y;
-                 mHdffCmdIf->CmdOsdDrawBitmap(mDisplay,
-                     Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y,
-                     pm->Data() + y * d, w, hc, hc * d,
-                     HDFF_COLOR_TYPE_ARGB8888, HDFF_INVALID_HANDLE);
+                int hc = Chunk;
+                if (y + hc > h)
+                    hc = h - y;
+                if (gHdffSetup.TrueColorFormat == 0) // ARGB8888 (32 bit)
+                {
+                    mHdffCmdIf->CmdOsdDrawBitmap(mDisplay,
+                        Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y,
+                        pm->Data() + y * d, w, hc, hc * d,
+                        HDFF_COLOR_TYPE_ARGB8888, HDFF_INVALID_HANDLE);
+                }
+                else if (gHdffSetup.TrueColorFormat == 1) // ARGB8565 (24 bit)
+                {
+                    const tColor * pixmapData = (const tColor *) (pm->Data() + y * d);
+                    uint8_t * bitmapData = buffer;
+                    for (int i = 0; i < hc * w; i++)
+                    {
+                        bitmapData[2] =  (pixmapData[i] & 0xFF000000) >> 24;
+                        bitmapData[1] = ((pixmapData[i] & 0x00F80000) >> 16)
+                                      | ((pixmapData[i] & 0x0000E000) >> 13);
+                        bitmapData[0] = ((pixmapData[i] & 0x00001C00) >> 5)
+                                      | ((pixmapData[i] & 0x000000F8) >> 3);
+                        bitmapData += 3;
+                    }
+                    mHdffCmdIf->CmdOsdDrawBitmap(mDisplay,
+                        Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y,
+                        buffer, w, hc, hc * w * 3,
+                        HDFF_COLOR_TYPE_ARGB8565, HDFF_INVALID_HANDLE);
+                }
+                else if (gHdffSetup.TrueColorFormat == 2) // ARGB4444 (16 bit)
+                {
+                    const tColor * pixmapData = (const tColor *) (pm->Data() + y * d);
+                    uint16_t * bitmapData = (uint16_t *) buffer;
+                    for (int i = 0; i < hc * w; i++)
+                    {
+                        bitmapData[i] = ((pixmapData[i] & 0xF0000000) >> 16)
+                                      | ((pixmapData[i] & 0x00F00000) >> 12)
+                                      | ((pixmapData[i] & 0x0000F000) >> 8)
+                                      | ((pixmapData[i] & 0x000000F0) >> 4);
+                    }
+                    mHdffCmdIf->CmdOsdDrawBitmap(mDisplay,
+                        Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y,
+                        buffer, w, hc, hc * w * 2,
+                        HDFF_COLOR_TYPE_ARGB4444, HDFF_INVALID_HANDLE);
+                }
             }
-            delete pm;
+            DestroyPixmap(pm);
             render = true;
         }
+        if (buffer)
+            delete[] buffer;
     }
     else
     {
@@ -757,10 +803,12 @@
     if (render)
     {
         mHdffCmdIf->CmdOsdRenderDisplay(mDisplay);
-        //gettimeofday(&end, &timeZone);
-        //int timeNeeded = end.tv_usec - start.tv_usec;
-        //timeNeeded += (end.tv_sec - start.tv_sec) * 1000000;
-        //printf("time = %d\n", timeNeeded);
+#ifdef MEASURE_OSD_TIME
+        gettimeofday(&end, &timeZone);
+        int timeNeeded = end.tv_usec - start.tv_usec;
+        timeNeeded += (end.tv_sec - start.tv_sec) * 1000000;
+        printf("time = %d\n", timeNeeded);
+#endif
     }
     refresh = false;
 }
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/menu.c vdr-2.2.0/PLUGINS/src/dvbhddevice/menu.c
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/menu.c	2012-02-07 20:29:05.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/menu.c	2014-11-16 11:19:03.000000000 +0100
@@ -13,7 +13,7 @@
 {
     mVideoConversionItem = new cOsdItem("", osUnknown, false);
     Add(mVideoConversionItem);
-    SetHelp(tr("Video Conversion"), tr("TV on"));
+    SetHelp(tr("Video Conversion"), tr("TV on"), tr("TV off"));
     SetVideoConversion();
 }
 
@@ -38,6 +38,11 @@
                 state = osEnd;
                 break;
 
+            case kYellow:
+                mHdffCmdIf->CmdHdmiSendCecCommand(HDFF_CEC_COMMAND_TV_OFF);
+                state = osEnd;
+                break;
+
             case kOk:
                 state = osEnd;
                 break;
@@ -51,12 +56,7 @@
 
 void cHdffMenu::SetVideoConversion(void)
 {
-    HdffVideoFormat_t videoFormat;
-    videoFormat.AutomaticEnabled = true;
-    videoFormat.AfdEnabled = false;
-    videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat;
-    videoFormat.VideoConversion = (HdffVideoConversion_t) gHdffSetup.VideoConversion;
-    mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
+    gHdffSetup.SetVideoFormat(mHdffCmdIf);
 
     char str[128];
     sprintf(str, "%s: %s", tr("Video Conversion"), gHdffSetup.GetVideoConversionString());
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/po/de_DE.po vdr-2.2.0/PLUGINS/src/dvbhddevice/po/de_DE.po
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/po/de_DE.po	2012-12-18 14:17:07.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/po/de_DE.po	2015-02-19 10:12:28.167447554 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file
-# Copyright (C) 2011 Andreas Regel
+# Copyright (C) 2015 Andreas Regel
 # This file is distributed under the same license as the dvbhddevice package.
-# Christoph Haubrich, 2011
+# Christoph Haubrich <christoph1.haubrich@arcor.de>, 2011
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
+"Project-Id-Version: vdr-dvbhddevice 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-11-13 20:08+0100\n"
+"POT-Creation-Date: 2014-09-21 14:01+0200\n"
 "PO-Revision-Date: 2011-04-25 21:44+0200\n"
 "Last-Translator: Christoph Haubrich\n"
 "Language-Team: <see README>\n"
@@ -25,6 +25,9 @@
 msgid "TV on"
 msgstr "TV ein"
 
+msgid "TV off"
+msgstr "TV aus"
+
 msgid "Automatic"
 msgstr "automatisch"
 
@@ -118,5 +121,8 @@
 msgid "Allow True Color OSD"
 msgstr "Erlaube True Color OSD"
 
+msgid "True Color format"
+msgstr "True Color Format"
+
 msgid "Hide mainmenu entry"
 msgstr "Hauptmenüeintrag verstecken"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/po/et_EE.po vdr-2.2.0/PLUGINS/src/dvbhddevice/po/et_EE.po
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/po/et_EE.po	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/po/et_EE.po	2015-02-19 10:12:28.088444167 +0100
@@ -0,0 +1,128 @@
+# VDR plugin language source file
+# Copyright (C) 2015 Andreas Regel
+# This file is distributed under the same license as the dvbhddevice package.
+# Arthur Konovalov <artlov@gmail.com>, 2015
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-dvbhddevice 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2014-09-21 14:01+0200\n"
+"PO-Revision-Date: 2011-04-25 21:44+0200\n"
+"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
+"Language-Team: Estonian <vdr@linuxtv.org>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "HD Full Featured DVB device"
+msgstr "Täisfunktsionaalne HD DVB seade"
+
+msgid "Video Conversion"
+msgstr "Video konverteerimine"
+
+msgid "TV on"
+msgstr "TV sisse"
+
+msgid "TV off"
+msgstr "TV välja"
+
+msgid "Automatic"
+msgstr "automaatne"
+
+msgid "Letterbox 16/9"
+msgstr "letterbox 16:9"
+
+msgid "Letterbox 14/9"
+msgstr "letterbox 14:9"
+
+msgid "Pillarbox"
+msgstr "pillarbox"
+
+msgid "CentreCutOut"
+msgstr "center cut out"
+
+msgid "Always 16/9"
+msgstr "alati 16:9"
+
+msgid "Zoom 16/9"
+msgstr "suum 16/9"
+
+msgid "Off"
+msgstr "väljas"
+
+msgid "Frame rate"
+msgstr "kaadrisagedus"
+
+msgid "HD Only"
+msgstr "ainult HD-resolutsioon"
+
+msgid "Always"
+msgstr "alati"
+
+msgid "Disabled"
+msgstr "keelatud"
+
+msgid "Analogue only"
+msgstr "ainult analoog"
+
+msgid "HDMI only"
+msgstr "ainult HDMI"
+
+msgid "Follow resolution"
+msgstr "vastavalt resolutsioonile"
+
+msgid "none"
+msgstr "ei"
+
+msgid "Resolution"
+msgstr "Resolutsioon"
+
+msgid "Video Mode Adaption"
+msgstr "Videorežiimi sobitus"
+
+msgid "TV format"
+msgstr "TV külgsuhe"
+
+msgid "Analogue Video"
+msgstr "Analoogvideo"
+
+msgid "Audio Delay (ms)"
+msgstr "Heli viide (ms)"
+
+msgid "Audio Downmix"
+msgstr "Heli downmix"
+
+msgid "A/V Sync Shift (ms)"
+msgstr "A/V-sünkro nihe (ms)"
+
+msgid "OSD Size"
+msgstr "Ekraanimenüü suurus"
+
+msgid "HDMI CEC"
+msgstr "HDMI CEC"
+
+msgid "CEC: Switch TV on"
+msgstr "CEC: TV sisselülitus"
+
+msgid "CEC: Switch TV off"
+msgstr "CEC: TV väljalülitus"
+
+msgid "Remote Control Protocol"
+msgstr "Kaugjuhtimispuldi protokoll"
+
+msgid "Remote Control Address"
+msgstr "Kaugjuhtimispuldi aadress"
+
+msgid "High Level OSD"
+msgstr "Kõrgema taseme ekraanimenüü"
+
+msgid "Allow True Color OSD"
+msgstr "True Color ekraanimenüü"
+
+msgid "True Color format"
+msgstr "True Color formaat"
+
+msgid "Hide mainmenu entry"
+msgstr "Peita peamenüü valikus"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/po/fi_FI.po vdr-2.2.0/PLUGINS/src/dvbhddevice/po/fi_FI.po
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/po/fi_FI.po	2013-02-18 09:48:07.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/po/fi_FI.po	2015-02-19 10:12:28.109445075 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file
-# Copyright (C) 2011 Andreas Regel
+# Copyright (C) 2015 Andreas Regel
 # This file is distributed under the same license as the dvbhddevice package.
-# Christoph Haubrich, 2011
+# Christoph Haubrich <christoph1.haubrich@arcor.de>, 2011
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
+"Project-Id-Version: vdr-dvbhddevice 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-11-13 20:08+0100\n"
+"POT-Creation-Date: 2014-09-21 14:01+0200\n"
 "PO-Revision-Date: 2011-04-25 21:44+0200\n"
 "Last-Translator: Rolf Ahrenberg\n"
 "Language-Team: Finnish <vdr@linuxtv.org>\n"
@@ -25,6 +25,9 @@
 msgid "TV on"
 msgstr "TV päälle"
 
+msgid "TV off"
+msgstr "TV kiinni"
+
 msgid "Automatic"
 msgstr "automaattinen"
 
@@ -118,5 +121,8 @@
 msgid "Allow True Color OSD"
 msgstr "Salli tosivärit kuvaruutunäytölle"
 
+msgid "True Color format"
+msgstr "Näytä tosivärit muodossa"
+
 msgid "Hide mainmenu entry"
 msgstr "Piilota valinta päävalikosta"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/po/it_IT.po vdr-2.2.0/PLUGINS/src/dvbhddevice/po/it_IT.po
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/po/it_IT.po	2013-02-19 10:03:33.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/po/it_IT.po	2015-02-19 10:12:28.139446356 +0100
@@ -1,24 +1,23 @@
 # VDR plugin language source file
-# Copyright (C) 2011 Andreas Regel
+# Copyright (C) 2015 Andreas Regel
 # This file is distributed under the same license as the dvbhddevice package.
-# Christoph Haubrich, 2011
+# Christoph Haubrich <christoph1.haubrich@arcor.de>, 2011
 # Diego Pierotto <vdr-italian@tiscali.it>, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
+"Project-Id-Version: vdr-dvbhddevice 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-11-13 20:08+0100\n"
-"PO-Revision-Date: 2013-02-18 23:42+0100\n"
+"POT-Creation-Date: 2014-09-21 14:01+0200\n"
+"PO-Revision-Date: 2015-02-08 19:48+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
 "Language-Team:  <see README>\n"
 "Language: it\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"
+"X-Generator: Poedit 1.5.4\n"
 
 msgid "HD Full Featured DVB device"
 msgstr "Scheda DVB HD Full Featured"
@@ -29,6 +28,9 @@
 msgid "TV on"
 msgstr "TV accesa"
 
+msgid "TV off"
+msgstr "TV spenta"
+
 msgid "Automatic"
 msgstr "Automatica"
 
@@ -122,5 +124,8 @@
 msgid "Allow True Color OSD"
 msgstr "Permetti OSD True Color"
 
+msgid "True Color format"
+msgstr "Formato True Color"
+
 msgid "Hide mainmenu entry"
 msgstr "Nascondi voce menu principale"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/po/uk_UA.po vdr-2.2.0/PLUGINS/src/dvbhddevice/po/uk_UA.po
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/po/uk_UA.po	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/po/uk_UA.po	2015-02-19 10:12:28.156447081 +0100
@@ -0,0 +1,129 @@
+# VDR plugin language source file.
+# Copyright (C) 2015 Andreas Regel
+# This file is distributed under the same license as the dvbhddevice package.
+# Yarema Aka Knedlyk <yupadmin@gmail.com>, 2015
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-dvbhddevice 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-02-03 13:28+0100\n"
+"PO-Revision-Date: 2015-02-01 23:31+0100\n"
+"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
+"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.7.3\n"
+
+msgid "HD Full Featured DVB device"
+msgstr "HD Full Featured DVB device"
+
+msgid "Video Conversion"
+msgstr "Конвертація відео"
+
+msgid "TV on"
+msgstr "Вкл ТБ"
+
+msgid "TV off"
+msgstr "Викл ТБ"
+
+msgid "Automatic"
+msgstr "Автоматично"
+
+msgid "Letterbox 16/9"
+msgstr "Конверт 16:9"
+
+msgid "Letterbox 14/9"
+msgstr "Конверт 14:9"
+
+msgid "Pillarbox"
+msgstr "Pillarbox"
+
+msgid "CentreCutOut"
+msgstr "Вірізання від центру"
+
+msgid "Always 16/9"
+msgstr "Завжди 16:9"
+
+msgid "Zoom 16/9"
+msgstr "Допасувати до 16:9"
+
+msgid "Off"
+msgstr "Викл"
+
+msgid "Frame rate"
+msgstr "Частота кадрів"
+
+msgid "HD Only"
+msgstr "Тільки HD"
+
+msgid "Always"
+msgstr "Завжди"
+
+msgid "Disabled"
+msgstr "Відключено"
+
+msgid "Analogue only"
+msgstr "Тільки аналоговий"
+
+msgid "HDMI only"
+msgstr "Тільки HDMI"
+
+msgid "Follow resolution"
+msgstr "Відносно розд. здатн."
+
+msgid "none"
+msgstr "немає"
+
+msgid "Resolution"
+msgstr "Розд. здатність"
+
+msgid "Video Mode Adaption"
+msgstr "Адаптація відео режиму"
+
+msgid "TV format"
+msgstr "Формат ТБ"
+
+msgid "Analogue Video"
+msgstr "Аналогове відео"
+
+msgid "Audio Delay (ms)"
+msgstr "Затримка аудіо (мс)"
+
+msgid "Audio Downmix"
+msgstr "Міксування аудіо"
+
+msgid "A/V Sync Shift (ms)"
+msgstr "А/В зсув синхронізації (мс)"
+
+msgid "OSD Size"
+msgstr "Розмір OSD"
+
+msgid "HDMI CEC"
+msgstr "HDMI CEC"
+
+msgid "CEC: Switch TV on"
+msgstr "CEC: включити ТБ"
+
+msgid "CEC: Switch TV off"
+msgstr "CEC: виключити ТБ"
+
+msgid "Remote Control Protocol"
+msgstr "Протокол пульта"
+
+msgid "Remote Control Address"
+msgstr "Адрес пульта"
+
+msgid "High Level OSD"
+msgstr "High Level OSD"
+
+msgid "Allow True Color OSD"
+msgstr "Включити True Color OSD"
+
+msgid "True Color format"
+msgstr "Формат True Color"
+
+msgid "Hide mainmenu entry"
+msgstr "Приховати в головному меню"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/setup.c vdr-2.2.0/PLUGINS/src/dvbhddevice/setup.c
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/setup.c	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/setup.c	2014-11-16 11:19:03.000000000 +0100
@@ -33,6 +33,7 @@
     RemoteAddress = -1;
     HighLevelOsd = 1;
     TrueColorOsd = 1;
+    TrueColorFormat = 0;
     HideMainMenu = 0;
 }
 
@@ -54,6 +55,7 @@
     else if (strcmp(Name, "RemoteAddress")     == 0) RemoteAddress     = atoi(Value);
     else if (strcmp(Name, "HighLevelOsd")      == 0) HighLevelOsd      = atoi(Value);
     else if (strcmp(Name, "TrueColorOsd")      == 0) TrueColorOsd      = atoi(Value);
+    else if (strcmp(Name, "TrueColorFormat")   == 0) TrueColorFormat   = atoi(Value);
     else if (strcmp(Name, "HideMainMenu")      == 0) HideMainMenu      = atoi(Value);
     else return false;
     return true;
@@ -180,6 +182,16 @@
     }
 }
 
+void cHdffSetup::SetVideoFormat(HDFF::cHdffCmdIf * HdffCmdIf)
+{
+    HdffVideoFormat_t videoFormat;
+
+    videoFormat.AutomaticEnabled = true;
+    videoFormat.AfdEnabled = false;
+    videoFormat.TvFormat = (HdffTvFormat_t) TvFormat;
+    videoFormat.VideoConversion = (HdffVideoConversion_t) VideoConversion;
+    HdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
+}
 
 cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf)
 {
@@ -190,6 +202,7 @@
     const int kAudioDownmixes = 5;
     const int kOsdSizes = 5;
     const int kRemoteProtocols = 3;
+    const int kTrueColorFormats = 3;
 
     static const char * ResolutionItems[kResolutions] =
     {
@@ -246,6 +259,13 @@
         "RC6",
     };
 
+    static const char * TrueColorFormatItems[kTrueColorFormats] =
+    {
+        "ARGB8888",
+        "ARGB8565",
+        "ARGB4444",
+    };
+
     mHdffCmdIf = pHdffCmdIf;
     mNewHdffSetup = gHdffSetup;
 
@@ -265,6 +285,7 @@
     Add(new cMenuEditIntItem(tr("Remote Control Address"), &mNewHdffSetup.RemoteAddress, -1, 31));
     Add(new cMenuEditBoolItem(tr("High Level OSD"), &mNewHdffSetup.HighLevelOsd));
     Add(new cMenuEditBoolItem(tr("Allow True Color OSD"), &mNewHdffSetup.TrueColorOsd));
+    Add(new cMenuEditStraItem(tr("True Color format"), &mNewHdffSetup.TrueColorFormat, kTrueColorFormats, TrueColorFormatItems));
     Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"), &mNewHdffSetup.HideMainMenu));
 
     mVideoConversion = 0;
@@ -395,6 +416,7 @@
     SetupStore("RemoteAddress", mNewHdffSetup.RemoteAddress);
     SetupStore("HighLevelOsd", mNewHdffSetup.HighLevelOsd);
     SetupStore("TrueColorOsd", mNewHdffSetup.TrueColorOsd);
+    SetupStore("TrueColorFormat", mNewHdffSetup.TrueColorFormat);
     SetupStore("HideMainMenu", mNewHdffSetup.HideMainMenu);
 
     if (mHdffCmdIf)
@@ -403,14 +425,9 @@
         {
             mHdffCmdIf->CmdHdmiSetVideoMode(mNewHdffSetup.GetVideoMode());
         }
-        HdffVideoFormat_t videoFormat;
         HdffHdmiConfig_t hdmiConfig;
 
-        videoFormat.AutomaticEnabled = true;
-        videoFormat.AfdEnabled = false;
-        videoFormat.TvFormat = (HdffTvFormat_t) mNewHdffSetup.TvFormat;
-        videoFormat.VideoConversion = (HdffVideoConversion_t) mNewHdffSetup.VideoConversion;
-        mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
+        mNewHdffSetup.SetVideoFormat(mHdffCmdIf);
 
         mHdffCmdIf->CmdAvSetAudioDelay(mNewHdffSetup.AudioDelay);
         mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) mNewHdffSetup.AudioDownmix);
diff -ruN vdr-2.0.0/PLUGINS/src/dvbhddevice/setup.h vdr-2.2.0/PLUGINS/src/dvbhddevice/setup.h
--- vdr-2.0.0/PLUGINS/src/dvbhddevice/setup.h	2013-03-02 18:58:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbhddevice/setup.h	2014-11-16 11:19:03.000000000 +0100
@@ -18,6 +18,7 @@
     HdffVideoMode_t GetVideoMode(void);
     void SetNextVideoConversion(void);
     const char * GetVideoConversionString(void);
+    void SetVideoFormat(HDFF::cHdffCmdIf * HdffCmdIf);
 
     int Resolution;
     int VideoModeAdaption;
@@ -36,6 +37,7 @@
 
     int HighLevelOsd;
     int TrueColorOsd;
+    int TrueColorFormat;
 
     int HideMainMenu;
 };
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/HISTORY vdr-2.2.0/PLUGINS/src/dvbsddevice/HISTORY
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/HISTORY	2015-02-17 14:46:28.000000000 +0100
@@ -45,3 +45,21 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2013-08-22: Version 2.0.1
+
+- Fixed handling the -o option (short form of --outputonly; problem reported by
+  Mario Edelmann).
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+- cDevice::TrickSpeed() now has an additional parameter named Forward.
+
+2014-03-15: Version 2.1.2
+
+- The function cDevice::GetVideoSystem() has been deprecated.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/Makefile vdr-2.2.0/PLUGINS/src/dvbsddevice/Makefile
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 1.20 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsddevice.c vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsddevice.c
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsddevice.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsddevice.c	2015-02-17 14:11:55.000000000 +0100
@@ -3,14 +3,14 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsddevice.c 1.10 2013/03/31 09:30:18 kls Exp $
+ * $Id: dvbsddevice.c 3.4 2015/02/17 13:11:55 kls Exp $
  */
 
 #include <getopt.h>
 #include <vdr/plugin.h>
 #include "dvbsdffdevice.h"
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "SD Full Featured DVB device";
 
 class cPluginDvbsddevice : public cPlugin {
@@ -48,7 +48,7 @@
      };
 
   int c;
-  while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+  while ((c = getopt_long(argc, argv, "o", long_options, NULL)) != -1) {
         switch (c) {
           case 'o': probe->SetOutputOnly(true);
                     break;
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.c vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.c	2013-02-17 14:16:18.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.c	2014-03-15 13:35:21.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsdffdevice.c 2.35 2013/02/17 13:16:18 kls Exp $
+ * $Id: dvbsdffdevice.c 3.3 2014/03/15 12:35:21 kls Exp $
  */
 
 #include "dvbsdffdevice.h"
@@ -241,21 +241,6 @@
   SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
 }
 
-eVideoSystem cDvbSdFfDevice::GetVideoSystem(void)
-{
-  eVideoSystem VideoSystem = vsPAL;
-  if (fd_video >= 0) {
-     video_size_t vs;
-     if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
-        if (vs.h == 480 || vs.h == 240)
-           VideoSystem = vsNTSC;
-        }
-     else
-        LOG_ERROR;
-     }
-  return VideoSystem;
-}
-
 void cDvbSdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
 {
   if (fd_video >= 0) {
@@ -400,8 +385,8 @@
 
   bool DoTune = !IsTunedToTransponder(Channel);
 
-  bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
-  bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
+  bool pidHandlesVideo = vpid && pidHandles[ptVideo].pid == vpid;
+  bool pidHandlesAudio = apid && pidHandles[ptAudio].pid == apid;
 
   bool TurnOffLivePIDs = DoTune
                          || !IsPrimaryDevice()
@@ -593,7 +578,7 @@
   return -1;
 }
 
-void cDvbSdFfDevice::TrickSpeed(int Speed)
+void cDvbSdFfDevice::TrickSpeed(int Speed, bool Forward)
 {
   if (fd_video >= 0)
      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.h vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.h	2013-02-17 14:16:29.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffdevice.h	2014-03-15 13:36:35.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsdffdevice.h 2.16 2013/02/17 13:16:29 kls Exp $
+ * $Id: dvbsdffdevice.h 3.2 2014/03/15 12:36:35 kls Exp $
  */
 
 #ifndef __DVBSDFFDEVICE_H
@@ -63,7 +63,6 @@
 public:
   virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat);
   virtual void SetVideoFormat(bool VideoFormat16_9);
-  virtual eVideoSystem GetVideoSystem(void);
   virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect);
   virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect);
 
@@ -94,7 +93,7 @@
   virtual int PlayTsAudio(const uchar *Data, int Length);
 public:
   virtual int64_t GetSTC(void);
-  virtual void TrickSpeed(int Speed);
+  virtual void TrickSpeed(int Speed, bool Forward);
   virtual void Clear(void);
   virtual void Play(void);
   virtual void Freeze(void);
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffosd.c vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffosd.c
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffosd.c	2011-04-17 14:55:09.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffosd.c	2011-04-17 14:55:09.000000000 +0200
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsdffosd.c 2.3 2011/04/17 12:55:09 kls Exp $
+ * $Id: dvbsdffosd.c 3.0 2011/04/17 12:55:09 kls Exp $
  */
 
 #include "dvbsdffosd.h"
diff -ruN vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffosd.h vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffosd.h
--- vdr-2.0.0/PLUGINS/src/dvbsddevice/dvbsdffosd.h	2012-12-03 14:43:55.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/dvbsddevice/dvbsdffosd.h	2012-12-03 14:43:55.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsdffosd.h 2.2 2012/12/03 13:43:55 kls Exp $
+ * $Id: dvbsdffosd.h 3.0 2012/12/03 13:43:55 kls Exp $
  */
 
 #ifndef __DVBSDFFODF_H
diff -ruN vdr-2.0.0/PLUGINS/src/epgtableid0/HISTORY vdr-2.2.0/PLUGINS/src/epgtableid0/HISTORY
--- vdr-2.0.0/PLUGINS/src/epgtableid0/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/epgtableid0/HISTORY	2015-02-17 14:46:44.000000000 +0100
@@ -16,3 +16,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/epgtableid0/Makefile vdr-2.2.0/PLUGINS/src/epgtableid0/Makefile
--- vdr-2.0.0/PLUGINS/src/epgtableid0/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/epgtableid0/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 1.13 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/epgtableid0/epgtableid0.c vdr-2.2.0/PLUGINS/src/epgtableid0/epgtableid0.c
--- vdr-2.0.0/PLUGINS/src/epgtableid0/epgtableid0.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/epgtableid0/epgtableid0.c	2015-02-17 14:12:18.000000000 +0100
@@ -3,13 +3,13 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: epgtableid0.c 1.4 2013/03/31 09:30:18 kls Exp $
+ * $Id: epgtableid0.c 3.2 2015/02/17 13:12:18 kls Exp $
  */
 
 #include <vdr/epg.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "EPG handler for events with table id 0x00";
 
 // --- cTable0Handler --------------------------------------------------------
diff -ruN vdr-2.0.0/PLUGINS/src/hello/HISTORY vdr-2.2.0/PLUGINS/src/hello/HISTORY
--- vdr-2.0.0/PLUGINS/src/hello/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/hello/HISTORY	2015-02-17 14:46:54.000000000 +0100
@@ -86,3 +86,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/hello/Makefile vdr-2.2.0/PLUGINS/src/hello/Makefile
--- vdr-2.0.0/PLUGINS/src/hello/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.18 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 LOCDIR = $(call PKGCFG,locdir)
 PLGCFG = $(call PKGCFG,plgcfg)
diff -ruN vdr-2.0.0/PLUGINS/src/hello/hello.c vdr-2.2.0/PLUGINS/src/hello/hello.c
--- vdr-2.0.0/PLUGINS/src/hello/hello.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/hello/hello.c	2015-02-17 14:12:26.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: hello.c 2.6 2013/03/31 09:30:18 kls Exp $
+ * $Id: hello.c 3.2 2015/02/17 13:12:26 kls Exp $
  */
 
 #include <getopt.h>
@@ -12,7 +12,7 @@
 #include <vdr/interface.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = trNOOP("A friendly greeting");
 static const char *MAINMENUENTRY  = trNOOP("Hello");
 
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/ca_ES.po vdr-2.2.0/PLUGINS/src/hello/po/ca_ES.po
--- vdr-2.0.0/PLUGINS/src/hello/po/ca_ES.po	2013-03-11 12:46:32.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/ca_ES.po	2015-02-19 10:12:30.578550596 +0100
@@ -1,5 +1,5 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Marc Rovira Vall <tm05462@salleURL.edu>, 2003
 # Ramon Roca <ramon.roca@xcombo.com>, 2003
@@ -7,7 +7,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/cs_CZ.po vdr-2.2.0/PLUGINS/src/hello/po/cs_CZ.po
--- vdr-2.0.0/PLUGINS/src/hello/po/cs_CZ.po	2013-03-11 12:46:41.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/cs_CZ.po	2015-02-19 10:12:30.656553930 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Vladim�r B�rta <vladimir.barta@k2atmitec.cz>, 2006
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/da_DK.po vdr-2.2.0/PLUGINS/src/hello/po/da_DK.po
--- vdr-2.0.0/PLUGINS/src/hello/po/da_DK.po	2013-03-11 12:46:48.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/da_DK.po	2015-02-19 10:12:30.599551493 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Mogens Elneff <mogens@elneff.dk>, 2004
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/de_DE.po vdr-2.2.0/PLUGINS/src/hello/po/de_DE.po
--- vdr-2.0.0/PLUGINS/src/hello/po/de_DE.po	2013-03-11 12:46:56.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/de_DE.po	2015-02-19 10:12:30.706556068 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Klaus Schmidinger <vdr@tvdr.de>, 2000
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/el_GR.po vdr-2.2.0/PLUGINS/src/hello/po/el_GR.po
--- vdr-2.0.0/PLUGINS/src/hello/po/el_GR.po	2013-03-11 12:47:16.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/el_GR.po	2015-02-19 10:12:30.643553373 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Dimitrios Dimitrakos <mail@dimitrios.de>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/es_ES.po vdr-2.2.0/PLUGINS/src/hello/po/es_ES.po
--- vdr-2.0.0/PLUGINS/src/hello/po/es_ES.po	2013-03-11 12:47:21.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/es_ES.po	2015-02-19 10:12:30.621552433 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Ruben Nunez Francisco <ruben.nunez@tang-it.com>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/et_EE.po vdr-2.2.0/PLUGINS/src/hello/po/et_EE.po
--- vdr-2.0.0/PLUGINS/src/hello/po/et_EE.po	2013-03-11 12:47:26.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/et_EE.po	2015-02-19 10:12:30.576550510 +0100
@@ -1,35 +1,35 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Arthur Konovalov <kasjas@hot.ee>, 2004
+# Arthur Konovalov <artlov@gmail.com>, 2004, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
-"Last-Translator: Arthur Konovalov <kasjas@hot.ee>\n"
+"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
 "Language-Team: Estonian <vdr@linuxtv.org>\n"
 "Language: et\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-13\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 msgid "A friendly greeting"
-msgstr ""
+msgstr "Sõbralik tervitus"
 
 msgid "Hello"
-msgstr ""
+msgstr "Tere"
 
 msgid "Greeting time (s)"
-msgstr ""
+msgstr "Tervituse kestus (s)"
 
 msgid "Use alternate greeting"
-msgstr ""
+msgstr "Vahelduv tervitus"
 
 msgid "Howdy folks!"
-msgstr ""
+msgstr "Kuidas käsi käib?"
 
 msgid "Hello world!"
-msgstr ""
+msgstr "Tere, maailm!"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/fi_FI.po vdr-2.2.0/PLUGINS/src/hello/po/fi_FI.po
--- vdr-2.0.0/PLUGINS/src/hello/po/fi_FI.po	2013-03-17 15:07:35.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/fi_FI.po	2015-02-19 10:12:30.609551922 +0100
@@ -1,5 +1,5 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Hannu Savolainen <hannu@opensound.com>, 2002
 # Jaakko Hyvätti <jaakko@hyvatti.iki.fi>, 2002
@@ -8,7 +8,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/fr_FR.po vdr-2.2.0/PLUGINS/src/hello/po/fr_FR.po
--- vdr-2.0.0/PLUGINS/src/hello/po/fr_FR.po	2013-03-11 12:47:36.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/fr_FR.po	2015-02-19 10:12:30.664554272 +0100
@@ -1,5 +1,5 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Jean-Claude Repetto <jc@repetto.org>, 2001
 # Olivier Jacques <jacquesolivier@hotmail.com>, 2003
@@ -8,7 +8,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/hr_HR.po vdr-2.2.0/PLUGINS/src/hello/po/hr_HR.po
--- vdr-2.0.0/PLUGINS/src/hello/po/hr_HR.po	2013-03-11 12:47:51.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/hr_HR.po	2015-02-19 10:12:30.689555341 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Adrian Caval <anrxc@sysphere.org>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2008-03-17 19:52+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/hu_HU.po vdr-2.2.0/PLUGINS/src/hello/po/hu_HU.po
--- vdr-2.0.0/PLUGINS/src/hello/po/hu_HU.po	2013-03-11 12:47:57.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/hu_HU.po	2015-02-19 10:12:30.589551067 +0100
@@ -1,12 +1,12 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Istvan Koenigsberger <istvnko@hotmail.com>, 2002
 # Guido Josten <guido.josten@t-online.de>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/it_IT.po vdr-2.2.0/PLUGINS/src/hello/po/it_IT.po
--- vdr-2.0.0/PLUGINS/src/hello/po/it_IT.po	2013-03-11 12:48:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/it_IT.po	2015-02-19 10:12:30.623552519 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Diego Pierotto <vdr-italian@tiscali.it>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2008-01-27 20:11+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/lt_LT.po vdr-2.2.0/PLUGINS/src/hello/po/lt_LT.po
--- vdr-2.0.0/PLUGINS/src/hello/po/lt_LT.po	2013-03-11 12:48:04.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/lt_LT.po	2015-02-19 10:12:30.672554614 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Valdemaras Pipiras <varas@ambernet.lt>, 2009
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2009-12-08 12:18+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/nl_NL.po vdr-2.2.0/PLUGINS/src/hello/po/nl_NL.po
--- vdr-2.0.0/PLUGINS/src/hello/po/nl_NL.po	2013-03-11 12:48:08.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/nl_NL.po	2015-02-19 10:12:30.638553162 +0100
@@ -1,5 +1,5 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>, 2001
 # Hans Dingemans <hans.dingemans@tacticalops.nl>, 2003
@@ -7,7 +7,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/nn_NO.po vdr-2.2.0/PLUGINS/src/hello/po/nn_NO.po
--- vdr-2.0.0/PLUGINS/src/hello/po/nn_NO.po	2013-03-11 12:48:11.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/nn_NO.po	2015-02-19 10:12:30.686555213 +0100
@@ -1,12 +1,12 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # J�rgen Tvedt <pjtvedt@online.no>, 2001
 # Truls Slevigen <truls@slevigen.no>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/pl_PL.po vdr-2.2.0/PLUGINS/src/hello/po/pl_PL.po
--- vdr-2.0.0/PLUGINS/src/hello/po/pl_PL.po	2013-03-11 12:48:14.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/pl_PL.po	2015-02-19 10:12:30.547549270 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Michael Rakowski <mrak@gmx.de>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/pt_PT.po vdr-2.2.0/PLUGINS/src/hello/po/pt_PT.po
--- vdr-2.0.0/PLUGINS/src/hello/po/pt_PT.po	2013-03-11 12:48:29.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/pt_PT.po	2015-02-19 10:12:30.544549144 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Paulo Lopes <pmml@netvita.pt>, 2001
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/ro_RO.po vdr-2.2.0/PLUGINS/src/hello/po/ro_RO.po
--- vdr-2.0.0/PLUGINS/src/hello/po/ro_RO.po	2013-03-11 12:48:35.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/ro_RO.po	2015-02-19 10:12:30.559549785 +0100
@@ -1,12 +1,12 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Paul Lacatus <paul@campina.iiruc.ro>, 2002
 # Lucian Muresan <lucianm@users.sourceforge.net>, 2004
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/ru_RU.po vdr-2.2.0/PLUGINS/src/hello/po/ru_RU.po
--- vdr-2.0.0/PLUGINS/src/hello/po/ru_RU.po	2013-03-11 12:48:40.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/ru_RU.po	2015-02-19 10:12:30.705556025 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Vyacheslav Dikonov <sdiconov@mail.ru>, 2004
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/sk_SK.po vdr-2.2.0/PLUGINS/src/hello/po/sk_SK.po
--- vdr-2.0.0/PLUGINS/src/hello/po/sk_SK.po	2013-03-11 12:48:44.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/sk_SK.po	2015-02-19 10:12:30.672554614 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Vladim�r B�rta <vladimir.barta@k2atmitec.cz>, 2006
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2009-09-30 09:48+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/sl_SI.po vdr-2.2.0/PLUGINS/src/hello/po/sl_SI.po
--- vdr-2.0.0/PLUGINS/src/hello/po/sl_SI.po	2013-03-11 12:48:47.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/sl_SI.po	2015-02-19 10:12:30.705556025 +0100
@@ -1,12 +1,12 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Miha Setina <mihasetina@softhome.net>, 2000
 # Matjaz Thaler <matjaz.thaler@guest.arnes.si>, 2003
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/sv_SE.po vdr-2.2.0/PLUGINS/src/hello/po/sv_SE.po
--- vdr-2.0.0/PLUGINS/src/hello/po/sv_SE.po	2013-03-11 12:48:52.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/sv_SE.po	2015-02-19 10:12:30.644553416 +0100
@@ -1,12 +1,12 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Tomas Prybil <tomas@prybil.se>, 2002
 # Jan Ekholm <chakie@infa.abo.fi>, 2003
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2007-08-11 12:34+0200\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/tr_TR.po vdr-2.2.0/PLUGINS/src/hello/po/tr_TR.po
--- vdr-2.0.0/PLUGINS/src/hello/po/tr_TR.po	2013-03-11 12:49:06.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/tr_TR.po	2015-02-19 10:12:30.716556496 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Oktay Yolge�en <oktay_73@yahoo.de>, 2007
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2008-05-12 22:34:4800\n"
diff -ruN vdr-2.0.0/PLUGINS/src/hello/po/zh_CN.po vdr-2.2.0/PLUGINS/src/hello/po/zh_CN.po
--- vdr-2.0.0/PLUGINS/src/hello/po/zh_CN.po	2013-03-11 12:49:10.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/hello/po/zh_CN.po	2015-02-19 10:12:30.589551067 +0100
@@ -1,14 +1,14 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Marc Rovira Vall <tm05462@salleURL.edu>, 2003
 # Ramon Roca <ramon.roca@xcombo.com>, 2003
 # Jordi Vilà <jvila@tinet.org>, 2003
-# Nan Feng VDR <nfgx@21cn.com>, 2009.2
+# Nan Feng VDR <nfgx@21cn.com>, 2009
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-hello 0.2.5\n"
+"Project-Id-Version: vdr-hello 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 14:04+0100\n"
 "PO-Revision-Date: 2009-01-23 09:48+0800\n"
diff -ruN vdr-2.0.0/PLUGINS/src/osddemo/HISTORY vdr-2.2.0/PLUGINS/src/osddemo/HISTORY
--- vdr-2.0.0/PLUGINS/src/osddemo/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/osddemo/HISTORY	2015-02-17 14:47:01.000000000 +0100
@@ -59,3 +59,16 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2014-02-06: Version 2.1.2
+
+- Fixed flickering if subtitles are active while the OSD demo is running.
+- Fixed a possible crash in the OSD demo (reported by Christopher Reimer).
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/osddemo/Makefile vdr-2.2.0/PLUGINS/src/osddemo/Makefile
--- vdr-2.0.0/PLUGINS/src/osddemo/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/osddemo/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.16 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/osddemo/osddemo.c vdr-2.2.0/PLUGINS/src/osddemo/osddemo.c
--- vdr-2.0.0/PLUGINS/src/osddemo/osddemo.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/osddemo/osddemo.c	2015-02-17 14:12:36.000000000 +0100
@@ -3,13 +3,13 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: osddemo.c 2.12 2013/03/31 09:30:18 kls Exp $
+ * $Id: osddemo.c 3.4 2015/02/17 13:12:36 kls Exp $
  */
 
 #include <vdr/osd.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "Demo of arbitrary OSD setup";
 static const char *MAINMENUENTRY  = "Osd Demo";
 
@@ -119,7 +119,7 @@
 
 void cLineGame::Show(void)
 {
-  osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop(), 50);
+  osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop());
   if (osd) {
      int x1 = cOsd::OsdWidth() - 1;
      int y1 = cOsd::OsdHeight() - 1;
@@ -480,6 +480,8 @@
         if (Delta < FrameTime)
            cCondWait::SleepMs(FrameTime - Delta);
         }
+  destroyablePixmap = NULL;
+  toggleablePixmap = NULL;
   delete OsdFont;
   delete SmlFont;
   delete LrgFont;
@@ -496,7 +498,7 @@
 
 void cTrueColorDemo::Show(void)
 {
-  osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop(), 50);
+  osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop());
   if (osd) {
      if (SetArea()) {
         osd->DrawRectangle(0, 0, osd->Width() - 1, osd->Height() - 1, clrGray50);
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/HISTORY vdr-2.2.0/PLUGINS/src/pictures/HISTORY
--- vdr-2.0.0/PLUGINS/src/pictures/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/pictures/HISTORY	2015-02-17 14:47:08.000000000 +0100
@@ -87,3 +87,15 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2013-07-01:
+
+- Added option -x to pic2mpg.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/Makefile vdr-2.2.0/PLUGINS/src/pictures/Makefile
--- vdr-2.0.0/PLUGINS/src/pictures/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.18 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 LOCDIR = $(call PKGCFG,locdir)
 PLGCFG = $(call PKGCFG,plgcfg)
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/entry.c vdr-2.2.0/PLUGINS/src/pictures/entry.c
--- vdr-2.0.0/PLUGINS/src/pictures/entry.c	2012-02-17 15:00:28.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/entry.c	2012-02-17 15:00:28.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: entry.c 2.1 2012/02/17 14:00:28 kls Exp $
+ * $Id: entry.c 3.0 2012/02/17 14:00:28 kls Exp $
  */
 
 #include "entry.h"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/entry.h vdr-2.2.0/PLUGINS/src/pictures/entry.h
--- vdr-2.0.0/PLUGINS/src/pictures/entry.h	2008-01-06 13:30:50.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/entry.h	2008-01-06 13:30:50.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: entry.h 2.0 2008/01/06 12:30:50 kls Exp $
+ * $Id: entry.h 3.0 2008/01/06 12:30:50 kls Exp $
  */
 
 #ifndef _ENTRY_H
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/menu.c vdr-2.2.0/PLUGINS/src/pictures/menu.c
--- vdr-2.0.0/PLUGINS/src/pictures/menu.c	2008-01-13 12:35:18.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/menu.c	2008-01-13 12:35:18.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: menu.c 2.0 2008/01/13 11:35:18 kls Exp $
+ * $Id: menu.c 3.0 2008/01/13 11:35:18 kls Exp $
  */
 
 #include "menu.h"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/menu.h vdr-2.2.0/PLUGINS/src/pictures/menu.h
--- vdr-2.0.0/PLUGINS/src/pictures/menu.h	2008-01-12 12:22:52.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/menu.h	2008-01-12 12:22:52.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: menu.h 2.0 2008/01/12 11:22:52 kls Exp $
+ * $Id: menu.h 3.0 2008/01/12 11:22:52 kls Exp $
  */
 
 #ifndef _MENU_H
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/pic2mpg vdr-2.2.0/PLUGINS/src/pictures/pic2mpg
--- vdr-2.0.0/PLUGINS/src/pictures/pic2mpg	2013-02-17 14:17:13.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/pic2mpg	2013-07-01 10:33:38.000000000 +0200
@@ -7,7 +7,7 @@
 #
 # See the README file for copyright information and how to reach the author.
 #
-# $Id: pic2mpg 2.5 2013/02/17 13:17:13 kls Exp $
+# $Id: pic2mpg 3.1 2013/07/01 08:33:38 kls Exp $
 
 use File::Path;
 use File::Spec;
@@ -23,9 +23,10 @@
          -o percent     overscan in percent
          -s size        Screen size (WIDTHxHEIGHT, default is 1920x1080)
          -v num         Verbose (0=none, 1=list files, 2=detailed)
+         -x dir[,...]   eXclude the given directories
 };
 
-getopts("fho:s:v:") || die $Usage;
+getopts("fho:s:v:x:") || die $Usage;
 
 die $Usage if $opt_h;
 
@@ -33,6 +34,7 @@
 $Overscan  = $opt_o || 0;
 $Size      = $opt_s || "1920x1080";
 $Verbose   = $opt_v;
+@Exclude   = split(',', $opt_x || "");
 
 $ListFiles = $Verbose >= 1;
 $Detailed  = $Verbose >= 2;
@@ -85,7 +87,11 @@
 @Pictures = `find -type f | sort`;
 chomp(@Pictures);
 
+PIC:
 for $pic (@Pictures) {
+    for (@Exclude) {
+        next PIC if ($pic =~ /\/$_\//);
+        }
     my $mpg = "$MPGDIR/$pic.mpg";
     if ($Force || !-e $mpg || -M $mpg > -M $pic) {
        (my $dir = $mpg) =~ s/\/[^\/]*$//;
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/pictures.c vdr-2.2.0/PLUGINS/src/pictures/pictures.c
--- vdr-2.0.0/PLUGINS/src/pictures/pictures.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/pictures/pictures.c	2015-02-17 14:12:50.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: pictures.c 2.9 2013/03/31 09:30:18 kls Exp $
+ * $Id: pictures.c 3.2 2015/02/17 13:12:50 kls Exp $
  */
 
 #include <getopt.h>
@@ -11,7 +11,7 @@
 #include "menu.h"
 #include "player.h"
 
-static const char *VERSION       = "2.0.0";
+static const char *VERSION       = "2.2.0";
 static const char *DESCRIPTION   = trNOOP("A simple picture viewer");
 static const char *MAINMENUENTRY = trNOOP("Pictures");
 
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/player.c vdr-2.2.0/PLUGINS/src/pictures/player.c
--- vdr-2.0.0/PLUGINS/src/pictures/player.c	2012-04-28 13:58:15.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/pictures/player.c	2014-02-08 13:48:12.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: player.c 2.2 2012/04/28 11:58:15 kls Exp $
+ * $Id: player.c 3.1 2014/02/08 12:48:12 kls Exp $
  */
 
 #include "player.h"
@@ -211,7 +211,7 @@
 
 eOSState cPictureControl::ProcessKey(eKeys Key)
 {
-  switch (Key) {
+  switch (int(Key)) {
     case kUp:
     case kPlay:   slideShowDelay.Set();
                   slideShow = true;
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/player.h vdr-2.2.0/PLUGINS/src/pictures/player.h
--- vdr-2.0.0/PLUGINS/src/pictures/player.h	2012-04-28 13:56:01.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/pictures/player.h	2012-04-28 13:56:01.000000000 +0200
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: player.h 2.1 2012/04/28 11:56:01 kls Exp $
+ * $Id: player.h 3.0 2012/04/28 11:56:01 kls Exp $
  */
 
 #ifndef _PLAYER_H
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/de_DE.po vdr-2.2.0/PLUGINS/src/pictures/po/de_DE.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/de_DE.po	2013-03-11 12:49:14.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/de_DE.po	2015-02-19 10:12:34.385713308 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Klaus Schmidinger <vdr@tvdr.de>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2008-01-12 17:41+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/et_EE.po vdr-2.2.0/PLUGINS/src/pictures/po/et_EE.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/et_EE.po	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/et_EE.po	2015-02-19 10:12:34.245707325 +0100
@@ -0,0 +1,33 @@
+# VDR plugin language source file.
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
+# This file is distributed under the same license as the VDR package.
+# Arthur Konovalov <artlov@gmail.com>, 2015
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-pictures 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2012-12-18 12:57+0100\n"
+"PO-Revision-Date: 2008-01-12 17:41+0100\n"
+"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
+"Language-Team: Estonian <vdr@linuxtv.org>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Pictures"
+msgstr "Pildid"
+
+msgid "A simple picture viewer"
+msgstr "Lihtne pildivaatur"
+
+msgid "Picture directory"
+msgstr "Pildikaust"
+
+msgid "Slide show delay (s)"
+msgstr "Slaidiseanssi viide (s)"
+
+msgid "No picture directory has been defined!"
+msgstr "Pildikaust on määramata!"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/fi_FI.po vdr-2.2.0/PLUGINS/src/pictures/po/fi_FI.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/fi_FI.po	2013-03-17 15:07:35.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/fi_FI.po	2015-02-19 10:12:34.269708349 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Rolf Ahrenberg <Rolf.Ahrenberg@sci.fi>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2008-01-12 17:41+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/fr_FR.po vdr-2.2.0/PLUGINS/src/pictures/po/fr_FR.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/fr_FR.po	2013-03-11 12:49:20.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/fr_FR.po	2015-02-19 10:12:34.335711168 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Patrice Staudt <ipatrice.staudt@laposte.net>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2008-01-12 17:41+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/it_IT.po vdr-2.2.0/PLUGINS/src/pictures/po/it_IT.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/it_IT.po	2013-03-11 12:49:23.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/it_IT.po	2015-02-19 10:12:34.290709242 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Diego Pierotto <vdr-italian@tiscali.it>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2008-01-27 20:22+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/lt_LT.po vdr-2.2.0/PLUGINS/src/pictures/po/lt_LT.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/lt_LT.po	2013-03-11 12:49:27.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/lt_LT.po	2015-02-19 10:12:34.317710401 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Valdemaras Pipiras <varas@ambernet.lt>, 2009
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2009-12-08 12:41+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/ru_RU.po vdr-2.2.0/PLUGINS/src/pictures/po/ru_RU.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/ru_RU.po	2013-03-11 12:49:29.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/ru_RU.po	2015-02-19 10:12:34.361712282 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Alexander Gross <Bikalexander@gmail.com>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2008-03-14 00:45+0100\n"
@@ -28,7 +28,7 @@
 msgstr "������� �����������"
 
 msgid "Slide show delay (s)"
-msgstr "��������  �����-��� (���)"
+msgstr "�������� �����-��� (���)"
 
 msgid "No picture directory has been defined!"
 msgstr "�� ��������� ������� �����������!"
diff -ruN vdr-2.0.0/PLUGINS/src/pictures/po/sk_SK.po vdr-2.2.0/PLUGINS/src/pictures/po/sk_SK.po
--- vdr-2.0.0/PLUGINS/src/pictures/po/sk_SK.po	2013-03-11 12:49:33.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/pictures/po/sk_SK.po	2015-02-19 10:12:34.376712923 +0100
@@ -1,11 +1,11 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Klaus Schmidinger <vdr@tvdr.de>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-pictures 0.1.3\n"
+"Project-Id-Version: vdr-pictures 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
 "POT-Creation-Date: 2012-12-18 12:57+0100\n"
 "PO-Revision-Date: 2009-09-30 12:54+0100\n"
diff -ruN vdr-2.0.0/PLUGINS/src/rcu/HISTORY vdr-2.2.0/PLUGINS/src/rcu/HISTORY
--- vdr-2.0.0/PLUGINS/src/rcu/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/rcu/HISTORY	2015-02-17 14:47:13.000000000 +0100
@@ -20,3 +20,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/rcu/Makefile vdr-2.2.0/PLUGINS/src/rcu/Makefile
--- vdr-2.0.0/PLUGINS/src/rcu/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/rcu/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 1.13 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/rcu/rcu.c vdr-2.2.0/PLUGINS/src/rcu/rcu.c
--- vdr-2.0.0/PLUGINS/src/rcu/rcu.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/rcu/rcu.c	2015-02-17 14:13:00.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: rcu.c 1.5 2013/03/31 09:30:18 kls Exp $
+ * $Id: rcu.c 3.2 2015/02/17 13:13:00 kls Exp $
  */
 
 #include <getopt.h>
@@ -16,7 +16,7 @@
 #include <vdr/thread.h>
 #include <vdr/tools.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "Remote Control Unit";
 
 #define REPEATLIMIT      150 // ms
diff -ruN vdr-2.0.0/PLUGINS/src/servicedemo/HISTORY vdr-2.2.0/PLUGINS/src/servicedemo/HISTORY
--- vdr-2.0.0/PLUGINS/src/servicedemo/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/servicedemo/HISTORY	2015-02-17 14:47:18.000000000 +0100
@@ -21,3 +21,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/servicedemo/Makefile vdr-2.2.0/PLUGINS/src/servicedemo/Makefile
--- vdr-2.0.0/PLUGINS/src/servicedemo/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/servicedemo/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.14 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -17,7 +17,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/servicedemo/svccli.c vdr-2.2.0/PLUGINS/src/servicedemo/svccli.c
--- vdr-2.0.0/PLUGINS/src/servicedemo/svccli.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/servicedemo/svccli.c	2015-02-17 14:13:05.000000000 +0100
@@ -3,14 +3,14 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: svccli.c 2.4 2013/03/31 09:30:18 kls Exp $
+ * $Id: svccli.c 3.2 2015/02/17 13:13:05 kls Exp $
  */
 
 #include <stdlib.h>
 #include <vdr/interface.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "Service demo client";
 static const char *MAINMENUENTRY  = "Service demo";
 
diff -ruN vdr-2.0.0/PLUGINS/src/servicedemo/svcsvr.c vdr-2.2.0/PLUGINS/src/servicedemo/svcsvr.c
--- vdr-2.0.0/PLUGINS/src/servicedemo/svcsvr.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/servicedemo/svcsvr.c	2015-02-17 14:13:09.000000000 +0100
@@ -3,14 +3,14 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: svcsvr.c 2.4 2013/03/31 09:30:18 kls Exp $
+ * $Id: svcsvr.c 3.2 2015/02/17 13:13:09 kls Exp $
  */
 
 #include <stdlib.h>
 #include <vdr/interface.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "Service demo server";
 
 class cPluginSvcSvr : public cPlugin {
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/HISTORY vdr-2.2.0/PLUGINS/src/skincurses/HISTORY
--- vdr-2.0.0/PLUGINS/src/skincurses/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/skincurses/HISTORY	2015-02-17 14:47:24.000000000 +0100
@@ -118,3 +118,15 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-08: Version 2.1.2
+
+- Added a missing trailing blank in some translations.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/Makefile vdr-2.2.0/PLUGINS/src/skincurses/Makefile
--- vdr-2.0.0/PLUGINS/src/skincurses/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.18 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 LOCDIR = $(call PKGCFG,locdir)
 PLGCFG = $(call PKGCFG,plgcfg)
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/de_DE.po vdr-2.2.0/PLUGINS/src/skincurses/po/de_DE.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/de_DE.po	2013-03-11 12:49:36.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/de_DE.po	2015-02-19 10:12:35.848775838 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Klaus Schmidinger <vdr@tvdr.de>, 2007
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2007-08-15 16:07+0200\n"
 "Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
 "Language-Team: German <vdr@linuxtv.org>\n"
@@ -22,8 +22,9 @@
 msgid "Key$Mute"
 msgstr "Stumm"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
-msgstr "Lautst�rke"
+msgstr "Lautst�rke "
 
 msgid "Text mode"
 msgstr "Text-Modus"
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/et_EE.po vdr-2.2.0/PLUGINS/src/skincurses/po/et_EE.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/et_EE.po	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/et_EE.po	2015-02-19 10:12:35.799773743 +0100
@@ -0,0 +1,30 @@
+# VDR plugin language source file.
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
+# This file is distributed under the same license as the VDR package.
+# Arthur Konovalov <artlov@gmail.com>, 2015
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
+"PO-Revision-Date: 2007-08-14 20:48+0300\n"
+"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
+"Language-Team: Estonian <vdr@linuxtv.org>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "A text only skin"
+msgstr "Tekstipõhine kest"
+
+msgid "Key$Mute"
+msgstr "Hääletu"
+
+#. TRANSLATORS: note the trailing blank!
+msgid "Volume "
+msgstr "Helitugevus "
+
+msgid "Text mode"
+msgstr "Tekst"
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/fi_FI.po vdr-2.2.0/PLUGINS/src/skincurses/po/fi_FI.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/fi_FI.po	2013-03-17 15:07:35.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/fi_FI.po	2015-02-19 10:12:35.801773829 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Rolf Ahrenberg <Rolf.Ahrenberg@sci.fi>, 2007
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2007-08-14 20:48+0300\n"
 "Last-Translator: Rolf Ahrenberg <Rolf.Ahrenberg@sci.fi>\n"
 "Language-Team: Finnish <vdr@linuxtv.org>\n"
@@ -22,6 +22,7 @@
 msgid "Key$Mute"
 msgstr "Mykistys"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
 msgstr "Äänenvoimakkuus "
 
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/it_IT.po vdr-2.2.0/PLUGINS/src/skincurses/po/it_IT.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/it_IT.po	2013-03-11 12:49:41.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/it_IT.po	2015-02-19 10:12:35.816774470 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Diego Pierotto <vdr-italian@tiscali.it>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2008-01-27 20:35+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
 "Language-Team: Italian <vdr@linuxtv.org>\n"
@@ -22,6 +22,7 @@
 msgid "Key$Mute"
 msgstr "Muto"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
 msgstr "Volume "
 
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/lt_LT.po vdr-2.2.0/PLUGINS/src/skincurses/po/lt_LT.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/lt_LT.po	2013-03-11 12:49:44.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/lt_LT.po	2015-02-19 10:12:35.820774640 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Valdemaras Pipiras <varas@ambernet.lt>, 2010
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2010-02-22 18:18+0200\n"
 "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
 "Language-Team: Lithuanian <vdr@linuxtv.org>\n"
@@ -22,6 +22,7 @@
 msgid "Key$Mute"
 msgstr "Išjungti garsą"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
 msgstr "Garsas "
 
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/ru_RU.po vdr-2.2.0/PLUGINS/src/skincurses/po/ru_RU.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/ru_RU.po	2013-03-11 12:49:46.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/ru_RU.po	2015-02-19 10:12:35.837775367 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Alexander Gross <Bikalexander@gmail.com>, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2008-03-14 00:21+0100\n"
 "Last-Translator: Alexander Gross <Bikalexander@gmail.com>\n"
 "Language-Team: Russian <vdr@linuxtv.org>\n"
@@ -23,8 +23,9 @@
 msgid "Key$Mute"
 msgstr "����. ���������"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
-msgstr "���������"
+msgstr "��������� "
 
 msgid "Text mode"
 msgstr "����� ������"
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/po/sk_SK.po vdr-2.2.0/PLUGINS/src/skincurses/po/sk_SK.po
--- vdr-2.0.0/PLUGINS/src/skincurses/po/sk_SK.po	2013-03-11 12:49:48.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/skincurses/po/sk_SK.po	2015-02-19 10:12:35.837775367 +0100
@@ -1,13 +1,13 @@
 # VDR plugin language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Klaus Schmidinger <vdr@tvdr.de>, 2007
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: vdr-skincurses 0.1.12\n"
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2012-12-18 14:04+0100\n"
+"POT-Creation-Date: 2015-02-08 11:16+0100\n"
 "PO-Revision-Date: 2009-09-30 12:52+0100\n"
 "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
 "Language-Team: Slovak <vdr@linuxtv.org>\n"
@@ -22,8 +22,9 @@
 msgid "Key$Mute"
 msgstr "Stlmi� zvuk"
 
+#. TRANSLATORS: note the trailing blank!
 msgid "Volume "
-msgstr "Hlasitos�"
+msgstr "Hlasitos� "
 
 msgid "Text mode"
 msgstr "Textov� re�im"
diff -ruN vdr-2.0.0/PLUGINS/src/skincurses/skincurses.c vdr-2.2.0/PLUGINS/src/skincurses/skincurses.c
--- vdr-2.0.0/PLUGINS/src/skincurses/skincurses.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/skincurses/skincurses.c	2015-02-17 14:13:17.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: skincurses.c 2.13 2013/03/31 09:30:18 kls Exp $
+ * $Id: skincurses.c 3.3 2015/02/17 13:13:17 kls Exp $
  */
 
 #include <ncurses.h>
@@ -12,7 +12,7 @@
 #include <vdr/skins.h>
 #include <vdr/videodir.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = trNOOP("A text only skin");
 static const char *MAINMENUENTRY  = NULL;
 
@@ -614,6 +614,7 @@
      osd->DrawText(0, 0, tr("Key$Mute"), clrGreen, clrBackground, &Font);
      }
   else {
+     // TRANSLATORS: note the trailing blank!
      const char *Prompt = tr("Volume ");
      int l = Utf8StrLen(Prompt);
      int p = (ScOsdWidth - l) * Current / Total;
diff -ruN vdr-2.0.0/PLUGINS/src/status/HISTORY vdr-2.2.0/PLUGINS/src/status/HISTORY
--- vdr-2.0.0/PLUGINS/src/status/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/status/HISTORY	2015-02-17 14:47:32.000000000 +0100
@@ -60,3 +60,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/status/Makefile vdr-2.2.0/PLUGINS/src/status/Makefile
--- vdr-2.0.0/PLUGINS/src/status/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/status/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.15 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/status/status.c vdr-2.2.0/PLUGINS/src/status/status.c
--- vdr-2.0.0/PLUGINS/src/status/status.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/status/status.c	2015-02-17 14:13:21.000000000 +0100
@@ -3,13 +3,13 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: status.c 2.4 2013/03/31 09:30:18 kls Exp $
+ * $Id: status.c 3.2 2015/02/17 13:13:21 kls Exp $
  */
 
 #include <vdr/plugin.h>
 #include <vdr/status.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "Status monitor test";
 static const char *MAINMENUENTRY  = NULL;
 
diff -ruN vdr-2.0.0/PLUGINS/src/svdrpdemo/HISTORY vdr-2.2.0/PLUGINS/src/svdrpdemo/HISTORY
--- vdr-2.0.0/PLUGINS/src/svdrpdemo/HISTORY	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/svdrpdemo/HISTORY	2015-02-17 14:47:36.000000000 +0100
@@ -25,3 +25,11 @@
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+
+2015-02-19: Version 2.2.0
+
+- Official release.
diff -ruN vdr-2.0.0/PLUGINS/src/svdrpdemo/Makefile vdr-2.2.0/PLUGINS/src/svdrpdemo/Makefile
--- vdr-2.0.0/PLUGINS/src/svdrpdemo/Makefile	2013-01-12 14:45:01.000000000 +0100
+++ vdr-2.2.0/PLUGINS/src/svdrpdemo/Makefile	2014-01-01 14:29:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a Video Disk Recorder plugin
 #
-# $Id: Makefile 2.15 2013/01/12 13:45:01 kls Exp $
+# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -16,7 +16,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 PLGCFG = $(call PKGCFG,plgcfg)
 #
diff -ruN vdr-2.0.0/PLUGINS/src/svdrpdemo/svdrpdemo.c vdr-2.2.0/PLUGINS/src/svdrpdemo/svdrpdemo.c
--- vdr-2.0.0/PLUGINS/src/svdrpdemo/svdrpdemo.c	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/PLUGINS/src/svdrpdemo/svdrpdemo.c	2015-02-17 14:13:29.000000000 +0100
@@ -3,12 +3,12 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: svdrpdemo.c 2.3 2013/03/31 09:30:18 kls Exp $
+ * $Id: svdrpdemo.c 3.2 2015/02/17 13:13:29 kls Exp $
  */
 
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.2.0";
 static const char *DESCRIPTION    = "How to add SVDRP support to a plugin";
 
 class cPluginSvdrpdemo : public cPlugin {
diff -ruN vdr-2.0.0/PLUGINS.html vdr-2.2.0/PLUGINS.html
--- vdr-2.0.0/PLUGINS.html	2013-03-11 14:09:44.000000000 +0100
+++ vdr-2.2.0/PLUGINS.html	2015-02-17 16:11:36.000000000 +0100
@@ -31,14 +31,14 @@
 <div class="center">
 <h1>The VDR Plugin System</h1>
 
-<b>Version 2.0</b>
+<b>Version 2.2</b>
 <p>
-Copyright &copy; 2013 Klaus Schmidinger<br>
+Copyright &copy; 2015 Klaus Schmidinger<br>
 <a href="mailto:vdr@tvdr.de">vdr@tvdr.de</a><br>
 <a href="http://www.tvdr.de">www.tvdr.de</a>
 </div>
 <div class="center">
-<modified>Important modifications introduced since version 1.6 are marked like this.</modified>
+<modified>Important modifications introduced since version 2.0 are marked like this.</modified>
 </div>
 <p>
 VDR provides an easy to use plugin interface that allows additional functionality
@@ -82,7 +82,7 @@
 <li><a href="#Wakeup">Wakeup</a>
 <li><a href="#Setup parameters">Setup parameters</a>
 <li><a href="#The Setup menu">The Setup menu</a>
-<li><modified><a href="#Additional files">Additional files</modified></a>
+<li><a href="#Additional files">Additional files</a>
 <li><a href="#Internationalization">Internationalization</a>
 <li><a href="#Custom services">Custom services</a>
 <li><a href="#SVDRP commands">SVDRP commands</a>
@@ -99,10 +99,12 @@
 <li><a href="#Skins">Skins</a>
 <li><a href="#Themes">Themes</a>
 <li><a href="#Devices">Devices</a>
+<li><modified><a href="#Positioners">Positioners</a></modified>
 <li><a href="#Audio">Audio</a>
 <li><a href="#Remote Control">Remote Control</a>
 <li><a href="#Conditional Access">Conditional Access</a>
-<li><modified><a href="#Electronic Program Guide">Electronic Program Guide</modified></a>
+<li><a href="#Electronic Program Guide">Electronic Program Guide</a>
+<li><modified><a href="#The video directory">The video directory</a></modified>
 </ul>
 </ul>
 
@@ -173,15 +175,13 @@
 the name of that plugin (in the above example that would be <tt>hello</tt>).
 What's inside the individual source directory of a
 plugin is entirely up to the author of that plugin. The only prerequisites are
-that there is a <tt>Makefile</tt> that provides the targets <tt>all</tt><modified>, <tt>install</tt></modified> and
+that there is a <tt>Makefile</tt> that provides the targets <tt>all</tt>, <tt>install</tt> and
 <tt>clean</tt>, and that a call to <tt>make all</tt> actually produces a dynamically
 loadable library file for that plugin (we'll get to the details later).
-<modified>
 The dynamically loadable library file for the plugin shall be located directly under
 the plugin's source directory.
 See the section <a href="#Initializing a new plugin directory">Initializing a new plugin directory</a>
 for how to generate an example Makefile.
-</modified>
 <p>
 The <tt>lib</tt> directory contains the dynamically loadable libraries of all
 available plugins. Note that the names of these files are created by concatenating
@@ -891,70 +891,51 @@
 your setup parameters and use that one to copy all parameters with one single statement
 (like VDR does with its cSetup class).
 
-<hr><h2><modified><a name="Additional files">Additional files</a></modified></h2>
+<hr><h2><a name="Additional files">Additional files</a></h2>
 
 <div class="blurb">I want my own stuff!</div><p>
 
-<modified>
 There may be situations where a plugin requires files of its own. While the plugin is
 free to store such files anywhere it sees fit, it might be a good idea to put them in a common
 place, preferably where such data already exists.
-</modified>
 <p>
-<modified>
 <i>configuration files</i>, maybe for data that can't be stored in the simple
 <a href="#Setup parameters">setup parameters</a> of VDR, or maybe because it needs to
 launch other programs that simply need a separate configuration file.
-</modified>
 <p>
-<modified>
 <i>cache files</i>, to store data so that future requests for that data can be served faster. The data
 that is stored within a cache might be values that have been computed earlier or duplicates of
 original values that are stored elsewhere.
-</modified>
 <p>
-<modified>
 <i>resource files</i>, for providing additional files, like pictures, movie clips or channel logos.
-</modified>
 <p>
-<modified>
 Therefore VDR provides the functions
 
 <p><table><tr><td class="code"><pre>
-<modified>
 const char *ConfigDirectory(const char *PluginName = NULL);
 const char *CacheDirectory(const char *PluginName = NULL);
 const char *ResourceDirectory(const char *PluginName = NULL);
-</modified>
 </pre></td></tr></table><p>
 
-<modified>
 each of which returns a string containing the directory that VDR uses for its own
 files (defined through the options in the call to VDR), extended by
-</modified>
 <tt>"/plugins"</tt>. So assuming the VDR configuration directory is <tt>/video</tt>
 (the default if no <tt><b>-c</b></tt> or <tt><b>-v</b></tt> option is given),
 a call to <tt>ConfigDirectory()</tt> will return <tt>/video/plugins</tt>. The first
 call to <tt>ConfigDirectory()</tt> will automatically make sure that the <tt>plugins</tt>
 subdirectory will exist. If, for some reason, this cannot be achieved, <tt>NULL</tt>
 will be returned.
-<modified>
 The behavior of <tt>CacheDirectory()</tt> and <tt>ResourceDirectory()</tt> is similar.
-</modified>
 <p>
 The additional <tt>plugins</tt> directory is used to keep files from plugins apart
 from those of VDR itself, making sure there will be no name clashes. If a plugin
-<modified>
 needs only one extra file, it is suggested that this file be named <tt>name.*</tt>,
 where <i>name</i> shall be the name of the plugin.
-</modified>
 <p>
 If a plugin needs more than one such file, it is suggested that the plugin stores
 these in a subdirectory of its own, named after the plugin. To easily get such a name
-<modified>
 the functions can be given an additional string that will be appended to the returned
 directory name, as in
-</modified>
 
 <p><table><tr><td class="code"><pre>
 const char *MyConfigDir = ConfigDirectory(Name());
@@ -965,16 +946,12 @@
 (or return <tt>NULL</tt> in case of an error).
 <p>
 <b>
-<modified>
 The returned strings are statically allocated and will be overwritten by subsequent calls!
-</modified>
 </b>
 <p>
-<modified>
 The <tt>ConfigDirectory()</tt>, <tt>CacheDirectory()</tt> and <tt>ResourceDirectory()</tt>
 functions are static member functions of the <tt>cPlugin</tt> class. This allows them to be
 called even from outside any member function of the derived plugin class, by writing
-</modified>
 
 <p><table><tr><td class="code"><pre>
 const char *MyConfigDir = cPlugin::ConfigDirectory();
@@ -1265,10 +1242,10 @@
 
 class cMyStatusMonitor : public cStatus {
 protected:
-  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber<modified>, bool LiveView</modified>);
+  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
   };
 
-void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber<modified>, bool LiveView</modified>)
+void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
 {
   if (ChannelNumber)
      dsyslog("channel switched to %d on DVB %d", ChannelNumber, Device-&gt;CardIndex());
@@ -1525,13 +1502,11 @@
   cMyReceiver(int Pid);
   };
 
-<modified>
 cMyReceiver::cMyReceiver(int Pid)
 :cReceiver(NULL, -1)
 {
   AddPid(Pid);
 }
-</modified>
 
 cMyReceiver::~cMyReceiver()
 {
@@ -1557,7 +1532,7 @@
 PID (for example the Teletext PID). In order to not interfere with other recording
 operations, it sets its priority to <tt>-1</tt> (any negative value will allow
 a <tt>cReceiver</tt> to be detached from its <tt>cDevice</tt> at any time
-<modified>in favor of a timer recording or live viewing</modified>).
+in favor of a timer recording or live viewing).
 <p>
 Once a <tt>cReceiver</tt> has been created, it needs to be <i>attached</i> to
 a <tt>cDevice</tt>:
@@ -1573,9 +1548,7 @@
 video stream (this may be different from the primary device in case of <i>Transfer
 Mode</i>).
 <p>
-<modified>
 The <tt>cReceiver</tt> must be detached from its device before it is deleted.
-</modified>
 
 <hr><h2><a name="Filters">Filters</a></h2>
 
@@ -1853,7 +1826,7 @@
 selecting which channel it shall tune to:
 
 <p><table><tr><td class="code"><pre>
-<modified>virtual int NumProvidedSystems(void) const;</modified>
+virtual int NumProvidedSystems(void) const;
 virtual bool ProvidesSource(int Source) const;
 virtual bool ProvidesTransponder(const cChannel *Channel) const;
 virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
@@ -1902,9 +1875,9 @@
 virtual bool CanReplay(void) const;
 virtual bool SetPlayMode(ePlayMode PlayMode);
 virtual int64_t GetSTC(void);
-<modified>virtual bool IsPlayingVideo(void) const;</modified>
+virtual bool IsPlayingVideo(void) const;
 virtual bool HasIBPTrickSpeed(void);
-virtual void TrickSpeed(int Speed);
+virtual void TrickSpeed(int Speed<modified>, bool Forward</modified>);
 virtual void Clear(void);
 virtual void Play(void);
 virtual void Freeze(void);
@@ -1931,7 +1904,7 @@
 
 <p><table><tr><td class="code"><pre>
 virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
-<modified>virtual int ReadFilter(int Handle, void *Buffer, size_t Length);</modified>
+virtual int ReadFilter(int Handle, void *Buffer, size_t Length);
 virtual void CloseFilter(int Handle);
 </pre></td></tr></table><p>
 
@@ -1987,7 +1960,6 @@
 a "full featured" DVB card) or through a graphics adapter that overlays its
 output with the video signal, doesn't matter.
 <p>
-<div class="modified">
 In order to be able to determine the proper size of the OSD, the device
 should implement the function
 
@@ -1996,7 +1968,6 @@
 </pre></td></tr></table><p>
 
 By default, an OSD size of 720x480 with an aspect ratio of 1.0 is assumed.
-</div modified>
 
 <p>
 <b>Initializing new devices</b>
@@ -2017,8 +1988,6 @@
 shut down (delete) all devices when the program terminates. It is therefore
 important that the devices are created on the heap, using the <tt>new</tt>
 operator!
-
-<div class="modified">
 <p>
 <b>Device hooks</b>
 <p>
@@ -2056,6 +2025,46 @@
 </pre></td></tr></table><p>
 
 and shall not delete this object. It will be automatically deleted when the program ends.
+
+<div class="modified">
+<hr><h2><a name="Positioners">Positioners</a></h2>
+
+<div class="blurb">Now you see me - now you don't!</div><p>
+
+If you are using a positioner (also known as "motor" or "rotor") to move your
+satellite dish to receive various satellites, you will be using the 'P' command
+in the <tt>diseqc.conf</tt> file. This command sends the necessary data to the
+positioner to move the dish to the satellite's orbital position. By default VDR
+uses its builtin DiSEqC positioner control. If your positioner requires a different
+method of controlling (like maybe via a serial link), you can derive a class
+from <tt>cPositioner</tt>, as in
+
+<p><table><tr><td class="code"><pre>
+#include &lt;vdr/positioner.h&gt;
+
+class cMyPositioner : public cPositioner {
+public:
+  cMyPositioner(void);
+  virtual void Drive(ePositionerDirection Direction);
+  virtual void Step(ePositionerDirection Direction, uint Steps = 1);
+  virtual void Halt(void);
+  virtual void SetLimit(ePositionerDirection Direction);
+  virtual void DisableLimits(void);
+  virtual void EnableLimits(void);
+  virtual void StorePosition(uint Number);
+  virtual void RecalcPositions(uint Number);
+  virtual void GotoPosition(uint Number, int Longitude);
+  virtual void GotoAngle(int Longitude);
+  };
+</pre></td></tr></table><p>
+
+See the implementation of <tt>cDiseqcPositioner</tt> in <tt>diseqc.c</tt> for details.
+<p>
+You should create your derived positioner object in the
+<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
+Note that the object has to be created on the heap (using <tt>new</tt>),
+and you shall not delete it at any point (it will be deleted automatically
+when the program ends).
 </div modified>
 
 <hr><h2><a name="Audio">Audio</a></h2>
@@ -2222,12 +2231,10 @@
 
 The other parameters have the same meaning as in the first version of this function.
 <p>
-<modified>
 If your remote control has a repeat function that automatically repeats key events
 if a key is held pressed down for a while, your derived class should use the global
 parameters <tt>Setup.RcRepeatDelay</tt> and <tt>Setup.RcRepeatDelta</tt> to allow
 users to configure the behavior of this function.
-</modified>
 
 <hr><h2><a name="Conditional Access">Conditional Access</a></h2>
 
@@ -2264,7 +2271,6 @@
 
 See the description of this function in <tt>ci.h</tt> for details.
 
-<div class="modified">
 <hr><h2><a name="Electronic Program Guide">Electronic Program Guide</a></h2>
 
 <div class="blurb">The grass is always greener on the other side...</div><p>
@@ -2294,6 +2300,41 @@
 to signal VDR that no other EPG handlers shall be queried after this one.
 <p>
 See <tt>VDR/epg.h</tt> for details.
+
+<div class="modified">
+<hr><h2><a name="The video directory">The video directory</a></h2>
+
+<div class="blurb">Bits and pieces...</div><p>
+
+By default VDR assumes that the video directory consists of one large
+volume, on which it can store its recordings. If you want to distribute your
+recordings over several physical drives, you can derive from <tt>cVideoDirectory</tt>,
+as in
+
+<p><table><tr><td class="code"><pre>
+#include &lt;vdr/videodir.h&gt;
+
+class cMyVideoDirectory : public cVideoDirectory {
+public:
+  cMyVideoDirectory(void);
+  virtual ~cMyVideoDirectory();
+  virtual int FreeMB(int *UsedMB = NULL);
+  virtual bool Register(const char *FileName);
+  virtual bool Rename(const char *OldName, const char *NewName);
+  virtual bool Move(const char *FromName, const char *ToName);
+  virtual bool Remove(const char *Name);
+  virtual void Cleanup(const char *IgnoreFiles[] = NULL);
+  virtual bool Contains(const char *Name);
+  };
+</pre></td></tr></table><p>
+
+See the description in <tt>videodir.h</tt> for details.
+<p>
+You should create your derived video directory object in the
+<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
+Note that the object has to be created on the heap (using <tt>new</tt>),
+and you shall not delete it at any point (it will be deleted automatically
+when the program ends).
 </div modified>
 
 </body>
diff -ruN vdr-2.0.0/UPDATE-2.2.0 vdr-2.2.0/UPDATE-2.2.0
--- vdr-2.0.0/UPDATE-2.2.0	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/UPDATE-2.2.0	2015-02-11 10:48:02.000000000 +0100
@@ -0,0 +1,419 @@
+This is a summary of the changes in VDR 2.2.0 since the last stable
+version 2.0.0. It only contains things that are of actual importance
+to the user and doesn't mention the many fixes and improvements that
+have been made "behind the scenes".
+
+See the file HISTORY for a detailed list of all changes.
+
+Support for steerable satellite dishes:
+
+- Added basic support for positioners to control steerable satellite dishes.
+  + Supports GotoN (aka "DiSEqC 1.2") and GotoX (aka "USALS").
+  + The new DiSEqC command code 'P' can be used to instruct a positioner to move the
+    dish to the required satellite position. When a 'P' code is processed, further
+    execution of the remaining DiSEqC sequence (if any) is postponed until the positioner
+    has reached the new satellite position.
+  + The new special source value of "S360E" can be used in diseqc.conf to indicate that
+    an entry using a positioner can move the dish to any requested position within its
+    range. Think of it as "full circle".
+  + The devices a particular cDiseqc or cScr applies to are now stored directly in each
+    cDiseqc or cScr, respectively.
+  + A plugin can implement a custom positioner control (see PLUGINS.html, section "Positioners").
+  + The new function cSkinDisplayChannel::SetPositioner() can be implemented by skins to
+    show the user a progress display when the dish is being moved. The default implementation
+    calls SetMessage() with a string indicating the new position the dish is being moved to.
+    The LCARS skin shows a progress bar indicating the movement of the dish.
+  + The new parameters "Site latitude", "Site longitude", "Positioner speed", and
+    "Positioner swing" in the "Setup/LNB" menu can be used to configure the necessary
+    values for a steerable dish.
+  + The cDvbTuner now has a new status tsPositioning, in which it waits until a steerable
+    dish has reached its target position. Parsing SI data is paused until the target
+    position has been reached.
+- Changed the sign of the satellite position value in cSource to reflect the standard
+  of western values being negative. The new member function cSource::Position() can be
+  used to retrieve the orbital position of a satellite.
+
+Conditional Access:
+
+- The new function cCamSlot::Decrypt() can be used by derived classes to implement a
+  CAM slot that can be freely assigned to any device, without being directly inserted
+  into the full TS data stream in hardware. A derived class that implements Decrypt()
+  will also need to set the new parameter WantsTsData in the call to the cCamSlot
+  base class constructor to true, in order to receive all the TS packets that
+  contain data necessary for decrypting.
+- Many member functions of cCamSlot have been made virtual to allow for easier
+  implementation of derived classes.
+- cDvbDevice::GetTSPacket() now calls CamSlot()->Decrypt() in order to allow CAM slots
+  that can be freely assigned to any device access to the TS data stream.
+- Improved locking for CAM slots and made the pure functions of cCiAdapter have
+  default implementations, to fix a possible crash with CI adapters and CAM slots
+  that are implemented in a plugin.
+- Added logging the supported system ids of a CAM.
+- Now waiting explicitly until all CAM slots are ready before switching to the
+  initial channel when VDR is started. This is necessary in case CI adapters are
+  used that are not physically connected to a dedicated device. The respective checks
+  in cDvbDevice have been removed to avoid redundancy.
+- Now unassigning CAMs from their devices when they are no longer used.
+- Now making sure the primary device goes into transfer mode for live viewing if the
+  CAM wants to receive the TS data.
+- Added a log message in case a receiver is detached from its device because the
+  assigned CAM can't decrypt the channel.
+- The pid of the PMT in which the CA descriptors of a given channel are broadcast
+  is now stored together with the CA descriptors and can be retrieved by calling
+  GetPmtPid() (this information is only required to receive encrypted channels
+  with the OctopusNet receiver via the 'satip' plugin).
+- Modified the CAM API so that it is possible to implement CAMs that can be freely
+  assigned to any devices.
+- The Yellow button in the "Setup/CAM" menu can now be used to put the selected
+  CAM into a mode where it remains assigned to a device that is tuned to the current
+  channel until the smart card it contains is activated and the CAM thus starts to
+  descramble (see MANUAL, section "Setup/CAM" for details).
+- Fixed cCamSlot::Assign(), so that it actually ignores the value of Query if Device
+  is NULL (as described in the header file).
+
+Satellite Channel Routing:
+
+- Added support for "Satellite Channel Routing" (SCR) according to EN50607, also
+  known as "JESS".
+
+Subtitles:
+
+- Improved displaying DVB subtitles.
+- The debug output now goes into an HTML file named dbg-log.htm and shows the actual
+  bitmaps (dbg-nnn.jpg) used to display the subtitles. That way it is much easier to
+  see what's actually going on.
+- The new function cOsd::DrawScaledBitmap() is now used for drawing subtitles.
+  This function can be reimplemented by high level OSDs which may be able to do
+  the scaling in hardware or otherwise more efficiently.
+- Fixed (well, actually worked around) a problem with subtitles not being displayed
+  because the broadcaster doesn't set the data's version numbers as required by the
+  DVB standard.
+- Added support for PGS subtitles.
+
+Plugins:
+
+- A plugin can implement a custom positioner control (see PLUGINS.html, section "Positioners").
+- The new menu category mcRecordingEdit is now used to mark menus that edit recording
+  properties.
+- The Recordings menu can now be called with a cRecordingFilter, which allows the
+  caller to have it display only a certain subset of the recordings.
+- cDevice::TrickSpeed() now has an additional parameter named Forward, which indicates
+  the direction in which replay is being done. This information may be necessary for
+  some output devices in order to properly implement trick modes. Authors of plugins
+  that implement output devices will need to add this parameter to their derived cDevice
+  class, regardless of whether they will make use of it or not.
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
+  Plugin authors may want to apply the following change to their Makefile:
+  -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+  +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
+- The new function cStatus::ChannelChange() can be implemented by plugins to be
+  informed about changes to the parameters of a channel that may require a retune.
+  This may, for instance, be useful for plugins that implement live streaming, so that
+  they can react on changes to a channel's PIDs or CA descriptors.
+- The new function cOsd::DrawScaledBitmap() is now used for drawing subtitles.
+  This function can be reimplemented by high level OSDs which may be able to do
+  the scaling in hardware or otherwise more efficiently.
+- Use of the function cOsd::GetBitmap() outside of derived classes is now deprecated,
+  and it may be made 'protected' in a future version, since it doesn't work with
+  TrueColor OSDs. Plugin authors may want to modify their code so that it
+  works without this function.
+- Modified the descriptions of several threads, so that the important information
+  (like device or frontend numbers) is within the first 15 characters of the
+  string, because only these are displayed in thread listings. Plugin authors may
+  want to do the same.
+- Plugins can now implement the function SetMenuSortMode() in their skin objects
+  derived from cSkinDisplayMenu, to get informed about the currently used sort
+  mode, if applicable.
+- Added cOsdProvider::OsdSizeChanged(), which plugins that implement an output device
+  can call to signal a change in the OSD that requires a redraw of the currently
+  displayed object.
+- cOsd::RenderPixmaps() now returns a pointer to cPixmap instead of cPixmapMemory
+  This is necessary to allow plugins with derived cPixmap implementations to use this
+  function. Plugins that use this function with cPixmapMemory now need to add
+  a dynamic cast to the call, as in
+
+    cPixmapMemory *pm = dynamic_cast<cPixmapMemory *>(RenderPixmaps()));
+
+  They also need to call DestroyPixmap(pm) instead of "delete pm" to properly release
+  the resulting pixmap after use.
+
+Skins:
+
+- The new function cSkinDisplayChannel::SetPositioner() can be implemented by skins to
+  show the user a progress display when the dish is being moved. The default implementation
+  calls SetMessage() with a string indicating the new position the dish is being moved to.
+  The LCARS skin shows a progress bar indicating the movement of the dish.
+- The LCARS skin now shows the source value of the current channel in its channel display.
+- Plugins can now implement the function SetMenuSortMode() in their skin objects
+  derived from cSkinDisplayMenu, to get informed about the currently used sort
+  mode, if applicable.
+
+Remote control:
+
+- Revised the section on "Learning the remote control keys" in the INSTALL file to
+  avoid the impression that there actually is a default remote.conf file, and to
+  not use any alphabetic keys for special functions, so that they remain available
+  for textual input.
+- The LIRC remote control now connects to the socket even if it doesn't yet exist when
+  VDR is started.
+- Added handling UTF-8 'umlaut' characters to cKbdRemote.
+
+Devices:
+
+- The cDvbTuner now has a new status tsPositioning, in which it waits until a steerable
+  dish has reached its target position. Parsing SI data is paused until the target
+  position has been reached.
+- cDevice::IsPrimaryDevice() now also checks whether the primary device actually has
+  a decoder and returns false otherwise. This should improve device allocation on
+  systems that are only used as a receiver and don't actually display anything.
+- All bonded devices (except for the master) now turn off their LNB power completely
+  to avoid problems when receiving vertically polarized transponders.
+- cDevice::TrickSpeed() now has an additional parameter named Forward, which indicates
+  the direction in which replay is being done. This information may be necessary for
+  some output devices in order to properly implement trick modes. Authors of plugins
+  that implement output devices will need to add this parameter to their derived cDevice
+  class, regardless of whether they will make use of it or not.
+- Now checking whether the primary device actually has a decoder before retuning the
+  current channel after a change in its parameters. This fixes broken recordings on
+  the primary device on "headless" systems.
+- Refactored setup parameter handling for output devices:
+  + The function cDevice::GetVideoSystem() has been deprecated and will be removed
+    in a future version. In order to check whether a particular plugin needs to be
+    modified if this function is removed, you can comment out the line
+    #define DEPRECATED_VIDEOSYSTEM
+    in device.h.
+  + Handling the "video (display) format" (things like 16:9, 4:3, pan&scan, letterbox
+    etc) shall now be done by the individual output devices, because the types and
+    numbers of parameters are too device specific. The Setup/DVB parameters
+    "Video format" and "Video display format" are still there for now and can be used
+    by SD devices. HD devices, however, shall not use these parameters (any more),
+    but rather implement their own setup menu with the necessary parameters for
+    controlling output.
+  + The dvbhdffdevice plugin has been modified accordingly.
+  + Made it clear that cDevice::SetDigitalAudioDevice() merely tells the output device
+    that the current audio track is Dolby Digital. This function was only used by the
+    original "full featured" DVB cards - do not use it for new developments!
+    If an output device has several ways of replaying audio (like HDMI or analog jack)
+    it shall implement the proper options in its plugin's SetupMenu() function.
+- Added support for "Pilot", "T2-System-Id" and "SISO/MISO" parameters.
+- Added subsystem id support for DVB devices connected via USB.
+
+DiSEqC:
+
+- The new DiSEqC command code 'P' can be used to instruct a positioner to move the
+  dish to the required satellite position. When a 'P' code is processed, further
+  execution of the remaining DiSEqC sequence (if any) is postponed until the positioner
+  has reached the new satellite position.
+
+EPG:
+
+- Added Begin/EndSegmentTransfer() to the EPG handler interface.
+
+OSD:
+
+- In the "Select folder" menu pressing Ok now selects the folder, even if this is a
+  folder that contains sub folders (marked with "..."). To open such a folder you
+  can press the Red key.
+- The Recordings menu now remembers the last recording the cursor was positioned on,
+  independent of the last replayed recording. When a replay ends, however, the cursor
+  will initially be positioned to the last replayed recording again when the menu
+  is opened.
+- Added cOsdProvider::OsdSizeChanged(), which plugins that implement an output device
+  can call to signal a change in the OSD that requires a redraw of the currently
+  displayed object.
+- The "Select folder" menu now adds the folder names of all existing recordings to
+  any names that have been predefined in "folders.conf".
+- Fixed an inconsistent behavior between opening the Recordings menu manually via the
+  main menu and by pressing the Recordings key. In the latter case it automatically
+  opened all sub folders to position the cursor to the last replayed recording, which
+  is unexpected at this point. You can still navigate to
+  the last replayed recording (if any) by pressing Ok repeatedly in the Recordings
+  menu.
+- cOsd::RenderPixmaps() now returns a pointer to cPixmap instead of cPixmapMemory
+  This is necessary to allow plugins with derived cPixmap implementations to use this
+  function. Plugins that use this function with cPixmapMemory now need to add
+  a dynamic cast to the call, as in
+
+    cPixmapMemory *pm = dynamic_cast<cPixmapMemory *>(RenderPixmaps()));
+
+  They also need to call DestroyPixmap(pm) instead of "delete pm" to properly release
+  the resulting pixmap after use.
+
+Channels:
+
+- Channels that are no longer contained in the current SDT of a transponder are now
+  marked with the keyword OBSOLETE in their name and provider fields. That way you can
+  identify obsolete channels when you switch to them, and you can get the complete
+  overview of all obsolete channels by sorting the Channels list by provider (by
+  pressing the 0 key twice). Automatic deletion of obsolete channels may follow later.
+- Added support for LCN (Logical Channel Numbers), which plugins may use to sort
+  channels.
+- Added "NORDIG" to the list of "DVB/Standard compliance" options and using it to
+  restrict the LCN (Logical Channel Numbers) parsing to networks that actually use
+  this non-standard feature.
+
+Recordings:
+
+- The code for distributing recordings over several video directories has been
+  removed. VDR now by default assumes that the video directory is one big disk.
+  Distributing the video directory over several disks was a useful feature in times
+  when disks were still relatively small, but it also caused serious problems in case
+  one of the disks failed. Nowadays hard disks come in sizes measured in terabytes,
+  and tools like "mhddfs" can be used to combine several disks to form one large volume.
+  A recommended method for a relatively safe disk setup in a VDR system is to use two
+  1TB (or larger) disks and use them as a RAID-1 (mirrored). That way, if one disk
+  fails, you can replace it without data loss.
+- If you absolutely need to use several separate disks to store recordings, you can
+  write a plugin that uses the new cVideoDirectory API to implement the necessary
+  functionality (see PLUGINS.html, section "The video directory"). You can copy the
+  respective code from previous versions of videodir.c.
+  IMPORTANT NOTE: If you write a plugin that implements a distributed video directory,
+  =============== be sure to make cVideoDirectory::Rename() follow symbolic links!
+                  This functionality was never implemented in VDR and it therefore
+                  used a workaround in cutter.c. See the section marked with
+                  // XXX this can be removed once RenameVideoFile() follows symlinks
+                  in previous versions of cutter.c.
+  + CloseVideoFile() is obsolete and has been removed.
+  + The functions OpenVideoFile(), RenameVideoFile(), RemoveVideoFile(), VideoFileSpaceAvailable(),
+    VideoDiskSpace(), RemoveEmptyVideoDirectories(), IsOnVideoDirectoryFileSystem() and
+    PrefixVideoFileName() are now static members of cVideoDirectory and need to be called
+    with the proper prefix.
+  + The name of the video directory is now available through cVideoDirectory::Name().
+- Added renaming and moving recordings and folders, editing a recording's priority and
+  lifetime, and queueing cutting jobs.
+  + The "Recording info" menu now has a new Blue button named "Edit", which opens a
+    dialog in which several properties of the selected recording can be changed. It can
+    be renamed or moved into another folder and its priority and lifetime can be
+    modified.
+    The new blue "Edit" button in the "Recordings" menu opens a dialog in which a folder
+    can be renamed or moved. See MANUAL, section "Managing folders".
+  + In the "Edit recording" menu the Yellow button ("Delete marks") allows you to delete
+    all editing marks of the selected recording.
+  + cCutter is no longer a static class. Cutting requests should now be invoked by
+    calling RecordingsHandler.Add(ruCut, FileName). See the new cRecordingsHandler
+    class in recording.h.
+  + Cutting jobs are now placed in a queue (together with any move or copy jobs) and
+    are processed one by one.
+  + The new SVDRP command MOVR can be used to rename a recording
+  + Note that in several places in the source code a "copy" operation is mentioned,
+    however there is no user interface for this, yet.
+- The function cRecordings::MBperMinute() now only takes into account recordings with
+  less than 5 seconds per megabyte, in an attempt to filter out radio recordings
+  The result of this function was way off any realistic
+  value in case there are many radio recordings in the video directory.
+- When cutting a recording it is now checked whether there is already an edited
+  version of this recording (with the same name, but starting with '%'), and the
+  user is prompted for confirmation to overwrite it.
+- Fixed numbering frames. Previously they were numbered starting from 1, while it
+  is apparently standard to number them from 0. Any existing recordings with editing
+  marks (which will now be off by one) can still be cut with all VDR versions from
+  1.7.32, because these will automatically adjust editing marks to I-frames.
+  Users of stable releases shouldn't notice any problems.
+- Now returning from removing deleted recordings after at most 10 seconds, or if the
+  user presses a remote control key, to keep the system from getting unresponsive
+  when removing a huge number of files.
+- Fixed generating the index file of an existing recording in case at the end of a TS file
+  there is less data in the buffer than needed by the frame detector. In such a case
+  it was possible that frames were missed, and there was most likely a distortion
+  when replaying that part of a recording. This is mostly a problem for recordings that
+  consist of more than one *.ts file. Single file recordings could only lose some
+  frames at their very end, which probably doesn't matter. At any rate, if you have
+  generated an index file with VDR version 2.0.6, 2.1.5 or 2.1.6, you may want to
+  do so again with this version to make sure the index is OK.
+- Added the new command line option --updindex, which can be used to update an
+  incomplete index of a recording.
+- In the "Edit recording" menu the '0' key can now be used on the "Name:" field to
+  remove the name of the recording and replace it with the last element of the
+  recording's folder path name. See MANUAL, section "Managing folders" for details.
+
+Replay:
+
+- The new option "Setup/Replay/Pause replay when jumping to a mark" can be used to
+  turn off pausing replay when jumping to an editing mark with the '9' key.
+- The new option "Setup/Replay/Skip edited parts" can be used to automatically skip
+  the edited parts of a recording during replay, without the need to actually cut
+  the recording.
+- The new option "Setup/Replay/Pause replay at last mark" can be used to make replay
+  go into Pause mode when it has reached the last "end" mark.
+- The '8' key for testing an edited sequence now also jumps to the next *end*
+  mark if "Setup/Replay/Skip edited parts" is active. This allows for testing edits
+  in recordings that have actually been cut, as well as recordings that have not
+  been cut, in case "Skip edited parts" is enabled.
+- A recording is now still considered unviewed when stopping replay within 10 seconds
+  of the first mark.
+- The keys '1' and '3' can now be used in replay mode to position an editing mark
+  in "adaptive" mode. See MANUAL, section "Editing a Recording".
+- The new option "Setup/Replay/Alternate behavior for adaptive skipping" can be used
+  to make adaptive skipping only halve the skip distance when the direction changes.
+  That way you can reach the desired point in a recording even if you make one too
+  many skips in a certain direction (see MANUAL for details).
+- The new options "Setup/Replay/Skip distance with Green/Yellow keys" and
+  "Setup/Replay/Skip distance with Green/Yellow keys in repeat" can be used to
+  configure the number of seconds to skip when pressing these keys once or pressing
+  and holding them.
+- The new option "Setup/Replay/Use Prev/Next keys for adaptive skipping" can be used
+  to change the behavior of these keys during replay. They normally jump between
+  editing marks, but with this option set to 'yes' they will do adaptive skipping.
+
+SVDRP:
+
+- The new SVDRP command MOVR can be used to rename a recording
+
+Audio:
+
+- Added the new parameters "Setup/Miscellaneous/Volume steps" and
+  ".../Volume linearize". See the MANUAL for details.
+- Added handling for DTS audio tracks to cPatPmtParser::ParsePmt().
+
+Misc:
+
+- Reverted the change from version 1.5.7 that made all logging go to LOG_ERR.
+- The Yellow button in the main menu no longer acts as "Pause" if "Pause key handling"
+  is set to "do not pause live video".
+- Added code for parsing LCN and AVC descriptors to libsi.
+- Changes in the teletext PID no longer cause retuning (and thus interrupting a
+  recording).
+- Made it clear that the Data parameter in cDevice::StillPicture() may point to a
+  series of packets, not just a single one.
+- Changed '%a' to the POSIX compliant '%m' in all scanf() calls.
+- cTSBuffer now provides the number of available bytes in its Get() function.
+- The script given to VDR with the '-r' option is now also called after the recording
+  process has actually started.
+- Updated 'sources.conf'.
+- Increased MIN_TS_PACKETS_FOR_FRAME_DETECTOR to 100 and introduced counting the number
+  of actual video TS packets in cTsPayload in order to be able to record channels that
+  sometimes need many TS packets for detecting frame borders.
+- Improved PAT/PMT scanning to speed up initial tuning to encrypted channels on
+  transponders with many PAT entries.
+- The SDT is now only parsed *after* the NIT has been read, and it explicitly uses
+  the source value derived from the NIT. This should prevent new channels from being
+  created with the wrong source.
+- Added support for systemd. To activate this you need to add "SDNOTIFY=1" to the
+  'make' call.
+- No longer logging an error message in DirSizeMB() if the given directory doesn't
+  exist. This avoids lots of log entries in case several VDRs use the same video
+  directory and one of them has already physically removed a recording directory,
+  while the others still have it in their list of deleted recordings.
+- VDR now reads command line options from *.conf files in /etc/vdr/conf.d.
+  See vdr.1 and vdr.5 for details.
+- Added the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement()
+  to the cVector class.
+- Added functions to set and retrieve the priority of a cReceiver.
+- Changed the German weekday names from "MonDieMitDonFreSamSon" to
+  "Mo.Di.Mi.Do.Fr.Sa.So.".
+- Added the channel name to log messages that reference a channel.
+- Added ARGSDIR to the ONEDIR section of Make.config.template.
+- Added SDNOTIFY, NO_KBD, BIDI and REMOTE to Make.config.template.
+- Modified runvdr.template to improve compatibility with the "bash" and "dash" shells.
+- Updated sources.conf to reflect the fact that Astra 4A and SES5 are actually in
+  two separate positions.
+- Fixed cMarks::GetNextBegin() and cMarks::GetNextEnd(). The behavior of these two
+  functions is now exacly as described in the header file. Editing marks that are
+  placed at exactly the same offset in a recording are now preserved in the cutting
+  process.
+- The new command line option --chartab can be used to set the default character
+  table to use for strings in the DVB data stream that don't begin with a proper
+  character table indicator. The old mechanism of using the environment variable
+  VDR_CHARSET_OVERRIDE still works, but is now deprecated and may be removed in a
+  future version. The value given in the --chartab option takes precedence over
+  that in VDR_CHARSET_OVERRIDE.
diff -ruN vdr-2.0.0/args.c vdr-2.2.0/args.c
--- vdr-2.0.0/args.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/args.c	2014-04-14 14:02:38.000000000 +0200
@@ -0,0 +1,129 @@
+/*
+ * args.c: Read arguments from files
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Original version written by Lars Hanisch <dvb@flensrocker.de>.
+ *
+ * $Id: args.c 1.1 2014/04/14 12:02:38 kls Exp $
+ */
+
+#include "args.h"
+#include <unistd.h>
+
+cArgs::cArgs(const char *Argv0)
+{
+  argv0 = Argv0;
+  argc = 0;
+  argv = NULL;
+}
+
+cArgs::~cArgs(void)
+{
+  if (argv != NULL)
+     delete [] argv;
+}
+
+bool cArgs::AddArg(const char *s)
+{
+  if (inVdrSection)
+     args.Append(strdup(s));
+  else if (*lastArg == NULL)
+     return false;
+  else
+     lastArg = cString::sprintf("%s %s", *lastArg, s);
+  return true;
+}
+
+bool cArgs::ReadDirectory(const char *Directory)
+{
+  if (argv != NULL)
+     delete [] argv;
+  argc = 0;
+  argv = NULL;
+  args.Clear();
+  lastArg = NULL;
+  inVdrSection = false;
+  cFileNameList files(Directory, false);
+  if (files.Size() == 0)
+     return false;
+  for (int i = 0; i < files.Size(); i++) {
+      const char *fileName = files.At(i);
+      if (startswith(fileName, ".") || !endswith(fileName, ".conf"))
+         continue;
+      cString fullFileName = AddDirectory(Directory, fileName);
+      struct stat fs;
+      if ((access(*fullFileName, F_OK) != 0) || (stat(*fullFileName, &fs) != 0) || S_ISDIR(fs.st_mode))
+         continue;
+      bool ok = true;
+      int line = 0;
+      FILE *f = fopen(*fullFileName, "r");
+      if (f) {
+         char *s;
+         cReadLine ReadLine;
+         while ((s = ReadLine.Read(f)) != NULL) {
+               line++;
+               s = stripspace(skipspace(s));
+               if (!isempty(s) && (s[0] != '#')) {
+                  if (startswith(s, "[") && endswith(s, "]")) {
+                     s[strlen(s) - 1] = 0;
+                     s++;
+                     if (*lastArg) {
+                        args.Append(strdup(*lastArg));
+                        lastArg = NULL;
+                        }
+                     if (strcmp(s, "vdr") == 0)
+                        inVdrSection = true;
+                     else {
+                        inVdrSection = false;
+                        lastArg = cString::sprintf("--plugin=%s", s);
+                        }
+                     }
+                  else {
+                     if ((strlen(s) > 2) && (s[0] == '-') && (s[1] != '-')) { // short option, split at first space
+                        char *p = strchr(s, ' ');
+                        if (p == NULL) {
+                           ok = AddArg(s);
+                           if (!ok)
+                              break;
+                           }
+                        else {
+                           *p = 0;
+                           p++;
+                           ok = AddArg(s);
+                           if (!ok)
+                              break;
+                           ok = AddArg(p);
+                           if (!ok)
+                              break;
+                           }
+                        }
+                     else {
+                        ok = AddArg(s);
+                        if (!ok)
+                           break;
+                        }
+                     }
+                  }
+               }
+         fclose(f);
+         }
+       if (!ok) {
+          esyslog("ERROR: args file %s, line %d", *fullFileName, line);
+          return false;
+          }
+      }
+  if (*lastArg) {
+     args.Append(strdup(*lastArg));
+     lastArg = NULL;
+     }
+  argv = new char*[args.Size() + 1];
+  argv[0] = strdup(*argv0);
+  argc = 1;
+  for (int i = 0; i < args.Size(); i++) {
+      argv[argc] = args.At(i);
+      argc++;
+      }
+  return true;
+}
diff -ruN vdr-2.0.0/args.h vdr-2.2.0/args.h
--- vdr-2.0.0/args.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/args.h	2014-04-14 13:54:21.000000000 +0200
@@ -0,0 +1,34 @@
+/*
+ * args.h: Read arguments from files
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Original version written by Lars Hanisch <dvb@flensrocker.de>.
+ *
+ * $Id: args.h 1.1 2014/04/14 11:54:21 kls Exp $
+ */
+
+#ifndef __ARGS_H
+#define __ARGS_H
+
+#include "tools.h"
+
+class cArgs {
+private:
+  cString argv0;
+  cStringList args;
+  cString lastArg;
+  bool inVdrSection;
+  int argc;
+  char **argv;
+  bool AddArg(const char *s);
+public:
+  cArgs(const char *Argv0);
+  ~cArgs(void);
+  bool ReadDirectory(const char *Directory);
+  int GetArgc(void) const { return argc; };
+  char **GetArgv(void) const { return argv; };
+  };
+
+#endif //__ARGS_H
diff -ruN vdr-2.0.0/audio.c vdr-2.2.0/audio.c
--- vdr-2.0.0/audio.c	2010-05-16 15:30:11.000000000 +0200
+++ vdr-2.2.0/audio.c	2010-05-16 15:30:11.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: audio.c 2.2 2010/05/16 13:30:11 kls Exp $
+ * $Id: audio.c 3.0 2010/05/16 13:30:11 kls Exp $
  */
 
 #include "audio.h"
diff -ruN vdr-2.0.0/audio.h vdr-2.2.0/audio.h
--- vdr-2.0.0/audio.h	2008-07-06 13:39:21.000000000 +0200
+++ vdr-2.2.0/audio.h	2008-07-06 13:39:21.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: audio.h 2.1 2008/07/06 11:39:21 kls Exp $
+ * $Id: audio.h 3.0 2008/07/06 11:39:21 kls Exp $
  */
 
 #ifndef __AUDIO_H
diff -ruN vdr-2.0.0/channels.c vdr-2.2.0/channels.c
--- vdr-2.0.0/channels.c	2012-07-14 14:34:47.000000000 +0200
+++ vdr-2.2.0/channels.c	2015-02-01 14:47:05.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.c 2.24 2012/07/14 12:34:47 kls Exp $
+ * $Id: channels.c 3.8 2015/02/01 13:47:05 kls Exp $
  */
 
 #include "channels.h"
@@ -29,7 +29,7 @@
   int tid;
   int sid;
   int rid = 0;
-  int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid);
+  int fields = sscanf(s, "%m[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid);
   if (fields == 4 || fields == 5) {
      int source = cSource::FromString(sourcebuf);
      free(sourcebuf);
@@ -64,6 +64,7 @@
   memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
   parameters = "";
   modification = CHANNELMOD_NONE;
+  seen         = 0;
   schedule     = NULL;
   linkChannels = NULL;
   refChannel   = NULL;
@@ -220,7 +221,7 @@
      nameSource = NULL;
      shortNameSource = NULL;
      if (Number() && !Quiet) {
-        dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
+        dsyslog("changing transponder data of channel %d (%s) from %s to %s", Number(), name, *OldTransponderData, *TransponderDataToString());
         modification |= CHANNELMOD_TRANSP;
         Channels.SetModified();
         }
@@ -232,7 +233,7 @@
 {
   if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) {
      if (Number()) {
-        dsyslog("changing id of channel %d from %d-%d-%d-%d to %d-%d-%d-%d", Number(), nid, tid, sid, rid, Nid, Tid, Sid, Rid);
+        dsyslog("changing id of channel %d (%s) from %d-%d-%d-%d to %d-%d-%d-%d", Number(), name, nid, tid, sid, rid, Nid, Tid, Sid, Rid);
         modification |= CHANNELMOD_ID;
         Channels.SetModified();
         Channels.UnhashChannel(this);
@@ -247,6 +248,15 @@
      }
 }
 
+void cChannel::SetLcn(int Lcn)
+{
+  if (lcn != Lcn) {
+     if (Number())
+        dsyslog("changing lcn of channel %d (%s) from %d to %d\n", Number(), name, lcn, Lcn);
+     lcn = Lcn;
+     }
+}
+
 void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider)
 {
   if (!isempty(Name)) {
@@ -277,7 +287,7 @@
 {
   if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) {
      if (Number()) {
-        dsyslog("changing portal name of channel %d from '%s' to '%s'", Number(), portalName, PortalName);
+        dsyslog("changing portal name of channel %d (%s) from '%s' to '%s'", Number(), name, portalName, PortalName);
         modification |= CHANNELMOD_NAME;
         Channels.SetModified();
         }
@@ -330,8 +340,10 @@
 void cChannel::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)
 {
   int mod = CHANNELMOD_NONE;
-  if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
+  if (vpid != Vpid || ppid != Ppid || vtype != Vtype)
      mod |= CHANNELMOD_PIDS;
+  if (tpid != Tpid)
+     mod |= CHANNELMOD_AUX;
   int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(atypes, Atypes) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(dtypes, Dtypes) | IntArraysDiffer(spids, Spids, slangs, SLangs);
   if (m & STRDIFF)
      mod |= CHANNELMOD_LANGS;
@@ -365,7 +377,7 @@
      q += IntArrayToString(q, Spids, 10, SLangs);
      *q = 0;
      if (Number())
-        dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
+        dsyslog("changing pids of channel %d (%s) from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), name, vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
      vpid = Vpid;
      ppid = Ppid;
      vtype = Vtype;
@@ -388,7 +400,8 @@
      spids[MAXSPIDS] = 0;
      tpid = Tpid;
      modification |= mod;
-     Channels.SetModified();
+     if (Number())
+        Channels.SetModified();
      }
 }
 
@@ -408,6 +421,11 @@
      }
 }
 
+void cChannel::SetSeen(void)
+{
+  seen = time(NULL);
+}
+
 void cChannel::SetCaIds(const int *CaIds)
 {
   if (caids[0] && caids[0] <= CA_USER_MAX)
@@ -418,7 +436,7 @@
      IntArrayToString(OldCaIdsBuf, caids, 16);
      IntArrayToString(NewCaIdsBuf, CaIds, 16);
      if (Number())
-        dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf);
+        dsyslog("changing caids of channel %d (%s) from %s to %s", Number(), name, OldCaIdsBuf, NewCaIdsBuf);
      for (int i = 0; i <= MAXCAIDS; i++) { // <= to copy the terminating 0
          caids[i] = CaIds[i];
          if (!CaIds[i])
@@ -435,7 +453,7 @@
      modification |= CHANNELMOD_CA;
      Channels.SetModified();
      if (Number() && Level > 1)
-        dsyslog("changing ca descriptors of channel %d", Number());
+        dsyslog("changing ca descriptors of channel %d (%s)", Number(), name);
      }
 }
 
@@ -461,7 +479,7 @@
      }
   char buffer[((linkChannels ? linkChannels->Count() : 0) + (LinkChannels ? LinkChannels->Count() : 0)) * 6 + 256]; // 6: 5 digit channel number plus blank, 256: other texts (see below) plus reserve
   char *q = buffer;
-  q += sprintf(q, "linking channel %d from", Number());
+  q += sprintf(q, "linking channel %d (%s) from", Number(), name);
   if (linkChannels) {
      for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
          lc->Channel()->SetRefChannel(NULL);
@@ -503,12 +521,14 @@
   char FullName[strlen(Channel->name) + 1 + strlen(Channel->shortName) + 1 + strlen(Channel->provider) + 1 + 10]; // +10: paranoia
   char *q = FullName;
   q += sprintf(q, "%s", Channel->name);
-  if (!isempty(Channel->shortName))
-     q += sprintf(q, ",%s", Channel->shortName);
-  else if (strchr(Channel->name, ','))
-     q += sprintf(q, ",");
-  if (!isempty(Channel->provider))
-     q += sprintf(q, ";%s", Channel->provider);
+  if (!Channel->groupSep) {
+     if (!isempty(Channel->shortName))
+        q += sprintf(q, ",%s", Channel->shortName);
+     else if (strchr(Channel->name, ','))
+        q += sprintf(q, ",");
+     if (!isempty(Channel->provider))
+        q += sprintf(q, ";%s", Channel->provider);
+     }
   *q = 0;
   strreplace(FullName, ':', '|');
   cString buffer;
@@ -584,7 +604,7 @@
      char *apidbuf = NULL;
      char *tpidbuf = NULL;
      char *caidbuf = NULL;
-     int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%a[^:]:%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpidbuf, &caidbuf, &sid, &nid, &tid, &rid);
+     int fields = sscanf(s, "%m[^:]:%d :%m[^:]:%m[^:] :%d :%m[^:]:%m[^:]:%m[^:]:%m[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpidbuf, &caidbuf, &sid, &nid, &tid, &rid);
      if (fields >= 9) {
         if (fields == 9) {
            // allow reading of old format
@@ -1014,6 +1034,7 @@
      NewChannel->CopyTransponderData(Transponder);
      NewChannel->SetId(Nid, Tid, Sid, Rid);
      NewChannel->SetName(Name, ShortName, Provider);
+     NewChannel->SetSeen();
      Add(NewChannel);
      ReNumber();
      return NewChannel;
@@ -1021,6 +1042,22 @@
   return NULL;
 }
 
+#define CHANNELMARKOBSOLETE "OBSOLETE"
+#define CHANNELTIMEOBSOLETE 3600 // seconds to wait before declaring a channel obsolete (in case it has actually been seen before)
+
+void cChannels::MarkObsoleteChannels(int Source, int Nid, int Tid)
+{
+  for (cChannel *channel = First(); channel; channel = Next(channel)) {
+      if (time(NULL) - channel->Seen() > CHANNELTIMEOBSOLETE && channel->Source() == Source && channel->Nid() == Nid && channel->Tid() == Tid && channel->Rid() == 0) {
+         bool OldShowChannelNamesWithSource = Setup.ShowChannelNamesWithSource;
+         Setup.ShowChannelNamesWithSource = false;
+         if (!endswith(channel->Name(), CHANNELMARKOBSOLETE))
+            channel->SetName(cString::sprintf("%s %s", channel->Name(), CHANNELMARKOBSOLETE), channel->ShortName(), cString::sprintf("%s %s", CHANNELMARKOBSOLETE, channel->Provider()));
+         Setup.ShowChannelNamesWithSource = OldShowChannelNamesWithSource;
+         }
+      }
+}
+
 cString ChannelString(const cChannel *Channel, int Number)
 {
   char buffer[256];
diff -ruN vdr-2.0.0/channels.h vdr-2.2.0/channels.h
--- vdr-2.0.0/channels.h	2012-06-17 13:21:33.000000000 +0200
+++ vdr-2.2.0/channels.h	2015-02-01 14:30:26.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.h 2.16 2012/06/17 11:21:33 kls Exp $
+ * $Id: channels.h 3.3 2015/02/01 13:30:26 kls Exp $
  */
 
 #ifndef __CHANNELS_H
@@ -22,6 +22,7 @@
 #define CHANNELMOD_NAME     0x01
 #define CHANNELMOD_PIDS     0x02
 #define CHANNELMOD_ID       0x04
+#define CHANNELMOD_AUX      0x08
 #define CHANNELMOD_CA       0x10
 #define CHANNELMOD_TRANSP   0x20
 #define CHANNELMOD_LANGS    0x40
@@ -120,6 +121,7 @@
   int tid;
   int sid;
   int rid;
+  int lcn;       // Logical channel number assigned by data stream (or -1 if not available)
   int number;    // Sequence number assigned on load
   bool groupSep;
   int __EndData__;
@@ -127,6 +129,7 @@
   mutable cString shortNameSource;
   cString parameters;
   int modification;
+  time_t seen; // When this channel was last seen in the SDT of its transponder
   mutable const cSchedule *schedule;
   cLinkChannels *linkChannels;
   cChannel *refChannel;
@@ -172,6 +175,7 @@
   int Tid(void) const { return tid; }
   int Sid(void) const { return sid; }
   int Rid(void) const { return rid; }
+  int Lcn(void) const { return lcn; }
   int Number(void) const { return number; }
   void SetNumber(int Number) { number = Number; }
   bool GroupSep(void) const { return groupSep; }
@@ -186,9 +190,11 @@
   tChannelID GetChannelID(void) const { return tChannelID(source, nid, (nid || tid) ? tid : Transponder(), sid, rid); }
   bool HasTimer(void) const;
   int Modification(int Mask = CHANNELMOD_ALL);
+  time_t Seen(void) { return seen; }
   void CopyTransponderData(const cChannel *Channel);
   bool SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet = false);
   void SetId(int Nid, int Tid, int Sid, int Rid = 0);
+  void SetLcn(int Lcn);
   void SetName(const char *Name, const char *ShortName, const char *Provider);
   void SetPortalName(const char *PortalName);
   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);
@@ -197,6 +203,7 @@
   void SetLinkChannels(cLinkChannels *LinkChannels);
   void SetRefChannel(cChannel *RefChannel);
   void SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds);
+  void SetSeen(void);
   };
 
 class cChannels : public cRwLock, public cConfig<cChannel> {
@@ -236,6 +243,7 @@
       ///< modification has been made, and 2 if the user has made a modification.
       ///< Calling this function resets the 'modified' flag to 0.
   cChannel *NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid = 0);
+  void MarkObsoleteChannels(int Source, int Nid, int Tid);
   };
 
 extern cChannels Channels;
diff -ruN vdr-2.0.0/ci.c vdr-2.2.0/ci.c
--- vdr-2.0.0/ci.c	2013-02-17 14:17:28.000000000 +0100
+++ vdr-2.2.0/ci.c	2015-02-02 14:57:39.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: ci.c 2.12 2013/02/17 13:17:28 kls Exp $
+ * $Id: ci.c 3.19 2015/02/02 13:57:39 kls Exp $
  */
 
 #include "ci.h"
@@ -19,6 +19,10 @@
 #include <unistd.h>
 #include "device.h"
 #include "pat.h"
+#include "receiver.h"
+#include "remux.h"
+#include "libsi/si.h"
+#include "skins.h"
 #include "tools.h"
 
 // Set these to 'true' for debug output:
@@ -102,6 +106,184 @@
   return NULL;
 }
 
+// --- cCaPidReceiver --------------------------------------------------------
+
+// A receiver that is used to make the device receive the ECM pids, as well as the
+// CAT and the EMM pids.
+
+class cCaPidReceiver : public cReceiver {
+private:
+  int catVersion;
+  cVector<int> emmPids;
+  uchar buffer[2048]; // 11 bit length, max. 2048 byte
+  uchar *bufp;
+  int length;
+  void AddEmmPid(int Pid);
+  void DelEmmPids(void);
+protected:
+  virtual void Activate(bool On);
+public:
+  cCaPidReceiver(void);
+  virtual ~cCaPidReceiver() { Detach(); }
+  virtual void Receive(uchar *Data, int Length);
+  bool HasCaPids(void) { return NumPids() - emmPids.Size() - 1 > 0; }
+  void Reset(void) { DelEmmPids(); catVersion = -1; }
+  };
+
+cCaPidReceiver::cCaPidReceiver(void)
+{
+  catVersion = -1;
+  bufp = NULL;
+  length = 0;
+  AddPid(CATPID);
+}
+
+void cCaPidReceiver::AddEmmPid(int Pid)
+{
+  for (int i = 0; i < emmPids.Size(); i++) {
+      if (emmPids[i] == Pid)
+         return;
+      }
+  emmPids.Append(Pid);
+  AddPid(Pid);
+}
+
+void cCaPidReceiver::DelEmmPids(void)
+{
+  for (int i = 0; i < emmPids.Size(); i++)
+      DelPid(emmPids[i]);
+  emmPids.Clear();
+}
+
+void cCaPidReceiver::Activate(bool On)
+{
+  catVersion = -1; // can be done independent of 'On'
+}
+
+void cCaPidReceiver::Receive(uchar *Data, int Length)
+{
+  if (TsPid(Data) == CATPID) {
+     uchar *p = NULL;
+     if (TsPayloadStart(Data)) {
+        if (Data[5] == SI::TableIdCAT) {
+           length = (int(Data[6] & 0x03) << 8) | Data[7]; // section length
+           if (length > 5) {
+              int v = (Data[10] & 0x3E) >> 1; // version number
+              if (v != catVersion) {
+                 if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
+                    if (length > TS_SIZE - 8) {
+                       int n = TS_SIZE - 13;
+                       memcpy(buffer, Data + 13, n);
+                       bufp = buffer + n;
+                       length -= n + 5; // 5 = header
+                       }
+                    else {
+                       p = Data + 13; // no need to copy the data
+                       length -= 5; // header
+                       }
+                    }
+                 else
+                    dsyslog("multi table CAT section - unhandled!");
+                 catVersion = v;
+                 }
+              }
+           }
+        }
+     else if (bufp && length > 0) {
+        int n = min(length, TS_SIZE - 4);
+        if (bufp + n - buffer <= int(sizeof(buffer))) {
+           memcpy(bufp, Data + 4, n);
+           bufp += n;
+           length -= n;
+           if (length <= 0) {
+              p = buffer;
+              length = bufp - buffer;
+              }
+           }
+        else {
+           esyslog("ERROR: buffer overflow in cCaPidReceiver::Receive()");
+           bufp = 0;
+           length = 0;
+           }
+        }
+     if (p) {
+        int OldCatVersion = catVersion; // must preserve the current version number
+        cDevice *AttachedDevice = Device();
+        if (AttachedDevice)
+           AttachedDevice->Detach(this);
+        DelEmmPids();
+        for (int i = 0; i < length - 4; i++) { // -4 = checksum
+            if (p[i] == 0x09) {
+               int CaId = int(p[i + 2] << 8) | p[i + 3];
+               int EmmPid = int(((p[i + 4] & 0x1F) << 8)) | p[i + 5];
+               AddEmmPid(EmmPid);
+               switch (CaId >> 8) {
+                 case 0x01: for (int j = i + 7; j < p[i + 1] + 2; j += 4) {
+                                EmmPid = (int(p[j] & 0x0F) << 8) | p[j + 1];
+                                AddEmmPid(EmmPid);
+                                }
+                            break;
+                 }
+               i += p[i + 1] + 2 - 1; // -1 to compensate for the loop increment
+               }
+            }
+        if (AttachedDevice)
+           AttachedDevice->AttachReceiver(this);
+        catVersion = OldCatVersion;
+        p = NULL;
+        bufp = 0;
+        length = 0;
+        }
+     }
+}
+
+// --- cCaActivationReceiver -------------------------------------------------
+
+// A receiver that is used to make the device stay on a given channel and
+// keep the CAM slot assigned.
+
+#define UNSCRAMBLE_TIME     5 // seconds of receiving purely unscrambled data before considering the smart card "activated"
+#define TS_PACKET_FACTOR 1024 // only process every TS_PACKET_FACTORth packet to keep the load down
+
+class cCaActivationReceiver : public cReceiver {
+private:
+  cCamSlot *camSlot;
+  time_t lastScrambledTime;
+  int numTsPackets;
+protected:
+  virtual void Receive(uchar *Data, int Length);
+public:
+  cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot);
+  virtual ~cCaActivationReceiver();
+  };
+
+cCaActivationReceiver::cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot)
+:cReceiver(Channel, MINPRIORITY + 1)
+{
+  camSlot = CamSlot;
+  lastScrambledTime = time(NULL);
+  numTsPackets = 0;
+}
+
+cCaActivationReceiver::~cCaActivationReceiver()
+{
+  Detach();
+}
+
+void cCaActivationReceiver::Receive(uchar *Data, int Length)
+{
+  if (numTsPackets++ % TS_PACKET_FACTOR == 0) {
+     time_t Now = time(NULL);
+     if (TsIsScrambled(Data))
+        lastScrambledTime = Now;
+     else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
+        dsyslog("CAM %d: activated!", camSlot->SlotNumber());
+        Skins.QueueMessage(mtInfo, tr("CAM activated!"));
+        Detach();
+        }
+     }
+}
+
 // --- cTPDU -----------------------------------------------------------------
 
 #define MAX_TPDU_SIZE  2048
@@ -403,7 +585,8 @@
   *p++ =  Tag        & 0xFF;
   p = SetLength(p, Length);
   if (p - buffer + Length < int(sizeof(buffer))) {
-     memcpy(p, Data, Length);
+     if (Data)
+        memcpy(p, Data, Length);
      p += Length;
      tc->SendData(p - buffer, buffer);
      }
@@ -569,7 +752,7 @@
 #define CPCI_QUERY            0x03
 #define CPCI_NOT_SELECTED     0x04
 
-class cCiCaPmt : public cListObject {
+class cCiCaPmt {
   friend class cCiConditionalAccessSupport;
 private:
   uint8_t cmdId;
@@ -707,11 +890,13 @@
      switch (Tag) {
        case AOT_CA_INFO: {
             dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
+            cString Ids;
             numCaSystemIds = 0;
             int l = 0;
             const uint8_t *d = GetData(Data, l);
             while (l > 1) {
                   uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
+                  Ids = cString::sprintf("%s %04X", *Ids ? *Ids : "", id);
                   dbgprotocol(" %04X", id);
                   d += 2;
                   l -= 2;
@@ -728,6 +913,7 @@
                timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
                state = 2; // got ca info
                }
+            dsyslog("CAM %d: system ids:%s", Tc()->CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
             }
             break;
        case AOT_CA_PMT_REPLY: {
@@ -846,7 +1032,9 @@
      int L = (M == 1 || M == 2) ? 1 : 0;
      int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
 #define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10))
+#pragma pack(1)
      struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
+#pragma pack()
      tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : short(htons(tm_loc.tm_gmtoff / 60)) };
      bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
      DumpTPDUDataTransfer &= DumpDateTime;
@@ -1491,7 +1679,6 @@
 cCiAdapter::cCiAdapter(void)
 :cThread("CI adapter")
 {
-  assignedDevice = NULL;
   for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
       camSlots[i] = NULL;
 }
@@ -1517,13 +1704,15 @@
      }
 }
 
-bool cCiAdapter::Ready(void)
+cCamSlot *cCiAdapter::ItCamSlot(int &Iter)
 {
-  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
-      if (camSlots[i] && !camSlots[i]->Ready())
-         return false;
-      }
-  return true;
+  if (Iter >= 0) {
+     for (; Iter < MAX_CAM_SLOTS_PER_ADAPTER; ) {
+         if (cCamSlot *Found = camSlots[Iter++])
+            return Found;
+         }
+     }
+  return NULL;
 }
 
 void cCiAdapter::Action(void)
@@ -1547,14 +1736,15 @@
 
 // --- cCamSlot --------------------------------------------------------------
 
-cCamSlots CamSlots;
-
 #define MODULE_CHECK_INTERVAL 500 // ms
 #define MODULE_RESET_TIMEOUT    2 // s
 
-cCamSlot::cCamSlot(cCiAdapter *CiAdapter)
+cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool ReceiveCaPids)
 {
   ciAdapter = CiAdapter;
+  assignedDevice = NULL;
+  caPidReceiver = ReceiveCaPids ? new cCaPidReceiver : NULL;
+  caActivationReceiver = NULL;
   slotIndex = -1;
   lastModuleStatus = msReset; // avoids initial reset log message
   resetTime = 0;
@@ -1571,6 +1761,10 @@
 
 cCamSlot::~cCamSlot()
 {
+  if (assignedDevice)
+     assignedDevice->SetCamSlot(NULL);
+  delete caPidReceiver;
+  delete caActivationReceiver;
   CamSlots.Del(this, false);
   DeleteAllConnections();
 }
@@ -1580,19 +1774,21 @@
   cMutexLock MutexLock(&mutex);
   if (ciAdapter) {
      if (ciAdapter->Assign(Device, true)) {
-        if (!Device && ciAdapter->assignedDevice)
-           ciAdapter->assignedDevice->SetCamSlot(NULL);
-        if (!Query) {
+        if (!Device && assignedDevice)
+           assignedDevice->SetCamSlot(NULL);
+        if (!Query || !Device) {
            StopDecrypting();
            source = transponder = 0;
            if (ciAdapter->Assign(Device)) {
-              ciAdapter->assignedDevice = Device;
+              assignedDevice = Device;
               if (Device) {
                  Device->SetCamSlot(this);
                  dsyslog("CAM %d: assigned to device %d", slotNumber, Device->DeviceNumber() + 1);
                  }
-              else
+              else {
+                 CancelActivation();
                  dsyslog("CAM %d: unassigned", slotNumber);
+                 }
               }
            else
               return false;
@@ -1603,17 +1799,6 @@
   return false;
 }
 
-cDevice *cCamSlot::Device(void)
-{
-  cMutexLock MutexLock(&mutex);
-  if (ciAdapter) {
-     cDevice *d = ciAdapter->assignedDevice;
-     if (d && d->CamSlot() == this)
-        return d;
-     }
-  return NULL;
-}
-
 void cCamSlot::NewConnection(void)
 {
   cMutexLock MutexLock(&mutex);
@@ -1639,6 +1824,8 @@
 void cCamSlot::Process(cTPDU *TPDU)
 {
   cMutexLock MutexLock(&mutex);
+  if (caActivationReceiver && !caActivationReceiver->IsAttached())
+     CancelActivation();
   if (TPDU) {
      int n = TPDU->Tcid();
      if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
@@ -1662,6 +1849,7 @@
                dbgprotocol("Slot %d: no module present\n", slotNumber);
                isyslog("CAM %d: no module present", slotNumber);
                DeleteAllConnections();
+               CancelActivation();
                break;
           case msReset:
                dbgprotocol("Slot %d: module reset\n", slotNumber);
@@ -1723,6 +1911,37 @@
   return false;
 }
 
+bool cCamSlot::CanActivate(void)
+{
+  return ModuleStatus() == msReady;
+}
+
+void cCamSlot::StartActivation(void)
+{
+  cMutexLock MutexLock(&mutex);
+  if (!caActivationReceiver) {
+     if (cDevice *d = Device()) {
+        if (cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel())) {
+           caActivationReceiver = new cCaActivationReceiver(Channel, this);
+           d->AttachReceiver(caActivationReceiver);
+           dsyslog("CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name());
+           }
+        }
+     }
+}
+
+void cCamSlot::CancelActivation(void)
+{
+  cMutexLock MutexLock(&mutex);
+  delete caActivationReceiver;
+  caActivationReceiver = NULL;
+}
+
+bool cCamSlot::IsActivating(void)
+{
+  return caActivationReceiver;
+}
+
 eModuleStatus cCamSlot::ModuleStatus(void)
 {
   cMutexLock MutexLock(&mutex);
@@ -1801,6 +2020,10 @@
      const int *CaSystemIds = cas->GetCaSystemIds();
      if (CaSystemIds && *CaSystemIds) {
         if (caProgramList.Count()) {
+           if (caPidReceiver && caPidReceiver->HasCaPids()) {
+              if (cDevice *d = Device())
+                 d->Detach(caPidReceiver);
+              }
            for (int Loop = 1; Loop <= 2; Loop++) {
                for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
                    if (p->modified || resendPmt) {
@@ -1813,6 +2036,15 @@
                              }
                           }
                       if ((Loop == 1) != Active) { // first remove, then add
+                         if (caPidReceiver) {
+                            int CaPids[MAXRECEIVEPIDS + 1];
+                            if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
+                               if (Loop == 1)
+                                  caPidReceiver->DelPids(CaPids);
+                               else
+                                  caPidReceiver->AddPids(CaPids);
+                               }
+                            }
                          if (cas->RepliesToQuery())
                             CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
                          if (Active || cas->RepliesToQuery())
@@ -1822,11 +2054,20 @@
                       }
                    }
                }
+           if (caPidReceiver && caPidReceiver->HasCaPids()) {
+              if (cDevice *d = Device())
+                 d->AttachReceiver(caPidReceiver);
+              }
            resendPmt = false;
            }
         else {
            cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
            cas->SendPMT(&CaPmt);
+           if (caPidReceiver) {
+              if (cDevice *d = Device())
+                 d->Detach(caPidReceiver);
+              caPidReceiver->Reset();
+              }
            }
         }
      }
@@ -1983,6 +2224,32 @@
   return false;
 }
 
+uchar *cCamSlot::Decrypt(uchar *Data, int &Count)
+{
+  Count = TS_SIZE;
+  return Data;
+}
+
+// --- cCamSlots -------------------------------------------------------------
+
+cCamSlots CamSlots;
+
+bool cCamSlots::WaitForAllCamSlotsReady(int Timeout)
+{
+  for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
+      bool ready = true;
+      for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+          if (!CamSlot->Ready()) {
+             ready = false;
+             cCondWait::SleepMs(100);
+             }
+          }
+      if (ready)
+         return true;
+      }
+  return false;
+}
+
 // --- cChannelCamRelation ---------------------------------------------------
 
 #define CAM_CHECKED_TIMEOUT  15 // seconds before a CAM that has been checked for a particular channel will be checked again
diff -ruN vdr-2.0.0/ci.h vdr-2.2.0/ci.h
--- vdr-2.0.0/ci.h	2012-02-29 11:24:27.000000000 +0100
+++ vdr-2.2.0/ci.h	2015-01-31 15:36:41.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: ci.h 2.2 2012/02/29 10:24:27 kls Exp $
+ * $Id: ci.h 3.11 2015/01/31 14:36:41 kls Exp $
  */
 
 #ifndef __CI_H
@@ -79,28 +79,31 @@
 class cCiAdapter : public cThread {
   friend class cCamSlot;
 private:
-  cDevice *assignedDevice;
   cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
   void AddCamSlot(cCamSlot *CamSlot);
        ///< Adds the given CamSlot to this CI adapter.
 protected:
+  cCamSlot *ItCamSlot(int &Iter);
+       ///< Iterates over all added CAM slots of this adapter. Iter has to be
+       ///< initialized to 0 and is required to store the iteration state.
+       ///< Returns NULL if no further CAM slot is found.
   virtual void Action(void);
        ///< Handles the attached CAM slots in a separate thread.
        ///< The derived class must call the Start() function to
        ///< actually start CAM handling.
-  virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
+  virtual int Read(uint8_t *Buffer, int MaxLength) { return 0; }
        ///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
        ///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
        ///< Returns the number of bytes read (in case of an error it will also
        ///< return 0).
-  virtual void Write(const uint8_t *Buffer, int Length) = 0;
+  virtual void Write(const uint8_t *Buffer, int Length) {}
        ///< Writes Length bytes of the given Buffer.
-  virtual bool Reset(int Slot) = 0;
+  virtual bool Reset(int Slot) { return false; }
        ///< Resets the CAM in the given Slot.
        ///< Returns true if the operation was successful.
-  virtual eModuleStatus ModuleStatus(int Slot) = 0;
+  virtual eModuleStatus ModuleStatus(int Slot) { return msNone; }
        ///< Returns the status of the CAM in the given Slot.
-  virtual bool Assign(cDevice *Device, bool Query = false) = 0;
+  virtual bool Assign(cDevice *Device, bool Query = false) { return false; }
        ///< Assigns this adapter to the given Device, if this is possible.
        ///< If Query is 'true', the adapter only checks whether it can be
        ///< assigned to the Device, but doesn't actually assign itself to it.
@@ -113,14 +116,14 @@
   cCiAdapter(void);
   virtual ~cCiAdapter();
        ///< The derived class must call Cancel(3) in its destructor.
-  virtual bool Ready(void);
-       ///< Returns 'true' if all present CAMs in this adapter are ready.
   };
 
 class cTPDU;
 class cCiTransportConnection;
 class cCiSession;
 class cCiCaProgramData;
+class cCaPidReceiver;
+class cCaActivationReceiver;
 
 class cCamSlot : public cListObject {
   friend class cCiAdapter;
@@ -129,6 +132,9 @@
   cMutex mutex;
   cCondVar processed;
   cCiAdapter *ciAdapter;
+  cDevice *assignedDevice;
+  cCaPidReceiver *caPidReceiver;
+  cCaActivationReceiver *caActivationReceiver;
   int slotIndex;
   int slotNumber;
   cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1];  // connection numbering starts with 1
@@ -147,10 +153,13 @@
   void Write(cTPDU *TPDU);
   cCiSession *GetSessionByResourceId(uint32_t ResourceId);
 public:
-  cCamSlot(cCiAdapter *CiAdapter);
+  cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false);
        ///< Creates a new CAM slot for the given CiAdapter.
        ///< The CiAdapter will take care of deleting the CAM slot,
        ///< so the caller must not delete it!
+       ///< If WantsTsData is true, the device this CAM slot is assigned to will
+       ///< call the Decrypt() function of this CAM slot, presenting it the complete
+       ///< TS data stream of the encrypted programme, including the CA pids.
   virtual ~cCamSlot();
   bool Assign(cDevice *Device, bool Query = false);
        ///< Assigns this CAM slot to the given Device, if this is possible.
@@ -161,58 +170,78 @@
        ///< device it was previously assigned to. The value of Query
        ///< is ignored in that case, and this function always returns
        ///< 'true'.
-  cDevice *Device(void);
+  cDevice *Device(void) { return assignedDevice; }
        ///< Returns the device this CAM slot is currently assigned to.
+  bool WantsTsData(void) const { return caPidReceiver != NULL; }
+       ///< Returns true if this CAM slot wants to receive the TS data through
+       ///< its Decrypt() function.
   int SlotIndex(void) { return slotIndex; }
        ///< Returns the index of this CAM slot within its CI adapter.
        ///< The first slot has an index of 0.
   int SlotNumber(void) { return slotNumber; }
        ///< Returns the number of this CAM slot within the whole system.
        ///< The first slot has the number 1.
-  bool Reset(void);
+  virtual bool Reset(void);
        ///< Resets the CAM in this slot.
        ///< Returns true if the operation was successful.
-  eModuleStatus ModuleStatus(void);
+  virtual bool CanActivate(void);
+       ///< Returns true if there is a CAM in this slot that can be put into
+       ///< activation mode.
+  virtual void StartActivation(void);
+       ///< Puts the CAM in this slot into a mode where an inserted smart card
+       ///< can be activated. The default action is to make IsActivating() return
+       ///< true, which causes the device this CAM slot is attached to to never
+       ///< automatically detach any receivers with negative priority if the
+       ///< PIDs they want to receive are not decrypted by the CAM.
+       ///< StartActivation() must be called *after* the CAM slot has been assigned
+       ///< to a device. The CAM slot will stay in activation mode until the CAM
+       ///< begins to decrypt, a call to CancelActivation() is made, or the device
+       ///< is needed for a recording.
+  virtual void CancelActivation(void);
+       ///< Cancels a previously started activation (if any).
+  virtual bool IsActivating(void);
+       ///< Returns true if this CAM slot is currently activating a smart card.
+  virtual eModuleStatus ModuleStatus(void);
        ///< Returns the status of the CAM in this slot.
-  const char *GetCamName(void);
+  virtual const char *GetCamName(void);
        ///< Returns the name of the CAM in this slot, or NULL if there is
        ///< no ready CAM in this slot.
-  bool Ready(void);
+  virtual bool Ready(void);
        ///< Returns 'true' if the CAM in this slot is ready to decrypt.
-  bool HasMMI(void);
+  virtual bool HasMMI(void);
        ///< Returns 'true' if the CAM in this slot has an active MMI.
-  bool HasUserIO(void);
+  virtual bool HasUserIO(void);
        ///< Returns true if there is a pending user interaction, which shall
        ///< be retrieved via GetMenu() or GetEnquiry().
-  bool EnterMenu(void);
+  virtual bool EnterMenu(void);
        ///< Requests the CAM in this slot to start its menu.
-  cCiMenu *GetMenu(void);
+  virtual cCiMenu *GetMenu(void);
        ///< Gets a pending menu, or NULL if there is no menu.
-  cCiEnquiry *GetEnquiry(void);
+  virtual cCiEnquiry *GetEnquiry(void);
        ///< Gets a pending enquiry, or NULL if there is no enquiry.
   int Priority(void);
-       ///< Returns the priority if the device this slot is currently assigned
+       ///< Returns the priority of the device this slot is currently assigned
        ///< to, or IDLEPRIORITY if it is not assigned to any device.
-  bool ProvidesCa(const int *CaSystemIds);
+  virtual bool ProvidesCa(const int *CaSystemIds);
        ///< Returns true if the CAM in this slot provides one of the given
        ///< CaSystemIds. This doesn't necessarily mean that it will be
        ///< possible to actually decrypt such a programme, since CAMs
        ///< usually advertise several CA system ids, while the actual
        ///< decryption is controlled by the smart card inserted into
        ///< the CAM.
-  void AddPid(int ProgramNumber, int Pid, int StreamType);
+  virtual void AddPid(int ProgramNumber, int Pid, int StreamType);
        ///< Adds the given PID information to the list of PIDs. A later call
        ///< to SetPid() will (de)activate one of these entries.
-  void SetPid(int Pid, bool Active);
+  virtual void SetPid(int Pid, bool Active);
        ///< Sets the given Pid (which has previously been added through a
        ///< call to AddPid()) to Active. A later call to StartDecrypting() will
        ///< send the full list of currently active CA_PMT entries to the CAM.
-  void AddChannel(const cChannel *Channel);
+  virtual void AddChannel(const cChannel *Channel);
        ///< Adds all PIDs if the given Channel to the current list of PIDs.
        ///< If the source or transponder of the channel are different than
        ///< what was given in a previous call to AddChannel(), any previously
        ///< added PIDs will be cleared.
-  bool CanDecrypt(const cChannel *Channel);
+  virtual bool CanDecrypt(const cChannel *Channel);
        ///< Returns true if there is a CAM in this slot that is able to decrypt
        ///< the given Channel (or at least claims to be able to do so).
        ///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
@@ -223,16 +252,56 @@
        ///< to the initial QUERY will perform this check at all. CAMs that never
        ///< replied to the initial QUERY are assumed not to be able to handle
        ///< more than one channel at a time.
-  void StartDecrypting(void);
+  virtual void StartDecrypting(void);
        ///< Triggers sending all currently active CA_PMT entries to the CAM,
        ///< so that it will start decrypting.
-  void StopDecrypting(void);
+  virtual void StopDecrypting(void);
        ///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
-  bool IsDecrypting(void);
+  virtual bool IsDecrypting(void);
        ///< Returns true if the CAM in this slot is currently used for decrypting.
+  virtual uchar *Decrypt(uchar *Data, int &Count);
+       ///< If this is a CAM slot that can be freely assigned to any device,
+       ///< but will not be directly inserted into the full TS data stream
+       ///< in hardware, it can implement this function to be given access
+       ///< to the data in the device's TS buffer. Data points to a buffer
+       ///< of Count bytes of TS data. The first byte in Data is guaranteed
+       ///< to be a TS_SYNC_BYTE.
+       ///< There are three possible ways a CAM can handle decryption:
+       ///< 1. If the full TS data is physically routed through the CAM in hardware,
+       ///< there is no need to reimplement this function.
+       ///< The default implementation simply sets Count to TS_SIZE and returns Data.
+       ///< 2. If the CAM works directly on Data and decrypts the TS "in place" it
+       ///< shall decrypt at least the very first TS packet in Data, set Count to
+       ///< TS_SIZE and return Data. It may decrypt as many TS packets in Data as it
+       ///< wants, but it must decrypt at least the very first TS packet (if at all
+       ///< possible - if, for whatever reasons, it can't decrypt the very first
+       ///< packet, it must return it regardless). Only this very first TS packet will
+       ///< be further processed after the call to this function. The next call will
+       ///< be done with Data pointing to the TS packet immediately following the
+       ///< previous one.
+       ///< 3. If the CAM needs to copy the data into a buffer of its own, and/or send
+       ///< the data to some file handle for processing and later retrieval, it shall
+       ///< set Count to the number of bytes it has read from Data and return a pointer
+       ///< to the next available decrypted TS packet (which will *not* be in the
+       ///< memory area pointed to by Data, but rather in some buffer that is under
+       ///< the CAM's control). If no decrypted TS packet is currently available, NULL
+       ///< shall be returned. If no data from Data can currently be processed, Count
+       ///< shall be set to 0 and the same Data pointer will be offered in the next
+       ///< call to Decrypt().
+       ///< A derived class that implements this function will also need
+       ///< to set the WantsTsData parameter in the call to the base class
+       ///< constructor to true in order to receive the TS data.
   };
 
-class cCamSlots : public cList<cCamSlot> {};
+class cCamSlots : public cList<cCamSlot> {
+public:
+  bool WaitForAllCamSlotsReady(int Timeout = 0);
+       ///< Waits until all CAM slots have become ready, or the given Timeout
+       ///< (seconds) has expired. While waiting, the Ready() function of each
+       ///< CAM slot is called in turn, until they all return true.
+       ///< Returns true if all CAM slots have become ready within the given
+       ///< timeout.
+  };
 
 extern cCamSlots CamSlots;
 
diff -ruN vdr-2.0.0/config.c vdr-2.2.0/config.c
--- vdr-2.0.0/config.c	2013-03-18 09:57:50.000000000 +0100
+++ vdr-2.2.0/config.c	2015-02-10 13:24:13.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.c 2.38 2013/03/18 08:57:50 kls Exp $
+ * $Id: config.c 3.10 2015/02/10 12:24:13 kls Exp $
  */
 
 #include "config.h"
@@ -389,6 +389,12 @@
   LnbFrequLo =  9750;
   LnbFrequHi = 10600;
   DiSEqC = 0;
+  UsePositioner = 0;
+  SiteLat = 0;
+  SiteLon = 0;
+  PositionerSpeed = 15;
+  PositionerSwing = 650;
+  PositionerLastLon = 0;
   SetSystemTime = 0;
   TimeSource = 0;
   TimeTransponder = 0;
@@ -464,9 +470,20 @@
   ShowRemainingTime = 0;
   ProgressDisplayTime = 0;
   PauseOnMarkSet = 0;
+  PauseOnMarkJump = 1;
+  SkipEdited = 0;
+  PauseAtLastMark = 0;
+  AdaptiveSkipInitial = 120;
+  AdaptiveSkipTimeout = 3;
+  AdaptiveSkipAlternate = 0;
+  AdaptiveSkipPrevNext = 0;
+  SkipSeconds = 60;
+  SkipSecondsRepeat = 60;
   ResumeID = 0;
   CurrentChannel = -1;
   CurrentVolume = MAXVOLUME;
+  VolumeSteps = 51;
+  VolumeLinearize = 0;
   CurrentDolby = 0;
   InitialChannel = "";
   DeviceBondings = "";
@@ -594,6 +611,12 @@
   else if (!strcasecmp(Name, "LnbFrequLo"))          LnbFrequLo         = atoi(Value);
   else if (!strcasecmp(Name, "LnbFrequHi"))          LnbFrequHi         = atoi(Value);
   else if (!strcasecmp(Name, "DiSEqC"))              DiSEqC             = atoi(Value);
+  else if (!strcasecmp(Name, "UsePositioner"))       UsePositioner      = atoi(Value);
+  else if (!strcasecmp(Name, "SiteLat"))             SiteLat            = atoi(Value);
+  else if (!strcasecmp(Name, "SiteLon"))             SiteLon            = atoi(Value);
+  else if (!strcasecmp(Name, "PositionerSpeed"))     PositionerSpeed    = atoi(Value);
+  else if (!strcasecmp(Name, "PositionerSwing"))     PositionerSwing    = atoi(Value);
+  else if (!strcasecmp(Name, "PositionerLastLon"))   PositionerLastLon  = atoi(Value);
   else if (!strcasecmp(Name, "SetSystemTime"))       SetSystemTime      = atoi(Value);
   else if (!strcasecmp(Name, "TimeSource"))          TimeSource         = cSource::FromString(Value);
   else if (!strcasecmp(Name, "TimeTransponder"))     TimeTransponder    = atoi(Value);
@@ -669,11 +692,22 @@
   else if (!strcasecmp(Name, "ShowRemainingTime"))   ShowRemainingTime  = atoi(Value);
   else if (!strcasecmp(Name, "ProgressDisplayTime")) ProgressDisplayTime= atoi(Value);
   else if (!strcasecmp(Name, "PauseOnMarkSet"))      PauseOnMarkSet     = atoi(Value);
+  else if (!strcasecmp(Name, "PauseOnMarkJump"))     PauseOnMarkJump    = atoi(Value);
+  else if (!strcasecmp(Name, "SkipEdited"))          SkipEdited         = atoi(Value);
+  else if (!strcasecmp(Name, "PauseAtLastMark"))     PauseAtLastMark    = atoi(Value);
+  else if (!strcasecmp(Name, "AdaptiveSkipInitial")) AdaptiveSkipInitial= atoi(Value);
+  else if (!strcasecmp(Name, "AdaptiveSkipTimeout")) AdaptiveSkipTimeout= atoi(Value);
+  else if (!strcasecmp(Name, "AdaptiveSkipAlternate")) AdaptiveSkipAlternate = atoi(Value);
+  else if (!strcasecmp(Name, "AdaptiveSkipPrevNext")) AdaptiveSkipPrevNext = atoi(Value);
+  else if (!strcasecmp(Name, "SkipSeconds"))         SkipSeconds        = atoi(Value);
+  else if (!strcasecmp(Name, "SkipSecondsRepeat"))   SkipSecondsRepeat  = atoi(Value);
   else if (!strcasecmp(Name, "ResumeID"))            ResumeID           = atoi(Value);
   else if (!strcasecmp(Name, "CurrentChannel"))      CurrentChannel     = atoi(Value);
   else if (!strcasecmp(Name, "CurrentVolume"))       CurrentVolume      = atoi(Value);
   else if (!strcasecmp(Name, "CurrentDolby"))        CurrentDolby       = atoi(Value);
   else if (!strcasecmp(Name, "InitialChannel"))      InitialChannel     = Value;
+  else if (!strcasecmp(Name, "VolumeSteps"))         VolumeSteps        = atoi(Value);
+  else if (!strcasecmp(Name, "VolumeLinearize"))     VolumeLinearize    = atoi(Value);
   else if (!strcasecmp(Name, "InitialVolume"))       InitialVolume      = atoi(Value);
   else if (!strcasecmp(Name, "DeviceBondings"))      DeviceBondings     = Value;
   else if (!strcasecmp(Name, "ChannelsWrap"))        ChannelsWrap       = atoi(Value);
@@ -703,6 +737,12 @@
   Store("LnbFrequLo",         LnbFrequLo);
   Store("LnbFrequHi",         LnbFrequHi);
   Store("DiSEqC",             DiSEqC);
+  Store("UsePositioner",      UsePositioner);
+  Store("SiteLat",            SiteLat);
+  Store("SiteLon",            SiteLon);
+  Store("PositionerSpeed",    PositionerSpeed);
+  Store("PositionerSwing",    PositionerSwing);
+  Store("PositionerLastLon",  PositionerLastLon);
   Store("SetSystemTime",      SetSystemTime);
   Store("TimeSource",         cSource::ToString(TimeSource));
   Store("TimeTransponder",    TimeTransponder);
@@ -778,11 +818,22 @@
   Store("ShowRemainingTime",  ShowRemainingTime);
   Store("ProgressDisplayTime",ProgressDisplayTime);
   Store("PauseOnMarkSet",     PauseOnMarkSet);
+  Store("PauseOnMarkJump",    PauseOnMarkJump);
+  Store("SkipEdited",         SkipEdited);
+  Store("PauseAtLastMark",    PauseAtLastMark);
+  Store("AdaptiveSkipInitial",AdaptiveSkipInitial);
+  Store("AdaptiveSkipTimeout",AdaptiveSkipTimeout);
+  Store("AdaptiveSkipAlternate", AdaptiveSkipAlternate);
+  Store("AdaptiveSkipPrevNext", AdaptiveSkipPrevNext);
+  Store("SkipSeconds",        SkipSeconds);
+  Store("SkipSecondsRepeat",  SkipSecondsRepeat);
   Store("ResumeID",           ResumeID);
   Store("CurrentChannel",     CurrentChannel);
   Store("CurrentVolume",      CurrentVolume);
   Store("CurrentDolby",       CurrentDolby);
   Store("InitialChannel",     InitialChannel);
+  Store("VolumeSteps",        VolumeSteps);
+  Store("VolumeLinearize",    VolumeLinearize);
   Store("InitialVolume",      InitialVolume);
   Store("DeviceBondings",     DeviceBondings);
   Store("ChannelsWrap",       ChannelsWrap);
diff -ruN vdr-2.0.0/config.h vdr-2.2.0/config.h
--- vdr-2.0.0/config.h	2013-03-31 11:30:18.000000000 +0200
+++ vdr-2.2.0/config.h	2015-02-13 16:39:08.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.h 2.76 2013/03/31 09:30:18 kls Exp $
+ * $Id: config.h 3.21 2015/02/13 15:39:08 kls Exp $
  */
 
 #ifndef __CONFIG_H
@@ -22,13 +22,13 @@
 
 // VDR's own version number:
 
-#define VDRVERSION  "2.0.0"
-#define VDRVERSNUM   20000  // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION  "2.2.0"
+#define VDRVERSNUM   20200  // Version * 10000 + Major * 100 + Minor
 
 // The plugin API's version number:
 
-#define APIVERSION  "2.0.0"
-#define APIVERSNUM   20000  // Version * 10000 + Major * 100 + Minor
+#define APIVERSION  "2.2.0"
+#define APIVERSNUM   20200  // 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
@@ -65,6 +65,7 @@
 
 #define STANDARD_DVB       0
 #define STANDARD_ANSISCTE  1
+#define STANDARD_NORDIG    2
 
 typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
 
@@ -266,6 +267,12 @@
   int LnbFrequLo;
   int LnbFrequHi;
   int DiSEqC;
+  int UsePositioner;
+  int SiteLat;
+  int SiteLon;
+  int PositionerSpeed;
+  int PositionerSwing;
+  int PositionerLastLon;
   int SetSystemTime;
   int TimeSource;
   int TimeTransponder;
@@ -327,9 +334,20 @@
   int ShowRemainingTime;
   int ProgressDisplayTime;
   int PauseOnMarkSet;
+  int PauseOnMarkJump;
+  int SkipEdited;
+  int PauseAtLastMark;
+  int AdaptiveSkipInitial;
+  int AdaptiveSkipTimeout;
+  int AdaptiveSkipAlternate;
+  int AdaptiveSkipPrevNext;
+  int SkipSeconds;
+  int SkipSecondsRepeat;
   int ResumeID;
   int CurrentChannel;
   int CurrentVolume;
+  int VolumeSteps;
+  int VolumeLinearize;
   int CurrentDolby;
   int InitialVolume;
   int ChannelsWrap;
diff -ruN vdr-2.0.0/cutter.c vdr-2.2.0/cutter.c
--- vdr-2.0.0/cutter.c	2013-03-18 10:40:49.000000000 +0100
+++ vdr-2.2.0/cutter.c	2013-10-02 15:18:02.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: cutter.c 2.25 2013/03/18 09:40:49 kls Exp $
+ * $Id: cutter.c 3.4 2013/10/02 13:18:02 kls Exp $
  */
 
 #include "cutter.h"
@@ -556,6 +556,8 @@
       bool Independent;
       int Length;
       if (LoadFrame(Index, Buffer, Independent, Length)) {
+         // Make sure there is enough disk space:
+         AssertFreeDiskSpace(-1);
          bool CutIn = !SeamlessBegin && Index == BeginIndex;
          bool CutOut = !SeamlessEnd && Index == EndIndex - 1;
          bool DeletedFrame = false;
@@ -581,7 +583,7 @@
             }
          fileSize += Length;
          // Generate marks at the editing points in the edited recording:
-         if (numSequences > 0 && Index == BeginIndex) {
+         if (numSequences > 1 && Index == BeginIndex) {
             if (toMarks.Count() > 0)
                toMarks.Add(toIndex->Last());
             toMarks.Add(toIndex->Last());
@@ -608,8 +610,6 @@
               cCondWait::SleepMs(100);
               continue;
               }
-           // Make sure there is enough disk space:
-           AssertFreeDiskSpace(-1);
            // Determine the actual begin and end marks, skipping any marks at the same position:
            cMark *EndMark = fromMarks.GetNextEnd(BeginMark);
            // Process the current sequence:
@@ -642,46 +642,47 @@
 
 // --- cCutter ---------------------------------------------------------------
 
-cMutex cCutter::mutex;
-cString cCutter::originalVersionName;
-cString cCutter::editedVersionName;
-cCuttingThread *cCutter::cuttingThread = NULL;
-bool cCutter::error = false;
-bool cCutter::ended = false;
+cCutter::cCutter(const char *FileName)
+{
+  cuttingThread = NULL;
+  error = false;
+  originalVersionName = FileName;
+}
 
-bool cCutter::Start(const char *FileName)
+cCutter::~cCutter()
 {
-  cMutexLock MutexLock(&mutex);
-  if (!cuttingThread) {
-     error = false;
-     ended = false;
-     originalVersionName = FileName;
-     cRecording Recording(FileName);
+  Stop();
+}
 
-     cMarks FromMarks;
-     FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
-     if (cMark *First = FromMarks.GetNextBegin())
+cString cCutter::EditedFileName(const char *FileName)
+{
+  cRecording Recording(FileName);
+  cMarks Marks;
+  if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording())) {
+     if (cMark *First = Marks.GetNextBegin())
         Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);
+     return Recording.PrefixFileName('%');
+     }
+  return NULL;
+}
 
-     const char *evn = Recording.PrefixFileName('%');
-     if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
-        // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
-        // remove a possible deleted recording with the same name to avoid symlink mixups:
-        char *s = strdup(evn);
-        char *e = strrchr(s, '.');
-        if (e) {
-           if (strcmp(e, ".rec") == 0) {
-              strcpy(e, ".del");
-              RemoveVideoFile(s);
+bool cCutter::Start(void)
+{
+  if (!cuttingThread) {
+     error = false;
+     if (*originalVersionName) {
+        cRecording Recording(originalVersionName);
+        editedVersionName = EditedFileName(originalVersionName);
+        if (*editedVersionName) {
+           if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
+              if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
+                 Recording.WriteInfo(editedVersionName);
+                 Recordings.AddByName(editedVersionName, false);
+                 cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
+                 return true;
+                 }
               }
            }
-        free(s);
-        // XXX
-        editedVersionName = evn;
-        Recording.WriteInfo();
-        Recordings.AddByName(editedVersionName, false);
-        cuttingThread = new cCuttingThread(FileName, editedVersionName);
-        return true;
         }
      }
   return false;
@@ -689,7 +690,6 @@
 
 void cCutter::Stop(void)
 {
-  cMutexLock MutexLock(&mutex);
   bool Interrupted = cuttingThread && cuttingThread->Active();
   const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
   delete cuttingThread;
@@ -701,42 +701,27 @@
         esyslog("ERROR: '%s' during editing process", Error);
      if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), editedVersionName) == 0)
         cControl::Shutdown();
-     RemoveVideoFile(editedVersionName);
+     cVideoDirectory::RemoveVideoFile(editedVersionName);
      Recordings.DelByName(editedVersionName);
      }
 }
 
-bool cCutter::Active(const char *FileName)
+bool cCutter::Active(void)
 {
-  cMutexLock MutexLock(&mutex);
   if (cuttingThread) {
      if (cuttingThread->Active())
-        return !FileName || strcmp(FileName, originalVersionName) == 0 || strcmp(FileName, editedVersionName) == 0;
+        return true;
      error = cuttingThread->Error();
      Stop();
      if (!error)
         cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName, originalVersionName);
-     originalVersionName = NULL;
-     editedVersionName = NULL;
-     ended = true;
      }
   return false;
 }
 
 bool cCutter::Error(void)
 {
-  cMutexLock MutexLock(&mutex);
-  bool result = error;
-  error = false;
-  return result;
-}
-
-bool cCutter::Ended(void)
-{
-  cMutexLock MutexLock(&mutex);
-  bool result = ended;
-  ended = false;
-  return result;
+  return error;
 }
 
 #define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process
@@ -749,10 +734,13 @@
         cMarks Marks;
         if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
            if (Marks.GetNumSequences()) {
-              if (cCutter::Start(FileName)) {
-                 while (cCutter::Active())
+              cCutter Cutter(FileName);
+              if (Cutter.Start()) {
+                 while (Cutter.Active())
                        cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
-                 return true;
+                 if (!Cutter.Error())
+                    return true;
+                 fprintf(stderr, "error while cutting\n");
                  }
               else
                  fprintf(stderr, "can't start editing process\n");
diff -ruN vdr-2.0.0/cutter.h vdr-2.2.0/cutter.h
--- vdr-2.0.0/cutter.h	2012-02-16 13:05:33.000000000 +0100
+++ vdr-2.2.0/cutter.h	2013-10-05 13:34:55.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: cutter.h 2.3 2012/02/16 12:05:33 kls Exp $
+ * $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $
  */
 
 #ifndef __CUTTER_H
@@ -17,21 +17,31 @@
 
 class cCutter {
 private:
-  static cMutex mutex;
-  static cString originalVersionName;
-  static cString editedVersionName;
-  static cCuttingThread *cuttingThread;
-  static bool error;
-  static bool ended;
+  cString originalVersionName;
+  cString editedVersionName;
+  cCuttingThread *cuttingThread;
+  bool error;
 public:
-  static bool Start(const char *FileName);
-  static void Stop(void);
-  static bool Active(const char *FileName = NULL);
-         ///< Returns true if the cutter is currently active.
-         ///< If a FileName is given, true is only returned if either the
-         ///< original or the edited file name is equal to FileName.
-  static bool Error(void);
-  static bool Ended(void);
+  cCutter(const char *FileName);
+      ///< Sets up a new cutter for the given FileName, which must be the full path
+      ///< name of an existing recording directory.
+  ~cCutter();
+  static cString EditedFileName(const char *FileName);
+      ///< Returns the full path name of the edited version of the recording with
+      ///< the given FileName. This static function can be used independent of any
+      ///< cCutter object, to determine the file name beforehand.
+      ///< Returns NULL in case of error.
+  bool Start(void);
+      ///< Starts the actual cutting process.
+      ///< Returns true if successful.
+      ///< If Start() is called while the cutting process is already active, nothing
+      ///< happens and false will be returned.
+  void Stop(void);
+      ///< Stops an ongoing cutting process.
+  bool Active(void);
+      ///< Returns true if the cutter is currently active.
+  bool Error(void);
+      ///< Returns true if an error occurred while cutting the recording.
   };
 
 bool CutRecording(const char *FileName);
diff -ruN vdr-2.0.0/device.c vdr-2.2.0/device.c
--- vdr-2.0.0/device.c	2013-03-07 14:18:35.000000000 +0100
+++ vdr-2.2.0/device.c	2015-01-30 13:11:30.000000000 +0100
@@ -4,11 +4,12 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: device.c 2.74 2013/03/07 13:18:35 kls Exp $
+ * $Id: device.c 3.20 2015/01/30 12:11:30 kls Exp $
  */
 
 #include "device.h"
 #include <errno.h>
+#include <math.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include "audio.h"
@@ -76,7 +77,7 @@
   cardIndex = nextCardIndex++;
   dsyslog("new device number %d", CardIndex() + 1);
 
-  SetDescription("receiver on device %d", CardIndex() + 1);
+  SetDescription("device %d receiver", CardIndex() + 1);
 
   mute = false;
   volume = Setup.CurrentVolume;
@@ -94,11 +95,11 @@
 
   player = NULL;
   isPlayingVideo = false;
+  keepTracks = false; // used in ClrAvailableTracks()!
   ClrAvailableTracks();
   currentAudioTrack = ttNone;
   currentAudioTrackMissingCount = 0;
   currentSubtitleTrack = ttNone;
-  keepTracks = false;
   liveSubtitle = NULL;
   dvbSubtitleConverter = NULL;
   autoSelectPreferredSubtitleLanguage = true;
@@ -118,6 +119,8 @@
   DetachAllReceivers();
   delete liveSubtitle;
   delete dvbSubtitleConverter;
+  if (this == primaryDevice)
+     primaryDevice = NULL;
 }
 
 bool cDevice::WaitForAllDevicesReady(int Timeout)
@@ -356,13 +359,13 @@
 
 void cDevice::SetCamSlot(cCamSlot *CamSlot)
 {
+  LOCK_THREAD;
   camSlot = CamSlot;
 }
 
 void cDevice::Shutdown(void)
 {
   deviceHooks.Clear();
-  primaryDevice = NULL;
   for (int i = 0; i < numDevices; i++) {
       delete device[i];
       device[i] = NULL;
@@ -428,11 +431,6 @@
 {
 }
 
-eVideoSystem cDevice::GetVideoSystem(void)
-{
-  return vsPAL;
-}
-
 void cDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
 {
   Width = 0;
@@ -573,7 +571,7 @@
      AttachFilter(eitFilter = new cEitFilter);
      AttachFilter(patFilter = new cPatFilter);
      AttachFilter(sdtFilter = new cSdtFilter(patFilter));
-     AttachFilter(nitFilter = new cNitFilter);
+     AttachFilter(nitFilter = new cNitFilter(sdtFilter));
      }
 }
 
@@ -665,6 +663,11 @@
   return 0;
 }
 
+const cPositioner *cDevice::Positioner(void) const
+{
+  return NULL;
+}
+
 int cDevice::SignalStrength(void) const
 {
   return -1;
@@ -693,7 +696,7 @@
 bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
 {
   if (LiveView) {
-     isyslog("switching to channel %d", Channel->Number());
+     isyslog("switching to channel %d (%s)", Channel->Number(), Channel->Name());
      cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
      }
   for (int i = 3; i--;) {
@@ -753,7 +756,10 @@
 
   cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, LIVEPRIORITY, true) : this;
 
-  bool NeedsTransferMode = Device != this;
+  bool NeedsTransferMode = LiveView && Device != PrimaryDevice();
+  // If the CAM slot wants the TS data, we need to switch to Transfer Mode:
+  if (!NeedsTransferMode && LiveView && IsPrimaryDevice() && CamSlot() && CamSlot()->WantsTsData())
+     NeedsTransferMode = true;
 
   eSetChannelResult Result = scrOk;
 
@@ -761,7 +767,7 @@
   // use the card that actually can receive it and transfer data from there:
 
   if (NeedsTransferMode) {
-     if (Device && CanReplay()) {
+     if (Device && PrimaryDevice()->CanReplay()) {
         if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
            cControl::Launch(new cTransferControl(Device, Channel));
         else
@@ -784,6 +790,7 @@
      if (SetChannelDevice(Channel, LiveView)) {
         // Start section handling:
         if (sectionHandler) {
+           patFilter->Trigger(Channel->Sid());
            sectionHandler->SetChannel(Channel);
            sectionHandler->SetStatus(true);
            }
@@ -912,8 +919,10 @@
 void cDevice::SetVolume(int Volume, bool Absolute)
 {
   int OldVolume = volume;
-  volume = constrain(Absolute ? Volume : volume + Volume, 0, MAXVOLUME);
-  SetVolumeDevice(volume);
+  double VolumeDelta = double(MAXVOLUME) / Setup.VolumeSteps;
+  double VolumeLinearize = (Setup.VolumeLinearize >= 0) ? (Setup.VolumeLinearize / 10.0 + 1.0) : (1.0 / ((-Setup.VolumeLinearize / 10.0) + 1.0));
+  volume = constrain(int(floor((Absolute ? Volume : volume + Volume) / VolumeDelta + 0.5) * VolumeDelta), 0, MAXVOLUME);
+  SetVolumeDevice(MAXVOLUME - int(pow(1.0 - pow(double(volume) / MAXVOLUME, VolumeLinearize), 1.0 / VolumeLinearize) * MAXVOLUME));
   Absolute |= mute;
   cStatus::MsgSetVolume(Absolute ? volume : volume - OldVolume, Absolute);
   if (volume > 0) {
@@ -1115,7 +1124,7 @@
   return -1;
 }
 
-void cDevice::TrickSpeed(int Speed)
+void cDevice::TrickSpeed(int Speed, bool Forward)
 {
 }
 
@@ -1576,13 +1585,13 @@
                  bool DetachReceivers = false;
                  bool DescramblingOk = false;
                  int CamSlotNumber = 0;
+                 cCamSlot *cs = NULL;
                  if (startScrambleDetection) {
-                    cCamSlot *cs = CamSlot();
+                    cs = CamSlot();
                     CamSlotNumber = cs ? cs->SlotNumber() : 0;
                     if (CamSlotNumber) {
-                       bool Scrambled = b[3] & TS_SCRAMBLING_CONTROL;
                        int t = time(NULL) - startScrambleDetection;
-                       if (Scrambled) {
+                       if (TsIsScrambled(b)) {
                           if (t > TS_SCRAMBLING_TIMEOUT)
                              DetachReceivers = true;
                           }
@@ -1596,7 +1605,8 @@
                  Lock();
                  for (int i = 0; i < MAXRECEIVERS; i++) {
                      if (receiver[i] && receiver[i]->WantsPid(Pid)) {
-                        if (DetachReceivers) {
+                        if (DetachReceivers && cs && (!cs->IsActivating() || receiver[i]->Priority() >= LIVEPRIORITY)) {
+                           dsyslog("detaching receiver - won't decrypt channel %s with CAM %d", *receiver[i]->ChannelID().ToString(), CamSlotNumber);
                            ChannelCamRelations.SetChecked(receiver[i]->ChannelID(), CamSlotNumber);
                            Detach(receiver[i]);
                            }
@@ -1660,7 +1670,7 @@
          Receiver->device = this;
          receiver[i] = Receiver;
          Unlock();
-         if (camSlot) {
+         if (camSlot && Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver
             camSlot->StartDecrypting();
             startScrambleDetection = time(NULL);
             }
@@ -1691,8 +1701,13 @@
       else if (receiver[i])
          receiversLeft = true;
       }
-  if (camSlot)
-     camSlot->StartDecrypting();
+  if (camSlot) {
+     if (Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver
+        camSlot->StartDecrypting();
+        if (!camSlot->IsDecrypting() && !camSlot->IsActivating())
+           camSlot->Assign(NULL);
+        }
+     }
   if (!receiversLeft)
      Cancel(-1);
 }
@@ -1720,7 +1735,7 @@
 
 cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
 {
-  SetDescription("TS buffer on device %d", CardIndex);
+  SetDescription("device %d TS buffer", CardIndex);
   f = File;
   cardIndex = CardIndex;
   delivered = false;
@@ -1758,7 +1773,7 @@
      }
 }
 
-uchar *cTSBuffer::Get(void)
+uchar *cTSBuffer::Get(int *Available)
 {
   int Count = 0;
   if (delivered) {
@@ -1779,7 +1794,15 @@
         return NULL;
         }
      delivered = true;
+     if (Available)
+        *Available = Count;
      return p;
      }
   return NULL;
 }
+
+void cTSBuffer::Skip(int Count)
+{
+  ringBuffer->Del(Count);
+  delivered = false;
+}
diff -ruN vdr-2.0.0/device.h vdr-2.2.0/device.h
--- vdr-2.0.0/device.h	2013-02-16 16:20:01.000000000 +0100
+++ vdr-2.2.0/device.h	2015-01-12 15:39:09.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: device.h 2.47 2013/02/16 15:20:01 kls Exp $
+ * $Id: device.h 3.10 2015/01/12 14:39:09 kls Exp $
  */
 
 #ifndef __DEVICE_H
@@ -17,6 +17,7 @@
 #include "filter.h"
 #include "nit.h"
 #include "pat.h"
+#include "positioner.h"
 #include "remux.h"
 #include "ringbuffer.h"
 #include "sdt.h"
@@ -29,11 +30,12 @@
 #define MAXPIDHANDLES      64 // the maximum number of different PIDs per device
 #define MAXRECEIVERS       16 // the maximum number of receivers per device
 #define MAXVOLUME         255
-#define VOLUMEDELTA         5 // used to increase/decrease the volume
+#define VOLUMEDELTA       (MAXVOLUME / Setup.VolumeSteps) // used to increase/decrease the volume
 #define MAXOCCUPIEDTIMEOUT 99 // max. time (in seconds) a device may be occupied
 
 enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
 
+// Note that VDR itself always uses pmAudioVideo when replaying a recording!
 enum ePlayMode { pmNone,           // audio/video from decoder
                  pmAudioVideo,     // audio/video from player
                  pmAudioOnly,      // audio only from player, video from decoder
@@ -53,9 +55,12 @@
                  // KNOWN TO YOUR PLAYER.
                };
 
+#define DEPRECATED_VIDEOSYSTEM
+#ifdef DEPRECATED_VIDEOSYSTEM
 enum eVideoSystem { vsPAL,
                     vsNTSC
                   };
+#endif
 
 enum eVideoDisplayFormat { vdfPanAndScan,
                            vdfLetterBox,
@@ -196,11 +201,11 @@
          ///< A derived class must call the MakePrimaryDevice() function of its
          ///< base class.
 public:
-  bool IsPrimaryDevice(void) const { return this == primaryDevice; }
+  bool IsPrimaryDevice(void) const { return this == primaryDevice && HasDecoder(); }
   int CardIndex(void) const { return cardIndex; }
          ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
   int DeviceNumber(void) const;
-         ///< Returns the number of this device (0 ... numDevices).
+         ///< Returns the number of this device (0 ... numDevices - 1).
   virtual cString DeviceType(void) const;
          ///< Returns a string identifying the type of this device (like "DVB-S").
          ///< If this device can receive different delivery systems, the returned
@@ -273,6 +278,11 @@
          ///< actually provide channels must implement this function.
          ///< The result of this function is used when selecting a device, in order
          ///< to avoid devices that provide more than one system.
+  virtual const cPositioner *Positioner(void) const;
+         ///< Returns a pointer to the positioner (if any) this device has used to
+         ///< move the satellite dish to the requested position (only applies to DVB-S
+         ///< devices). If no positioner is involved, or this is not a DVB-S device,
+         ///< NULL will be returned.
   virtual int SignalStrength(void) const;
          ///< Returns the "strength" of the currently received signal.
          ///< This is a value in the range 0 (no signal at all) through
@@ -455,12 +465,19 @@
          ///< Sets the video display format to the given one (only useful
          ///< if this device has an MPEG decoder).
          ///< A derived class must first call the base class function!
+         ///< NOTE: this is only for SD devices. HD devices shall implement their
+         ///< own setup menu with the necessary parameters for controlling output.
   virtual void SetVideoFormat(bool VideoFormat16_9);
          ///< Sets the output video format to either 16:9 or 4:3 (only useful
          ///< if this device has an MPEG decoder).
-  virtual eVideoSystem GetVideoSystem(void);
+         ///< NOTE: this is only for SD devices. HD devices shall implement their
+         ///< own setup menu with the necessary parameters for controlling output.
+#ifdef DEPRECATED_VIDEOSYSTEM
+  virtual eVideoSystem GetVideoSystem(void) { return vsPAL; }
          ///< Returns the video system of the currently displayed material
          ///< (default is PAL).
+         ///< This function is deprecated and will be removed in a future version!
+#endif
   virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect);
          ///< Returns the Width, Height and VideoAspect ratio of the currently
          ///< displayed video material. Width and Height are given in pixel
@@ -559,8 +576,9 @@
   virtual void SetVolumeDevice(int Volume);
        ///< Sets the audio volume on this device (Volume = 0...255).
   virtual void SetDigitalAudioDevice(bool On);
-       ///< Tells the actual device that digital audio output shall be switched
-       ///< on or off.
+       ///< Tells the output device that the current audio track is Dolby Digital.
+       ///< Only used by the original "full featured" DVB cards - do not use for new
+       ///< developments!
 public:
   bool IsMute(void) const { return mute; }
   bool ToggleMute(void);
@@ -692,10 +710,11 @@
   virtual bool HasIBPTrickSpeed(void) { return false; }
        ///< Returns true if this device can handle all frames in 'fast forward'
        ///< trick speeds.
-  virtual void TrickSpeed(int Speed);
+  virtual void TrickSpeed(int Speed, bool Forward);
        ///< Sets the device into a mode where replay is done slower.
        ///< Every single frame shall then be displayed the given number of
-       ///< times.
+       ///< times. Forward is true if replay is done in the normal (forward)
+       ///< direction, false if it is done reverse.
        ///< The cDvbPlayer uses the following values for the various speeds:
        ///<                   1x   2x   3x
        ///< Fast Forward       6    3    1
@@ -717,7 +736,7 @@
        ///< all registered cAudio objects are notified.
   virtual void StillPicture(const uchar *Data, int Length);
        ///< Displays the given I-frame as a still picture.
-       ///< Data points either to TS (first byte is 0x47) or PES (first byte
+       ///< Data points either to a series of TS (first byte is 0x47) or PES (first byte
        ///< is 0x00) data of the given Length. The default implementation
        ///< converts TS to PES and calls itself again, allowing a derived class
        ///< to display PES if it can't handle TS directly.
@@ -821,8 +840,21 @@
   virtual void Action(void);
 public:
   cTSBuffer(int File, int Size, int CardIndex);
-  ~cTSBuffer();
-  uchar *Get(void);
+  virtual ~cTSBuffer();
+  uchar *Get(int *Available = NULL);
+     ///< Returns a pointer to the first TS packet in the buffer. If Available is given,
+     ///< it will return the total number of consecutive bytes pointed to in the buffer.
+     ///< It is guaranteed that the returned pointer points to a TS_SYNC_BYTE and that
+     ///< there are at least TS_SIZE bytes in the buffer. Otherwise NULL will be
+     ///< returned and the value in Available (if given) is undefined.
+     ///< Each call to Get() returns a pointer to the next TS packet in the buffer.
+  void Skip(int Count);
+     ///< If after a call to Get() more or less than TS_SIZE of the available data
+     ///< has been processed, a call to Skip() with the number of processed bytes
+     ///< will disable the automatic incrementing of the data pointer as described
+     ///< in Get() and skip the given number of bytes instead. Count may be 0 if the
+     ///< caller wants the previous TS packet to be delivered again in the next call
+     ///< to Get().
   };
 
 #endif //__DEVICE_H
diff -ruN vdr-2.0.0/diseqc.c vdr-2.2.0/diseqc.c
--- vdr-2.0.0/diseqc.c	2011-09-17 16:13:31.000000000 +0200
+++ vdr-2.2.0/diseqc.c	2015-01-26 13:02:14.000000000 +0100
@@ -4,24 +4,37 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: diseqc.c 2.9 2011/09/17 14:13:31 kls Exp $
+ * $Id: diseqc.c 3.4 2015/01/26 12:02:14 kls Exp $
  */
 
 #include "diseqc.h"
 #include <ctype.h>
+#include <linux/dvb/frontend.h>
+#include <sys/ioctl.h>
 #include "sources.h"
 #include "thread.h"
 
-static bool ParseDeviceNumbers(const char *s, int &Devices)
+#define ALL_DEVICES (~0) // all bits set to '1'
+#define MAX_DEVICES 32   // each bit in a 32-bit integer represents one device
+
+static int CurrentDevices = 0;
+
+static bool IsDeviceNumbers(const char *s)
+{
+  return *s && s[strlen(s) - 1] == ':';
+}
+
+static bool ParseDeviceNumbers(const char *s)
 {
-  if (*s && s[strlen(s) - 1] == ':') {
+  if (IsDeviceNumbers(s)) {
+     CurrentDevices = 0;
      const char *p = s;
      while (*p && *p != ':') {
            char *t = NULL;
            int d = strtol(p, &t, 10);
            p = t;
-           if (0 < d && d < 31)
-              Devices |= (1 << d - 1);
+           if (0 < d && d <= MAX_DEVICES)
+              CurrentDevices |= (1 << d - 1);
            else {
               esyslog("ERROR: invalid device number %d in '%s'", d, s);
               return false;
@@ -31,6 +44,107 @@
   return true;
 }
 
+// --- cDiseqcPositioner -----------------------------------------------------
+
+// See http://www.eutelsat.com/files/live/sites/eutelsatv2/files/contributed/satellites/pdf/Diseqc/associated%20docs/positioner_appli_notice.pdf
+
+cDiseqcPositioner::cDiseqcPositioner(void)
+{
+  SetCapabilities(pcCanDrive |
+                  pcCanStep |
+                  pcCanHalt |
+                  pcCanSetLimits |
+                  pcCanDisableLimits |
+                  pcCanEnableLimits |
+                  pcCanStorePosition |
+                  pcCanRecalcPositions |
+                  pcCanGotoPosition |
+                  pcCanGotoAngle
+                  );
+}
+
+void cDiseqcPositioner::SendDiseqc(uint8_t *Codes, int NumCodes)
+{
+  struct dvb_diseqc_master_cmd cmd;
+  NumCodes = min(NumCodes, int(sizeof(cmd.msg) - 2));
+  cmd.msg_len = 0;
+  cmd.msg[cmd.msg_len++] = 0xE0;
+  cmd.msg[cmd.msg_len++] = 0x31;
+  for (int i = 0; i < NumCodes; i++)
+      cmd.msg[cmd.msg_len++] = Codes[i];
+  CHECK(ioctl(Frontend(), FE_DISEQC_SEND_MASTER_CMD, &cmd));
+}
+
+void cDiseqcPositioner::Drive(ePositionerDirection Direction)
+{
+  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x68 : 0x69), 0x00 };
+  SendDiseqc(Code, 2);
+}
+
+void cDiseqcPositioner::Step(ePositionerDirection Direction, uint Steps)
+{
+  if (Steps == 0)
+     return;
+  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x68 : 0x69), 0xFF };
+  Code[1] -= min(Steps, uint(0x7F)) - 1;
+  SendDiseqc(Code, 2);
+}
+
+void cDiseqcPositioner::Halt(void)
+{
+  uint8_t Code[] = { 0x60 };
+  SendDiseqc(Code, 1);
+}
+
+void cDiseqcPositioner::SetLimit(ePositionerDirection Direction)
+{
+  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x66 : 0x67) };
+  SendDiseqc(Code, 1);
+}
+
+void cDiseqcPositioner::DisableLimits(void)
+{
+  uint8_t Code[] = { 0x63 };
+  SendDiseqc(Code, 1);
+}
+
+void cDiseqcPositioner::EnableLimits(void)
+{
+  uint8_t Code[] = { 0x6A, 0x00 };
+  SendDiseqc(Code, 2);
+}
+
+void cDiseqcPositioner::StorePosition(uint Number)
+{
+  uint8_t Code[] = { 0x6A, uint8_t(Number) };
+  SendDiseqc(Code, 2);
+}
+
+void cDiseqcPositioner::RecalcPositions(uint Number)
+{
+  uint8_t Code[] = { 0x6F, uint8_t(Number), 0x00, 0x00 };
+  SendDiseqc(Code, 4);
+}
+
+void cDiseqcPositioner::GotoPosition(uint Number, int Longitude)
+{
+  uint8_t Code[] = { 0x6B, uint8_t(Number) };
+  SendDiseqc(Code, 2);
+  cPositioner::GotoPosition(Number, Longitude);
+}
+
+void cDiseqcPositioner::GotoAngle(int Longitude)
+{
+  uint8_t Code[] = { 0x6E, 0x00, 0x00 };
+  int Angle = CalcHourAngle(Longitude);
+  int a = abs(Angle);
+  Code[1] = a / 10 / 16;
+  Code[2] = a / 10 % 16 * 16 + a % 10 * 16 / 10;
+  Code[1] |= (Angle < 0) ? 0xE0 : 0xD0;
+  SendDiseqc(Code, 3);
+  cPositioner::GotoAngle(Longitude);
+}
+
 // --- cScr ------------------------------------------------------------------
 
 cScr::cScr(void)
@@ -44,14 +158,13 @@
 
 bool cScr::Parse(const char *s)
 {
-  if (!ParseDeviceNumbers(s, devices))
-     return false;
-  if (devices)
-     return true;
+  if (IsDeviceNumbers(s))
+     return ParseDeviceNumbers(s);
+  devices = CurrentDevices;
   bool result = false;
   int fields = sscanf(s, "%d %u %d", &channel, &userBand, &pin);
   if (fields == 2 || fields == 3) {
-     if (channel >= 0 && channel < 8) {
+     if (channel >= 0 && channel < 32) {
         result = true;
         if (fields == 3 && (pin < 0 || pin > 255)) {
            esyslog("Error: invalid SCR pin '%d'", pin);
@@ -68,16 +181,17 @@
 
 cScrs Scrs;
 
+bool cScrs::Load(const char *FileName, bool AllowComments, bool MustExist)
+{
+  CurrentDevices = ALL_DEVICES;
+  return cConfig<cScr>::Load(FileName, AllowComments, MustExist);
+}
+
 cScr *cScrs::GetUnused(int Device)
 {
   cMutexLock MutexLock(&mutex);
-  int Devices = 0;
   for (cScr *p = First(); p; p = Next(p)) {
-      if (p->Devices()) {
-         Devices = p->Devices();
-         continue;
-         }
-      if (Devices && !(Devices & (1 << Device - 1)))
+      if (!IsBitSet(p->Devices(), Device - 1))
          continue;
       if (!p->Used()) {
         p->SetUsed(true);
@@ -96,6 +210,7 @@
   slof = 0;
   polarization = 0;
   lof = 0;
+  position = -1;
   scrBank = -1;
   commands = NULL;
   parsing = false;
@@ -108,15 +223,14 @@
 
 bool cDiseqc::Parse(const char *s)
 {
-  if (!ParseDeviceNumbers(s, devices))
-     return false;
-  if (devices)
-     return true;
+  if (IsDeviceNumbers(s))
+     return ParseDeviceNumbers(s);
+  devices = CurrentDevices;
   bool result = false;
   char *sourcebuf = NULL;
-  int fields = sscanf(s, "%a[^ ] %d %c %d %a[^\n]", &sourcebuf, &slof, &polarization, &lof, &commands);
+  int fields = sscanf(s, "%m[^ ] %d %c %d %m[^\n]", &sourcebuf, &slof, &polarization, &lof, &commands);
   if (fields == 4)
-     commands = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %a argument results in an empty string
+     commands = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %m argument results in an empty string
   if (4 <= fields && fields <= 5) {
      source = cSource::FromString(sourcebuf);
      if (Sources.Get(source)) {
@@ -141,26 +255,52 @@
 
 uint cDiseqc::SetScrFrequency(uint SatFrequency, const cScr *Scr, uint8_t *Codes) const
 {
-  uint t = SatFrequency == 0 ? 0 : (SatFrequency + Scr->UserBand() + 2) / 4 - 350; // '+ 2' together with '/ 4' results in rounding!
-  if (t < 1024 && Scr->Channel() >= 0 && Scr->Channel() < 8) {
-     Codes[3] = t >> 8 | (t == 0 ? 0 : scrBank << 2) | Scr->Channel() << 5;
-     Codes[4] = t;
-     if (t)
-        return (t + 350) * 4 - SatFrequency;
+  if ((Codes[0] & 0xF0) == 0x70 ) { // EN50607 aka JESS
+     uint t = SatFrequency == 0 ? 0 : (SatFrequency - 100);
+     if (t < 2048 && Scr->Channel() >= 0 && Scr->Channel() < 32) {
+        Codes[1] = t >> 8 | Scr->Channel() << 3;
+        Codes[2] = t;
+        Codes[3] = (t == 0 ? 0 : scrBank);
+        if (t)
+           return Scr->UserBand();
+        }
      }
+  else { // EN50494 aka Unicable
+     uint t = SatFrequency == 0 ? 0 : (SatFrequency + Scr->UserBand() + 2) / 4 - 350; // '+ 2' together with '/ 4' results in rounding!
+     if (t < 1024 && Scr->Channel() >= 0 && Scr->Channel() < 8) {
+        Codes[3] = t >> 8 | (t == 0 ? 0 : scrBank << 2) | Scr->Channel() << 5;
+        Codes[4] = t;
+        if (t)
+           return (t + 350) * 4 - SatFrequency;
+        }
+     }
+  esyslog("ERROR: invalid SCR channel number %d or frequency %d", Scr->Channel(),SatFrequency);
   return 0;
 }
 
 int cDiseqc::SetScrPin(const cScr *Scr, uint8_t *Codes) const
 {
-  if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
-     Codes[2] = 0x5C;
-     Codes[5] = Scr->Pin();
-     return 6;
-     }
-  else {
-     Codes[2] = 0x5A;
-     return 5;
+  if ((Codes[0] & 0xF0) == 0x70 ) { // EN50607 aka JESS
+     if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
+        Codes[0] = 0x71;
+        Codes[4] = Scr->Pin();
+        return 5;
+        }
+     else {
+        Codes[0] = 0x70;
+        return 4;
+        }
+     }
+  else { // EN50494 aka Unicable
+     if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
+        Codes[2] = 0x5C;
+        Codes[5] = Scr->Pin();
+        return 6;
+        }
+     else {
+        Codes[2] = 0x5A;
+        return 5;
+        }
      }
 }
 
@@ -178,12 +318,34 @@
   return NULL;
 }
 
+const char *cDiseqc::GetPosition(const char *s) const
+{
+  if (!*s || !isdigit(*s)) {
+     position = 0;
+     return s;
+     }
+  char *p = NULL;
+  errno = 0;
+  int n = strtol(s, &p, 10);
+  if (!errno && p != s && n >= 0 && n < 0xFF) {
+     if (parsing) {
+        if (position < 0)
+           position = n;
+        else
+           esyslog("ERROR: more than one position in '%s'", s - 1);
+        }
+     return p;
+     }
+  esyslog("ERROR: invalid satellite position in '%s'", s - 1);
+  return NULL;
+}
+
 const char *cDiseqc::GetScrBank(const char *s) const
 {
   char *p = NULL;
   errno = 0;
   int n = strtol(s, &p, 10);
-  if (!errno && p != s && n >= 0 && n < 8) {
+  if (!errno && p != s && n >= 0 && n < 256) {
      if (parsing) {
         if (scrBank < 0)
            scrBank = n;
@@ -192,7 +354,7 @@
         }
      return p;
      }
-  esyslog("ERROR: more than one scr bank in '%s'", s - 1);
+  esyslog("ERROR: invalid value for scr bank in '%s'", s - 1);
   return NULL;
 }
 
@@ -250,8 +412,12 @@
           case 'V': return daVoltage18;
           case 'A': return daMiniA;
           case 'B': return daMiniB;
-          case 'W': *CurrentAction = Wait(*CurrentAction); break;
-          case 'S': *CurrentAction = GetScrBank(*CurrentAction); break;
+          case 'W': *CurrentAction = Wait(*CurrentAction); return daWait;
+          case 'P': *CurrentAction = GetPosition(*CurrentAction);
+                    if (Setup.UsePositioner)
+                       return position ? daPositionN : daPositionA;
+                    break;
+          case 'S': *CurrentAction = GetScrBank(*CurrentAction); return daScr;
           case '[': *CurrentAction = GetCodes(*CurrentAction, Codes, MaxCodes);
                     if (*CurrentAction) {
                        if (Scr && Frequency) {
@@ -261,7 +427,8 @@
                        return daCodes;
                        }
                     break;
-          default: return daNone;
+          default:  esyslog("ERROR: unknown diseqc code '%c'", *(*CurrentAction - 1));
+                    return daNone;
           }
         }
   return daNone;
@@ -271,17 +438,18 @@
 
 cDiseqcs Diseqcs;
 
+bool cDiseqcs::Load(const char *FileName, bool AllowComments, bool MustExist)
+{
+  CurrentDevices = ALL_DEVICES;
+  return cConfig<cDiseqc>::Load(FileName, AllowComments, MustExist);
+}
+
 const cDiseqc *cDiseqcs::Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const
 {
-  int Devices = 0;
   for (const cDiseqc *p = First(); p; p = Next(p)) {
-      if (p->Devices()) {
-         Devices = p->Devices();
-         continue;
-         }
-      if (Devices && !(Devices & (1 << Device - 1)))
+      if (!IsBitSet(p->Devices(), Device - 1))
          continue;
-      if (p->Source() == Source && p->Slof() > Frequency && p->Polarization() == toupper(Polarization)) {
+      if (cSource::Matches(p->Source(), Source) && p->Slof() > Frequency && p->Polarization() == toupper(Polarization)) {
          if (p->IsScr() && Scr && !*Scr) {
             *Scr = Scrs.GetUnused(Device);
             if (*Scr)
diff -ruN vdr-2.0.0/diseqc.conf vdr-2.2.0/diseqc.conf
--- vdr-2.0.0/diseqc.conf	2011-09-10 14:41:58.000000000 +0200
+++ vdr-2.2.0/diseqc.conf	2015-01-26 12:59:30.000000000 +0100
@@ -5,6 +5,9 @@
 # satellite slof polarization lof command...
 #
 # satellite:      one of the 'S' codes defined in sources.conf
+#                 the special value 'S360E' means that this entry uses a positioner
+#                 (command 'P') that can move the dish to any requested satellite
+#                 position within its range
 # slof:           switch frequency of LNB; the first entry with
 #                 an slof greater than the actual transponder
 #                 frequency will be used
@@ -18,7 +21,9 @@
 #   V         voltage high (18V)
 #   A         mini A
 #   B         mini B
-#   Sn        Satellite channel routing code sequence for bank n follows
+#   Pn        use positioner to move dish to satellite position n (or to the
+#             satellite's orbital position, if no position number is given)
+#   Sn        satellite channel routing code sequence for bank n follows
 #   Wnn       wait nn milliseconds (nn may be any positive integer number)
 #   [xx ...]  hex code sequence (max. 6)
 #
@@ -77,7 +82,7 @@
 # S19.2E  12110 V 11080 t v
 # S19.2E  99999 V 10720 t v
 #
-# SCR (Satellite Channel Routing):
+# SCR (Satellite Channel Routing) EN50494:
 #
 # S19.2E  11700 V  9750  t V W10 S0 [E0 10 5A 00 00] W10 v
 # S19.2E  99999 V 10600  t V W10 S1 [E0 10 5A 00 00] W10 v
@@ -88,3 +93,32 @@
 # S13.0E  99999 V 10600  t V W10 S5 [E0 10 5A 00 00] W10 v
 # S13.0E  11700 H  9750  t V W10 S6 [E0 10 5A 00 00] W10 v
 # S13.0E  99999 H 10600  t V W10 S7 [E0 10 5A 00 00] W10 v
+#
+# SCR (Satellite Channel Routing) EN50607 "JESS":
+#
+# S19.2E  11700 V  9750  t V W10 S0 [70 00 00 00] W10 v
+# S19.2E  99999 V 10600  t V W10 S1 [70 00 00 00] W10 v
+# S19.2E  11700 H  9750  t V W10 S2 [70 00 00 00] W10 v
+# S19.2E  99999 H 10600  t V W10 S3 [70 00 00 00] W10 v
+#
+# S13.0E  11700 V  9750  t V W10 S4 [70 00 00 00] W10 v
+# S13.0E  99999 V 10600  t V W10 S5 [70 00 00 00] W10 v
+# S13.0E  11700 H  9750  t V W10 S6 [70 00 00 00] W10 v
+# S13.0E  99999 H 10600  t V W10 S7 [70 00 00 00] W10 v
+#
+# S23.0E  11700 V  9750  t V W10 S8 [70 00 00 00] W10 v
+# S23.0E  99999 V 10600  t V W10 S9 [70 00 00 00] W10 v
+# S23.0E  11700 H  9750  t V W10 S10 [70 00 00 00] W10 v
+# S23.0E  99999 H 10600  t V W10 S11 [70 00 00 00] W10 v
+#
+# S28.2E  11700 V  9750  t V W10 S12 [70 00 00 00] W10 v
+# S28.2E  99999 V 10600  t V W10 S13 [70 00 00 00] W10 v
+# S28.2E  11700 H  9750  t V W10 S14 [70 00 00 00] W10 v
+# S28.2E  99999 H 10600  t V W10 S15 [70 00 00 00] W10 v
+#
+# Positioner for steerable dish:
+#
+# S360E  11700 V  9750  t V W20 P W20 t v
+# S360E  99999 V 10600  t V W20 P W20 T v
+# S360E  11700 H  9750  t V W20 P W20 t V
+# S360E  99999 H 10600  t V W20 P W20 T V
diff -ruN vdr-2.0.0/diseqc.h vdr-2.2.0/diseqc.h
--- vdr-2.0.0/diseqc.h	2011-09-17 15:15:17.000000000 +0200
+++ vdr-2.2.0/diseqc.h	2013-06-12 13:52:17.000000000 +0200
@@ -4,15 +4,33 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: diseqc.h 2.5 2011/09/17 13:15:17 kls Exp $
+ * $Id: diseqc.h 3.1 2013/06/12 11:52:17 kls Exp $
  */
 
 #ifndef __DISEQC_H
 #define __DISEQC_H
 
 #include "config.h"
+#include "positioner.h"
 #include "thread.h"
 
+class cDiseqcPositioner : public cPositioner {
+private:
+  void SendDiseqc(uint8_t *Codes, int NumCodes);
+public:
+  cDiseqcPositioner(void);
+  virtual void Drive(ePositionerDirection Direction);
+  virtual void Step(ePositionerDirection Direction, uint Steps = 1);
+  virtual void Halt(void);
+  virtual void SetLimit(ePositionerDirection Direction);
+  virtual void DisableLimits(void);
+  virtual void EnableLimits(void);
+  virtual void StorePosition(uint Number);
+  virtual void RecalcPositions(uint Number);
+  virtual void GotoPosition(uint Number, int Longitude);
+  virtual void GotoAngle(int Longitude);
+  };
+
 class cScr : public cListObject {
 private:
   int devices;
@@ -35,6 +53,7 @@
 private:
   cMutex mutex;
 public:
+  bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
   cScr *GetUnused(int Device);
   };
 
@@ -50,8 +69,11 @@
     daVoltage18,
     daMiniA,
     daMiniB,
+    daPositionN,
+    daPositionA,
     daScr,
     daCodes,
+    daWait,
     };
   enum { MaxDiseqcCodes = 6 };
 private:
@@ -60,12 +82,14 @@
   int slof;
   char polarization;
   int lof;
+  mutable int position;
   mutable int scrBank;
   char *commands;
   bool parsing;
   uint SetScrFrequency(uint SatFrequency, const cScr *Scr, uint8_t *Codes) const;
   int SetScrPin(const cScr *Scr, uint8_t *Codes) const;
   const char *Wait(const char *s) const;
+  const char *GetPosition(const char *s) const;
   const char *GetScrBank(const char *s) const;
   const char *GetCodes(const char *s, uchar *Codes = NULL, uint8_t *MaxCodes = NULL) const;
 public:
@@ -89,16 +113,31 @@
       ///< Frequency must be the frequency the tuner will be tuned to, and will be
       ///< set to the proper SCR frequency upon return (if SCR is used).
   int Devices(void) const { return devices; }
+      ///< Returns an integer where each bit represents one of the system's devices.
+      ///< If a bit is set, this DiSEqC sequence applies to the corresponding device.
   int Source(void) const { return source; }
+      ///< Returns the satellite source this DiSEqC sequence applies to.
   int Slof(void) const { return slof; }
+      ///< Returns the switch frequency of the LNB this DiSEqC sequence applies to.
   char Polarization(void) const { return polarization; }
+      ///< Returns the signal polarization this DiSEqC sequence applies to.
   int Lof(void) const { return lof; }
-  bool IsScr() const { return scrBank >= 0; }
+      ///< Returns the local oscillator frequency of the LNB this DiSEqC sequence applies to.
+  int Position(void) const { return position; }
+      ///< Indicates which positioning mode to use in order to move the dish to a given
+      ///< satellite position. -1 means "no positioning" (i.e. fixed dish); 0 means the
+      ///< positioner can be moved to any arbitrary satellite position (within its
+      ///< limits); and a positive number means "move the dish to the position stored
+      ///< under the given number".
+  bool IsScr(void) const { return scrBank >= 0; }
+      ///< Returns true if this DiSEqC sequence uses Satellite Channel Routing.
   const char *Commands(void) const { return commands; }
+      ///< Returns a pointer to the actual commands of this DiSEqC sequence.
   };
 
 class cDiseqcs : public cConfig<cDiseqc> {
 public:
+  bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
   const cDiseqc *Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const;
       ///< Selects a DiSEqC entry suitable for the given Device and tuning parameters.
       ///< If this DiSEqC entry requires SCR and the given *Scr is NULL
diff -ruN vdr-2.0.0/dvbci.c vdr-2.2.0/dvbci.c
--- vdr-2.0.0/dvbci.c	2007-01-04 13:49:10.000000000 +0100
+++ vdr-2.2.0/dvbci.c	2015-01-14 12:13:49.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbci.c 2.0 2007/01/04 12:49:10 kls Exp $
+ * $Id: dvbci.c 3.1 2015/01/14 11:13:49 kls Exp $
  */
 
 #include "dvbci.h"
@@ -17,7 +17,7 @@
 cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
 {
   device = Device;
-  SetDescription("CI adapter on device %d", device->DeviceNumber());
+  SetDescription("device %d CI adapter", device->DeviceNumber());
   fd = Fd;
   ca_caps_t Caps;
   if (ioctl(fd, CA_GET_CAP, &Caps) == 0) {
diff -ruN vdr-2.0.0/dvbci.h vdr-2.2.0/dvbci.h
--- vdr-2.0.0/dvbci.h	2006-11-26 12:19:42.000000000 +0100
+++ vdr-2.2.0/dvbci.h	2006-11-26 12:19:42.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbci.h 2.0 2006/11/26 11:19:42 kls Exp $
+ * $Id: dvbci.h 3.0 2006/11/26 11:19:42 kls Exp $
  */
 
 #ifndef __DVBCI_H
diff -ruN vdr-2.0.0/dvbdevice.c vdr-2.2.0/dvbdevice.c
--- vdr-2.0.0/dvbdevice.c	2013-03-16 16:23:35.000000000 +0100
+++ vdr-2.2.0/dvbdevice.c	2015-01-14 13:09:19.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbdevice.c 2.88 2013/03/16 15:23:35 kls Exp $
+ * $Id: dvbdevice.c 3.14 2015/01/14 12:09:19 kls Exp $
  */
 
 #include "dvbdevice.h"
@@ -21,10 +21,7 @@
 #include "menuitems.h"
 #include "sourceparams.h"
 
-#if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) < 0x0508
-#define DTV_STREAM_ID DTV_DVBT2_PLP_ID
-#define FE_CAN_MULTISTREAM 0x4000000
-#endif
+static int DvbApiVersion = 0x0000; // the version of the DVB driver actually in use (will be determined by the first device created)
 
 #define DVBS_TUNE_TIMEOUT  9000 //ms
 #define DVBS_LOCK_TIMEOUT  2000 //ms
@@ -39,6 +36,13 @@
 
 // --- DVB Parameter Maps ----------------------------------------------------
 
+const tDvbParameterMap PilotValues[] = {
+  {   0, PILOT_OFF,  trNOOP("off") },
+  {   1, PILOT_ON,   trNOOP("on") },
+  { 999, PILOT_AUTO, trNOOP("auto") },
+  {  -1, 0, NULL }
+  };
+
 const tDvbParameterMap InversionValues[] = {
   {   0, INVERSION_OFF,  trNOOP("off") },
   {   1, INVERSION_ON,   trNOOP("on") },
@@ -209,6 +213,9 @@
   hierarchy    = HIERARCHY_AUTO;
   rollOff      = ROLLOFF_AUTO;
   streamId     = 0;
+  t2systemId   = 0;
+  sisoMiso     = 0;
+  pilot        = PILOT_AUTO;
   Parse(Parameters);
 }
 
@@ -230,10 +237,13 @@
   ST("   T*")  q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
   ST("ACST*")  q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
   ST("ACST*")  q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
+  ST("  S 2")  q += PrintParameter(q, 'N', MapToUser(pilot, PilotValues));
   ST("  S 2")  q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
   ST("  ST2")  q += PrintParameter(q, 'P', streamId);
+  ST("   T2")  q += PrintParameter(q, 'Q', t2systemId);
   ST("  ST*")  q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter
   ST("   T*")  q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+  ST("   T2")  q += PrintParameter(q, 'X', sisoMiso);
   ST("   T*")  q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
   return buffer;
 }
@@ -266,12 +276,15 @@
           case 'I': s = ParseParameter(s, inversion, InversionValues); break;
           case 'L': polarization = 'L'; s++; break;
           case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
+          case 'N': s = ParseParameter(s, pilot, PilotValues); break;
           case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
           case 'P': s = ParseParameter(s, streamId); break;
+          case 'Q': s = ParseParameter(s, t2systemId); break;
           case 'R': polarization = 'R'; s++; break;
           case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter
           case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
           case 'V': polarization = 'V'; s++; break;
+          case 'X': s = ParseParameter(s, sisoMiso); break;
           case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
           default: esyslog("ERROR: unknown parameter key '%c'", *s);
                    return false;
@@ -287,7 +300,7 @@
 class cDvbTuner : public cThread {
 private:
   static cMutex bondMutex;
-  enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
+  enum eTunerStatus { tsIdle, tsSet, tsPositioning, tsTuned, tsLocked };
   int frontendType;
   const cDvbDevice *device;
   int fd_frontend;
@@ -298,6 +311,9 @@
   time_t lastTimeoutReport;
   cChannel channel;
   const cDiseqc *lastDiseqc;
+  int diseqcOffset;
+  int lastSource;
+  cPositioner *positioner;
   const cScr *scr;
   bool lnbPowerTurnedOn;
   eTunerStatus tunerStatus;
@@ -312,6 +328,7 @@
   bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; }
   void ClearEventQueue(void) const;
   bool GetFrontendStatus(fe_status_t &Status) const;
+  cPositioner *GetPositioner(void);
   void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency);
   void ResetToneAndVoltage(void);
   bool SetFrontend(void);
@@ -328,6 +345,7 @@
   bool IsTunedTo(const cChannel *Channel) const;
   void SetChannel(const cChannel *Channel);
   bool Locked(int TimeoutMs = 0);
+  const cPositioner *Positioner(void) const { return positioner; }
   int GetSignalStrength(void) const;
   int GetSignalQuality(void) const;
   };
@@ -346,12 +364,15 @@
   lockTimeout = 0;
   lastTimeoutReport = 0;
   lastDiseqc = NULL;
+  diseqcOffset = 0;
+  lastSource = 0;
+  positioner = NULL;
   scr = NULL;
   lnbPowerTurnedOn = false;
   tunerStatus = tsIdle;
   bondedTuner = NULL;
   bondedMaster = false;
-  SetDescription("tuner on frontend %d/%d", adapter, frontend);
+  SetDescription("frontend %d/%d tuner", adapter, frontend);
   Start();
 }
 
@@ -485,6 +506,7 @@
      cMutexLock MutexLock(&mutex);
      if (!IsTunedTo(Channel))
         tunerStatus = tsSet;
+     diseqcOffset = 0;
      channel = *Channel;
      lastTimeoutReport = 0;
      newSet.Broadcast();
@@ -665,6 +687,15 @@
   return f;
 }
 
+cPositioner *cDvbTuner::GetPositioner(void)
+{
+  if (!positioner) {
+     positioner = cPositioner::GetPositioner();
+     positioner->SetFrontend(fd_frontend);
+     }
+  return positioner;
+}
+
 void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency)
 {
   if (!lnbPowerTurnedOn) {
@@ -676,23 +707,47 @@
      Mutex.Lock();
   struct dvb_diseqc_master_cmd cmd;
   const char *CurrentAction = NULL;
-  for (;;) {
+  cPositioner *Positioner = NULL;
+  bool Break = false;
+  for (int i = 0; !Break; i++) {
       cmd.msg_len = sizeof(cmd.msg);
       cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency);
-      if (da == cDiseqc::daNone)
+      if (da == cDiseqc::daNone) {
+         diseqcOffset = 0;
          break;
+         }
+      bool d = i >= diseqcOffset;
       switch (da) {
-        case cDiseqc::daToneOff:   CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
-        case cDiseqc::daToneOn:    CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
-        case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
-        case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
-        case cDiseqc::daMiniA:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
-        case cDiseqc::daMiniB:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
-        case cDiseqc::daCodes:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
+        case cDiseqc::daToneOff:   if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
+        case cDiseqc::daToneOn:    if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
+        case cDiseqc::daVoltage13: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
+        case cDiseqc::daVoltage18: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
+        case cDiseqc::daMiniA:     if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
+        case cDiseqc::daMiniB:     if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
+        case cDiseqc::daCodes:     if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
+        case cDiseqc::daPositionN: if ((Positioner = GetPositioner()) != NULL) {
+                                      if (d) {
+                                         Positioner->GotoPosition(Diseqc->Position(), cSource::Position(channel.Source()));
+                                         Break = Positioner->IsMoving();
+                                         }
+                                      }
+                                   break;
+        case cDiseqc::daPositionA: if ((Positioner = GetPositioner()) != NULL) {
+                                      if (d) {
+                                         Positioner->GotoAngle(cSource::Position(channel.Source()));
+                                         Break = Positioner->IsMoving();
+                                         }
+                                      }
+                                   break;
+        case cDiseqc::daScr:
+        case cDiseqc::daWait:      break;
         default: esyslog("ERROR: unknown diseqc command %d", da);
         }
+      if (Break)
+         diseqcOffset = i + 1;
       }
-  if (scr)
+  positioner = Positioner;
+  if (scr && !Break)
      ResetToneAndVoltage(); // makes sure we don't block the bus!
   if (Diseqc->IsScr())
      Mutex.Unlock();
@@ -700,7 +755,7 @@
 
 void cDvbTuner::ResetToneAndVoltage(void)
 {
-  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13));
+  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13));
   CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
 }
 
@@ -716,7 +771,7 @@
   else if (Channel->IsTerr())
      ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2;
   else
-     esyslog("ERROR: can't determine frontend type for channel %d", Channel->Number());
+     esyslog("ERROR: can't determine frontend type for channel %d (%s)", Channel->Number(), Channel->Name());
   return ds;
 }
 
@@ -755,7 +810,7 @@
      if (Setup.DiSEqC) {
         if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) {
            frequency -= diseqc->Lof();
-           if (diseqc != lastDiseqc || diseqc->IsScr()) {
+           if (diseqc != lastDiseqc || diseqc->IsScr() || diseqc->Position() >= 0 && channel.Source() != lastSource) {
               if (IsBondedMaster()) {
                  ExecuteDiseqc(diseqc, &frequency);
                  if (frequency == 0)
@@ -764,10 +819,11 @@
               else
                  ResetToneAndVoltage();
               lastDiseqc = diseqc;
+              lastSource = channel.Source();
               }
            }
         else {
-           esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number());
+           esyslog("ERROR: no DiSEqC parameters found for channel %d (%s)", channel.Number(), channel.Name());
            return false;
            }
         }
@@ -799,9 +855,10 @@
      SETCMD(DTV_INVERSION, dtp.Inversion());
      if (frontendType == SYS_DVBS2) {
         // DVB-S2
-        SETCMD(DTV_PILOT, PILOT_AUTO);
+        SETCMD(DTV_PILOT, dtp.Pilot());
         SETCMD(DTV_ROLLOFF, dtp.RollOff());
-        SETCMD(DTV_STREAM_ID, dtp.StreamId());
+        if (DvbApiVersion >= 0x0508)
+           SETCMD(DTV_STREAM_ID, dtp.StreamId());
         }
      else {
         // DVB-S
@@ -835,7 +892,11 @@
      SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
      if (frontendType == SYS_DVBT2) {
         // DVB-T2
-        SETCMD(DTV_STREAM_ID, dtp.StreamId());
+        if (DvbApiVersion >= 0x0508) {
+           SETCMD(DTV_STREAM_ID, dtp.StreamId());
+           }
+        else if (DvbApiVersion >= 0x0503)
+           SETCMD(DTV_DVBT2_PLP_ID_LEGACY, dtp.StreamId());
         }
 
      tuneTimeout = DVBT_TUNE_TIMEOUT;
@@ -875,33 +936,50 @@
         int WaitTime = 1000;
         switch (tunerStatus) {
           case tsIdle:
-               break;
+               break; // we want the TimedWait() below!
           case tsSet:
-               tunerStatus = SetFrontend() ? tsTuned : tsIdle;
-               Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0));
+               tunerStatus = SetFrontend() ? tsPositioning : tsIdle;
                continue;
+          case tsPositioning:
+               if (positioner) {
+                  if (positioner->IsMoving())
+                     break; // we want the TimedWait() below!
+                  else if (diseqcOffset) {
+                     lastDiseqc = NULL;
+                     tunerStatus = tsSet; // have it process the rest of the DiSEqC sequence
+                     continue;
+                     }
+                  }
+               tunerStatus = tsTuned;
+               Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0));
+               if (positioner)
+                  continue;
+               // otherwise run directly into tsTuned...
           case tsTuned:
                if (Timer.TimedOut()) {
                   tunerStatus = tsSet;
                   lastDiseqc = NULL;
+                  lastSource = 0;
                   if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
-                     isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
+                     isyslog("frontend %d/%d timed out while tuning to channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
                      lastTimeoutReport = time(NULL);
                      }
                   continue;
                   }
                WaitTime = 100; // allows for a quick change from tsTuned to tsLocked
+               // run into tsLocked...
           case tsLocked:
                if (Status & FE_REINIT) {
                   tunerStatus = tsSet;
                   lastDiseqc = NULL;
+                  lastSource = 0;
                   isyslog("frontend %d/%d was reinitialized", adapter, frontend);
                   lastTimeoutReport = 0;
                   continue;
                   }
                else if (Status & FE_HAS_LOCK) {
                   if (LostLock) {
-                     isyslog("frontend %d/%d regained lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
+                     isyslog("frontend %d/%d regained lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
                      LostLock = false;
                      }
                   tunerStatus = tsLocked;
@@ -910,7 +988,7 @@
                   }
                else if (tunerStatus == tsLocked) {
                   LostLock = true;
-                  isyslog("frontend %d/%d lost lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
+                  isyslog("frontend %d/%d lost lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
                   tunerStatus = tsTuned;
                   Timer.Set(lockTimeout);
                   lastTimeoutReport = 0;
@@ -976,6 +1054,9 @@
     case 10: ST("   T")  return new cMenuEditMapItem( tr("Hierarchy"),    &dtp.hierarchy,    HierarchyValues);    else return GetOsdItem();
     case 11: ST("  S ")  return new cMenuEditMapItem( tr("Rolloff"),      &dtp.rollOff,      RollOffValues);      else return GetOsdItem();
     case 12: ST("  ST")  return new cMenuEditIntItem( tr("StreamId"),     &dtp.streamId,     0, 255);             else return GetOsdItem();
+    case 13: ST("  S ")  return new cMenuEditMapItem( tr("Pilot"),        &dtp.pilot,        PilotValues);        else return GetOsdItem();
+    case 14: ST("   T")  return new cMenuEditIntItem( tr("T2SystemId"),   &dtp.t2systemId,   0, 65535);           else return GetOsdItem();
+    case 15: ST("   T")  return new cMenuEditIntItem( tr("SISO/MISO"),    &dtp.sisoMiso,     0, 1);               else return GetOsdItem();
     default: return NULL;
     }
   return NULL;
@@ -1142,8 +1223,8 @@
               }
            }
      }
-  int Checked = 0;
   int Found = 0;
+  int Used = 0;
   if (Nodes.Size() > 0) {
      Nodes.Sort();
      for (int i = 0; i < Nodes.Size(); i++) {
@@ -1151,10 +1232,11 @@
          int Frontend;
          if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) {
             if (Exists(Adapter, Frontend)) {
-               if (Checked++ < MAXDVBDEVICES) {
+               if (Found < MAXDEVICES) {
+                  Found++;
                   if (UseDevice(NextCardIndex())) {
                      if (Probe(Adapter, Frontend))
-                        Found++;
+                        Used++;
                      }
                   else
                      NextCardIndex(1); // skips this one
@@ -1163,9 +1245,11 @@
             }
          }
      }
-  NextCardIndex(MAXDVBDEVICES - Checked); // skips the rest
-  if (Found > 0)
+  if (Found > 0) {
      isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : "");
+     if (Used != Found)
+        isyslog("using only %d DVB device%s", Used, Used > 1 ? "s" : "");
+     }
   else
      isyslog("no DVB device found");
   return Found > 0;
@@ -1178,28 +1262,44 @@
      LOG_ERROR;
      return false;
      }
-#if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) >= 0x0505
   dtv_property Frontend[1];
-  memset(&Frontend, 0, sizeof(Frontend));
   dtv_properties CmdSeq;
-  memset(&CmdSeq, 0, sizeof(CmdSeq));
-  CmdSeq.props = Frontend;
-  SETCMD(DTV_ENUM_DELSYS, 0);
-  int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
-  if (Result == 0) {
-     for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
-         if (numDeliverySystems >= MAXDELIVERYSYSTEMS) {
-            esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend);
-            break;
+  // Determine the version of the running DVB API:
+  if (!DvbApiVersion) {
+     memset(&Frontend, 0, sizeof(Frontend));
+     memset(&CmdSeq, 0, sizeof(CmdSeq));
+     CmdSeq.props = Frontend;
+     SETCMD(DTV_API_VERSION, 0);
+     if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
+        LOG_ERROR;
+        return false;
+        }
+     DvbApiVersion = Frontend[0].u.data;
+     isyslog("DVB API version is 0x%04X (VDR was built with 0x%04X)", DvbApiVersion, DVBAPIVERSION);
+     }
+  // Determine the types of delivery systems this device provides:
+  bool LegacyMode = true;
+  if (DvbApiVersion >= 0x0505) {
+     memset(&Frontend, 0, sizeof(Frontend));
+     memset(&CmdSeq, 0, sizeof(CmdSeq));
+     CmdSeq.props = Frontend;
+     SETCMD(DTV_ENUM_DELSYS, 0);
+     int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
+     if (Result == 0) {
+        for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
+            if (numDeliverySystems >= MAXDELIVERYSYSTEMS) {
+               esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend);
+               break;
+               }
+            deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i];
             }
-         deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i];
-         }
+        LegacyMode = false;
+        }
+     else {
+        esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend);
+        }
      }
-  else {
-     esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend);
-#else
-     {
-#endif
+  if (LegacyMode) {
      // Legacy mode (DVB-API < 5.5):
      switch (frontendInfo.type) {
        case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS;
@@ -1239,13 +1339,6 @@
   return false;
 }
 
-bool cDvbDevice::Ready(void)
-{
-  if (ciAdapter)
-     return ciAdapter->Ready();
-  return true;
-}
-
 bool cDvbDevice::BondDevices(const char *Bondings)
 {
   UnBondDevices();
@@ -1339,7 +1432,7 @@
 bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
 {
   cMutexLock MutexLock(&bondMutex);
-  if (bondedDevice)
+  if (bondedDevice || Positioner())
      return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied);
   return true;
 }
@@ -1521,6 +1614,11 @@
   return numDeliverySystems + numModulations;
 }
 
+const cPositioner *cDvbDevice::Positioner(void) const
+{
+  return dvbTuner ? dvbTuner->Positioner() : NULL;
+}
+
 int cDvbDevice::SignalStrength(void) const
 {
   return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
@@ -1585,6 +1683,17 @@
 bool cDvbDevice::GetTSPacket(uchar *&Data)
 {
   if (tsBuffer) {
+     if (cCamSlot *cs = CamSlot()) {
+        if (cs->WantsTsData()) {
+           int Available;
+           Data = tsBuffer->Get(&Available);
+           if (Data) {
+              Data = cs->Decrypt(Data, Available);
+              tsBuffer->Skip(Available);
+              }
+           return true;
+           }
+        }
      Data = tsBuffer->Get();
      return true;
      }
@@ -1642,12 +1751,28 @@
                                 SubsystemId = strtoul(s, NULL, 0) << 16;
                              fclose(f);
                              }
+                          else {
+                             FileName = cString::sprintf("/sys/class/dvb/%s/device/idVendor", e->d_name);
+                             if ((f = fopen(FileName, "r")) != NULL) {
+                                if (char *s = ReadLine.Read(f))
+                                   SubsystemId = strtoul(s, NULL, 16) << 16;
+                                fclose(f);
+                                }
+                             }
                           FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name);
                           if ((f = fopen(FileName, "r")) != NULL) {
                              if (char *s = ReadLine.Read(f))
                                 SubsystemId |= strtoul(s, NULL, 0);
                              fclose(f);
                              }
+                          else {
+                             FileName = cString::sprintf("/sys/class/dvb/%s/device/idProduct", e->d_name);
+                             if ((f = fopen(FileName, "r")) != NULL) {
+                                if (char *s = ReadLine.Read(f))
+                                   SubsystemId |= strtoul(s, NULL, 16);
+                                fclose(f);
+                                }
+                             }
                           break;
                           }
                        }
diff -ruN vdr-2.0.0/dvbdevice.h vdr-2.2.0/dvbdevice.h
--- vdr-2.0.0/dvbdevice.h	2013-03-07 10:42:29.000000000 +0100
+++ vdr-2.2.0/dvbdevice.h	2014-03-16 11:38:31.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbdevice.h 2.29 2013/03/07 09:42:29 kls Exp $
+ * $Id: dvbdevice.h 3.5 2014/03/16 10:38:31 kls Exp $
  */
 
 #ifndef __DVBDEVICE_H
@@ -14,11 +14,59 @@
 #include <linux/dvb/version.h>
 #include "device.h"
 
-#if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) < 0x0503
-#error VDR requires Linux DVB driver API version 5.3 or higher!
+#define DVBAPIVERSION (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR)
+
+#if DVBAPIVERSION < 0x0500
+#error VDR requires Linux DVB driver API version 5.0 or higher!
+#endif
+
+// --- Definitions for older DVB API versions --------------------------------
+
+#if DVBAPIVERSION < 0x0501
+enum {
+  FE_CAN_2G_MODULATION = 0x10000000,
+  };
+enum {
+  TRANSMISSION_MODE_4K = TRANSMISSION_MODE_AUTO + 1,
+  };
+#endif
+
+#if DVBAPIVERSION < 0x0502
+enum {
+  FE_CAN_TURBO_FEC = 0x8000000,
+  };
+#endif
+
+#if DVBAPIVERSION < 0x0503
+enum {
+  TRANSMISSION_MODE_1K = TRANSMISSION_MODE_4K + 1,
+  TRANSMISSION_MODE_16K,
+  TRANSMISSION_MODE_32K,
+  };
+enum {
+  GUARD_INTERVAL_1_128 = GUARD_INTERVAL_AUTO + 1,
+  GUARD_INTERVAL_19_128,
+  GUARD_INTERVAL_19_256,
+  };
+enum {
+  SYS_DVBT2 = SYS_DAB + 1,
+  };
+#endif
+
+#if DVBAPIVERSION < 0x0505
+#define DTV_ENUM_DELSYS  44
 #endif
 
-#define MAXDVBDEVICES  8
+#if DVBAPIVERSION < 0x0508
+enum {
+  FE_CAN_MULTISTREAM = 0x4000000,
+  };
+#define DTV_STREAM_ID            42
+#define DTV_DVBT2_PLP_ID_LEGACY  43
+#endif
+
+// --- End of definitions for older DVB API versions -------------------------
+
 #define MAXDELIVERYSYSTEMS 8
 
 #define DEV_VIDEO         "/dev/video"
@@ -44,6 +92,7 @@
 int UserIndex(int Value, const tDvbParameterMap *Map);
 int DriverIndex(int Value, const tDvbParameterMap *Map);
 
+extern const tDvbParameterMap PilotValues[];
 extern const tDvbParameterMap InversionValues[];
 extern const tDvbParameterMap BandwidthValues[];
 extern const tDvbParameterMap CoderateValues[];
@@ -70,6 +119,9 @@
   int hierarchy;
   int rollOff;
   int streamId;
+  int t2systemId;
+  int sisoMiso;
+  int pilot;
   int PrintParameter(char *p, char Name, int Value) const;
   const char *ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map = NULL);
 public:
@@ -86,6 +138,9 @@
   int Hierarchy(void) const { return hierarchy; }
   int RollOff(void) const { return rollOff; }
   int StreamId(void) const { return streamId; }
+  int T2SystemId(void) const { return t2systemId; }
+  int SisoMiso(void) const { return sisoMiso; }
+  int Pilot(void) const { return pilot; }
   void SetPolarization(char Polarization) { polarization = Polarization; }
   void SetInversion(int Inversion) { inversion = Inversion; }
   void SetBandwidth(int Bandwidth) { bandwidth = Bandwidth; }
@@ -98,6 +153,9 @@
   void SetHierarchy(int Hierarchy) { hierarchy = Hierarchy; }
   void SetRollOff(int RollOff) { rollOff = RollOff; }
   void SetStreamId(int StreamId) { streamId = StreamId; }
+  void SetT2SystemId(int T2SystemId) { t2systemId = T2SystemId; }
+  void SetSisoMiso(int SisoMiso) { sisoMiso = SisoMiso; }
+  void SetPilot(int Pilot) { pilot = Pilot; }
   cString ToString(char Type) const;
   bool Parse(const char *s);
   };
@@ -137,7 +195,6 @@
   virtual ~cDvbDevice();
   int Adapter(void) const { return adapter; }
   int Frontend(void) const { return frontend; }
-  virtual bool Ready(void);
   virtual cString DeviceType(void) const;
   virtual cString DeviceName(void) const;
   static bool BondDevices(const char *Bondings);
@@ -184,6 +241,7 @@
   virtual bool ProvidesChannel(const cChannel *Channel, int Priority = IDLEPRIORITY, bool *NeedsDetachReceivers = NULL) const;
   virtual bool ProvidesEIT(void) const;
   virtual int NumProvidedSystems(void) const;
+  virtual const cPositioner *Positioner(void) const;
   virtual int SignalStrength(void) const;
   virtual int SignalQuality(void) const;
   virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
diff -ruN vdr-2.0.0/dvbplayer.c vdr-2.2.0/dvbplayer.c
--- vdr-2.0.0/dvbplayer.c	2013-03-08 14:44:19.000000000 +0100
+++ vdr-2.2.0/dvbplayer.c	2015-02-13 16:12:57.000000000 +0100
@@ -4,13 +4,12 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbplayer.c 2.35 2013/03/08 13:44:19 kls Exp $
+ * $Id: dvbplayer.c 3.6 2015/02/13 15:12:57 kls Exp $
  */
 
 #include "dvbplayer.h"
 #include <math.h>
 #include <stdlib.h>
-#include "recording.h"
 #include "remux.h"
 #include "ringbuffer.h"
 #include "thread.h"
@@ -211,6 +210,7 @@
   cNonBlockingFileReader *nonBlockingFileReader;
   cRingBufferFrame *ringBuffer;
   cPtsIndex ptsIndex;
+  cMarks *marks;
   cFileName *fileName;
   cIndexFile *index;
   cUnbufferedFile *replayFile;
@@ -239,6 +239,7 @@
 public:
   cDvbPlayer(const char *FileName, bool PauseLive);
   virtual ~cDvbPlayer();
+  void SetMarks(cMarks *Marks);
   bool Active(void) { return cThread::Running(); }
   void Pause(void);
   void Play(void);
@@ -264,6 +265,7 @@
 {
   nonBlockingFileReader = NULL;
   ringBuffer = NULL;
+  marks = NULL;
   index = NULL;
   cRecording Recording(FileName);
   framesPerSecond = Recording.FramesPerSecond();
@@ -306,6 +308,12 @@
   delete index;
   delete fileName;
   delete ringBuffer;
+  // don't delete marks here, we don't own them!
+}
+
+void cDvbPlayer::SetMarks(cMarks *Marks)
+{
+  marks = Marks;
 }
 
 void cDvbPlayer::TrickSpeed(int Increment)
@@ -324,7 +332,7 @@
      int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
      if (sp > MAX_VIDEO_SLOWMOTION)
         sp = MAX_VIDEO_SLOWMOTION;
-     DeviceTrickSpeed(sp);
+     DeviceTrickSpeed(sp, playDir == pdForward);
      }
 }
 
@@ -374,6 +382,12 @@
   if (index) {
      int Index = ptsIndex.FindIndex(DeviceGetSTC());
      if (Index >= 0) {
+        if (Setup.SkipEdited && marks) {
+           marks->Lock();
+           if (marks->First() && abs(Index - marks->First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond)))
+              Index = 0; // when stopping within RESUMEBACKUP seconds of the first mark the recording shall still be considered unviewed
+           marks->Unlock();
+           }
         Index -= int(round(RESUMEBACKUP * framesPerSecond));
         if (Index > 0)
            Index = index->GetNextIFrame(Index, false);
@@ -402,8 +416,21 @@
   int pc = 0;
 
   readIndex = Resume();
-  if (readIndex >= 0)
+  if (readIndex > 0)
      isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+  else if (Setup.SkipEdited && marks) {
+     marks->Lock();
+     if (marks->First() && index) {
+        int Index = marks->First()->Position();
+        uint16_t FileNumber;
+        off_t FileOffset;
+        if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
+           isyslog("starting replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true, framesPerSecond));
+           readIndex = Index;
+           }
+        }
+     marks->Unlock();
+     }
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
@@ -413,6 +440,8 @@
   uint32_t LastStc = 0;
   int LastReadIFrame = -1;
   int SwitchToPlayFrame = 0;
+  bool CutIn = false;
+  bool AtLastMark = false;
 
   if (pauseLive)
      Goto(0, true);
@@ -433,7 +462,7 @@
 
           if (playMode != pmStill && playMode != pmPause) {
              if (!readFrame && (replayFile || readIndex >= 0)) {
-                if (!nonBlockingFileReader->Reading()) {
+                if (!nonBlockingFileReader->Reading() && !AtLastMark) {
                    if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
                       uint16_t FileNumber;
                       off_t FileOffset;
@@ -468,8 +497,31 @@
                    else if (index) {
                       uint16_t FileNumber;
                       off_t FileOffset;
-                      if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
+                      if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) {
                          readIndex++;
+                         if ((Setup.SkipEdited || Setup.PauseAtLastMark) && marks) {
+                            marks->Lock();
+                            cMark *m = marks->Get(readIndex);
+                            if (m && (m->Index() & 0x01) != 0) { // we're at an end mark
+                               m = marks->GetNextBegin(m);
+                               int Index = -1;
+                               if (m)
+                                  Index = m->Position(); // skip to next begin mark
+                               else if (Setup.PauseAtLastMark)
+                                  AtLastMark = true; // triggers going into Pause mode
+                               else if (index->IsStillRecording())
+                                  Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), false); // skip, but stay off end of live-recordings
+                               else
+                                  AtLastMark = true; // triggers stopping replay
+                               if (Setup.SkipEdited && Index > readIndex) {
+                                  isyslog("skipping from %d (%s) to %d (%s)", readIndex - 1, *IndexToHMSF(readIndex - 1, true, framesPerSecond), Index, *IndexToHMSF(Index, true, framesPerSecond));
+                                  readIndex = Index;
+                                  CutIn = true;
+                                  }
+                               }
+                            marks->Unlock();
+                            }
+                         }
                       else
                          eof = true;
                       }
@@ -512,6 +564,11 @@
              // Store the frame in the buffer:
 
              if (readFrame) {
+                if (CutIn) {
+                   if (isPesRecording)
+                      cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
+                   CutIn = false;
+                   }
                 if (ringBuffer->Put(readFrame))
                    readFrame = NULL;
                 else
@@ -578,8 +635,17 @@
                 p = NULL;
                 }
              }
-          else
+          else {
+             if (AtLastMark) {
+                if (Setup.PauseAtLastMark) {
+                   playMode = pmPause;
+                   AtLastMark = false;
+                   }
+                else
+                   eof = true;
+                }
              Sleep = true;
+             }
 
           // Handle hitting begin/end of recording:
 
@@ -800,18 +866,26 @@
      off_t FileOffset;
      int Length;
      Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
-     if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
-        uchar b[MAXFRAMESIZE];
-        int r = ReadFrame(replayFile, b, Length, sizeof(b));
-        if (r > 0) {
-           if (playMode == pmPause)
-              DevicePlay();
-           DeviceStillPicture(b, r);
-           ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index);
+     if (Index >= 0) {
+        if (Still) {
+           if (NextFile(FileNumber, FileOffset)) {
+              uchar b[MAXFRAMESIZE];
+              int r = ReadFrame(replayFile, b, Length, sizeof(b));
+              if (r > 0) {
+                 if (playMode == pmPause)
+                    DevicePlay();
+                 DeviceStillPicture(b, r);
+                 ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index);
+                 }
+              playMode = pmStill;
+              readIndex = Index;
+              }
+           }
+        else {
+           readIndex = Index - 1; // Action() will first increment it!
+           Play();
            }
-        playMode = pmStill;
         }
-     readIndex = Index;
      }
 }
 
@@ -869,6 +943,12 @@
   Stop();
 }
 
+void cDvbPlayerControl::SetMarks(cMarks *Marks)
+{
+  if (player)
+     player->SetMarks(Marks);
+}
+
 bool cDvbPlayerControl::Active(void)
 {
   return player && player->Active();
diff -ruN vdr-2.0.0/dvbplayer.h vdr-2.2.0/dvbplayer.h
--- vdr-2.0.0/dvbplayer.h	2012-02-19 12:40:36.000000000 +0100
+++ vdr-2.2.0/dvbplayer.h	2015-02-06 13:27:39.000000000 +0100
@@ -4,13 +4,14 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbplayer.h 2.1 2012/02/19 11:40:36 kls Exp $
+ * $Id: dvbplayer.h 3.2 2015/02/06 12:27:39 kls Exp $
  */
 
 #ifndef __DVBPLAYER_H
 #define __DVBPLAYER_H
 
 #include "player.h"
+#include "recording.h"
 #include "thread.h"
 
 class cDvbPlayer;
@@ -25,6 +26,7 @@
        // file of the recording is long enough to allow the player to display
        // the first frame in still picture mode.
   virtual ~cDvbPlayerControl();
+  void SetMarks(cMarks *Marks);
   bool Active(void);
   void Stop(void);
        // Stops the current replay session (if any).
@@ -56,7 +58,7 @@
        // and >0 if this is multi speed mode.
   void Goto(int Index, bool Still = false);
        // Positions to the given index and displays that frame as a still picture
-       // if Still is true.
+       // if Still is true. If Still is false, Play() will be called.
   };
 
 #endif //__DVBPLAYER_H
diff -ruN vdr-2.0.0/dvbspu.c vdr-2.2.0/dvbspu.c
--- vdr-2.0.0/dvbspu.c	2013-02-22 16:25:16.000000000 +0100
+++ vdr-2.2.0/dvbspu.c	2013-02-22 16:25:16.000000000 +0100
@@ -8,7 +8,7 @@
  *
  * parts of this file are derived from the OMS program.
  *
- * $Id: dvbspu.c 2.11 2013/02/22 15:25:16 kls Exp $
+ * $Id: dvbspu.c 3.0 2013/02/22 15:25:16 kls Exp $
  */
 
 #include "dvbspu.h"
diff -ruN vdr-2.0.0/dvbspu.h vdr-2.2.0/dvbspu.h
--- vdr-2.0.0/dvbspu.h	2013-01-20 11:15:47.000000000 +0100
+++ vdr-2.2.0/dvbspu.h	2014-02-08 13:27:34.000000000 +0100
@@ -8,7 +8,7 @@
  *
  * parts of this file are derived from the OMS program.
  *
- * $Id: dvbspu.h 2.6 2013/01/20 10:15:47 kls Exp $
+ * $Id: dvbspu.h 3.1 2014/02/08 12:27:34 kls Exp $
  */
 
 #ifndef __DVBSPU_H
@@ -101,7 +101,6 @@
     uint8_t *spu;
     uint32_t spupts;
     bool clean;
-    bool ready;
     bool restricted_osd;
 
     enum spFlag { spNONE, spHIDE, spSHOW, spMENU };
diff -ruN vdr-2.0.0/dvbsubtitle.c vdr-2.2.0/dvbsubtitle.c
--- vdr-2.0.0/dvbsubtitle.c	2013-02-22 16:25:25.000000000 +0100
+++ vdr-2.2.0/dvbsubtitle.c	2015-01-20 15:53:57.000000000 +0100
@@ -5,9 +5,9 @@
  * how to reach the author.
  *
  * Original author: Marco Schluessler <marco@lordzodiac.de>
- * With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
+ * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
  *
- * $Id: dvbsubtitle.c 2.34 2013/02/22 15:25:25 kls Exp $
+ * $Id: dvbsubtitle.c 3.10 2015/01/20 14:53:57 kls Exp $
  */
 
 #include "dvbsubtitle.h"
@@ -16,8 +16,6 @@
 #include "device.h"
 #include "libsi/si.h"
 
-//#define FINISHPAGE_HACK
-
 #define PAGE_COMPOSITION_SEGMENT    0x10
 #define REGION_COMPOSITION_SEGMENT  0x11
 #define CLUT_DEFINITION_SEGMENT     0x12
@@ -27,36 +25,136 @@
 #define END_OF_DISPLAY_SET_SEGMENT  0x80
 #define STUFFING_SEGMENT            0xFF
 
-// Set these to 'true' for debug output:
-static bool DebugConverter = false;
-static bool DebugSegments = false;
-static bool DebugPages = false;
-static bool DebugRegions = false;
-static bool DebugObjects = false;
-static bool DebugCluts = false;
-
-#define dbgconverter(a...) if (DebugConverter) fprintf(stderr, a)
-#define dbgsegments(a...) if (DebugSegments) fprintf(stderr, a)
-#define dbgpages(a...) if (DebugPages) fprintf(stderr, a)
-#define dbgregions(a...) if (DebugRegions) fprintf(stderr, a)
-#define dbgobjects(a...) if (DebugObjects) fprintf(stderr, a)
-#define dbgcluts(a...) if (DebugCluts) fprintf(stderr, a)
+#define PGS_PALETTE_SEGMENT         0x14
+#define PGS_OBJECT_SEGMENT          0x15
+#define PGS_PRESENTATION_SEGMENT    0x16
+#define PGS_WINDOW_SEGMENT          0x17
+#define PGS_DISPLAY_SEGMENT         0x80
+
+// Set these to 'true' for debug output, which is written into the file dbg-log.htm
+// in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
+// used to display the subtitles.
+static bool DebugNormal    = false; // shows pages, regions and objects
+static bool DebugVerbose   = false; // shows everything
+static bool DebugDisplay   = DebugVerbose || DebugNormal;
+static bool DebugPages     = DebugVerbose || DebugNormal;
+static bool DebugRegions   = DebugVerbose || DebugNormal;
+static bool DebugObjects   = DebugVerbose || DebugNormal;
+static bool DebugConverter = DebugVerbose;
+static bool DebugSegments  = DebugVerbose;
+static bool DebugPixel     = DebugVerbose;
+static bool DebugCluts     = DebugVerbose;
+static bool DebugOutput    = DebugVerbose;
+
+#define dbgdisplay(a...)   if (DebugDisplay)   SD.WriteHtml(a)
+#define dbgpages(a...)     if (DebugPages)     SD.WriteHtml(a)
+#define dbgregions(a...)   if (DebugRegions)   SD.WriteHtml(a)
+#define dbgobjects(a...)   if (DebugObjects)   SD.WriteHtml(a)
+#define dbgconverter(a...) if (DebugConverter) SD.WriteHtml(a)
+#define dbgsegments(a...)  if (DebugSegments)  SD.WriteHtml(a)
+#define dbgpixel(a...)     if (DebugPixel)     SD.WriteHtml(a)
+#define dbgcluts(a...)     if (DebugCluts)     SD.WriteHtml(a)
+#define dbgoutput(a...)    if (DebugOutput)    SD.WriteHtml(a)
+
+#define DBGMAXBITMAPS  100 // debug output will be stopped after this many bitmaps
+#define DBGBITMAPWIDTH 400
+
+#define FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY // some don't properly handle version numbers, which renders them useless because subtitles are not displayed
+
+// --- cSubtitleDebug --------------------------------------------------------
+
+class cSubtitleDebug {
+private:
+  cMutex mutex;
+  int imgCnt;
+  int64_t firstPts;
+  bool newFile;
+  double factor;
+public:
+  cSubtitleDebug(void) { Reset(); }
+  void Reset(void);
+  bool Active(void) { return imgCnt < DBGMAXBITMAPS; }
+  int64_t FirstPts(void) { return firstPts; }
+  void SetFirstPts(int64_t FirstPts) { if (firstPts < 0) firstPts = FirstPts; }
+  void SetFactor(double Factor) { factor = Factor; }
+  cString WriteJpeg(const cBitmap *Bitmap, int MaxX = 0, int MaxY = 0);
+  void WriteHtml(const char *Format, ...);
+  };
+
+void cSubtitleDebug::Reset(void)
+{
+  imgCnt = 0;
+  firstPts = -1;
+  newFile = true;
+  factor = 1.0;
+}
+
+cString cSubtitleDebug::WriteJpeg(const cBitmap *Bitmap, int MaxX, int MaxY)
+{
+  if (!Active())
+     return NULL;
+  cMutexLock MutexLock(&mutex);
+  cBitmap *Scaled = Bitmap->Scaled(factor, factor, true);
+  int w = MaxX ? int(round(MaxX * factor)) : Scaled->Width();
+  int h = MaxY ? int(round(MaxY * factor)) : Scaled->Height();
+  uchar mem[w * h * 3];
+  for (int x = 0; x < w; x++) {
+      for (int y = 0; y < h; y++) {
+          tColor c = Scaled->GetColor(x, y);
+          int o = (y * w + x) * 3;
+          mem[o++] = (c & 0x00FF0000) >> 16;
+          mem[o++] = (c & 0x0000FF00) >> 8;
+          mem[o]   = (c & 0x000000FF);
+          }
+      }
+  delete Scaled;
+  int Size = 0;
+  uchar *Jpeg = RgbToJpeg(mem, w, h, Size);
+  cString ImgName = cString::sprintf("dbg-%03d.jpg", imgCnt++);
+  int f = open(ImgName, O_WRONLY | O_CREAT, DEFFILEMODE);
+  if (f >= 0) {
+     if (write(f, Jpeg, Size) < 0)
+        LOG_ERROR_STR(*ImgName);
+     close(f);
+     }
+  free(Jpeg);
+  return ImgName;
+}
+
+void cSubtitleDebug::WriteHtml(const char *Format, ...)
+{
+  if (!Active())
+     return;
+  cMutexLock MutexLock(&mutex);
+  if (FILE *f = fopen("dbg-log.htm", newFile ? "w" : "a")) {
+     va_list ap;
+     va_start(ap, Format);
+     vfprintf(f, Format, ap);
+     va_end(ap);
+     fclose(f);
+     newFile = false;
+     }
+}
+
+static cSubtitleDebug SD;
 
 // --- cSubtitleClut ---------------------------------------------------------
 
 class cSubtitleClut : public cListObject {
 private:
   int clutId;
-  int version;
+  int clutVersionNumber;
   cPalette palette2;
   cPalette palette4;
   cPalette palette8;
+  tColor yuv2rgb(int Y, int Cb, int Cr);
+  void SetColor(int Bpp, int Index, tColor Color);
 public:
   cSubtitleClut(int ClutId);
+  void Parse(cBitStream &bs);
+  void ParsePgs(cBitStream &bs);
   int ClutId(void) { return clutId; }
-  int Version(void) { return version; }
-  void SetVersion(int Version) { version = Version; }
-  void SetColor(int Bpp, int Index, tColor Color);
+  int ClutVersionNumber(void) { return clutVersionNumber; }
   const cPalette *GetPalette(int Bpp);
   };
 
@@ -67,7 +165,7 @@
 {
   int a = 0, r = 0, g = 0, b = 0;
   clutId = ClutId;
-  version = -1;
+  clutVersionNumber = -1;
   // ETSI EN 300 743 10.3: 4-entry CLUT default contents
   palette2.SetColor(0, ArgbToColor(  0,   0,   0,   0));
   palette2.SetColor(1, ArgbToColor(255, 255, 255, 255));
@@ -129,6 +227,94 @@
       }
 }
 
+void cSubtitleClut::Parse(cBitStream &bs)
+{
+  int Version = bs.GetBits(4);
+#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
+  if (clutVersionNumber == Version)
+     return; // no update
+#endif
+  clutVersionNumber = Version;
+  bs.SkipBits(4); // reserved
+  dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
+  while (!bs.IsEOF()) {
+        uchar clutEntryId = bs.GetBits(8);
+        bool entryClut2Flag = bs.GetBit();
+        bool entryClut4Flag = bs.GetBit();
+        bool entryClut8Flag = bs.GetBit();
+        bs.SkipBits(4); // reserved
+        uchar yval;
+        uchar crval;
+        uchar cbval;
+        uchar tval;
+        if (bs.GetBit()) { // full_range_flag
+           yval  = bs.GetBits(8);
+           crval = bs.GetBits(8);
+           cbval = bs.GetBits(8);
+           tval  = bs.GetBits(8);
+           }
+        else {
+           yval  = bs.GetBits(6) << 2;
+           crval = bs.GetBits(4) << 4;
+           cbval = bs.GetBits(4) << 4;
+           tval  = bs.GetBits(2) << 6;
+           }
+        tColor value = 0;
+        if (yval) {
+           value = yuv2rgb(yval, cbval, crval);
+           value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
+           }
+        dbgcluts("%2d %d %d %d %08X<br>\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value);
+        if (entryClut2Flag)
+           SetColor(2, clutEntryId, value);
+        if (entryClut4Flag)
+           SetColor(4, clutEntryId, value);
+        if (entryClut8Flag)
+           SetColor(8, clutEntryId, value);
+        }
+}
+
+void cSubtitleClut::ParsePgs(cBitStream &bs)
+{
+  int Version = bs.GetBits(8);
+  if (clutVersionNumber == Version)
+     return; // no update
+  clutVersionNumber = Version;
+  dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
+  for (int i = 0; i < 256; ++i)
+      SetColor(8, i, ArgbToColor(0, 0, 0, 0));
+  while (!bs.IsEOF()) {
+        uchar clutEntryId = bs.GetBits(8);
+        uchar yval  = bs.GetBits(8);
+        uchar crval = bs.GetBits(8);
+        uchar cbval = bs.GetBits(8);
+        uchar tval  = bs.GetBits(8);
+        tColor value = 0;
+        if (yval) {
+           value = yuv2rgb(yval, cbval, crval);
+           value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * tval / 10) << 24;
+           }
+        dbgcluts("%2d %08X<br>\n", clutEntryId, value);
+        SetColor(8, clutEntryId, value);
+        }
+}
+
+tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr)
+{
+  int Ey, Epb, Epr;
+  int Eg, Eb, Er;
+
+  Ey = (Y - 16);
+  Epb = (Cb - 128);
+  Epr = (Cr - 128);
+  /* ITU-R 709 */
+  Er = constrain((298 * Ey             + 460 * Epr) / 256, 0, 255);
+  Eg = constrain((298 * Ey -  55 * Epb - 137 * Epr) / 256, 0, 255);
+  Eb = constrain((298 * Ey + 543 * Epb            ) / 256, 0, 255);
+
+  return (Er << 16) | (Eg << 8) | Eb;
+}
+
 void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color)
 {
   switch (Bpp) {
@@ -155,86 +341,158 @@
 class cSubtitleObject : public cListObject {
 private:
   int objectId;
-  int version;
-  int codingMethod;
+  int objectVersionNumber;
+  int objectCodingMethod;
   bool nonModifyingColorFlag;
-  uchar backgroundPixelCode;
-  uchar foregroundPixelCode;
-  int providerFlag;
-  int px;
-  int py;
-  cBitmap *bitmap;
-  char textData[Utf8BufSize(256)]; // number of character codes is an 8-bit field
-  void DrawLine(int x, int y, tIndex Index, int Length);
-  bool Decode2BppCodeString(cBitStream *bs, int&x, int y, const uint8_t *MapTable);
-  bool Decode4BppCodeString(cBitStream *bs, int&x, int y, const uint8_t *MapTable);
-  bool Decode8BppCodeString(cBitStream *bs, int&x, int y);
+  int topLength;
+  int botLength;
+  int topIndex;
+  uchar *topData;
+  uchar *botData;
+  char *txtData;
+  int lineHeight;
+  void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length);
+  bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
+  bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
+  bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
+  bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
+  void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even);
+  void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
 public:
-  cSubtitleObject(int ObjectId, cBitmap *Bitmap);
+  cSubtitleObject(int ObjectId);
+  ~cSubtitleObject();
+  void Parse(cBitStream &bs);
+  void ParsePgs(cBitStream &bs);
   int ObjectId(void) { return objectId; }
-  int Version(void) { return version; }
-  int CodingMethod(void) { return codingMethod; }
-  uchar BackgroundPixelCode(void) { return backgroundPixelCode; }
-  uchar ForegroundPixelCode(void) { return foregroundPixelCode; }
-  const char *TextData(void) { return &textData[0]; }
-  int X(void) { return px; }
-  int Y(void) { return py; }
+  int ObjectVersionNumber(void) { return objectVersionNumber; }
+  int ObjectCodingMethod(void) { return objectCodingMethod; }
   bool NonModifyingColorFlag(void) { return nonModifyingColorFlag; }
-  void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
-  void DecodeSubBlock(const uchar *Data, int Length, bool Even);
-  void SetVersion(int Version) { version = Version; }
-  void SetBackgroundPixelCode(uchar BackgroundPixelCode) { backgroundPixelCode = BackgroundPixelCode; }
-  void SetForegroundPixelCode(uchar ForegroundPixelCode) { foregroundPixelCode = ForegroundPixelCode; }
-  void SetNonModifyingColorFlag(bool NonModifyingColorFlag) { nonModifyingColorFlag = NonModifyingColorFlag; }
-  void SetCodingMethod(int CodingMethod) { codingMethod = CodingMethod; }
-  void SetPosition(int x, int y) { px = x; py = y; }
-  void SetProviderFlag(int ProviderFlag) { providerFlag = ProviderFlag; }
+  void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg);
   };
 
-cSubtitleObject::cSubtitleObject(int ObjectId, cBitmap *Bitmap)
+cSubtitleObject::cSubtitleObject(int ObjectId)
 {
   objectId = ObjectId;
-  version = -1;
-  codingMethod = -1;
+  objectVersionNumber = -1;
+  objectCodingMethod = -1;
   nonModifyingColorFlag = false;
-  backgroundPixelCode = 0;
-  foregroundPixelCode = 0;
-  providerFlag = -1;
-  px = py = 0;
-  bitmap = Bitmap;
-  memset(textData, 0, sizeof(textData));
+  topLength = 0;
+  botLength = 0;
+  topIndex = 0;
+  topData = NULL;
+  botData = NULL;
+  txtData = NULL;
+  lineHeight = 26; // configurable subtitling font size?
+}
+
+cSubtitleObject::~cSubtitleObject()
+{
+  free(topData);
+  free(botData);
+  free(txtData);
+}
+
+void cSubtitleObject::Parse(cBitStream &bs)
+{
+  int Version = bs.GetBits(4);
+#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
+  if (objectVersionNumber == Version)
+     return; // no update
+#endif
+  objectVersionNumber = Version;
+  objectCodingMethod = bs.GetBits(2);
+  nonModifyingColorFlag = bs.GetBit();
+  bs.SkipBit(); // reserved
+  dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag); // no "<br>\n" here, DecodeCharacterString() may add data
+  if (objectCodingMethod == 0) { // coding of pixels
+     topLength = bs.GetBits(16);
+     botLength = bs.GetBits(16);
+     free(topData);
+     if ((topData = MALLOC(uchar, topLength)) != NULL)
+        memcpy(topData, bs.GetData(), topLength);
+     else
+        topLength = 0;
+     free(botData);
+     if ((botData = MALLOC(uchar, botLength)) != NULL)
+        memcpy(botData, bs.GetData() + topLength, botLength);
+     else
+        botLength = 0;
+     bs.WordAlign();
+     }
+  else if (objectCodingMethod == 1) { // coded as a string of characters
+     int numberOfCodes = bs.GetBits(8);
+     DecodeCharacterString(bs.GetData(), numberOfCodes);
+     }
+  dbgobjects("<br>\n");
+  if (DebugObjects) {
+     // We can't get the actual clut here, so we use a default one. This may lead to
+     // funny colors, but we just want to get a rough idea of what's in the object, anyway.
+     cSubtitleClut Clut(0);
+     cBitmap b(1920, 1080, 8);
+     b.Replace(*Clut.GetPalette(b.Bpp()));
+     b.Clean();
+     Render(&b, 0, 0, 0, 1);
+     int x1, y1, x2, y2;
+     if (b.Dirty(x1, y1, x2, y2)) {
+        cString ImgName = SD.WriteJpeg(&b, x2, y2);
+        dbgobjects("<img src=\"%s\"><br>\n", *ImgName);
+        }
+     }
+}
+
+void cSubtitleObject::ParsePgs(cBitStream &bs)
+{
+  int Version = bs.GetBits(8);
+  if (objectVersionNumber == Version)
+     return; // no update
+  objectVersionNumber = Version;
+  objectCodingMethod = 0;
+  int sequenceDescriptor = bs.GetBits(8);
+  if (!(sequenceDescriptor & 0x80) && topData != NULL) {
+     memcpy(topData + topIndex, bs.GetData(), (bs.Length() - bs.Index()) / 8);
+     topIndex += (bs.Length() - bs.Index()) / 8;
+     return;
+     }
+  topLength = bs.GetBits(24) - 4 + 1; // exclude width / height, add sub block type
+  bs.SkipBits(32);
+  if ((topData = MALLOC(uchar, topLength)) != NULL) {
+     topData[topIndex++] = 0xFF; // PGS end of line
+     memcpy(topData + 1, bs.GetData(), (bs.Length() - bs.Index()) / 8);
+     topIndex += (bs.Length() - bs.Index()) / 8 + 1;
+     }
+  dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag);
 }
 
 void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
 {
+  // "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
+  // character_code to be a 16-bit index number into the character table identified
+  // in the subtitle_descriptor. However, the "subtitling_descriptor" <sic> according to
+  // "ETSI EN 300 468 V1.13.1 (2012-04)" doesn't contain a "character table identifier".
+  // It only contains a three letter language code, without any specification as to how
+  // this is related to a specific character table.
+  // Apparently the first "code" in textual subtitles contains the character table
+  // identifier, and all codes are 8-bit only. So let's first make Data a string of
+  // 8-bit characters:
   if (NumberOfCodes > 0) {
+     char txt[NumberOfCodes + 1];
+     for (int i = 0; i < NumberOfCodes; i++)
+         txt[i] = Data[i * 2 + 1];
+     txt[NumberOfCodes] = 0;
      bool singleByte;
-     const uchar *from = &Data[1];
-     int len = NumberOfCodes * 2 - 1;
-     cCharSetConv conv(SI::getCharacterTable(from, len, &singleByte));
-     if (singleByte) {
-        char txt[NumberOfCodes + 1];
-        char *p = txt;
-        for (int i = 2; i < NumberOfCodes; ++i) {
-            uchar c = Data[i * 2 + 1] & 0xFF;
-            if (c == 0)
-               break;
-            if (' ' <= c && c <= '~' || c == '\n' || 0xA0 <= c)
-               *(p++) = c;
-            else if (c == 0x8A)
-               *(p++) = '\n';
-            }
-        *p = 0;
-        const char *s = conv.Convert(txt);
-        Utf8Strn0Cpy(textData, s, Utf8StrLen(s));
-        }
-     else {
-        // TODO: add proper multibyte support for "UTF-16", "EUC-KR", "GB2312", "GBK", "UTF-8"
-        }
+     const uchar *from = (uchar *)txt;
+     int len = NumberOfCodes;
+     const char *CharacterTable = SI::getCharacterTable(from, len, &singleByte);
+     dbgobjects(" table %s single %d raw '%s'", CharacterTable, singleByte, from);
+     cCharSetConv conv(CharacterTable, cCharSetConv::SystemCharacterTable());
+     const char *s = conv.Convert((const char *)from);
+     dbgobjects(" conv '%s'", s);
+     free(txtData);
+     txtData = strdup(s);
      }
 }
 
-void cSubtitleObject::DecodeSubBlock(const uchar *Data, int Length, bool Even)
+void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
 {
   int x = 0;
   int y = Even ? 0 : 1;
@@ -246,69 +504,72 @@
   while (!bs.IsEOF()) {
         switch (bs.GetBits(8)) {
           case 0x10:
-               dbgobjects("2-bit / pixel code string\n");
-               switch (bitmap->Bpp()) {
+               dbgpixel("2-bit / pixel code string<br>\n");
+               switch (Bitmap->Bpp()) {
                  case 8:  mapTable = map2to8; break;
                  case 4:  mapTable = map2to4; break;
                  default: mapTable = NULL;    break;
                  }
-               while (Decode2BppCodeString(&bs, x, y, mapTable) && !bs.IsEOF())
+               while (Decode2BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
                      ;
                bs.ByteAlign();
                break;
           case 0x11:
-               dbgobjects("4-bit / pixel code string\n");
-               switch (bitmap->Bpp()) {
+               dbgpixel("4-bit / pixel code string<br>\n");
+               switch (Bitmap->Bpp()) {
                  case 8:  mapTable = map4to8; break;
                  default: mapTable = NULL;    break;
                  }
-               while (Decode4BppCodeString(&bs, x, y, mapTable) && !bs.IsEOF())
+               while (Decode4BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
                      ;
                bs.ByteAlign();
                break;
           case 0x12:
-               dbgobjects("8-bit / pixel code string\n");
-               while (Decode8BppCodeString(&bs, x, y) && !bs.IsEOF())
+               dbgpixel("8-bit / pixel code string<br>\n");
+               while (Decode8BppCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF())
                      ;
                break;
           case 0x20:
-               dbgobjects("sub block 2 to 4 map\n");
-               map2to4[0] = bs.GetBits(4);
-               map2to4[1] = bs.GetBits(4);
-               map2to4[2] = bs.GetBits(4);
-               map2to4[3] = bs.GetBits(4);
+               dbgpixel("sub block 2 to 4 map<br>\n");
+               for (int i = 0; i < 4; ++i)
+                   map2to4[i] = bs.GetBits(4);
                break;
           case 0x21:
-               dbgobjects("sub block 2 to 8 map\n");
+               dbgpixel("sub block 2 to 8 map<br>\n");
                for (int i = 0; i < 4; ++i)
                    map2to8[i] = bs.GetBits(8);
                break;
           case 0x22:
-               dbgobjects("sub block 4 to 8 map\n");
+               dbgpixel("sub block 4 to 8 map<br>\n");
                for (int i = 0; i < 16; ++i)
                    map4to8[i] = bs.GetBits(8);
                break;
           case 0xF0:
-               dbgobjects("end of object line\n");
+               dbgpixel("end of object line<br>\n");
                x = 0;
                y += 2;
                break;
-          default: dbgobjects("unknown sub block %s %d\n", __FUNCTION__, __LINE__);
+          case 0xFF:
+               dbgpixel("PGS code string, including EOLs<br>\n");
+               while (DecodePgsCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF()) {
+                     x = 0;
+                     y++;
+                     }
+               break;
+          default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__);
           }
         }
 }
 
-void cSubtitleObject::DrawLine(int x, int y, tIndex Index, int Length)
+void cSubtitleObject::DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
 {
   if (nonModifyingColorFlag && Index == 1)
      return;
-  x += px;
-  y += py;
   for (int pos = x; pos < x + Length; pos++)
-      bitmap->SetIndex(pos, y, Index);
+      Bitmap->SetIndex(pos, y, Index);
 }
 
-bool cSubtitleObject::Decode2BppCodeString(cBitStream *bs, int &x, int y, const uint8_t *MapTable)
+bool cSubtitleObject::Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
 {
   int rl = 0;
   int color = 0;
@@ -343,12 +604,12 @@
      }
   if (MapTable)
      color = MapTable[color];
-  DrawLine(x, y, color, rl);
+  DrawLine(Bitmap, px + x, py + y, color, rl);
   x += rl;
   return true;
 }
 
-bool cSubtitleObject::Decode4BppCodeString(cBitStream *bs, int &x, int y, const uint8_t *MapTable)
+bool cSubtitleObject::Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
 {
   int rl = 0;
   int color = 0;
@@ -388,12 +649,12 @@
      }
   if (MapTable)
      color = MapTable[color];
-  DrawLine(x, y, color, rl);
+  DrawLine(Bitmap, px + x, py + y, color, rl);
   x += rl;
   return true;
 }
 
-bool cSubtitleObject::Decode8BppCodeString(cBitStream *bs, int &x, int y)
+bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
 {
   int rl = 0;
   int color = 0;
@@ -413,105 +674,299 @@
      else
         return false;
      }
-  DrawLine(x, y, color, rl);
+  DrawLine(Bitmap, px + x, py + y, color, rl);
   x += rl;
   return true;
 }
 
+bool cSubtitleObject::DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
+{
+  while (!bs->IsEOF()) {
+        int color = bs->GetBits(8);
+        int rl = 1;
+        if (!color) {
+           int flags = bs->GetBits(8);
+           rl = flags & 0x3f;
+           if (flags & 0x40)
+              rl = (rl << 8) + bs->GetBits(8);
+           color = flags & 0x80 ? bs->GetBits(8) : 0;
+           }
+        if (rl > 0) {
+           DrawLine(Bitmap, px + x, py + y, color, rl);
+           x += rl;
+           }
+        else if (!rl)
+           return true;
+        }
+  return false;
+}
+
+void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
+{
+  if (objectCodingMethod == 0) { // coding of pixels
+     DecodeSubBlock(Bitmap, px, py, topData, topLength, true);
+     if (botLength)
+        DecodeSubBlock(Bitmap, px, py, botData, botLength, false);
+     else
+        DecodeSubBlock(Bitmap, px, py, topData, topLength, false);
+     }
+  else if (objectCodingMethod == 1) { // coded as a string of characters
+     if (txtData) {
+        //TODO couldn't we draw the text directly into Bitmap?
+        cFont *font = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize);
+        cBitmap tmp(font->Width(txtData), font->Height(), Bitmap->Bpp());
+        double factor = (double)lineHeight / font->Height();
+        tmp.DrawText(0, 0, txtData, Bitmap->Color(IndexFg), Bitmap->Color(IndexBg), font);
+        cBitmap *scaled = tmp.Scaled(factor, factor, true);
+        Bitmap->DrawBitmap(px, py, *scaled);
+        delete scaled;
+        delete font;
+        }
+     }
+}
+
+// --- cSubtitleObjects ------------------------------------------------------
+
+class cSubtitleObjects : public cList<cSubtitleObject> {
+public:
+  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
+  };
+
+cSubtitleObject *cSubtitleObjects::GetObjectById(int ObjectId, bool New)
+{
+  for (cSubtitleObject *so = First(); so; so = Next(so)) {
+      if (so->ObjectId() == ObjectId)
+         return so;
+      }
+  if (!New)
+     return NULL;
+  cSubtitleObject *Object = new cSubtitleObject(ObjectId);
+  Add(Object);
+  return Object;
+}
+
+// --- cSubtitleObjectRef ----------------------------------------------------
+
+class cSubtitleObjectRef : public cListObject {
+protected:
+  int objectId;
+  int objectType;
+  int objectProviderFlag;
+  int objectHorizontalPosition;
+  int objectVerticalPosition;
+  int foregroundPixelCode;
+  int backgroundPixelCode;
+public:
+  cSubtitleObjectRef(void);
+  cSubtitleObjectRef(cBitStream &bs);
+  int ObjectId(void) { return objectId; }
+  int ObjectType(void) { return objectType; }
+  int ObjectProviderFlag(void) { return objectProviderFlag; }
+  int ObjectHorizontalPosition(void) { return objectHorizontalPosition; }
+  int ObjectVerticalPosition(void) { return objectVerticalPosition; }
+  int ForegroundPixelCode(void) { return foregroundPixelCode; }
+  int BackgroundPixelCode(void) { return backgroundPixelCode; }
+  };
+
+cSubtitleObjectRef::cSubtitleObjectRef(void)
+{
+  objectId = 0;
+  objectType = 0;
+  objectProviderFlag = 0;
+  objectHorizontalPosition = 0;
+  objectVerticalPosition = 0;
+  foregroundPixelCode = 0;
+  backgroundPixelCode = 0;
+}
+
+cSubtitleObjectRef::cSubtitleObjectRef(cBitStream &bs)
+{
+  objectId = bs.GetBits(16);
+  objectType = bs.GetBits(2);
+  objectProviderFlag = bs.GetBits(2);
+  objectHorizontalPosition = bs.GetBits(12);
+  bs.SkipBits(4); // reserved
+  objectVerticalPosition = bs.GetBits(12);
+  if (objectType == 0x01 || objectType == 0x02) {
+     foregroundPixelCode = bs.GetBits(8);
+     backgroundPixelCode = bs.GetBits(8);
+     }
+  else {
+     foregroundPixelCode = 0;
+     backgroundPixelCode = 0;
+     }
+  dbgregions("<b>objectref</b> id %d type %d flag %d x %d y %d fg %d bg %d<br>\n", objectId, objectType, objectProviderFlag, objectHorizontalPosition, objectVerticalPosition, foregroundPixelCode, backgroundPixelCode);
+}
+
+// --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
+
+class cSubtitleObjectRefPgs : public cSubtitleObjectRef {
+private:
+  int windowId;
+  int compositionFlag;
+  int cropX;
+  int cropY;
+  int cropW;
+  int cropH;
+public:
+  cSubtitleObjectRefPgs(cBitStream &bs);
+};
+
+cSubtitleObjectRefPgs::cSubtitleObjectRefPgs(cBitStream &bs)
+:cSubtitleObjectRef()
+{
+  objectId = bs.GetBits(16);
+  windowId = bs.GetBits(8);
+  compositionFlag = bs.GetBits(8);
+  bs.SkipBits(32); // skip absolute position, object is aligned to region
+  if ((compositionFlag & 0x80) != 0) {
+     cropX = bs.GetBits(16);
+     cropY = bs.GetBits(16);
+     cropW = bs.GetBits(16);
+     cropH = bs.GetBits(16);
+     }
+  else
+     cropX = cropY = cropW = cropH = 0;
+  dbgregions("<b>objectrefPgs</b> id %d flag %d x %d y %d cropX %d cropY %d cropW %d cropH %d<br>\n", objectId, compositionFlag, objectHorizontalPosition, objectVerticalPosition, cropX, cropY, cropW, cropH);
+}
+
 // --- cSubtitleRegion -------------------------------------------------------
 
-class cSubtitleRegion : public cListObject, public cBitmap {
+class cSubtitleRegion : public cListObject {
 private:
   int regionId;
-  int version;
+  int regionVersionNumber;
+  bool regionFillFlag;
+  int regionWidth;
+  int regionHeight;
+  int regionLevelOfCompatibility;
+  int regionDepth;
   int clutId;
-  int horizontalAddress;
-  int verticalAddress;
-  int level;
-  int lineHeight;
-  cList<cSubtitleObject> objects;
+  int region8bitPixelCode;
+  int region4bitPixelCode;
+  int region2bitPixelCode;
+  cList<cSubtitleObjectRef> objectRefs;
 public:
   cSubtitleRegion(int RegionId);
+  void Parse(cBitStream &bs);
+  void ParsePgs(cBitStream &bs);
+  void SetDimensions(int Width, int Height);
   int RegionId(void) { return regionId; }
-  int Version(void) { return version; }
+  int RegionVersionNumber(void) { return regionVersionNumber; }
+  bool RegionFillFlag(void) { return regionFillFlag; }
+  int RegionWidth(void) { return regionWidth; }
+  int RegionHeight(void) { return regionHeight; }
+  int RegionLevelOfCompatibility(void) { return regionLevelOfCompatibility; }
+  int RegionDepth(void) { return regionDepth; }
   int ClutId(void) { return clutId; }
-  int Level(void) { return level; }
-  int Depth(void) { return Bpp(); }
-  void FillRegion(tIndex Index);
-  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
-  int HorizontalAddress(void) { return horizontalAddress; }
-  int VerticalAddress(void) { return verticalAddress; }
-  void SetVersion(int Version) { version = Version; }
-  void SetClutId(int ClutId) { clutId = ClutId; }
-  void SetLevel(int Level);
-  void SetDepth(int Depth);
-  void SetHorizontalAddress(int HorizontalAddress) { horizontalAddress = HorizontalAddress; }
-  void SetVerticalAddress(int VerticalAddress) { verticalAddress = VerticalAddress; }
-  void UpdateTextData(cSubtitleClut *Clut);
+  void Render(cBitmap *Bitmap, cSubtitleObjects *Objects);
   };
 
 cSubtitleRegion::cSubtitleRegion(int RegionId)
-:cBitmap(1, 1, 4)
 {
   regionId = RegionId;
-  version = -1;
+  regionVersionNumber = -1;
+  regionFillFlag = false;
+  regionWidth = 0;
+  regionHeight = 0;
+  regionLevelOfCompatibility = 0;
+  regionDepth = 0;
   clutId = -1;
-  horizontalAddress = 0;
-  verticalAddress = 0;
-  level = 0;
-  lineHeight = 26; // configurable subtitling font size
+  region8bitPixelCode = 0;
+  region4bitPixelCode = 0;
+  region2bitPixelCode = 0;
 }
 
-void cSubtitleRegion::FillRegion(tIndex Index)
-{
-  dbgregions("FillRegion %d\n", Index);
-  for (int y = 0; y < Height(); y++) {
-      for (int x = 0; x < Width(); x++)
-          SetIndex(x, y, Index);
-      }
-}
-
-cSubtitleObject *cSubtitleRegion::GetObjectById(int ObjectId, bool New)
+void cSubtitleRegion::Parse(cBitStream &bs)
 {
-  cSubtitleObject *result = NULL;
-  for (cSubtitleObject *so = objects.First(); so; so = objects.Next(so)) {
-      if (so->ObjectId() == ObjectId)
-         result = so;
-      }
-  if (!result && New) {
-     result = new cSubtitleObject(ObjectId, this);
-     objects.Add(result);
-     }
-  return result;
-}
-
-void cSubtitleRegion::UpdateTextData(cSubtitleClut *Clut)
-{
-  const cPalette *palette = Clut ? Clut->GetPalette(Depth()) : NULL;
-  for (cSubtitleObject *so = objects.First(); so && palette; so = objects.Next(so)) {
-      if (Utf8StrLen(so->TextData()) > 0) {
-         cFont *font = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize);
-         cBitmap tmp(font->Width(so->TextData()), font->Height(), Depth());
-         double factor = (double)lineHeight / font->Height();
-         tmp.DrawText(0, 0, so->TextData(), palette->Color(so->ForegroundPixelCode()), palette->Color(so->BackgroundPixelCode()), font);
-         cBitmap *scaled = tmp.Scaled(factor, factor, true);
-         DrawBitmap(so->X(), so->Y(), *scaled);
-         delete scaled;
-         delete font;
+  int Version = bs.GetBits(4);
+#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
+  if (regionVersionNumber == Version)
+     return; // no update
+#endif
+  regionVersionNumber = Version;
+  regionFillFlag = bs.GetBit();
+  bs.SkipBits(3); // reserved
+  regionWidth = bs.GetBits(16);
+  regionHeight = bs.GetBits(16);
+  regionLevelOfCompatibility = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
+  regionDepth = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
+  bs.SkipBits(2); // reserved
+  clutId = bs.GetBits(8);
+  region8bitPixelCode = bs.GetBits(8);
+  region4bitPixelCode = bs.GetBits(4);
+  region2bitPixelCode = bs.GetBits(2);
+  bs.SkipBits(2); // reserved
+  dbgregions("<b>region</b> id %d version %d fill %d width %d height %d level %d depth %d clutId %d<br>\n", regionId, regionVersionNumber, regionFillFlag, regionWidth, regionHeight, regionLevelOfCompatibility, regionDepth, clutId);
+  // no objectRefs.Clear() here!
+  while (!bs.IsEOF())
+        objectRefs.Add(new cSubtitleObjectRef(bs));
+}
+
+void cSubtitleRegion::ParsePgs(cBitStream &bs)
+{
+  regionDepth = 8;
+  bs.SkipBits(8); // skip palette update flag
+  clutId = bs.GetBits(8);
+  dbgregions("<b>region</b> id %d version %d clutId %d<br>\n", regionId, regionVersionNumber, clutId);
+  int objects = bs.GetBits(8);
+  while (objects--)
+        objectRefs.Add(new cSubtitleObjectRefPgs(bs));
+}
+
+void cSubtitleRegion::SetDimensions(int Width, int Height)
+{
+  regionWidth = Width;
+  regionHeight = Height;
+  dbgregions("<b>region</b> id %d width %d height %d<br>\n", regionId, regionWidth, regionHeight);
+}
+
+void cSubtitleRegion::Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
+{
+  if (regionFillFlag) {
+     switch (Bitmap->Bpp()) {
+       case 2: Bitmap->Fill(region2bitPixelCode); break;
+       case 4: Bitmap->Fill(region4bitPixelCode); break;
+       case 8: Bitmap->Fill(region8bitPixelCode); break;
+       default: dbgregions("unknown bpp %d (%s %d)<br>\n", Bitmap->Bpp(), __FUNCTION__, __LINE__);
+       }
+     }
+  for (cSubtitleObjectRef *sor = objectRefs.First(); sor; sor = objectRefs.Next(sor)) {
+      if (cSubtitleObject *so = Objects->GetObjectById(sor->ObjectId())) {
+         so->Render(Bitmap, sor->ObjectHorizontalPosition(), sor->ObjectVerticalPosition(), sor->ForegroundPixelCode(), sor->BackgroundPixelCode());
          }
       }
 }
 
-void cSubtitleRegion::SetLevel(int Level)
-{
-  if (Level > 0 && Level < 4)
-     level = 1 << Level;
-}
+// --- cSubtitleRegionRef ----------------------------------------------------
 
-void cSubtitleRegion::SetDepth(int Depth)
+class cSubtitleRegionRef : public cListObject {
+private:
+  int regionId;
+  int regionHorizontalAddress;
+  int regionVerticalAddress;
+public:
+  cSubtitleRegionRef(int id, int x, int y);
+  cSubtitleRegionRef(cBitStream &bs);
+  int RegionId(void) { return regionId; }
+  int RegionHorizontalAddress(void) { return regionHorizontalAddress; }
+  int RegionVerticalAddress(void) { return regionVerticalAddress; }
+  };
+
+cSubtitleRegionRef::cSubtitleRegionRef(int id, int x, int y)
 {
-  if (Depth > 0 && Depth < 4)
-     SetBpp(1 << Depth);
+  regionId = id;
+  regionHorizontalAddress = x;
+  regionVerticalAddress = y;
+  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
+}
+cSubtitleRegionRef::cSubtitleRegionRef(cBitStream &bs)
+{
+  regionId = bs.GetBits(8);
+  bs.SkipBits(8); // reserved
+  regionHorizontalAddress = bs.GetBits(16);
+  regionVerticalAddress = bs.GetBits(16);
+  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
 }
 
 // --- cDvbSubtitlePage ------------------------------------------------------
@@ -519,119 +974,165 @@
 class cDvbSubtitlePage : public cListObject {
 private:
   int pageId;
-  int version;
-  int state;
+  int pageTimeout;
+  int pageVersionNumber;
+  int pageState;
   int64_t pts;
-  int timeout;
+  bool pending;
+  cSubtitleObjects objects;
   cList<cSubtitleClut> cluts;
-public:
   cList<cSubtitleRegion> regions;
+  cList<cSubtitleRegionRef> regionRefs;
+public:
   cDvbSubtitlePage(int PageId);
-  virtual ~cDvbSubtitlePage();
+  void Parse(int64_t Pts, cBitStream &bs);
+  void ParsePgs(int64_t Pts, cBitStream &bs);
   int PageId(void) { return pageId; }
-  int Version(void) { return version; }
-  int State(void) { return state; }
-  tArea *GetAreas(double FactorX, double FactorY);
+  int PageTimeout(void) { return pageTimeout; }
+  int PageVersionNumber(void) { return pageVersionNumber; }
+  int PageState(void) { return pageState; }
+  int64_t Pts(void) const { return pts; }
+  bool Pending(void) { return pending; }
+  cSubtitleObjects *Objects(void) { return &objects; }
+  tArea *GetAreas(int &NumAreas, double FactorX, double FactorY);
+  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
   cSubtitleClut *GetClutById(int ClutId, bool New = false);
-  cSubtitleObject *GetObjectById(int ObjectId);
   cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
-  int64_t Pts(void) const { return pts; }
-  int Timeout(void) { return timeout; }
-  void SetVersion(int Version) { version = Version; }
-  void SetPts(int64_t Pts) { pts = Pts; }
-  void SetState(int State);
-  void SetTimeout(int Timeout) { timeout = Timeout; }
+  cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
+  void AddRegionRef(cSubtitleRegionRef *rf) { regionRefs.Add(rf); }
+  void SetPending(bool Pending) { pending = Pending; }
   };
 
 cDvbSubtitlePage::cDvbSubtitlePage(int PageId)
 {
   pageId = PageId;
-  version = -1;
-  state = -1;
-  pts = 0;
-  timeout = 0;
+  pageTimeout = 0;
+  pageVersionNumber = -1;
+  pageState = -1;
+  pts = -1;
+  pending = false;
+}
+
+void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
+{
+  if (Pts >= 0)
+     pts = Pts;
+  pageTimeout = bs.GetBits(8);
+  int Version = bs.GetBits(4);
+#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
+  if (pageVersionNumber == Version)
+     return; // no update
+#endif
+  pageVersionNumber = Version;
+  pageState = bs.GetBits(2);
+  switch (pageState) {
+    case 0: // normal case - page update
+         break;
+    case 1: // acquisition point - page refresh
+         regions.Clear();
+         objects.Clear();
+         break;
+    case 2: // mode change - new page
+         regions.Clear();
+         cluts.Clear();
+         objects.Clear();
+         break;
+    case 3: // reserved
+         break;
+    default: dbgpages("unknown page state: %d<br>\n", pageState);
+    }
+  bs.SkipBits(2); // reserved
+  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
+  regionRefs.Clear();
+  while (!bs.IsEOF())
+        regionRefs.Add(new cSubtitleRegionRef(bs));
+  pending = true;
 }
 
-cDvbSubtitlePage::~cDvbSubtitlePage()
-{
+void cDvbSubtitlePage::ParsePgs(int64_t Pts, cBitStream &bs)
+{
+  if (Pts >= 0)
+     pts = Pts;
+  pageTimeout = 60000;
+  int Version = bs.GetBits(16);
+  if (pageVersionNumber == Version)
+     return;
+  pageVersionNumber = Version;
+  pageState = bs.GetBits(2);
+  switch (pageState) {
+    case 0: // normal case - page update
+         regions.Clear();
+         break;
+    case 1: // acquisition point - page refresh
+    case 2: // epoch start - new page
+    case 3: // epoch continue - new page
+         regions.Clear();
+         cluts.Clear();
+         objects.Clear();
+         break;
+    default: dbgpages("unknown page state: %d<br>\n", pageState);
+    }
+  bs.SkipBits(6);
+  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
+  regionRefs.Clear();
+  pending = true;
 }
 
-tArea *cDvbSubtitlePage::GetAreas(double FactorX, double FactorY)
+tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY)
 {
   if (regions.Count() > 0) {
-     tArea *Areas = new tArea[regions.Count()];
+     NumAreas = regionRefs.Count();
+     tArea *Areas = new tArea[NumAreas];
      tArea *a = Areas;
-     for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
-         a->x1 = int(round(FactorX * sr->HorizontalAddress()));
-         a->y1 = int(round(FactorY * sr->VerticalAddress()));
-         a->x2 = int(round(FactorX * (sr->HorizontalAddress() + sr->Width() - 1)));
-         a->y2 = int(round(FactorY * (sr->VerticalAddress() + sr->Height() - 1)));
-         a->bpp = sr->Bpp();
-         while ((a->Width() & 3) != 0)
-               a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
+     for (cSubtitleRegionRef *srr = regionRefs.First(); srr; srr = regionRefs.Next(srr)) {
+         if (cSubtitleRegion *sr = GetRegionById(srr->RegionId())) {
+            a->x1 = int(round(FactorX * srr->RegionHorizontalAddress()));
+            a->y1 = int(round(FactorY * srr->RegionVerticalAddress()));
+            a->x2 = int(round(FactorX * (srr->RegionHorizontalAddress() + sr->RegionWidth() - 1)));
+            a->y2 = int(round(FactorY * (srr->RegionVerticalAddress() + sr->RegionHeight() - 1)));
+            a->bpp = sr->RegionDepth();
+            while ((a->Width() & 3) != 0)
+                  a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
+            }
+         else
+            a->x1 = a->y1 = a->x2 = a->y2 = a->bpp = 0;
          a++;
          }
      return Areas;
      }
+  NumAreas = 0;
   return NULL;
 }
 
 cSubtitleClut *cDvbSubtitlePage::GetClutById(int ClutId, bool New)
 {
-  cSubtitleClut *result = NULL;
   for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
       if (sc->ClutId() == ClutId)
-         result = sc;
+         return sc;
       }
-  if (!result && New) {
-     result = new cSubtitleClut(ClutId);
-     cluts.Add(result);
-     }
-  return result;
+  if (!New)
+     return NULL;
+  cSubtitleClut *Clut = new cSubtitleClut(ClutId);
+  cluts.Add(Clut);
+  return Clut;
 }
 
 cSubtitleRegion *cDvbSubtitlePage::GetRegionById(int RegionId, bool New)
 {
-  cSubtitleRegion *result = NULL;
   for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
       if (sr->RegionId() == RegionId)
-         result = sr;
+         return sr;
       }
-  if (!result && New) {
-     result = new cSubtitleRegion(RegionId);
-     regions.Add(result);
-     }
-  return result;
+  if (!New)
+     return NULL;
+  cSubtitleRegion *Region = new cSubtitleRegion(RegionId);
+  regions.Add(Region);
+  return Region;
 }
 
-cSubtitleObject *cDvbSubtitlePage::GetObjectById(int ObjectId)
+cSubtitleObject *cDvbSubtitlePage::GetObjectById(int ObjectId, bool New)
 {
-  cSubtitleObject *result = NULL;
-  for (cSubtitleRegion *sr = regions.First(); sr && !result; sr = regions.Next(sr))
-      result = sr->GetObjectById(ObjectId);
-  return result;
-}
-
-void cDvbSubtitlePage::SetState(int State)
-{
-  state = State;
-  switch (state) {
-    case 0: // normal case - page update
-         dbgpages("page update\n");
-         break;
-    case 1: // acquisition point - page refresh
-         dbgpages("page refresh\n");
-         regions.Clear();
-         break;
-    case 2: // mode change - new page
-         dbgpages("new Page\n");
-         regions.Clear();
-         cluts.Clear();
-         break;
-    case 3: // reserved
-         break;
-    default: dbgpages("unknown page state (%s %d)\n", __FUNCTION__, __LINE__);
-    }
+  return objects.GetObjectById(ObjectId, New);
 }
 
 // --- cDvbSubtitleAssembler -------------------------------------------------
@@ -714,6 +1215,7 @@
 
 class cDvbSubtitleBitmaps : public cListObject {
 private:
+  int state;
   int64_t pts;
   int timeout;
   tArea *areas;
@@ -722,16 +1224,20 @@
   double osdFactorY;
   cVector<cBitmap *> bitmaps;
 public:
-  cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
+  cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
   ~cDvbSubtitleBitmaps();
+  int State(void) { return state; }
   int64_t Pts(void) { return pts; }
   int Timeout(void) { return timeout; }
   void AddBitmap(cBitmap *Bitmap);
+  bool HasBitmaps(void) { return bitmaps.Size(); }
   void Draw(cOsd *Osd);
+  void DbgDump(int WindowWidth, int WindowHeight);
   };
 
-cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
+cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
 {
+  state = State;
   pts = Pts;
   timeout = Timeout;
   areas = Areas;
@@ -765,29 +1271,56 @@
          }
      if (Osd->CanHandleAreas(areas, numAreas) != oeOk) {
         for (int i = 0; i < numAreas; i++)
-            Bpp[i] = areas[i].bpp = Bpp[i];
+            areas[i].bpp = Bpp[i];
         AntiAlias = false;
         }
      }
-  if (Osd->SetAreas(areas, numAreas) == oeOk) {
+  if (State() == 0 || Osd->SetAreas(areas, numAreas) == oeOk) {
      for (int i = 0; i < bitmaps.Size(); i++) {
          cBitmap *b = bitmaps[i];
-         if (Scale)
-            b = b->Scaled(osdFactorX, osdFactorY, AntiAlias);
-         Osd->DrawBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b);
-         if (b != bitmaps[i])
-            delete b;
+         Osd->DrawScaledBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b, osdFactorX, osdFactorY, AntiAlias);
          }
      Osd->Flush();
      }
 }
 
+void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
+{
+  if (!SD.Active())
+     return;
+  SD.SetFirstPts(Pts());
+  double STC = double(cDevice::PrimaryDevice()->GetSTC() - SD.FirstPts()) / 90000;
+  double Start = double(Pts() - SD.FirstPts()) / 90000;
+  double Duration = Timeout();
+  double End = Start + Duration;
+  cBitmap Bitmap(WindowWidth, WindowHeight, 8);
+#define DBGBACKGROUND 0xA0A0A0
+  Bitmap.DrawRectangle(0, 0, WindowWidth - 1, WindowHeight - 1, DBGBACKGROUND);
+  for (int i = 0; i < bitmaps.Size(); i++) {
+      cBitmap *b = bitmaps[i];
+      Bitmap.DrawBitmap(b->X0(), b->Y0(), *b);
+      }
+  cString ImgName = SD.WriteJpeg(&Bitmap);
+#define BORDER //" border=1"
+  SD.WriteHtml("<p>%s<br>", State() == 0 ? "page update" : State() == 1 ? "page refresh" : State() == 2 ? "new page" : "???");
+  SD.WriteHtml("<table" BORDER "><tr><td>");
+  SD.WriteHtml("%.2f", STC);
+  SD.WriteHtml("</td><td>");
+  SD.WriteHtml("<img src=\"%s\">", *ImgName);
+  SD.WriteHtml("</td><td style=\"height:100%%\"><table" BORDER " style=\"height:100%%\">");
+  SD.WriteHtml("<tr><td valign=top><b>%.2f</b></td></tr>", Start);
+  SD.WriteHtml("<tr><td valign=middle>%.2f</td></tr>", Duration);
+  SD.WriteHtml("<tr><td valign=bottom>%.2f</td></tr>", End);
+  SD.WriteHtml("</table></td>");
+  SD.WriteHtml("</tr></table>\n");
+}
+
 // --- cDvbSubtitleConverter -------------------------------------------------
 
 int cDvbSubtitleConverter::setupLevel = 0;
 
 cDvbSubtitleConverter::cDvbSubtitleConverter(void)
-:cThread("subtitleConverter")
+:cThread("subtitle converter")
 {
   dvbSubtitleAssembler = new cDvbSubtitleAssembler;
   osd = NULL;
@@ -799,6 +1332,7 @@
   windowVerticalOffset = 0;
   pages = new cList<cDvbSubtitlePage>;
   bitmaps = new cList<cDvbSubtitleBitmaps>;
+  SD.Reset();
   Start();
 }
 
@@ -818,7 +1352,7 @@
 
 void cDvbSubtitleConverter::Reset(void)
 {
-  dbgconverter("Converter reset -----------------------\n");
+  dbgconverter("converter reset -----------------------<br>\n");
   dvbSubtitleAssembler->Reset();
   Lock();
   pages->Clear();
@@ -848,9 +1382,9 @@
         }
 
      if (Length > PayloadOffset + SubstreamHeaderLength) {
-        int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
-        if (pts)
-           dbgconverter("Converter PTS: %"PRId64"\n", pts);
+        int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
+        if (pts >= 0)
+           dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
         const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
         int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
         if (ResetSubtitleAssembler)
@@ -884,27 +1418,29 @@
   if (Data && Length > 8) {
      int PayloadOffset = PesPayloadOffset(Data);
      if (Length > PayloadOffset) {
-        int64_t pts = PesGetPts(Data);
-        if (pts)
-           dbgconverter("Converter PTS: %"PRId64"\n", pts);
+        int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
+        if (pts >= 0)
+           dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
         const uchar *data = Data + PayloadOffset;
         int length = Length - PayloadOffset;
-        if (length > 3) {
-           if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
+        if (length > 0) {
+           if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
               data += 2;
               length -= 2;
               }
            const uchar *b = data;
            while (length > 0) {
-                 if (b[0] == 0x0F) {
-                    int n = ExtractSegment(b, length, pts);
-                    if (n < 0)
-                       break;
-                    b += n;
-                    length -= n;
-                    }
+                 if (b[0] == STUFFING_SEGMENT)
+                    break;
+                 int n;
+                 if (b[0] == 0x0F)
+                    n = ExtractSegment(b, length, pts);
                  else
+                    n = ExtractPgsSegment(b, length, pts);
+                 if (n < 0)
                     break;
+                 b += n;
+                 length -= n;
                  }
            }
         }
@@ -914,7 +1450,6 @@
 }
 
 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
-#define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms
 
 void cDvbSubtitleConverter::Action(void)
 {
@@ -927,55 +1462,47 @@
            if (osd) {
               int NewSetupLevel = setupLevel;
               if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
+                 dbgoutput("closing osd<br>\n");
                  DELETENULL(osd);
                  }
               LastSetupLevel = NewSetupLevel;
               }
-           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
-              if (Delta > (int64_t(1) << 31))
-                 Delta -= (int64_t(1) << 32);
-              else if (Delta < -((int64_t(1) << 31) - 1))
-                 Delta += (int64_t(1) << 32);
-              Delta /= 90; // STC and PTS are in 1/90000s
-              if (Delta <= MAXDELTA) {
-                 if (Delta <= 0) {
-                    dbgconverter("Got %d bitmaps, showing #%d\n", bitmaps->Count(), sb->Index() + 1);
-                    if (AssertOsd()) {
-                       sb->Draw(osd);
-                       Timeout.Set(sb->Timeout() * 1000);
-                       dbgconverter("PTS: %"PRId64"  STC: %"PRId64" (%"PRId64") timeout: %d\n", sb->Pts(), cDevice::PrimaryDevice()->GetSTC(), Delta, sb->Timeout());
-                       }
-                    bitmaps->Del(sb);
-                    }
-                 else if (Delta < WaitMs)
-                    WaitMs = Delta;
-                 }
-              else
-                 bitmaps->Del(sb);
-              }
+           for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
+               // Calculate the Delta between the STC (the current timestamp of the video)
+               // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
+               // A negative Delta means that the bitmap will be presented in the future:
+               int64_t STC = cDevice::PrimaryDevice()->GetSTC();
+               int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits
+               if (Delta > (int64_t(1) << 31))
+                  Delta -= (int64_t(1) << 32);
+               else if (Delta < -((int64_t(1) << 31) - 1))
+                  Delta += (int64_t(1) << 32);
+               Delta /= 90; // STC and PTS are in 1/90000s
+               if (Delta >= 0) { // found a bitmap that shall be displayed...
+                  if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
+                     if (!sb->HasBitmaps()) {
+                        Timeout.Set();
+                        WaitMs = 0;
+                        }
+                     else if (AssertOsd()) {
+                        dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
+                        sb->Draw(osd);
+                        Timeout.Set(sb->Timeout() * 1000);
+                        dbgconverter("PTS: %" PRId64 "  STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
+                        }
+                     }
+                  else
+                     WaitMs = 0; // bitmap already timed out, so try next one immediately
+                  dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
+                  bitmaps->Del(sb);
+                  break;
+                  }
+               }
            }
         cCondWait::SleepMs(WaitMs);
         }
 }
 
-tColor cDvbSubtitleConverter::yuv2rgb(int Y, int Cb, int Cr)
-{
-  int Ey, Epb, Epr;
-  int Eg, Eb, Er;
-
-  Ey = (Y - 16);
-  Epb = (Cb - 128);
-  Epr = (Cr - 128);
-  /* ITU-R 709 */
-  Er = constrain((298 * Ey             + 460 * Epr) / 256, 0, 255);
-  Eg = constrain((298 * Ey -  55 * Epb - 137 * Epr) / 256, 0, 255);
-  Eb = constrain((298 * Ey + 543 * Epb            ) / 256, 0, 255);
-
-  return (Er << 16) | (Eg << 8) | Eb;
-}
-
 void cDvbSubtitleConverter::SetOsdData(void)
 {
   int OsdWidth, OsdHeight;
@@ -1006,6 +1533,19 @@
   return osd != NULL;
 }
 
+cDvbSubtitlePage *cDvbSubtitleConverter::GetPageById(int PageId, bool New)
+{
+  for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
+      if (sp->PageId() == PageId)
+         return sp;
+      }
+  if (!New)
+     return NULL;
+  cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
+  pages->Add(Page);
+  return Page;
+}
+
 int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
 {
   cBitStream bs(Data, Length * 8);
@@ -1013,199 +1553,66 @@
      int segmentType = bs.GetBits(8);
      if (segmentType == STUFFING_SEGMENT)
         return -1;
-     int pageId = bs.GetBits(16);
+     LOCK_THREAD;
+     cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
      int segmentLength = bs.GetBits(16);
      if (!bs.SetLength(bs.Index() + segmentLength * 8))
         return -1;
-     cDvbSubtitlePage *page = NULL;
-     LOCK_THREAD;
-     for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
-         if (sp->PageId() == pageId) {
-            page = sp;
-            break;
-            }
-         }
-     if (!page) {
-        page = new cDvbSubtitlePage(pageId);
-        pages->Add(page);
-        dbgpages("Create SubtitlePage %d (total pages = %d)\n", pageId, pages->Count());
-        }
-     if (Pts)
-        page->SetPts(Pts);
      switch (segmentType) {
        case PAGE_COMPOSITION_SEGMENT: {
-            dbgsegments("PAGE_COMPOSITION_SEGMENT\n");
-            int pageTimeout = bs.GetBits(8);
-            int pageVersion = bs.GetBits(4);
-            if (pageVersion == page->Version())
-               break; // no update
-            page->SetVersion(pageVersion);
-            page->SetTimeout(pageTimeout);
-            page->SetState(bs.GetBits(2));
-            bs.SkipBits(2); // reserved
-            dbgpages("Update page id %d version %d pts %"PRId64" timeout %d state %d\n", pageId, page->Version(), page->Pts(), page->Timeout(), page->State());
-            while (!bs.IsEOF()) {
-                  cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
-                  bs.SkipBits(8); // reserved
-                  region->SetHorizontalAddress(bs.GetBits(16));
-                  region->SetVerticalAddress(bs.GetBits(16));
-                  }
+            if (page->Pending()) {
+               dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
+               FinishPage(page);
+               }
+            dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
+            page->Parse(Pts, bs);
+            SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
             break;
             }
        case REGION_COMPOSITION_SEGMENT: {
-            dbgsegments("REGION_COMPOSITION_SEGMENT\n");
-            cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8));
-            if (!region)
-               break;
-            int regionVersion = bs.GetBits(4);
-            if (regionVersion == region->Version())
-               break; // no update
-            region->SetVersion(regionVersion);
-            bool regionFillFlag = bs.GetBit();
-            bs.SkipBits(3); // reserved
-            int regionWidth = bs.GetBits(16);
-            if (regionWidth < 1)
-               regionWidth = 1;
-            int regionHeight = bs.GetBits(16);
-            if (regionHeight < 1)
-               regionHeight = 1;
-            region->SetSize(regionWidth, regionHeight);
-            region->SetLevel(bs.GetBits(3));
-            region->SetDepth(bs.GetBits(3));
-            bs.SkipBits(2); // reserved
-            region->SetClutId(bs.GetBits(8));
-            dbgregions("Region pageId %d id %d version %d fill %d width %d height %d level %d depth %d clutId %d\n", pageId, region->RegionId(), region->Version(), regionFillFlag, regionWidth, regionHeight, region->Level(), region->Depth(), region->ClutId());
-            int region8bitPixelCode = bs.GetBits(8);
-            int region4bitPixelCode = bs.GetBits(4);
-            int region2bitPixelCode = bs.GetBits(2);
-            bs.SkipBits(2); // reserved
-            if (regionFillFlag) {
-               switch (region->Bpp()) {
-                 case 2: region->FillRegion(region8bitPixelCode); break;
-                 case 4: region->FillRegion(region4bitPixelCode); break;
-                 case 8: region->FillRegion(region2bitPixelCode); break;
-                 default: dbgregions("unknown bpp %d (%s %d)\n", region->Bpp(), __FUNCTION__, __LINE__);
-                 }
-               }
-            while (!bs.IsEOF()) {
-                  cSubtitleObject *object = region->GetObjectById(bs.GetBits(16), true);
-                  int objectType = bs.GetBits(2);
-                  object->SetCodingMethod(objectType);
-                  object->SetProviderFlag(bs.GetBits(2));
-                  int objectHorizontalPosition = bs.GetBits(12);
-                  bs.SkipBits(4); // reserved
-                  int objectVerticalPosition = bs.GetBits(12);
-                  object->SetPosition(objectHorizontalPosition, objectVerticalPosition);
-                  if (objectType == 0x01 || objectType == 0x02) {
-                     object->SetForegroundPixelCode(bs.GetBits(8));
-                     object->SetBackgroundPixelCode(bs.GetBits(8));
-                     }
-                  }
+            dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
+            cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
+            region->Parse(bs);
             break;
             }
        case CLUT_DEFINITION_SEGMENT: {
-            dbgsegments("CLUT_DEFINITION_SEGMENT\n");
+            dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
             cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
-            int clutVersion = bs.GetBits(4);
-            if (clutVersion == clut->Version())
-               break; // no update
-            clut->SetVersion(clutVersion);
-            bs.SkipBits(4); // reserved
-            dbgcluts("Clut pageId %d id %d version %d\n", pageId, clut->ClutId(), clut->Version());
-            while (!bs.IsEOF()) {
-                  uchar clutEntryId = bs.GetBits(8);
-                  bool entryClut2Flag = bs.GetBit();
-                  bool entryClut4Flag = bs.GetBit();
-                  bool entryClut8Flag = bs.GetBit();
-                  bs.SkipBits(4); // reserved
-                  uchar yval;
-                  uchar crval;
-                  uchar cbval;
-                  uchar tval;
-                  if (bs.GetBit()) { // full_range_flag
-                     yval  = bs.GetBits(8);
-                     crval = bs.GetBits(8);
-                     cbval = bs.GetBits(8);
-                     tval  = bs.GetBits(8);
-                     }
-                  else {
-                     yval  = bs.GetBits(6) << 2;
-                     crval = bs.GetBits(4) << 4;
-                     cbval = bs.GetBits(4) << 4;
-                     tval  = bs.GetBits(2) << 6;
-                     }
-                  tColor value = 0;
-                  if (yval) {
-                     value = yuv2rgb(yval, cbval, crval);
-                     value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
-                     }
-                  dbgcluts("%2d %d %d %d %08X\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value);
-                  if (entryClut2Flag)
-                     clut->SetColor(2, clutEntryId, value);
-                  if (entryClut4Flag)
-                     clut->SetColor(4, clutEntryId, value);
-                  if (entryClut8Flag)
-                     clut->SetColor(8, clutEntryId, value);
-                  }
-            dbgcluts("\n");
+            clut->Parse(bs);
             break;
             }
        case OBJECT_DATA_SEGMENT: {
-            dbgsegments("OBJECT_DATA_SEGMENT\n");
-            cSubtitleObject *object = page->GetObjectById(bs.GetBits(16));
-            if (!object)
-               break;
-            int objectVersion = bs.GetBits(4);
-            if (objectVersion == object->Version())
-               break; // no update
-            object->SetVersion(objectVersion);
-            int codingMethod = bs.GetBits(2);
-            object->SetNonModifyingColorFlag(bs.GetBit());
-            bs.SkipBit(); // reserved
-            dbgobjects("Object pageId %d id %d version %d method %d modify %d\n", pageId, object->ObjectId(), object->Version(), object->CodingMethod(), object->NonModifyingColorFlag());
-            if (codingMethod == 0) { // coding of pixels
-               int topFieldLength = bs.GetBits(16);
-               int bottomFieldLength = bs.GetBits(16);
-               object->DecodeSubBlock(bs.GetData(), topFieldLength, true);
-               if (bottomFieldLength)
-                  object->DecodeSubBlock(bs.GetData() + topFieldLength, bottomFieldLength, false);
-               else
-                  object->DecodeSubBlock(bs.GetData(), topFieldLength, false);
-               bs.WordAlign();
-               }
-            else if (codingMethod == 1) { // coded as a string of characters
-               int numberOfCodes = bs.GetBits(8);
-               object->DecodeCharacterString(bs.GetData(), numberOfCodes);
-               }
-#ifdef FINISHPAGE_HACK
-            FinishPage(page); // flush to OSD right away
-#endif
+            dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
+            cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
+            object->Parse(bs);
             break;
             }
        case DISPLAY_DEFINITION_SEGMENT: {
-            dbgsegments("DISPLAY_DEFINITION_SEGMENT\n");
+            dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
             int version = bs.GetBits(4);
-            if (version != ddsVersionNumber) {
-               bool displayWindowFlag = bs.GetBit();
-               windowHorizontalOffset = 0;
-               windowVerticalOffset   = 0;
-               bs.SkipBits(3); // reserved
-               displayWidth  = windowWidth  = bs.GetBits(16) + 1;
-               displayHeight = windowHeight = bs.GetBits(16) + 1;
-               if (displayWindowFlag) {
-                  windowHorizontalOffset = bs.GetBits(16);                              // displayWindowHorizontalPositionMinimum
-                  windowWidth            = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
-                  windowVerticalOffset   = bs.GetBits(16);                              // displayWindowVerticalPositionMinimum
-                  windowHeight           = bs.GetBits(16) - windowVerticalOffset + 1;   // displayWindowVerticalPositionMaximum
-                  }
-               SetOsdData();
-               SetupChanged();
-               ddsVersionNumber = version;
+#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
+            if (version == ddsVersionNumber)
+               break; // no update
+#endif
+            bool displayWindowFlag = bs.GetBit();
+            windowHorizontalOffset = 0;
+            windowVerticalOffset   = 0;
+            bs.SkipBits(3); // reserved
+            displayWidth  = windowWidth  = bs.GetBits(16) + 1;
+            displayHeight = windowHeight = bs.GetBits(16) + 1;
+            if (displayWindowFlag) {
+               windowHorizontalOffset = bs.GetBits(16);                              // displayWindowHorizontalPositionMinimum
+               windowWidth            = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
+               windowVerticalOffset   = bs.GetBits(16);                              // displayWindowVerticalPositionMinimum
+               windowHeight           = bs.GetBits(16) - windowVerticalOffset + 1;   // displayWindowVerticalPositionMaximum
                }
+            SetOsdData();
+            ddsVersionNumber = version;
+            dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
             break;
             }
        case DISPARITY_SIGNALING_SEGMENT: {
-            dbgsegments("DISPARITY_SIGNALING_SEGMENT\n");
+            dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
             bs.SkipBits(4); // dss_version_number
             bool disparity_shift_update_sequence_page_flag = bs.GetBit();
             bs.SkipBits(3); // reserved
@@ -1246,12 +1653,78 @@
             break;
             }
        case END_OF_DISPLAY_SET_SEGMENT: {
-            dbgsegments("END_OF_DISPLAY_SET_SEGMENT\n");
+            dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
             FinishPage(page);
+            page->SetPending(false);
             break;
             }
        default:
-            dbgsegments("*** unknown segment type: %02X\n", segmentType);
+            dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
+       }
+     return bs.Length() / 8;
+     }
+  return -1;
+}
+
+int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
+{
+  cBitStream bs(Data, Length * 8);
+  if (Length >= 3) {
+     int segmentType = bs.GetBits(8);
+     int segmentLength = bs.GetBits(16);
+     if (!bs.SetLength(bs.Index() + segmentLength * 8))
+        return -1;
+     LOCK_THREAD;
+     cDvbSubtitlePage *page = GetPageById(0, true);
+     switch (segmentType) {
+       case PGS_PRESENTATION_SEGMENT: {
+            if (page->Pending()) {
+               dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
+               FinishPage(page);
+               }
+            dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
+            displayWidth  = windowWidth  = bs.GetBits(16);
+            displayHeight = windowHeight = bs.GetBits(16);
+            bs.SkipBits(8);
+            page->ParsePgs(Pts, bs);
+            SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
+            cSubtitleRegion *region = page->GetRegionById(0, true);
+            region->ParsePgs(bs);
+            break;
+            }
+       case PGS_WINDOW_SEGMENT: {
+            bs.SkipBits(16);
+            int regionHorizontalAddress = bs.GetBits(16);
+            int regionVerticalAddress   = bs.GetBits(16);
+            int regionWidth  = bs.GetBits(16);
+            int regionHeight = bs.GetBits(16);
+            cSubtitleRegion *region = page->GetRegionById(0, true);
+            region->SetDimensions(regionWidth, regionHeight);
+            page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
+            dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
+            break;
+            }
+       case PGS_PALETTE_SEGMENT: {
+            dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
+            cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
+            clut->ParsePgs(bs);
+            break;
+            }
+       case PGS_OBJECT_SEGMENT: {
+            dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
+            cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
+            object->ParsePgs(bs);
+            break;
+            }
+       case PGS_DISPLAY_SEGMENT: {
+            dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
+            FinishPage(page);
+            page->SetPending(false);
+            break;
+            }
+       default:
+            dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
+            return -1;
        }
      return bs.Length() / 8;
      }
@@ -1262,11 +1735,12 @@
 {
   if (!AssertOsd())
      return;
-  tArea *Areas = Page->GetAreas(osdFactorX, osdFactorY);
-  int NumAreas = Page->regions.Count();
+  int NumAreas;
+  tArea *Areas = Page->GetAreas(NumAreas, osdFactorX, osdFactorY);
   int Bpp = 8;
   bool Reduced = false;
   while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) {
+        dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(Areas, NumAreas));
         int HalfBpp = Bpp / 2;
         if (HalfBpp >= 2) {
            for (int i = 0; i < NumAreas; i++) {
@@ -1280,37 +1754,35 @@
         else
            return; // unable to draw bitmaps
         }
-  cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->Pts(), Page->Timeout(), Areas, NumAreas, osdFactorX, osdFactorY);
+  cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY);
   bitmaps->Add(Bitmaps);
   for (int i = 0; i < NumAreas; i++) {
-      cSubtitleRegion *sr = Page->regions.Get(i);
-      cSubtitleClut *clut = Page->GetClutById(sr->ClutId());
-      if (!clut)
-         continue;
-      sr->Replace(*clut->GetPalette(sr->Bpp()));
-      sr->UpdateTextData(clut);
-      if (Reduced) {
-         if (sr->Bpp() != Areas[i].bpp) {
-            if (sr->Level() <= Areas[i].bpp) {
-               //TODO this is untested - didn't have any such subtitle stream
-               cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
-               if (Clut) {
-                  dbgregions("reduce region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp);
-                  sr->ReduceBpp(*Clut->GetPalette(sr->Bpp()));
+      if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
+         if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
+            if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
+               cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
+               bm->Replace(*clut->GetPalette(sr->RegionDepth()));
+               sr->Render(bm, Page->Objects());
+               if (Reduced) {
+                  if (sr->RegionDepth() != Areas[i].bpp) {
+                     if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
+                        //TODO this is untested - didn't have any such subtitle stream
+                        cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
+                        dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
+                        bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
+                        }
+                     else {
+                        dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
+                        bm->ShrinkBpp(Areas[i].bpp);
+                        }
+                     }
                   }
-               }
-            else {
-               dbgregions("condense region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp);
-               sr->ShrinkBpp(Areas[i].bpp);
+               bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
+               Bitmaps->AddBitmap(bm);
                }
             }
          }
-      int posX = sr->HorizontalAddress();
-      int posY = sr->VerticalAddress();
-      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);
-         }
       }
+  if (DebugPages)
+     Bitmaps->DbgDump(windowWidth, windowHeight);
 }
diff -ruN vdr-2.0.0/dvbsubtitle.h vdr-2.2.0/dvbsubtitle.h
--- vdr-2.0.0/dvbsubtitle.h	2012-03-11 14:34:12.000000000 +0100
+++ vdr-2.2.0/dvbsubtitle.h	2015-01-14 11:01:48.000000000 +0100
@@ -6,7 +6,7 @@
  *
  * Original author: Marco Schluessler <marco@lordzodiac.de>
  *
- * $Id: dvbsubtitle.h 2.7 2012/03/11 13:34:12 kls Exp $
+ * $Id: dvbsubtitle.h 3.2 2015/01/14 10:01:48 kls Exp $
  */
 
 #ifndef __DVBSUBTITLE_H
@@ -39,10 +39,11 @@
   double osdFactorY;
   cList<cDvbSubtitlePage> *pages;
   cList<cDvbSubtitleBitmaps> *bitmaps;
-  tColor yuv2rgb(int Y, int Cb, int Cr);
+  cDvbSubtitlePage *GetPageById(int PageId, bool New = false);
   void SetOsdData(void);
   bool AssertOsd(void);
   int ExtractSegment(const uchar *Data, int Length, int64_t Pts);
+  int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts);
   void FinishPage(cDvbSubtitlePage *Page);
 public:
   cDvbSubtitleConverter(void);
diff -ruN vdr-2.0.0/eit.c vdr-2.2.0/eit.c
--- vdr-2.0.0/eit.c	2012-12-04 12:10:10.000000000 +0100
+++ vdr-2.2.0/eit.c	2015-02-01 15:55:27.000000000 +0100
@@ -8,7 +8,7 @@
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
  *
- * $Id: eit.c 2.23 2012/12/04 11:10:10 kls Exp $
+ * $Id: eit.c 3.6 2015/02/01 14:55:27 kls Exp $
  */
 
 #include "eit.h"
@@ -46,6 +46,7 @@
      return;
      }
 
+  EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus);
   bool handledExternally = EpgHandlers.HandledExternally(channel);
   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
 
@@ -53,8 +54,8 @@
   bool Modified = false;
   time_t SegmentStart = 0;
   time_t SegmentEnd = 0;
-  struct tm tm_r;
-  struct tm t = *localtime_r(&Now, &tm_r); // this initializes the time zone in 't'
+  struct tm t = { 0 };
+  localtime_r(&Now, &t); // this initializes the time zone in 't'
 
   SI::EIT::Event SiEitEvent;
   for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) {
@@ -135,8 +136,8 @@
                     UseExtendedEventDescriptor = true;
                     }
                  if (UseExtendedEventDescriptor) {
-                    ExtendedEventDescriptors->Add(eed);
-                    d = NULL; // so that it is not deleted
+                    if (ExtendedEventDescriptors->Add(eed))
+                       d = NULL; // so that it is not deleted
                     }
                  if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber())
                     UseExtendedEventDescriptor = false;
@@ -219,7 +220,7 @@
             case SI::LinkageDescriptorTag: {
                  SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d;
                  tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId());
-                 if (ld->getLinkageType() == 0xB0) { // Premiere World
+                 if (ld->getLinkageType() == SI::LinkageTypePremiere) { // Premiere World
                     bool hit = StartTime <= Now && Now < StartTime + Duration;
                     if (hit) {
                        char linkName[ld->privateData.getLength() + 1];
@@ -310,6 +311,7 @@
      Schedules->SetModified(pSchedule);
      }
   Channels.Unlock();
+  EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus);
 }
 
 // --- cTDT ------------------------------------------------------------------
@@ -404,7 +406,7 @@
          }
          break;
     case 0x14: {
-         if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
+         if (Setup.SetSystemTime && Setup.TimeSource == Source() && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
             cTDT TDT(Data);
          }
          break;
diff -ruN vdr-2.0.0/eit.h vdr-2.2.0/eit.h
--- vdr-2.0.0/eit.h	2010-01-03 16:28:34.000000000 +0100
+++ vdr-2.2.0/eit.h	2010-01-03 16:28:34.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: eit.h 2.1 2010/01/03 15:28:34 kls Exp $
+ * $Id: eit.h 3.0 2010/01/03 15:28:34 kls Exp $
  */
 
 #ifndef __EIT_H
diff -ruN vdr-2.0.0/eitscan.c vdr-2.2.0/eitscan.c
--- vdr-2.0.0/eitscan.c	2012-04-07 16:39:28.000000000 +0200
+++ vdr-2.2.0/eitscan.c	2012-04-07 16:39:28.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: eitscan.c 2.7 2012/04/07 14:39:28 kls Exp $
+ * $Id: eitscan.c 3.0 2012/04/07 14:39:28 kls Exp $
  */
 
 #include "eitscan.h"
diff -ruN vdr-2.0.0/eitscan.h vdr-2.2.0/eitscan.h
--- vdr-2.0.0/eitscan.h	2012-03-07 14:54:16.000000000 +0100
+++ vdr-2.2.0/eitscan.h	2012-03-07 14:54:16.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: eitscan.h 2.1 2012/03/07 13:54:16 kls Exp $
+ * $Id: eitscan.h 3.0 2012/03/07 13:54:16 kls Exp $
  */
 
 #ifndef __EITSCAN_H
diff -ruN vdr-2.0.0/epg.c vdr-2.2.0/epg.c
--- vdr-2.0.0/epg.c	2013-02-17 15:12:07.000000000 +0100
+++ vdr-2.2.0/epg.c	2013-12-28 12:33:08.000000000 +0100
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  *
- * $Id: epg.c 2.23 2013/02/17 14:12:07 kls Exp $
+ * $Id: epg.c 3.3 2013/12/28 11:33:08 kls Exp $
  */
 
 #include "epg.h"
@@ -32,7 +32,7 @@
 bool tComponent::FromString(const char *s)
 {
   unsigned int Stream, Type;
-  int n = sscanf(s, "%X %02X %7s %a[^\n]", &Stream, &Type, language, &description); // 7 = MAXLANGCODE2 - 1
+  int n = sscanf(s, "%X %02X %7s %m[^\n]", &Stream, &Type, language, &description); // 7 = MAXLANGCODE2 - 1
   if (n != 4 || isempty(description)) {
      free(description);
      description = NULL;
@@ -1140,16 +1140,19 @@
 class cEpgDataWriter : public cThread {
 private:
   cMutex mutex;
+  bool dump;
 protected:
   virtual void Action(void);
 public:
   cEpgDataWriter(void);
+  void SetDump(bool Dump) { dump = Dump; }
   void Perform(void);
   };
 
 cEpgDataWriter::cEpgDataWriter(void)
 :cThread("epg data writer", true)
 {
+  dump = false;
 }
 
 void cEpgDataWriter::Action(void)
@@ -1169,7 +1172,8 @@
            p->Cleanup(now);
        }
   }
-  cSchedules::Dump();
+  if (dump)
+     cSchedules::Dump();
 }
 
 static cEpgDataWriter EpgDataWriter;
@@ -1203,6 +1207,7 @@
 {
   free(epgDataFileName);
   epgDataFileName = FileName ? strdup(FileName) : NULL;
+  EpgDataWriter.SetDump(epgDataFileName != NULL);
 }
 
 void cSchedules::SetModified(cSchedule *Schedule)
@@ -1217,12 +1222,10 @@
      lastDump = 0;
   time_t now = time(NULL);
   if (now - lastDump > EPGDATAWRITEDELTA) {
-     if (epgDataFileName) {
-        if (Force)
-           EpgDataWriter.Perform();
-        else if (!EpgDataWriter.Active())
-           EpgDataWriter.Start();
-        }
+     if (Force)
+        EpgDataWriter.Perform();
+     else if (!EpgDataWriter.Active())
+        EpgDataWriter.Start();
      lastDump = now;
      }
 }
@@ -1537,3 +1540,19 @@
       }
   Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
 }
+
+void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus)
+{
+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
+      if (eh->BeginSegmentTransfer(Channel, OnlyRunningStatus))
+         return;
+      }
+}
+
+void cEpgHandlers::EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
+{
+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
+      if (eh->EndSegmentTransfer(Modified, OnlyRunningStatus))
+         return;
+      }
+}
diff -ruN vdr-2.0.0/epg.h vdr-2.2.0/epg.h
--- vdr-2.0.0/epg.h	2012-09-24 14:53:53.000000000 +0200
+++ vdr-2.2.0/epg.h	2013-08-23 12:50:05.000000000 +0200
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  *
- * $Id: epg.h 2.15 2012/09/24 12:53:53 kls Exp $
+ * $Id: epg.h 3.1 2013/08/23 10:50:05 kls Exp $
  */
 
 #ifndef __EPG_H
@@ -273,6 +273,12 @@
   virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; }
           ///< Takes a look at all EPG events between SegmentStart and SegmentEnd and
           ///< drops outdated events.
+  virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) { return false; }
+          ///< Called directly after IgnoreChannel() before any other handler method is called.
+          ///< Designed to give handlers the possibility to prepare a database transaction.
+  virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) { return false; }
+          ///< Called after the segment data has been processed.
+          ///< At this point handlers should close/commit/rollback any pending database transactions.
   };
 
 class cEpgHandlers : public cList<cEpgHandler> {
@@ -295,6 +301,8 @@
   void HandleEvent(cEvent *Event);
   void SortSchedule(cSchedule *Schedule);
   void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+  void BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus);
+  void EndSegmentTransfer(bool Modified, bool OnlyRunningStatus);
   };
 
 extern cEpgHandlers EpgHandlers;
diff -ruN vdr-2.0.0/epg2html vdr-2.2.0/epg2html
--- vdr-2.0.0/epg2html	2013-03-04 14:02:20.000000000 +0100
+++ vdr-2.2.0/epg2html	2013-03-04 14:02:20.000000000 +0100
@@ -12,7 +12,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: epg2html 2.2 2013/03/04 13:02:20 kls Exp $
+# $Id: epg2html 3.0 2013/03/04 13:02:20 kls Exp $
 
 @Index = ();
 ($Charset = $ENV{LANG}) =~ s/^[^.]*\.?(.*)/$1/;
diff -ruN vdr-2.0.0/filter.c vdr-2.2.0/filter.c
--- vdr-2.0.0/filter.c	2004-01-11 14:31:34.000000000 +0100
+++ vdr-2.2.0/filter.c	2004-01-11 14:31:34.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: filter.c 2.0 2004/01/11 13:31:34 kls Exp $
+ * $Id: filter.c 3.0 2004/01/11 13:31:34 kls Exp $
  */
 
 #include "filter.h"
diff -ruN vdr-2.0.0/filter.h vdr-2.2.0/filter.h
--- vdr-2.0.0/filter.h	2004-01-11 14:31:59.000000000 +0100
+++ vdr-2.2.0/filter.h	2004-01-11 14:31:59.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: filter.h 2.0 2004/01/11 13:31:59 kls Exp $
+ * $Id: filter.h 3.0 2004/01/11 13:31:59 kls Exp $
  */
 
 #ifndef __FILTER_H
diff -ruN vdr-2.0.0/font.c vdr-2.2.0/font.c
--- vdr-2.0.0/font.c	2012-06-02 15:38:28.000000000 +0200
+++ vdr-2.2.0/font.c	2014-01-07 13:19:45.000000000 +0100
@@ -6,7 +6,7 @@
  *
  * BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya.
  *
- * $Id: font.c 2.13 2012/06/02 13:38:28 kls Exp $
+ * $Id: font.c 3.2 2014/01/07 12:19:45 kls Exp $
  */
 
 #include "font.h"
@@ -382,10 +382,13 @@
 // A dummy font, in case there are no fonts installed:
 
 class cDummyFont : public cFont {
+private:
+  int height;
 public:
-  virtual int Width(uint c) const { return 10; }
-  virtual int Width(const char *s) const { return 50; }
-  virtual int Height(void) const { return 20; }
+  cDummyFont(int CharHeight) { height = CharHeight; }
+  virtual int Width(uint c) const { return height; }
+  virtual int Width(const char *s) const { return height; }
+  virtual int Height(void) const { return height; }
   virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}
   virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {};
   };
@@ -396,11 +399,8 @@
 
 void cFont::SetFont(eDvbFont Font, const char *Name, int CharHeight)
 {
-  cFont *f = CreateFont(Name, constrain(CharHeight, MINFONTSIZE, MAXFONTSIZE));
-  if (!f || !f->Height())
-     f = new cDummyFont;
   delete fonts[Font];
-  fonts[Font] = f;
+  fonts[Font] = CreateFont(Name, constrain(CharHeight, MINFONTSIZE, MAXFONTSIZE));
 }
 
 const cFont *cFont::GetFont(eDvbFont Font)
@@ -423,9 +423,10 @@
 cFont *cFont::CreateFont(const char *Name, int CharHeight, int CharWidth)
 {
   cString fn = GetFontFileName(Name);
-  if (*fn)
-     return new cFreetypeFont(fn, CharHeight, CharWidth);
-  return NULL;
+  cFont *f = *fn ? new cFreetypeFont(fn, CharHeight, CharWidth) : NULL;
+  if (!f || !f->Height())
+     f = new cDummyFont(CharHeight);
+  return f;
 }
 
 bool cFont::GetAvailableFontNames(cStringList *FontNames, bool Monospaced)
@@ -592,7 +593,7 @@
             p = Blank;
             continue;
             }
-         else {
+         else if (w > 0) { // there has to be at least one character before the newline
             // Here's the ugly part, where we don't have any whitespace to
             // punch in a newline, so we need to make room for it:
             if (Delim)
@@ -608,8 +609,7 @@
             continue;
             }
          }
-      else
-         w += cw;
+      w += cw;
       if (strchr("-.,:;!?_", *p)) {
          Delim = p;
          Blank = NULL;
diff -ruN vdr-2.0.0/font.h vdr-2.2.0/font.h
--- vdr-2.0.0/font.h	2013-02-17 14:17:42.000000000 +0100
+++ vdr-2.2.0/font.h	2014-01-07 13:11:55.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: font.h 2.7 2013/02/17 13:17:42 kls Exp $
+ * $Id: font.h 3.1 2014/01/07 12:11:55 kls Exp $
  */
 
 #ifndef __FONT_H
@@ -75,7 +75,7 @@
           ///< default width. Name is of the form "Family:Style", for instance
           ///< "Verdana:Bold Italic" or "Times New Roman". See GetAvailableFontNames()
           ///< for how to get a list of all available font names.
-          ///< If the requested font can't be created, NULL is returned.
+          ///< If the requested font can't be created, a dummy font is returned.
           ///< The caller must delete the font when it is no longer needed.
   static bool GetAvailableFontNames(cStringList *FontNames, bool Monospaced = false);
           ///< Queries the font configuration for a list of available font names,
diff -ruN vdr-2.0.0/i18n.c vdr-2.2.0/i18n.c
--- vdr-2.0.0/i18n.c	2012-09-01 12:53:43.000000000 +0200
+++ vdr-2.2.0/i18n.c	2012-09-01 12:53:43.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: i18n.c 2.5 2012/09/01 10:53:43 kls Exp $
+ * $Id: i18n.c 3.0 2012/09/01 10:53:43 kls Exp $
  */
 
 /*
diff -ruN vdr-2.0.0/i18n.h vdr-2.2.0/i18n.h
--- vdr-2.0.0/i18n.h	2012-03-11 15:07:45.000000000 +0100
+++ vdr-2.2.0/i18n.h	2012-03-11 15:07:45.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: i18n.h 2.1 2012/03/11 14:07:45 kls Exp $
+ * $Id: i18n.h 3.0 2012/03/11 14:07:45 kls Exp $
  */
 
 #ifndef __I18N_H
diff -ruN vdr-2.0.0/interface.c vdr-2.2.0/interface.c
--- vdr-2.0.0/interface.c	2012-11-19 13:21:43.000000000 +0100
+++ vdr-2.2.0/interface.c	2015-01-11 14:37:47.000000000 +0100
@@ -4,12 +4,15 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: interface.c 2.2 2012/11/19 12:21:43 kls Exp $
+ * $Id: interface.c 3.1 2015/01/11 13:37:47 kls Exp $
  */
 
 #include "interface.h"
 #include <ctype.h>
 #include <stdlib.h>
+#ifdef SDNOTIFY
+#include <systemd/sd-daemon.h>
+#endif
 #include <unistd.h>
 #include "i18n.h"
 #include "status.h"
@@ -159,6 +162,9 @@
       bool known = Keys.KnowsRemote(Remote->Name());
       dsyslog("remote control %s - %s", Remote->Name(), known ? "keys known" : "learning keys");
       if (!known) {
+#ifdef SDNOTIFY
+         sd_notify(0, "READY=1\nSTATUS=Learning keys...");
+#endif
          cSkinDisplayMenu *DisplayMenu = Skins.Current()->DisplayMenu();
          DisplayMenu->SetMenuCategory(mcUnknown);
          char Headline[256];
diff -ruN vdr-2.0.0/interface.h vdr-2.2.0/interface.h
--- vdr-2.0.0/interface.h	2004-05-01 13:11:13.000000000 +0200
+++ vdr-2.2.0/interface.h	2004-05-01 13:11:13.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: interface.h 2.0 2004/05/01 11:11:13 kls Exp $
+ * $Id: interface.h 3.0 2004/05/01 11:11:13 kls Exp $
  */
 
 #ifndef __INTERFACE_H
diff -ruN vdr-2.0.0/keys.c vdr-2.2.0/keys.c
--- vdr-2.0.0/keys.c	2012-12-04 13:52:52.000000000 +0100
+++ vdr-2.2.0/keys.c	2012-12-04 13:52:52.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: keys.c 2.2 2012/12/04 12:52:52 kls Exp $
+ * $Id: keys.c 3.0 2012/12/04 12:52:52 kls Exp $
  */
 
 #include "keys.h"
diff -ruN vdr-2.0.0/keys.h vdr-2.2.0/keys.h
--- vdr-2.0.0/keys.h	2012-12-04 13:51:25.000000000 +0100
+++ vdr-2.2.0/keys.h	2015-01-27 11:45:18.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: keys.h 2.2 2012/12/04 12:51:25 kls Exp $
+ * $Id: keys.h 3.1 2015/01/27 10:45:18 kls Exp $
  */
 
 #ifndef __KEYS_H
@@ -65,6 +65,8 @@
 
 // This is in preparation for having more key codes:
 #define kMarkToggle      k0
+#define kMarkSkipBack    k1
+#define kMarkSkipForward k3
 #define kMarkMoveBack    k4
 #define kMarkMoveForward k6
 #define kMarkJumpBack    k7
diff -ruN vdr-2.0.0/libsi/Makefile vdr-2.2.0/libsi/Makefile
--- vdr-2.0.0/libsi/Makefile	2012-12-18 14:35:54.000000000 +0100
+++ vdr-2.2.0/libsi/Makefile	2015-02-11 11:24:54.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # Makefile for a libsi
 #
-# $Id: Makefile 2.4 2012/12/18 13:35:54 kls Exp $
+# $Id: Makefile 3.1 2015/02/11 10:24:54 kls Exp $
 
 ### The archiver options:
 
@@ -32,6 +32,7 @@
 ### Targets:
 
 all: libsi.a
+	@:
 
 libsi.a : $(OBJS)
 	$(AR) $(ARFLAGS) $@ $(OBJS)
diff -ruN vdr-2.0.0/libsi/descriptor.c vdr-2.2.0/libsi/descriptor.c
--- vdr-2.0.0/libsi/descriptor.c	2012-01-11 12:35:17.000000000 +0100
+++ vdr-2.2.0/libsi/descriptor.c	2013-10-30 11:16:18.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: descriptor.c 2.4 2012/01/11 11:35:17 kls Exp $
+ *   $Id: descriptor.c 3.1 2013/10/30 10:16:18 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -922,6 +922,48 @@
    return extended_data_flag ? s->tfs_flag : -1;
 }
 
+void LogicalChannelDescriptor::Parse() {
+   //this descriptor is only a header and a loop
+   logicalChannelLoop.setData(data+sizeof(descr_logical_channel), getLength()-sizeof(descr_logical_channel));
+}
+
+int LogicalChannelDescriptor::LogicalChannel::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int LogicalChannelDescriptor::LogicalChannel::getVisibleServiceFlag() const {
+   return s->visible_service_flag;
+}
+
+int LogicalChannelDescriptor::LogicalChannel::getLogicalChannelNumber() const {
+   return HILO(s->logical_channel_number);
+}
+
+void LogicalChannelDescriptor::LogicalChannel::Parse() {
+   s=data.getData<const item_logical_channel>();
+}
+
+void HdSimulcastLogicalChannelDescriptor::Parse() {
+   //this descriptor is only a header and a loop
+   hdSimulcastLogicalChannelLoop.setData(data+sizeof(descr_hd_simulcast_logical_channel), getLength()-sizeof(descr_hd_simulcast_logical_channel));
+}
+
+int HdSimulcastLogicalChannelDescriptor::HdSimulcastLogicalChannel::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int HdSimulcastLogicalChannelDescriptor::HdSimulcastLogicalChannel::getVisibleServiceFlag() const {
+   return s->visible_service_flag;
+}
+
+int HdSimulcastLogicalChannelDescriptor::HdSimulcastLogicalChannel::getLogicalChannelNumber() const {
+   return HILO(s->logical_channel_number);
+}
+
+void HdSimulcastLogicalChannelDescriptor::HdSimulcastLogicalChannel::Parse() {
+   s=data.getData<const item_hd_simulcast_logical_channel>();
+}
+
 int PremiereContentTransmissionDescriptor::getOriginalNetworkId() const {
    return HILO(s->original_network_id);
 }
@@ -1144,5 +1186,60 @@
    if (checkSize(getLength()-offset))
       privateData.assign(data.getData(offset), getLength()-offset);
 }
+
+int AVCDescriptor::getProfileIdc() const {
+   return s->profile_idc;
+}
+
+int AVCDescriptor::getConstraintSet0Flag() const {
+   return s->constraint_set0_flag;
+}
+
+int AVCDescriptor::getConstraintSet1Flag() const {
+   return s->constraint_set1_flag;
+}
+
+int AVCDescriptor::getConstraintSet2Flag() const {
+   return s->constraint_set2_flag;
+}
+
+int AVCDescriptor::getConstraintSet3Flag() const {
+   return s->constraint_set3_flag;
+}
+
+int AVCDescriptor::getConstraintSet4Flag() const {
+   return s->constraint_set4_flag;
+}
+
+int AVCDescriptor::getConstraintSet5Flag() const {
+   return s->constraint_set5_flag;
+}
+
+int AVCDescriptor::getAVCCompatibleFlags() const {
+   return s->avc_compatible_flags;
+}
+
+int AVCDescriptor::getLevelIdc() const {
+   return s->level_idc;
+}
+
+int AVCDescriptor::getAVCStillPresent() const {
+   return s->avc_still_present;
+}
+
+int AVCDescriptor::getAVC24HourPictureFlag() const {
+   return s->avc_24_hour_picture_flag;
+}
+
+int AVCDescriptor::getFramePackingSEINotPresentFlag() const {
+   return s->frame_packing_sei_not_present_flag;
+}
+
+void AVCDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_avc>(s, offset);
+   if (checkSize(getLength()-offset))
+      privateData.assign(data.getData(offset), getLength()-offset);
+}
 
 } //end of namespace
diff -ruN vdr-2.0.0/libsi/descriptor.h vdr-2.2.0/libsi/descriptor.h
--- vdr-2.0.0/libsi/descriptor.h	2012-01-11 12:35:17.000000000 +0100
+++ vdr-2.2.0/libsi/descriptor.h	2014-02-08 13:44:17.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: descriptor.h 2.4 2012/01/11 11:35:17 kls Exp $
+ *   $Id: descriptor.h 3.2 2014/02/08 12:44:17 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -489,8 +489,6 @@
    StructureLoop<Language> languageLoop;
 protected:
    virtual void Parse();
-private:
-   const descr_iso_639_language *s;
 };
 
 class PDCDescriptor : public Descriptor {
@@ -557,6 +555,42 @@
    int extended_data_flag;
 };
 
+class LogicalChannelDescriptor : public Descriptor {
+public:
+   class LogicalChannel : public LoopElement {
+   public:
+      int getServiceId() const;
+      int getVisibleServiceFlag() const;
+      int getLogicalChannelNumber() const;
+      virtual int getLength() { return sizeof(item_logical_channel); }
+   protected:
+      virtual void Parse();
+   private:
+      const item_logical_channel *s;
+   };
+   StructureLoop<LogicalChannel> logicalChannelLoop;
+protected:
+   virtual void Parse();
+};
+
+class HdSimulcastLogicalChannelDescriptor : public Descriptor {
+public:
+   class HdSimulcastLogicalChannel : public LoopElement {
+   public:
+      int getServiceId() const;
+      int getVisibleServiceFlag() const;
+      int getLogicalChannelNumber() const;
+      virtual int getLength() { return sizeof(item_hd_simulcast_logical_channel); }
+   protected:
+      virtual void Parse();
+   private:
+      const item_hd_simulcast_logical_channel *s;
+   };
+   StructureLoop<HdSimulcastLogicalChannel> hdSimulcastLogicalChannelLoop;
+protected:
+   virtual void Parse();
+};
+
 // Private DVB Descriptor  Premiere.de
 // 0xF2  Content Transmission Descriptor
 // http://dvbsnoop.sourceforge.net/examples/example-private-section.html
@@ -735,6 +769,27 @@
    const descr_registration *s;
 };
 
+class AVCDescriptor : public Descriptor {
+public:
+   int getProfileIdc() const;
+   int getConstraintSet0Flag() const;
+   int getConstraintSet1Flag() const;
+   int getConstraintSet2Flag() const;
+   int getConstraintSet3Flag() const;
+   int getConstraintSet4Flag() const;
+   int getConstraintSet5Flag() const;
+   int getAVCCompatibleFlags() const;
+   int getLevelIdc() const;
+   int getAVCStillPresent() const;
+   int getAVC24HourPictureFlag() const;
+   int getFramePackingSEINotPresentFlag() const;
+   CharArray privateData;
+protected:
+   virtual void Parse();
+private:
+   const descr_avc *s;
+};
+
 } //end of namespace
 
 #endif //LIBSI_TABLE_H
diff -ruN vdr-2.0.0/libsi/gendescr vdr-2.2.0/libsi/gendescr
--- vdr-2.0.0/libsi/gendescr	2003-12-13 11:40:53.000000000 +0100
+++ vdr-2.2.0/libsi/gendescr	2003-12-13 11:40:53.000000000 +0100
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-# $Id: gendescr 2.0 2003/12/13 10:40:53 kls Exp $
+# $Id: gendescr 3.0 2003/12/13 10:40:53 kls Exp $
 
 print "Name (ohne ...Descriptor):";
 $name=<STDIN>;
diff -ruN vdr-2.0.0/libsi/headers.h vdr-2.2.0/libsi/headers.h
--- vdr-2.0.0/libsi/headers.h	2012-06-09 16:37:24.000000000 +0200
+++ vdr-2.2.0/libsi/headers.h	2013-10-30 11:16:18.000000000 +0100
@@ -10,7 +10,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: headers.h 2.5 2012/06/09 14:37:24 kls Exp $
+ *   $Id: headers.h 3.1 2013/10/30 10:16:18 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -1870,6 +1870,54 @@
    fields looping to the end */
 };
 
+/* 0x83 logical_channel_descriptor */
+
+#define DESCR_LOGICAL_CHANNEL_LEN 2
+struct descr_logical_channel {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define ITEM_LOGICAL_CHANNEL_LEN 4
+struct item_logical_channel {
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char visible_service_flag                   :1;
+   u_char reserved                               :5;
+   u_char logical_channel_number_hi              :2;
+#else
+   u_char logical_channel_number_hi              :2;
+   u_char reserved                               :5;
+   u_char visible_service_flag                   :1;
+#endif
+   u_char logical_channel_number_lo              :8;
+};
+
+/* 0x88 hd_simulcast_logical_channel_descriptor */
+
+#define DESCR_HD_SIMULCAST_LOGICAL_CHANNEL_LEN 2
+struct descr_hd_simulcast_logical_channel {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define ITEM_HD_SIMULCAST_LOGICAL_CHANNEL_LEN 4
+struct item_hd_simulcast_logical_channel {
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char visible_service_flag                   :1;
+   u_char reserved                               :5;
+   u_char logical_channel_number_hi              :2;
+#else
+   u_char logical_channel_number_hi              :2;
+   u_char reserved                               :5;
+   u_char visible_service_flag                   :1;
+#endif
+   u_char logical_channel_number_lo              :8;
+};
+
 /* MHP 0x00 application_descriptor */
 
 #define DESCR_APPLICATION_LEN 3
@@ -2088,6 +2136,27 @@
    u_char format_identifier_lo_lo                :8;
 };
 
+/* 0x28 avc_descriptor */
+
+#define DESCR_AVC_LEN 6
+struct descr_avc {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char profile_idc                            :8;
+   u_char constraint_set0_flag                   :1;
+   u_char constraint_set1_flag                   :1;
+   u_char constraint_set2_flag                   :1;
+   u_char constraint_set3_flag                   :1;
+   u_char constraint_set4_flag                   :1;
+   u_char constraint_set5_flag                   :1;
+   u_char avc_compatible_flags                   :2;
+   u_char level_idc                              :8;
+   u_char avc_still_present                      :1;
+   u_char avc_24_hour_picture_flag               :1;
+   u_char frame_packing_sei_not_present_flag     :1;
+   u_char reserved                               :5;
+};
+
 } //end of namespace
 
 #endif //LIBSI_HEADERS_H
diff -ruN vdr-2.0.0/libsi/section.c vdr-2.2.0/libsi/section.c
--- vdr-2.0.0/libsi/section.c	2006-04-14 12:53:44.000000000 +0200
+++ vdr-2.2.0/libsi/section.c	2006-04-14 12:53:44.000000000 +0200
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: section.c 2.0 2006/04/14 10:53:44 kls Exp $
+ *   $Id: section.c 3.0 2006/04/14 10:53:44 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff -ruN vdr-2.0.0/libsi/section.h vdr-2.2.0/libsi/section.h
--- vdr-2.0.0/libsi/section.h	2012-02-26 14:58:26.000000000 +0100
+++ vdr-2.2.0/libsi/section.h	2012-02-26 14:58:26.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: section.h 2.1 2012/02/26 13:58:26 kls Exp $
+ *   $Id: section.h 3.0 2012/02/26 13:58:26 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff -ruN vdr-2.0.0/libsi/si.c vdr-2.2.0/libsi/si.c
--- vdr-2.0.0/libsi/si.c	2012-09-29 16:44:20.000000000 +0200
+++ vdr-2.2.0/libsi/si.c	2015-02-10 14:42:41.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: si.c 2.8 2012/09/29 14:44:20 kls Exp $
+ *   $Id: si.c 3.3 2015/02/10 13:42:41 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -198,17 +198,18 @@
       }
 }
 
-void DescriptorGroup::Add(GroupDescriptor *d) {
+bool DescriptorGroup::Add(GroupDescriptor *d) {
    if (!array) {
       length=d->getLastDescriptorNumber()+1;
       array=new GroupDescriptor*[length]; //numbering is zero-based
       for (int i=0;i<length;i++)
          array[i]=0;
    } else if (length != d->getLastDescriptorNumber()+1)
-      return; //avoid crash in case of misuse
+      return false; //avoid crash in case of misuse
    if (length <= d->getDescriptorNumber())
-      return; // see http://www.vdr-portal.de/board60-linux/board14-betriebssystem/board69-c-t-vdr/p1025777-segfault-mit-vdr-1-7-21/#post1025777
+      return false; // see http://www.vdr-portal.de/board60-linux/board14-betriebssystem/board69-c-t-vdr/p1025777-segfault-mit-vdr-1-7-21/#post1025777
    array[d->getDescriptorNumber()]=d;
+   return true;
 }
 
 bool DescriptorGroup::isComplete() {
@@ -318,6 +319,14 @@
   return SystemCharacterTableIsSingleByte;
 }
 
+static char *OverrideCharacterTable = NULL;
+
+void SetOverrideCharacterTable(const char *CharacterTable)
+{
+  free(OverrideCharacterTable);
+  OverrideCharacterTable = CharacterTable ? strdup(CharacterTable) : NULL;
+}
+
 bool SetSystemCharacterTable(const char *CharacterTable) {
    if (CharacterTable) {
       for (unsigned int i = 0; i < NumEntries(CharacterTables1); i++) {
@@ -347,9 +356,8 @@
    // Workaround for broadcaster stupidity: according to
    // "ETSI EN 300 468" the default character set is ISO6937. But unfortunately some
    // broadcasters actually use ISO-8859-9, but fail to correctly announce that.
-   static const char *CharsetOverride = getenv("VDR_CHARSET_OVERRIDE");
-   if (CharsetOverride)
-      cs = CharsetOverride;
+   if (OverrideCharacterTable)
+      cs = OverrideCharacterTable;
    if (isSingleByte)
       *isSingleByte = false;
    if (length <= 0)
@@ -508,6 +516,9 @@
          case CarouselIdentifierDescriptorTag:
             d=new CarouselIdentifierDescriptor();
             break;
+         case AVCDescriptorTag:
+            d=new AVCDescriptor();
+            break;
          case NetworkNameDescriptorTag:
             d=new NetworkNameDescriptor();
             break;
@@ -614,6 +625,12 @@
          case ExtensionDescriptorTag:
             d=new ExtensionDescriptor();
             break;
+         case LogicalChannelDescriptorTag:
+            d=new LogicalChannelDescriptor();
+            break;
+         case HdSimulcastLogicalChannelDescriptorTag:
+            d=new HdSimulcastLogicalChannelDescriptor();
+            break;
          case RegistrationDescriptorTag:
             d=new RegistrationDescriptor();
             break;
diff -ruN vdr-2.0.0/libsi/si.h vdr-2.2.0/libsi/si.h
--- vdr-2.0.0/libsi/si.h	2012-10-15 13:56:06.000000000 +0200
+++ vdr-2.2.0/libsi/si.h	2015-02-10 14:54:28.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: si.h 2.6 2012/10/15 11:56:06 kls Exp $
+ *   $Id: si.h 3.4 2015/02/10 13:54:28 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -70,6 +70,10 @@
   // defined by ISO-13818-6 (DSM-CC)
                CarouselIdentifierDescriptorTag = 0x13,
                // 0x14 - 0x3F  Reserved
+  // defined by ISO/IEC 13818-1 Amendment
+               AVCDescriptorTag = 0x28,
+               SVCExtensionDescriptorTag = 0x30,
+               MVCExtensionDescriptorTag = 0x31,
   // defined by ETSI (EN 300 468)
                NetworkNameDescriptorTag = 0x40,
                ServiceListDescriptorTag = 0x41,
@@ -134,6 +138,12 @@
                DTSDescriptorTag = 0x7B,
                AACDescriptorTag = 0x7C,
                ExtensionDescriptorTag = 0x7F,
+ // defined by EICTA/EACEM/DIGITALEUROPE
+               LogicalChannelDescriptorTag = 0x83,
+               PreferredNameListDescriptorTag = 0x84,
+               PreferredNameIdentifierDescriptorTag = 0x85,
+               EacemStreamIdentifierDescriptorTag = 0x86,
+               HdSimulcastLogicalChannelDescriptorTag = 0x88,
  // Extension descriptors
                ImageIconDescriptorTag = 0x00,
                CpcmDeliverySignallingDescriptor = 0x01,
@@ -147,6 +157,12 @@
                TargetRegionDescriptorTag = 0x09,
                TargetRegionNameDescriptorTag = 0x0A,
                ServiceRelocatedDescriptorTag = 0x0B,
+ // defined by ETSI (EN 300 468) v 1.12.1
+               XAITPidDescriptorTag = 0x0C,
+               C2DeliverySystemDescriptorTag = 0x0D,
+               // 0x0E - 0x0F Reserved
+               VideoDepthRangeDescriptorTag = 0x10,
+               T2MIDescriptorTag = 0x11,
 
  // Defined by ETSI TS 102 812 (MHP)
                // They once again start with 0x00 (see page 234, MHP specification)
@@ -194,7 +210,8 @@
                    LinkageTypeRCSMap = 0x07,
                    LinkageTypeMobileHandover = 0x08,
                    LinkageTypeSystemSoftwareUpdateService = 0x09,
-                   LinkageTypeTSContainingSsuBatOrNit = 0x0A
+                   LinkageTypeTSContainingSsuBatOrNit = 0x0A,
+                   LinkageTypePremiere = 0xB0
                  };
 
 enum AudioType { AudioTypeUndefined = 0x00,
@@ -466,7 +483,7 @@
 public:
    DescriptorGroup(bool deleteOnDesctruction=true);
    ~DescriptorGroup();
-   void Add(GroupDescriptor *d);
+   bool Add(GroupDescriptor *d);
    void Delete();
    int getLength() { return length; }
    GroupDescriptor **getDescriptors() { return array; }
@@ -505,6 +522,9 @@
    void decodeText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion);
 };
 
+// Set the character table to use for strings that do not begin with a character
+// table indicator. Call with NULL to turn this off.
+void SetOverrideCharacterTable(const char *CharacterTable);
 // Call this function to set the system character table. CharacterTable is a string
 // like "iso8859-15" or "utf-8" (case insensitive).
 // Returns true if the character table was recognized.
diff -ruN vdr-2.0.0/libsi/util.c vdr-2.2.0/libsi/util.c
--- vdr-2.0.0/libsi/util.c	2006-02-18 12:17:50.000000000 +0100
+++ vdr-2.2.0/libsi/util.c	2006-02-18 12:17:50.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: util.c 2.0 2006/02/18 11:17:50 kls Exp $
+ *   $Id: util.c 3.0 2006/02/18 11:17:50 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff -ruN vdr-2.0.0/libsi/util.h vdr-2.2.0/libsi/util.h
--- vdr-2.0.0/libsi/util.h	2012-02-26 14:58:26.000000000 +0100
+++ vdr-2.2.0/libsi/util.h	2012-02-26 14:58:26.000000000 +0100
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: util.h 2.3 2012/02/26 13:58:26 kls Exp $
+ *   $Id: util.h 3.0 2012/02/26 13:58:26 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff -ruN vdr-2.0.0/lirc.c vdr-2.2.0/lirc.c
--- vdr-2.0.0/lirc.c	2013-02-11 16:25:42.000000000 +0100
+++ vdr-2.2.0/lirc.c	2013-10-29 13:32:12.000000000 +0100
@@ -6,7 +6,7 @@
  *
  * LIRC support added by Carsten Koch <Carsten.Koch@icem.de>  2000-06-16.
  *
- * $Id: lirc.c 2.5 2013/02/11 15:25:42 kls Exp $
+ * $Id: lirc.c 3.2 2013/10/29 12:32:12 kls Exp $
  */
 
 #include "lirc.h"
@@ -21,11 +21,9 @@
 {
   addr.sun_family = AF_UNIX;
   strcpy(addr.sun_path, DeviceName);
-  if (Connect()) {
-     Start();
-     return;
-     }
-  f = -1;
+  if (!Connect())
+     f = -1;
+  Start();
 }
 
 cLircRemote::~cLircRemote()
@@ -67,14 +65,15 @@
   bool repeat = false;
   int timeout = -1;
 
-  while (Running() && f >= 0) {
+  while (Running()) {
 
-        bool ready = cFile::FileReady(f, timeout);
+        bool ready = f >= 0 && cFile::FileReady(f, timeout);
         int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
 
-        if (ready && ret <= 0 ) {
+        if (f < 0 || ready && ret <= 0) {
            esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
-           close(f);
+           if (f >= 0)
+              close(f);
            f = -1;
            while (Running() && f < 0) {
                  cCondWait::SleepMs(RECONNECTDELAY);
@@ -111,6 +110,7 @@
            else if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
               continue; // skip same keys coming in too fast
            else {
+              pressed = true;
               repeat = true;
               timeout = Delta * 10 / 9;
               }
diff -ruN vdr-2.0.0/lirc.h vdr-2.2.0/lirc.h
--- vdr-2.0.0/lirc.h	2006-01-27 17:00:19.000000000 +0100
+++ vdr-2.2.0/lirc.h	2006-01-27 17:00:19.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: lirc.h 2.0 2006/01/27 16:00:19 kls Exp $
+ * $Id: lirc.h 3.0 2006/01/27 16:00:19 kls Exp $
  */
 
 #ifndef __LIRC_H
diff -ruN vdr-2.0.0/menu.c vdr-2.2.0/menu.c
--- vdr-2.0.0/menu.c	2013-03-18 10:11:48.000000000 +0100
+++ vdr-2.2.0/menu.c	2015-02-10 13:37:06.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.c 2.82 2013/03/18 09:11:48 kls Exp $
+ * $Id: menu.c 3.48 2015/02/10 12:37:06 kls Exp $
  */
 
 #include "menu.h"
@@ -181,9 +181,13 @@
      strn0cpy(name, data.name, sizeof(name));
      if (New) {
         channel = NULL;
+        // clear non-editable members:
         data.nid = 0;
         data.tid = 0;
         data.rid = 0;
+        *data.shortName  = 0;
+        *data.provider   = 0;
+        *data.portalName = 0;
         }
      }
   Setup();
@@ -210,9 +214,9 @@
   Add(new cMenuEditIntItem( tr("Tpid"),         &data.tpid,  0, 0x1FFF));
   Add(new cMenuEditCaItem(  tr("CA"),           &data.caids[0]));
   Add(new cMenuEditIntItem( tr("Sid"),          &data.sid, 1, 0xFFFF));
-  /* XXX not yet used
   Add(new cMenuEditIntItem( tr("Nid"),          &data.nid, 0));
   Add(new cMenuEditIntItem( tr("Tid"),          &data.tid, 0));
+  /* XXX not yet used
   Add(new cMenuEditIntItem( tr("Rid"),          &data.rid, 0));
   XXX*/
   // Parameters for specific types of sources:
@@ -383,6 +387,9 @@
             currentItem = item;
          }
       }
+  SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName :
+                  cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider :
+                  msmNumber);
   if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber)
      Sort();
   SetCurrent(currentItem);
@@ -719,9 +726,11 @@
   list = nestedItemList = NestedItemList;
   firstFolder = NULL;
   editing = false;
+  helpKeys = -1;
   Set();
-  SetHelpKeys();
   DescendPath(Path);
+  Display();
+  SetHelpKeys();
 }
 
 cMenuFolder::cMenuFolder(const char *Title, cList<cNestedItem> *List, cNestedItemList *NestedItemList, const char *Dir, const char *Path)
@@ -733,18 +742,72 @@
   dir = Dir;
   firstFolder = NULL;
   editing = false;
+  helpKeys = -1;
   Set();
-  SetHelpKeys();
   DescendPath(Path);
+  Display();
+  SetHelpKeys();
 }
 
 void cMenuFolder::SetHelpKeys(void)
 {
-  SetHelp(firstFolder ? tr("Button$Select") : NULL, tr("Button$New"), firstFolder ? tr("Button$Delete") : NULL, firstFolder ? tr("Button$Edit") : NULL);
+  if (HasSubMenu())
+     return;
+  int NewHelpKeys = 0;
+  if (firstFolder) {
+     if (cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current())) {
+        if (Folder->Folder()->SubItems())
+           NewHelpKeys = 1;
+        }
+     }
+  if (NewHelpKeys != helpKeys) {
+     helpKeys = NewHelpKeys;
+     SetHelp(NewHelpKeys > 0 ? tr("Button$Open") : NULL, tr("Button$New"), firstFolder ? tr("Button$Delete") : NULL, firstFolder ? tr("Button$Edit") : NULL);
+     }
+}
+
+#define FOLDERDELIMCHARSUBST 0x01
+static void AddRecordingFolders(cList<cNestedItem> *List, char *Path)
+{
+  if (Path) {
+     char *p = strchr(Path, FOLDERDELIMCHARSUBST);
+     if (p)
+        *p++ = 0;
+     cNestedItem *Folder;
+     for (Folder = List->First(); Folder; Folder = List->Next(Folder)) {
+         if (strcmp(Path, Folder->Text()) == 0)
+            break;
+         }
+     if (!Folder)
+        List->Add(Folder = new cNestedItem(Path));
+     if (p) {
+        Folder->SetSubItems(true);
+        AddRecordingFolders(Folder->SubItems(), p);
+        }
+     }
+  else {
+     cThreadLock RecordingsLock(&Recordings);
+     cStringList Dirs;
+     for (cRecording *Recording = Recordings.First(); Recording; Recording = Recordings.Next(Recording)) {
+         cString Folder = Recording->Folder();
+         strreplace((char *)*Folder, FOLDERDELIMCHAR, FOLDERDELIMCHARSUBST); // makes sure parent folders come before subfolders
+         if (Dirs.Find(Folder) < 0)
+            Dirs.Append(strdup(Folder));
+         }
+     Dirs.Sort();
+     for (int i = 0; i < Dirs.Size(); i++) {
+         char *s = Dirs[i];
+         if (*s)
+            AddRecordingFolders(&Folders, s);
+         }
+     }
 }
 
 void cMenuFolder::Set(const char *CurrentFolder)
 {
+  static int RecordingsState = -1;
+  if (list == &Folders && Recordings.StateChanged(RecordingsState))
+     AddRecordingFolders(&Folders, NULL);
   firstFolder = NULL;
   Clear();
   if (!isempty(dir)) {
@@ -769,7 +832,7 @@
         for (cMenuFolderItem *Folder = (cMenuFolderItem *)firstFolder; Folder; Folder = (cMenuFolderItem *)Next(Folder)) {
             if (strncmp(Folder->Folder()->Text(), Path, p - Path) == 0) {
                SetCurrent(Folder);
-               if (Folder->Folder()->SubItems())
+               if (Folder->Folder()->SubItems() && strchr(p + 1, FOLDERDELIMCHAR))
                   AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text(), p + 1));
                break;
                }
@@ -778,12 +841,12 @@
     }
 }
 
-eOSState cMenuFolder::Select(void)
+eOSState cMenuFolder::Select(bool Open)
 {
   if (firstFolder) {
      cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
      if (Folder) {
-        if (Folder->Folder()->SubItems())
+        if (Open && Folder->Folder()->SubItems())
            return AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text()));
         else
            return osEnd;
@@ -828,8 +891,7 @@
 
 eOSState cMenuFolder::SetFolder(void)
 {
-  cMenuEditFolder *mef = (cMenuEditFolder *)SubMenu();
-  if (mef) {
+  if (cMenuEditFolder *mef = dynamic_cast<cMenuEditFolder *>(SubMenu())) {
      Set(mef->GetFolder());
      SetHelpKeys();
      Display();
@@ -843,8 +905,7 @@
   if (firstFolder) {
      cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
      if (Folder) {
-        cMenuFolder *mf = (cMenuFolder *)SubMenu();
-        if (mf)
+        if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu()))
            return cString::sprintf("%s%c%s", Folder->Folder()->Text(), FOLDERDELIMCHAR, *mf->GetFolder());
         return Folder->Folder()->Text();
         }
@@ -860,8 +921,8 @@
 
   if (state == osUnknown) {
      switch (Key) {
-       case kOk:
-       case kRed:    return Select();
+       case kOk:     return Select(false);
+       case kRed:    return Select(true);
        case kGreen:  return New();
        case kYellow: return Delete();
        case kBlue:   return Edit();
@@ -870,6 +931,7 @@
      }
   else if (state == osEnd && HasSubMenu() && editing)
      state = SetFolder();
+  SetHelpKeys();
   return state;
 }
 
@@ -930,8 +992,7 @@
 
 eOSState cMenuEditTimer::SetFolder(void)
 {
-  cMenuFolder *mf = (cMenuFolder *)SubMenu();
-  if (mf) {
+  if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
      cString Folder = mf->GetFolder();
      char *p = strrchr(data.file, FOLDERDELIMCHAR);
      if (p)
@@ -965,7 +1026,7 @@
                        if (!*data.file)
                           strcpy(data.file, data.Channel()->ShortName(true));
                        if (timer) {
-                          if (memcmp(timer, &data, sizeof(data)) != 0)
+                          if (memcmp((void *)timer, &data, sizeof(data)) != 0)
                              *timer = data;
                           if (addIfConfirmed)
                              Timers.Add(timer);
@@ -1356,6 +1417,7 @@
 class cMenuWhatsOn : public cOsdMenu {
 private:
   bool now;
+  bool canSwitch;
   int helpKeys;
   int timerState;
   eOSState Record(void);
@@ -1380,7 +1442,8 @@
 {
   SetMenuCategory(Now ? mcScheduleNow : mcScheduleNext);
   now = Now;
-  helpKeys = -1;
+  canSwitch = false;
+  helpKeys = 0;
   timerState = 0;
   Timers.Modified(timerState);
   for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
@@ -1413,16 +1476,27 @@
 void cMenuWhatsOn::SetHelpKeys(void)
 {
   cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
+  canSwitch = false;
   int NewHelpKeys = 0;
   if (item) {
      if (item->timerMatch == tmFull)
-        NewHelpKeys = 2;
+        NewHelpKeys |= 0x02; // "Timer"
      else
-        NewHelpKeys = 1;
+        NewHelpKeys |= 0x01; // "Record"
+     if (now)
+        NewHelpKeys |= 0x04; // "Next"
+     else
+        NewHelpKeys |= 0x08; // "Now"
+     if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) {
+        if (Channel->Number() != cDevice::CurrentChannel()) {
+           NewHelpKeys |= 0x10; // "Switch"
+           canSwitch = true;
+           }
+        }
      }
   if (NewHelpKeys != helpKeys) {
      const char *Red[] = { NULL, tr("Button$Record"), tr("Button$Timer") };
-     SetHelp(Red[NewHelpKeys], now ? tr("Button$Next") : tr("Button$Now"), tr("Button$Schedule"), tr("Button$Switch"));
+     SetHelp(Red[NewHelpKeys & 0x03], now ? tr("Button$Next") : tr("Button$Now"), tr("Button$Schedule"), canSwitch ? tr("Button$Switch") : NULL);
      helpKeys = NewHelpKeys;
      }
 }
@@ -1498,10 +1572,12 @@
                           }
                      }
                      break;
-       case kBlue:   return Switch();
+       case kBlue:   if (canSwitch)
+                        return Switch();
+                     break;
        case kInfo:
        case kOk:     if (Count())
-                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, true, true));
+                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true));
                      break;
        default:      break;
        }
@@ -1522,7 +1598,7 @@
   cSchedulesLock schedulesLock;
   const cSchedules *schedules;
   bool now, next;
-  int otherChannel;
+  bool canSwitch;
   int helpKeys;
   int timerState;
   eOSState Number(void);
@@ -1545,8 +1621,8 @@
 {
   SetMenuCategory(mcSchedule);
   now = next = false;
-  otherChannel = 0;
-  helpKeys = -1;
+  canSwitch = false;
+  helpKeys = 0;
   timerState = 0;
   Timers.Modified(timerState);
   cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
@@ -1652,16 +1728,23 @@
 void cMenuSchedule::SetHelpKeys(void)
 {
   cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
+  canSwitch = false;
   int NewHelpKeys = 0;
   if (item) {
      if (item->timerMatch == tmFull)
-        NewHelpKeys = 2;
+        NewHelpKeys |= 0x02; // "Timer"
      else
-        NewHelpKeys = 1;
+        NewHelpKeys |= 0x01; // "Record"
+     if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) {
+        if (Channel->Number() != cDevice::CurrentChannel()) {
+           NewHelpKeys |= 0x10; // "Switch"
+           canSwitch = true;
+           }
+        }
      }
   if (NewHelpKeys != helpKeys) {
      const char *Red[] = { NULL, tr("Button$Record"), tr("Button$Timer") };
-     SetHelp(Red[NewHelpKeys], tr("Button$Now"), tr("Button$Next"));
+     SetHelp(Red[NewHelpKeys & 0x03], tr("Button$Now"), tr("Button$Next"), canSwitch ? tr("Button$Switch") : NULL);
      helpKeys = NewHelpKeys;
      }
 }
@@ -1727,9 +1810,12 @@
 
 eOSState cMenuSchedule::Switch(void)
 {
-  if (otherChannel) {
-     if (Channels.SwitchTo(otherChannel))
-        return osEnd;
+  cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
+  if (item) {
+     if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) {
+        if (Channels.SwitchTo(Channel->Number()))
+           return osEnd;
+        }
      }
   Skins.Message(mtError, tr("Can't switch channel!"));
   return osContinue;
@@ -1763,12 +1849,12 @@
        case kYellow: if (schedules)
                         return AddSubMenu(new cMenuWhatsOn(schedules, false, cMenuWhatsOn::CurrentChannel()));
                      break;
-       case kBlue:   if (Count() && otherChannel)
+       case kBlue:   if (canSwitch)
                         return Switch();
                      break;
        case kInfo:
        case kOk:     if (Count())
-                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, otherChannel, true));
+                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true));
                      break;
        default:      break;
        }
@@ -1781,10 +1867,6 @@
         if (channel) {
            cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
            PrepareScheduleAllThis(NULL, channel);
-           if (channel->Number() != cDevice::CurrentChannel()) {
-              otherChannel = channel->Number();
-              SetHelp(Count() ? tr("Button$Record") : NULL, tr("Button$Now"), tr("Button$Next"), tr("Button$Switch"));
-              }
            Display();
            }
         }
@@ -2105,30 +2187,378 @@
   return CamMenuIsOpen;
 }
 
+// --- cMenuPathEdit ---------------------------------------------------------
+
+class cMenuPathEdit : public cOsdMenu {
+private:
+  cString path;
+  char folder[PATH_MAX];
+  char name[NAME_MAX];
+  cMenuEditStrItem *folderItem;
+  int pathIsInUse;
+  eOSState SetFolder(void);
+  eOSState Folder(void);
+  eOSState ApplyChanges(void);
+public:
+  cMenuPathEdit(const char *Path);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuPathEdit::cMenuPathEdit(const char *Path)
+:cOsdMenu(tr("Edit path"), 12)
+{
+  SetMenuCategory(mcRecordingEdit);
+  path = Path;
+  *folder = 0;
+  *name = 0;
+  const char *s = strrchr(path, FOLDERDELIMCHAR);
+  if (s) {
+     strn0cpy(folder, cString(path, s), sizeof(folder));
+     s++;
+     }
+  else
+     s = path;
+  strn0cpy(name, s, sizeof(name));
+  pathIsInUse = Recordings.PathIsInUse(path);
+  cOsdItem *p;
+  Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder)));
+  p->SetSelectable(!pathIsInUse);
+  Add(p = new cMenuEditStrItem(tr("Name"), name, sizeof(name)));
+  p->SetSelectable(!pathIsInUse);
+  if (pathIsInUse) {
+     Add(new cOsdItem("", osUnknown, false));
+     Add(new cOsdItem(tr("This folder is currently in use - no changes are possible!"), osUnknown, false));
+     }
+  Display();
+  if (!pathIsInUse)
+     SetHelp(tr("Button$Folder"));
+}
+
+eOSState cMenuPathEdit::SetFolder(void)
+{
+  if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
+     strn0cpy(folder, mf->GetFolder(), sizeof(folder));
+     SetCurrent(folderItem);
+     Display();
+     }
+  return CloseSubMenu();
+}
+
+eOSState cMenuPathEdit::Folder(void)
+{
+  return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, path));
+}
+
+eOSState cMenuPathEdit::ApplyChanges(void)
+{
+  if (!*name) {
+     *name = ' '; // name must not be empty!
+     name[1] = 0;
+     }
+  cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
+  NewPath.CompactChars(FOLDERDELIMCHAR);
+  if (strcmp(NewPath, path)) {
+     int NumRecordings = Recordings.GetNumRecordingsInPath(path);
+     if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings)))
+        return osContinue;
+     if (!Recordings.MoveRecordings(path, NewPath)) {
+        Skins.Message(mtError, tr("Error while moving folder!"));
+        return osContinue;
+        }
+     cMenuRecordings::SetPath(NewPath); // makes sure the Recordings menu will reposition to the new path
+     return osUser1;
+     }
+  return osBack;
+}
+
+eOSState cMenuPathEdit::ProcessKey(eKeys Key)
+{
+  eOSState state = cOsdMenu::ProcessKey(Key);
+  if (state == osUnknown) {
+     if (!pathIsInUse) {
+        switch (Key) {
+          case kRed: return Folder();
+          case kOk:  return ApplyChanges();
+          default: break;
+          }
+        }
+     else if (Key == kOk)
+        return osBack;
+     }
+  else if (state == osEnd && HasSubMenu())
+     state = SetFolder();
+  return state;
+}
+
+// --- cMenuRecordingEdit ----------------------------------------------------
+
+class cMenuRecordingEdit : public cOsdMenu {
+private:
+  cRecording *recording;
+  cString originalFileName;
+  int recordingsState;
+  char folder[PATH_MAX];
+  char name[NAME_MAX];
+  int priority;
+  int lifetime;
+  cMenuEditStrItem *folderItem;
+  cMenuEditStrItem *nameItem;
+  const char *buttonFolder;
+  const char *buttonAction;
+  const char *buttonDeleteMarks;
+  const char *actionCancel;
+  const char *doCut;
+  int recordingIsInUse;
+  void Set(void);
+  void SetHelpKeys(void);
+  bool RefreshRecording(void);
+  eOSState SetFolder(void);
+  eOSState Folder(void);
+  eOSState Action(void);
+  eOSState RemoveName(void);
+  eOSState DeleteMarks(void);
+  eOSState ApplyChanges(void);
+public:
+  cMenuRecordingEdit(cRecording *Recording);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuRecordingEdit::cMenuRecordingEdit(cRecording *Recording)
+:cOsdMenu(tr("Edit recording"), 12)
+{
+  SetMenuCategory(mcRecordingEdit);
+  recording = Recording;
+  originalFileName = recording->FileName();
+  Recordings.StateChanged(recordingsState); // just to get the current state
+  strn0cpy(folder, recording->Folder(), sizeof(folder));
+  strn0cpy(name, recording->BaseName(), sizeof(name));
+  priority = recording->Priority();
+  lifetime = recording->Lifetime();
+  folderItem = NULL;
+  nameItem = NULL;
+  buttonFolder = NULL;
+  buttonAction = NULL;
+  buttonDeleteMarks = NULL;
+  actionCancel = NULL;
+  doCut = NULL;
+  recordingIsInUse = ruNone;
+  Set();
+}
+
+void cMenuRecordingEdit::Set(void)
+{
+  int current = Current();
+  Clear();
+  recordingIsInUse = recording->IsInUse();
+  cOsdItem *p;
+  Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder)));
+  p->SetSelectable(!recordingIsInUse);
+  Add(p = nameItem = new cMenuEditStrItem(tr("Name"), name, sizeof(name)));
+  p->SetSelectable(!recordingIsInUse);
+  Add(p = new cMenuEditIntItem(tr("Priority"), &priority, 0, MAXPRIORITY));
+  p->SetSelectable(!recordingIsInUse);
+  Add(p = new cMenuEditIntItem(tr("Lifetime"), &lifetime, 0, MAXLIFETIME));
+  p->SetSelectable(!recordingIsInUse);
+  if (recordingIsInUse) {
+     Add(new cOsdItem("", osUnknown, false));
+     Add(new cOsdItem(tr("This recording is currently in use - no changes are possible!"), osUnknown, false));
+     }
+  SetCurrent(Get(current));
+  Display();
+  SetHelpKeys();
+}
+
+void cMenuRecordingEdit::SetHelpKeys(void)
+{
+  buttonFolder = !recordingIsInUse ? tr("Button$Folder") : NULL;
+  buttonAction = NULL;
+  buttonDeleteMarks = NULL;
+  actionCancel = NULL;
+  doCut = NULL;
+  if ((recordingIsInUse & ruCut) != 0)
+     buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel cutting") : tr("Button$Stop cutting");
+  else if ((recordingIsInUse & ruMove) != 0)
+     buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel moving") : tr("Button$Stop moving");
+  else if ((recordingIsInUse & ruCopy) != 0)
+     buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel copying") : tr("Button$Stop copying");
+  else if (recording->HasMarks()) {
+     buttonAction = doCut = tr("Button$Cut");
+     buttonDeleteMarks = tr("Button$Delete marks");
+     }
+  SetHelp(buttonFolder, buttonAction, buttonDeleteMarks);
+}
+
+bool cMenuRecordingEdit::RefreshRecording(void)
+{
+  if (Recordings.StateChanged(recordingsState)) {
+     if ((recording = Recordings.GetByName(originalFileName)) != NULL)
+        Set();
+     else {
+        Skins.Message(mtWarning, tr("Recording vanished!"));
+        return false;
+        }
+     }
+  return true;
+}
+
+eOSState cMenuRecordingEdit::SetFolder(void)
+{
+  if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
+     strn0cpy(folder, mf->GetFolder(), sizeof(folder));
+     SetCurrent(folderItem);
+     Display();
+     }
+  return CloseSubMenu();
+}
+
+eOSState cMenuRecordingEdit::Folder(void)
+{
+  return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, recording->Name()));
+}
+
+eOSState cMenuRecordingEdit::Action(void)
+{
+  if (actionCancel)
+     RecordingsHandler.Del(recording->FileName());
+  else if (doCut) {
+     if (access(cCutter::EditedFileName(recording->FileName()), F_OK) != 0 || Interface->Confirm(tr("Edited version already exists - overwrite?"))) {
+        if (!RecordingsHandler.Add(ruCut, recording->FileName()))
+           Skins.Message(mtError, tr("Error while queueing recording for cutting!"));
+        }
+     }
+  recordingIsInUse = recording->IsInUse();
+  RefreshRecording();
+  SetHelpKeys();
+  return osContinue;
+}
+
+eOSState cMenuRecordingEdit::RemoveName(void)
+{
+  if (Get(Current()) == nameItem) {
+     if (Interface->Confirm(tr("Rename recording to folder name?"))) {
+        char *s = strrchr(folder, FOLDERDELIMCHAR);
+        if (s)
+           *s++ = 0;
+        else
+           s = folder;
+        strn0cpy(name, s, sizeof(name));
+        if (s == folder)
+           *s = 0;
+        Set();
+        }
+     }
+  return osContinue;
+}
+
+eOSState cMenuRecordingEdit::DeleteMarks(void)
+{
+  if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) {
+     if (recording->DeleteMarks())
+        SetHelpKeys();
+     else
+        Skins.Message(mtError, tr("Error while deleting editing marks!"));
+     }
+  return osContinue;
+}
+
+eOSState cMenuRecordingEdit::ApplyChanges(void)
+{
+  bool Modified = false;
+  if (priority != recording->Priority() || lifetime != recording->Lifetime()) {
+     if (!recording->ChangePriorityLifetime(priority, lifetime)) {
+        Skins.Message(mtError, tr("Error while changing priority/lifetime!"));
+        return osContinue;
+        }
+     Modified = true;
+     }
+  if (!*name) {
+     *name = ' '; // name must not be empty!
+     name[1] = 0;
+     }
+  cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
+  NewName.CompactChars(FOLDERDELIMCHAR);
+  if (strcmp(NewName, recording->Name())) {
+     if (!recording->ChangeName(NewName)) {
+        Skins.Message(mtError, tr("Error while changing folder/name!"));
+        return osContinue;
+        }
+     Modified = true;
+     }
+  if (Modified) {
+     cMenuRecordings::SetRecording(recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording
+     return osUser1;
+     }
+  return osBack;
+}
+
+eOSState cMenuRecordingEdit::ProcessKey(eKeys Key)
+{
+  if (!HasSubMenu()) {
+     if (!RefreshRecording())
+        return osBack; // the recording has vanished, so close this menu
+     }
+  eOSState state = cOsdMenu::ProcessKey(Key);
+  if (state == osUnknown) {
+     switch (Key) {
+       case k0:      return RemoveName();
+       case kRed:    return buttonFolder ? Folder() : osContinue;
+       case kGreen:  return buttonAction ? Action() : osContinue;
+       case kYellow: return buttonDeleteMarks ? DeleteMarks() : osContinue;
+       case kOk:     return !recordingIsInUse ? ApplyChanges() : osBack;
+       default: break;
+       }
+     }
+  else if (state == osEnd && HasSubMenu())
+     state = SetFolder();
+  return state;
+}
+
 // --- cMenuRecording --------------------------------------------------------
 
 class cMenuRecording : public cOsdMenu {
 private:
-  const cRecording *recording;
+  cRecording *recording;
+  cString originalFileName;
+  int recordingsState;
   bool withButtons;
+  bool RefreshRecording(void);
 public:
-  cMenuRecording(const cRecording *Recording, bool WithButtons = false);
+  cMenuRecording(cRecording *Recording, bool WithButtons = false);
   virtual void Display(void);
   virtual eOSState ProcessKey(eKeys Key);
 };
 
-cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
+cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons)
 :cOsdMenu(tr("Recording info"))
 {
   SetMenuCategory(mcRecordingInfo);
   recording = Recording;
+  originalFileName = recording->FileName();
+  Recordings.StateChanged(recordingsState); // just to get the current state
   withButtons = WithButtons;
   if (withButtons)
-     SetHelp(tr("Button$Play"), tr("Button$Rewind"));
+     SetHelp(tr("Button$Play"), tr("Button$Rewind"), NULL, tr("Button$Edit"));
+}
+
+bool cMenuRecording::RefreshRecording(void)
+{
+  if (Recordings.StateChanged(recordingsState)) {
+     if ((recording = Recordings.GetByName(originalFileName)) != NULL)
+        Display();
+     else {
+        Skins.Message(mtWarning, tr("Recording vanished!"));
+        return false;
+        }
+     }
+  return true;
 }
 
 void cMenuRecording::Display(void)
 {
+  if (HasSubMenu()) {
+     SubMenu()->Display();
+     return;
+     }
   cOsdMenu::Display();
   DisplayMenu()->SetRecording(recording);
   if (recording->Info()->Description())
@@ -2137,6 +2567,10 @@
 
 eOSState cMenuRecording::ProcessKey(eKeys Key)
 {
+  if (HasSubMenu())
+     return cOsdMenu::ProcessKey(Key);
+  else if (!RefreshRecording())
+     return osBack; // the recording has vanished, so close this menu
   switch (int(Key)) {
     case kUp|k_Repeat:
     case kUp:
@@ -2164,6 +2598,9 @@
                      cRemote::Put(Key, true);
                      // continue with osBack to close the info menu and process the key
        case kOk:     return osBack;
+       case kBlue:   if (withButtons)
+                        return AddSubMenu(new cMenuRecordingEdit(recording));
+                     break;
        default: break;
        }
      }
@@ -2183,8 +2620,10 @@
   ~cMenuRecordingItem();
   void IncrementCounter(bool New);
   const char *Name(void) { return name; }
+  int Level(void) { return level; }
   cRecording *Recording(void) { return recording; }
   bool IsDirectory(void) { return name != NULL; }
+  void SetRecording(cRecording *Recording) { recording = Recording; }
   virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable);
   };
 
@@ -2220,27 +2659,38 @@
 
 // --- cMenuRecordings -------------------------------------------------------
 
-cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
+cString cMenuRecordings::path;
+cString cMenuRecordings::fileName;
+
+cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus, const cRecordingFilter *Filter)
 :cOsdMenu(Base ? Base : tr("Recordings"), 9, 6, 6)
 {
   SetMenuCategory(mcRecording);
   base = Base ? strdup(Base) : NULL;
   level = Setup.RecordingDirs ? Level : -1;
+  filter = Filter;
   Recordings.StateChanged(recordingsState); // just to get the current state
   helpKeys = -1;
   Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
   Set();
   if (Current() < 0)
      SetCurrent(First());
-  else if (OpenSubMenus && cReplayControl::LastReplayed() && Open(true))
-     return;
+  else if (OpenSubMenus && (cReplayControl::LastReplayed() || *path || *fileName)) {
+     if (!*path || Level < strcountchr(path, FOLDERDELIMCHAR)) {
+        if (Open(true))
+           return;
+        }
+     }
   Display();
   SetHelpKeys();
 }
 
 cMenuRecordings::~cMenuRecordings()
 {
-  helpKeys = -1;
+  if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) {
+     if (!ri->IsDirectory())
+        SetRecording(ri->Recording()->FileName());
+     }
   free(base);
 }
 
@@ -2251,18 +2701,14 @@
   if (ri) {
      if (ri->IsDirectory())
         NewHelpKeys = 1;
-     else {
+     else
         NewHelpKeys = 2;
-        if (ri->Recording()->Info()->Title())
-           NewHelpKeys = 3;
-        }
      }
   if (NewHelpKeys != helpKeys) {
      switch (NewHelpKeys) {
        case 0: SetHelp(NULL); break;
-       case 1: SetHelp(tr("Button$Open")); break;
-       case 2:
-       case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), NewHelpKeys == 3 ? tr("Button$Info") : NULL);
+       case 1: SetHelp(tr("Button$Open"), NULL, NULL, tr("Button$Edit")); break;
+       case 2: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), tr("Button$Info"));
        default: ;
        }
      helpKeys = NewHelpKeys;
@@ -2271,7 +2717,7 @@
 
 void cMenuRecordings::Set(bool Refresh)
 {
-  const char *CurrentRecording = cReplayControl::LastReplayed();
+  const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed();
   cMenuRecordingItem *LastItem = NULL;
   cThreadLock RecordingsLock(&Recordings);
   if (Refresh) {
@@ -2282,7 +2728,7 @@
   GetRecordingsSortMode(DirectoryName());
   Recordings.Sort();
   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
-      if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
+      if ((!filter || filter->Filter(recording)) && (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) {
          cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
          cMenuRecordingItem *LastDir = NULL;
          if (Item->IsDirectory()) {
@@ -2302,23 +2748,36 @@
             }
          else
             delete Item;
-         if (LastItem) {
-            if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0)
-               SetCurrent(LastItem);
+         if (LastItem || LastDir) {
+            if (*path) {
+               if (strcmp(path, recording->Folder()) == 0)
+                  SetCurrent(LastDir ? LastDir : LastItem);
+               }
+            else if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0)
+               SetCurrent(LastDir ? LastDir : LastItem);
             }
-         if (LastDir) {
+         if (LastDir)
             LastDir->IncrementCounter(recording->IsNew());
-            LastItem = LastDir;
-            }
          }
       }
+  SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime);
   if (Refresh)
      Display();
 }
 
+void cMenuRecordings::SetPath(const char *Path)
+{
+  path = Path;
+}
+
+void cMenuRecordings::SetRecording(const char *FileName)
+{
+  fileName = FileName;
+}
+
 cString cMenuRecordings::DirectoryName(void)
 {
-  cString d(VideoDirectory);
+  cString d(cVideoDirectory::Name());
   if (base) {
      char *s = ExchangeChars(strdup(base), true);
      d = AddDirectory(d, s);
@@ -2330,14 +2789,14 @@
 bool cMenuRecordings::Open(bool OpenSubMenus)
 {
   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
-  if (ri && ri->IsDirectory()) {
+  if (ri && ri->IsDirectory() && (!*path || strcountchr(path, FOLDERDELIMCHAR) > 0)) {
      const char *t = ri->Name();
      cString buffer;
      if (base) {
-        buffer = cString::sprintf("%s~%s", base, t);
+        buffer = cString::sprintf("%s%c%s", base, FOLDERDELIMCHAR, t);
         t = buffer;
         }
-     AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus));
+     AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus, filter));
      return true;
      }
   return false;
@@ -2397,10 +2856,10 @@
            }
         cRecording *recording = ri->Recording();
         cString FileName = recording->FileName();
-        if (cCutter::Active(ri->Recording()->FileName())) {
+        if (RecordingsHandler.GetUsage(FileName)) {
            if (Interface->Confirm(tr("Recording is being edited - really delete?"))) {
-              cCutter::Stop();
-              recording = Recordings.GetByName(FileName); // cCutter::Stop() might have deleted it if it was the edited version
+              RecordingsHandler.Del(FileName);
+              recording = Recordings.GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version
               // we continue with the code below even if recording is NULL,
               // in order to have the menu updated etc.
               }
@@ -2418,6 +2877,7 @@
            Display();
            if (!Count())
               return osBack;
+           return osUser2;
            }
         else
            Skins.Message(mtError, tr("Error while deleting recording!"));
@@ -2430,9 +2890,12 @@
 {
   if (HasSubMenu() || Count() == 0)
      return osContinue;
-  cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
-  if (ri && !ri->IsDirectory() && ri->Recording()->Info()->Title())
-     return AddSubMenu(new cMenuRecording(ri->Recording(), true));
+  if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) {
+     if (ri->IsDirectory())
+        return AddSubMenu(new cMenuPathEdit(cString(ri->Recording()->Name(), strchrn(ri->Recording()->Name(), FOLDERDELIMCHAR, ri->Level() + 1))));
+     else
+        return AddSubMenu(new cMenuRecording(ri->Recording(), true));
+     }
   return osContinue;
 }
 
@@ -2483,6 +2946,25 @@
        default: break;
        }
      }
+  else if (state == osUser1) {
+     // a recording or path was renamed, so let's refresh the menu
+     CloseSubMenu(false);
+     if (base)
+        return state; // closes all recording menus except for the top one
+     Set(); // this is the top level menu, so we refresh it...
+     Open(true); // ...and open any necessary submenus to show the new name
+     Display();
+     path = NULL;
+     fileName = NULL;
+     }
+  else if (state == osUser2) {
+     // a recording in a sub folder was deleted, so update the current item
+     cOsdMenu *m = HasSubMenu() ? SubMenu() : this;
+     if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) {
+        if (cMenuRecordingItem *riSub = (cMenuRecordingItem *)m->Get(m->Current()))
+           ri->SetRecording(riSub->Recording());
+        }
+     }
   if (Key == kYellow && HadSubMenu && !HasSubMenu()) {
      // the last recording in a subdirectory was deleted, so let's go back up
      cOsdMenu::Del(Current());
@@ -2650,7 +3132,7 @@
         ModifiedAppearance = true;
      if (strcmp(data.FontFix, Setup.FontFix) || !DoubleEqual(data.FontFixSizeP, Setup.FontFixSizeP))
         ModifiedAppearance = true;
-     if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst)
+     if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs)
         Recordings.ClearSortNames();
      }
 
@@ -2786,7 +3268,7 @@
   void Setup(void);
   const char *videoDisplayFormatTexts[3];
   const char *updateChannelsTexts[6];
-  const char *standardComplianceTexts[2];
+  const char *standardComplianceTexts[3];
 public:
   cMenuSetupDVB(void);
   virtual eOSState ProcessKey(eKeys Key);
@@ -2812,6 +3294,7 @@
   updateChannelsTexts[5] = tr("add new transponders");
   standardComplianceTexts[0] = "DVB";
   standardComplianceTexts[1] = "ANSI/SCTE";
+  standardComplianceTexts[2] = "NORDIG";
 
   SetSection(tr("DVB"));
   SetHelp(NULL, tr("Button$Audio"), tr("Button$Subtitles"), NULL);
@@ -2825,7 +3308,7 @@
   Clear();
 
   Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
-  Add(new cMenuEditStraItem(tr("Setup.DVB$Standard compliance"),   &data.StandardCompliance, 2, standardComplianceTexts));
+  Add(new cMenuEditStraItem(tr("Setup.DVB$Standard compliance"),   &data.StandardCompliance, 3, standardComplianceTexts));
   Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"),          &data.VideoFormat, "4:3", "16:9"));
   if (data.VideoFormat == 0)
      Add(new cMenuEditStraItem(tr("Setup.DVB$Video display format"), &data.VideoDisplayFormat, 3, videoDisplayFormatTexts));
@@ -2973,6 +3456,14 @@
          }
      }
 
+  Add(new cMenuEditBoolItem(tr("Setup.LNB$Use dish positioner"), &data.UsePositioner));
+  if (data.UsePositioner) {
+     Add(new cMenuEditIntxItem(tr("Setup.LNB$Site latitude (degrees)"), &data.SiteLat, -900, 900, 10, tr("South"), tr("North")));
+     Add(new cMenuEditIntxItem(tr("Setup.LNB$Site longitude (degrees)"), &data.SiteLon, -1800, 1800, 10, tr("West"), tr("East")));
+     Add(new cMenuEditIntxItem(tr("Setup.LNB$Max. positioner swing (degrees)"), &data.PositionerSwing, 0, 900, 10));
+     Add(new cMenuEditIntxItem(tr("Setup.LNB$Positioner speed (degrees/s)"), &data.PositionerSpeed, 1, 1800, 10));
+     }
+
   SetCurrent(Get(current));
   Display();
 }
@@ -2980,6 +3471,7 @@
 eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
 {
   int oldDiSEqC = data.DiSEqC;
+  int oldUsePositioner = data.UsePositioner;
   bool DeviceBondingsChanged = false;
   if (Key == kOk) {
      cString NewDeviceBondings = satCableNumbers.ToString();
@@ -2988,7 +3480,7 @@
      }
   eOSState state = cMenuSetupBase::ProcessKey(Key);
 
-  if (Key != kNone && data.DiSEqC != oldDiSEqC)
+  if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner))
      Setup();
   else if (DeviceBondingsChanged)
      cDvbDevice::BondDevices(data.DeviceBondings);
@@ -3015,7 +3507,7 @@
 
 bool cMenuSetupCAMItem::Changed(void)
 {
-  char buffer[32];
+  const char *Activating = "";
   const char *CamName = camSlot->GetCamName();
   if (!CamName) {
      switch (camSlot->ModuleStatus()) {
@@ -3025,7 +3517,10 @@
        default:        CamName = "-"; break;
        }
      }
-  snprintf(buffer, sizeof(buffer), " %d %s", camSlot->SlotNumber(), CamName);
+  else if (camSlot->IsActivating())
+     // TRANSLATORS: note the leading blank!
+     Activating = tr(" (activating)");
+  cString buffer = cString::sprintf(" %d %s%s", camSlot->SlotNumber(), CamName, Activating);
   if (strcmp(buffer, Text()) != 0) {
      SetText(buffer);
      return true;
@@ -3035,8 +3530,11 @@
 
 class cMenuSetupCAM : public cMenuSetupBase {
 private:
+  const char *activationHelp;
   eOSState Menu(void);
   eOSState Reset(void);
+  eOSState Activate(void);
+  void SetHelpKeys(void);
 public:
   cMenuSetupCAM(void);
   virtual eOSState ProcessKey(eKeys Key);
@@ -3044,13 +3542,33 @@
 
 cMenuSetupCAM::cMenuSetupCAM(void)
 {
+  activationHelp = NULL;
   SetMenuCategory(mcSetupCam);
   SetSection(tr("CAM"));
   SetCols(15);
   SetHasHotkeys();
   for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
       Add(new cMenuSetupCAMItem(CamSlot));
-  SetHelp(tr("Button$Menu"), tr("Button$Reset"));
+  SetHelpKeys();
+}
+
+void cMenuSetupCAM::SetHelpKeys(void)
+{
+  if (HasSubMenu())
+     return;
+  cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
+  const char *NewActivationHelp = "";
+  if (item) {
+     cCamSlot *CamSlot = item->CamSlot();
+     if (CamSlot->IsActivating())
+        NewActivationHelp = tr("Button$Cancel activation");
+     else if (CamSlot->CanActivate())
+        NewActivationHelp = tr("Button$Activate");
+     }
+  if (NewActivationHelp != activationHelp) {
+     activationHelp = NewActivationHelp;
+     SetHelp(tr("Button$Menu"), tr("Button$Reset"), activationHelp);
+     }
 }
 
 eOSState cMenuSetupCAM::Menu(void)
@@ -3080,6 +3598,43 @@
   return osContinue;
 }
 
+eOSState cMenuSetupCAM::Activate(void)
+{
+  cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
+  if (item) {
+     cCamSlot *CamSlot = item->CamSlot();
+     if (CamSlot->IsActivating())
+        CamSlot->CancelActivation();
+     else if (CamSlot->CanActivate()) {
+        if (CamSlot->Priority() < LIVEPRIORITY) { // don't interrupt recordings
+           if (cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel())) {
+              for (int i = 0; i < cDevice::NumDevices(); i++) {
+                  if (cDevice *Device = cDevice::GetDevice(i)) {
+                     if (Device->ProvidesChannel(Channel)) {
+                        if (Device->Priority() < LIVEPRIORITY) { // don't interrupt recordings
+                           if (CamSlot->CanActivate()) {
+                              if (CamSlot->Assign(Device, true)) { // query
+                                 cControl::Shutdown(); // must end transfer mode before assigning CAM, otherwise it might be unassigned again
+                                 if (CamSlot->Assign(Device)) {
+                                    if (Device->SwitchChannel(Channel, true)) {
+                                       CamSlot->StartActivation();
+                                       return osContinue;
+                                       }
+                                    }
+                                 }
+                              }
+                           }
+                        }
+                     }
+                  }
+              }
+           }
+        Skins.Message(mtError, tr("Can't activate CAM!"));
+        }
+     }
+  return osContinue;
+}
+
 eOSState cMenuSetupCAM::Reset(void)
 {
   cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
@@ -3101,12 +3656,14 @@
        case kOk:
        case kRed:    return Menu();
        case kGreen:  state = Reset(); break;
+       case kYellow: state = Activate(); break;
        default: break;
        }
      for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
          if (ci->Changed())
             DisplayItem(ci);
          }
+     SetHelpKeys();
      }
   return state;
 }
@@ -3167,6 +3724,15 @@
   Add(new cMenuEditBoolItem(tr("Setup.Replay$Show remaining time"), &data.ShowRemainingTime));
   Add(new cMenuEditIntItem( tr("Setup.Replay$Progress display time (s)"), &data.ProgressDisplayTime, 0, 60));
   Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay when setting mark"), &data.PauseOnMarkSet));
+  Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay when jumping to a mark"), &data.PauseOnMarkJump));
+  Add(new cMenuEditBoolItem(tr("Setup.Replay$Skip edited parts"), &data.SkipEdited));
+  Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay at last mark"), &data.PauseAtLastMark));
+  Add(new cMenuEditIntItem( tr("Setup.Replay$Initial duration for adaptive skipping (s)"), &data.AdaptiveSkipInitial, 10, 600));
+  Add(new cMenuEditIntItem( tr("Setup.Replay$Reset timeout for adaptive skipping (s)"), &data.AdaptiveSkipTimeout, 0, 10));
+  Add(new cMenuEditBoolItem(tr("Setup.Replay$Alternate behavior for adaptive skipping"), &data.AdaptiveSkipAlternate));
+  Add(new cMenuEditBoolItem(tr("Setup.Replay$Use Prev/Next keys for adaptive skipping"), &data.AdaptiveSkipPrevNext));
+  Add(new cMenuEditIntItem( tr("Setup.Replay$Skip distance with Green/Yellow keys (s)"), &data.SkipSeconds, 5, 600));
+  Add(new cMenuEditIntItem( tr("Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"), &data.SkipSecondsRepeat, 5, 600));
   Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
 }
 
@@ -3197,6 +3763,8 @@
   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delta (ms)"), &data.RcRepeatDelta, 0));
   Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"),            &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"),             &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
+  Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Volume steps"),               &data.VolumeSteps, 5, 255));
+  Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Volume linearize"),           &data.VolumeLinearize, -20, 20));
   Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Channels wrap"),              &data.ChannelsWrap));
   Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Show channel names with source"), &data.ShowChannelNamesWithSource));
   Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"),             &data.EmergencyExit));
@@ -3366,7 +3934,7 @@
 
 cOsdObject *cMenuMain::pluginOsdObject = NULL;
 
-cMenuMain::cMenuMain(eOSState State)
+cMenuMain::cMenuMain(eOSState State, bool OpenSubMenus)
 :cOsdMenu("")
 {
   SetMenuCategory(mcMain);
@@ -3383,7 +3951,7 @@
     case osSchedule:   AddSubMenu(new cMenuSchedule); break;
     case osChannels:   AddSubMenu(new cMenuChannels); break;
     case osTimers:     AddSubMenu(new cMenuTimers); break;
-    case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break;
+    case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, OpenSubMenus)); break;
     case osSetup:      AddSubMenu(new cMenuSetup); break;
     case osCommands:   AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); break;
     default: break;
@@ -3450,18 +4018,18 @@
         stopReplayItem = NULL;
         }
      // Color buttons:
-     SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play"));
+     SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying || !Setup.PauseKeyHandling ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play"));
      result = true;
      }
 
   // Editing control:
-  bool CutterActive = cCutter::Active();
-  if (CutterActive && !cancelEditingItem) {
+  bool EditingActive = RecordingsHandler.Active();
+  if (EditingActive && !cancelEditingItem) {
      // TRANSLATORS: note the leading blank!
      Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
      result = true;
      }
-  else if (cancelEditingItem && !CutterActive) {
+  else if (cancelEditingItem && !EditingActive) {
      Del(cancelEditingItem->Index());
      cancelEditingItem = NULL;
      result = true;
@@ -3511,7 +4079,7 @@
                           }
                        break;
     case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
-                          cCutter::Stop();
+                          RecordingsHandler.DelAll();
                           return osEnd;
                           }
                        break;
@@ -3545,7 +4113,7 @@
                                 }
                              break;
                case kYellow: if (!HadSubMenu)
-                                state = replaying ? osContinue : osPause;
+                                state = replaying || !Setup.PauseKeyHandling ? osContinue : osPause;
                              break;
                case kBlue:   if (!HadSubMenu)
                                 state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osRecordings;
@@ -3627,6 +4195,8 @@
   displayChannel = Skins.Current()->DisplayChannel(withInfo);
   number = 0;
   timeout = Switched || Setup.TimeoutRequChInfo;
+  cOsdProvider::OsdSizeChanged(osdState); // just to get the current state
+  positioner = NULL;
   channel = Channels.GetByNumber(Number);
   lastPresent = lastFollowing = NULL;
   if (channel) {
@@ -3648,6 +4218,7 @@
   lastTime.Set();
   withInfo = Setup.ShowInfoOnChSwitch;
   displayChannel = Skins.Current()->DisplayChannel(withInfo);
+  positioner = NULL;
   channel = Channels.GetByNumber(cDevice::CurrentChannel());
   ProcessKey(FirstKey);
 }
@@ -3710,6 +4281,10 @@
 
 eOSState cDisplayChannel::ProcessKey(eKeys Key)
 {
+  if (cOsdProvider::OsdSizeChanged(osdState)) {
+     delete displayChannel;
+     displayChannel = Skins.Current()->DisplayChannel(withInfo);
+     }
   cChannel *NewChannel = NULL;
   if (Key != kNone)
      lastTime.Set();
@@ -3855,7 +4430,7 @@
             return osEnd;
             }
     };
-  if (!timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) {
+  if (positioner || !timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) {
      if (Key == kNone && !number && group < 0 && !NewChannel && channel && channel->Number() != cDevice::CurrentChannel()) {
         // makes sure a channel switch through the SVDRP CHAN command is displayed
         channel = Channels.GetByNumber(cDevice::CurrentChannel());
@@ -3863,13 +4438,24 @@
         lastTime.Set();
         }
      DisplayInfo();
-     displayChannel->Flush();
      if (NewChannel) {
         SetTrackDescriptions(NewChannel->Number()); // to make them immediately visible in the channel display
         Channels.SwitchTo(NewChannel->Number());
         SetTrackDescriptions(NewChannel->Number()); // switching the channel has cleared them
         channel = NewChannel;
         }
+     const cPositioner *Positioner = cDevice::ActualDevice()->Positioner();
+     bool PositionerMoving = Positioner && Positioner->IsMoving();
+     SetNeedsFastResponse(PositionerMoving);
+     if (!PositionerMoving) {
+        if (positioner)
+           lastTime.Set(); // to keep the channel display up a few seconds after the target position has been reached
+        Positioner = NULL;
+        }
+     if (Positioner || positioner) // making sure we call SetPositioner(NULL) if there is a switch from "with" to "without" positioner
+        displayChannel->SetPositioner(Positioner);
+     positioner = Positioner;
+     displayChannel->Flush();
      return osContinue;
      }
   return osEnd;
@@ -4322,7 +4908,7 @@
      AssertFreeDiskSpace(Timer->Priority(), !Timer->Pending());
      Timer->SetPending(true);
      }
-  VideoDiskSpace(&FreeMB);
+  cVideoDirectory::VideoDiskSpace(&FreeMB);
   if (FreeMB < MINFREEDISK) {
      if (!Timer || time(NULL) - LastNoDiskSpaceMessage > NODISKSPACEDELTA) {
         isyslog("not enough disk space to start recording%s%s", Timer ? " timer " : "", Timer ? *Timer->ToDescr() : "");
@@ -4341,7 +4927,7 @@
      int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
      cDevice *device = cDevice::GetDevice(channel, Priority, false);
      if (device) {
-        dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
+        dsyslog("switching device %d to channel %d (%s)", device->DeviceNumber() + 1, channel->Number(), channel->Name());
         if (!device->SwitchChannel(channel, false)) {
            ShutdownHandler.RequestEmergencyExit();
            return false;
@@ -4356,7 +4942,7 @@
            }
         }
      else if (!Timer || !Timer->Pending()) {
-        isyslog("no free DVB device to record channel %d!", ch);
+        isyslog("no free DVB device to record channel %d (%s)!", ch, channel->Name());
         Skins.Message(mtError, tr("No free DVB device to record!"));
         }
      }
@@ -4451,7 +5037,7 @@
       if (RecordControls[i]) {
          if (RecordControls[i]->Timer() && RecordControls[i]->Timer()->Channel() == Channel) {
             if (RecordControls[i]->Device()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
-               isyslog("stopping recording due to modification of channel %d", Channel->Number());
+               isyslog("stopping recording due to modification of channel %d (%s)", Channel->Number(), Channel->Name());
                RecordControls[i]->Stop();
                // This will restart the recording, maybe even from a different
                // device in case conditional access has changed.
@@ -4486,6 +5072,42 @@
   return Result;
 }
 
+// --- cAdaptiveSkipper ------------------------------------------------------
+
+cAdaptiveSkipper::cAdaptiveSkipper(void)
+{
+  initialValue = NULL;
+  currentValue = 0;
+  framesPerSecond = 0;
+  lastKey = kNone;
+}
+
+void cAdaptiveSkipper::Initialize(int *InitialValue, double FramesPerSecond)
+{
+  initialValue = InitialValue;
+  framesPerSecond = FramesPerSecond;
+  currentValue = 0;
+}
+
+int cAdaptiveSkipper::GetValue(eKeys Key)
+{
+  if (!initialValue)
+     return 0;
+  if (timeout.TimedOut()) {
+     currentValue = int(round(*initialValue * framesPerSecond));
+     lastKey = Key;
+     }
+  else if (Key != lastKey) {
+     currentValue /= 2;
+     if (Setup.AdaptiveSkipAlternate)
+        lastKey = Key; // only halve the value when the direction is changed
+     else
+        lastKey = kNone; // once the direction has changed, every further call halves the value
+     }
+  timeout.Set(Setup.AdaptiveSkipTimeout * 1000);
+  return max(currentValue, 1);
+}
+
 // --- cReplayControl --------------------------------------------------------
 
 cReplayControl *cReplayControl::currentReplayControl = NULL;
@@ -4507,6 +5129,8 @@
   cRecording Recording(fileName);
   cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
   marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
+  SetMarks(&marks);
+  adaptiveSkipper.Initialize(&Setup.AdaptiveSkipInitial, Recording.FramesPerSecond());
   SetTrackDescriptions(false);
   if (Setup.ProgressDisplayTime)
      ShowTimed(Setup.ProgressDisplayTime);
@@ -4552,6 +5176,7 @@
         }
      }
   cDvbPlayerControl::Stop();
+  cMenuRecordings::SetRecording(NULL); // make sure opening the Recordings menu navigates to the last replayed recording
 }
 
 void cReplayControl::SetRecording(const char *FileName)
@@ -4731,7 +5356,8 @@
     case kOk:
          if (timeSearchPos > 0) {
             Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
-            Goto(SecondsToFrames(Seconds, FramesPerSecond()), Key == kDown || Key == kPause || Key == kOk);
+            bool Still = Key == kDown || Key == kPause || Key == kOk;
+            Goto(SecondsToFrames(Seconds, FramesPerSecond()), Still);
             }
          timeSearchActive = false;
          break;
@@ -4773,14 +5399,21 @@
   int Current, Total;
   if (GetIndex(Current, Total, true)) {
      lastCurrent = -1; // triggers redisplay
-     if (cMark *m = marks.Get(Current))
+     if (cMark *m = marks.Get(Current)) {
+        marks.Lock();
         marks.Del(m);
+        marks.Unlock();
+        }
      else {
+        marks.Lock();
         marks.Add(Current);
+        marks.Unlock();
         bool Play, Forward;
         int Speed;
-        if (Setup.PauseOnMarkSet || GetReplayMode(Play, Forward, Speed) && !Play)
+        if (Setup.PauseOnMarkSet || GetReplayMode(Play, Forward, Speed) && !Play) {
            Goto(Current, true);
+           displayFrames = true;
+           }
         }
      ShowTimed(2);
      marksModified = true;
@@ -4793,6 +5426,14 @@
   if (GetIndex(Current, Total)) {
      if (marks.Count()) {
         if (cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current)) {
+           if (!Setup.PauseOnMarkJump) {
+              bool Playing, Fwd;
+              int Speed;
+              if (GetReplayMode(Playing, Fwd, Speed) && Playing && Forward && m->Position() < Total - SecondsToFrames(3, FramesPerSecond())) {
+                 Goto(m->Position());
+                 return;
+                 }
+              }
            Goto(m->Position(), true);
            displayFrames = true;
            return;
@@ -4804,26 +5445,40 @@
      }
 }
 
-void cReplayControl::MarkMove(bool Forward)
+void cReplayControl::MarkMove(int Frames, bool MarkRequired)
 {
   int Current, Total;
   if (GetIndex(Current, Total)) {
-     if (cMark *m = marks.Get(Current)) {
+     bool Play, Forward;
+     int Speed;
+     GetReplayMode(Play, Forward, Speed);
+     cMark *m = marks.Get(Current);
+     if (!Play && m) {
         displayFrames = true;
-        int p = SkipFrames(Forward ? 1 : -1);
         cMark *m2;
-        if (Forward) {
+        if (Frames > 0) {
+           // Handle marks at the same offset:
            while ((m2 = marks.Next(m)) != NULL && m2->Position() == m->Position())
                  m = m2;
+           // Don't skip the next mark:
+           if ((m2 = marks.Next(m)) != NULL)
+              Frames = min(Frames, m2->Position() - m->Position() - 1);
            }
         else {
+           // Handle marks at the same offset:
            while ((m2 = marks.Prev(m)) != NULL && m2->Position() == m->Position())
                  m = m2;
+           // Don't skip the next mark:
+           if ((m2 = marks.Prev(m)) != NULL)
+              Frames = -min(-Frames, m->Position() - m2->Position() - 1);
            }
+        int p = SkipFrames(Frames);
         m->SetPosition(p);
         Goto(m->Position(), true);
         marksModified = true;
         }
+     else if (!MarkRequired)
+        Goto(SkipFrames(Frames), !Play);
      }
 }
 
@@ -4831,12 +5486,14 @@
 {
   if (*fileName) {
      Hide();
-     if (!cCutter::Active()) {
+     if (!RecordingsHandler.GetUsage(fileName)) {
         if (!marks.Count())
            Skins.Message(mtError, tr("No editing marks defined!"));
         else if (!marks.GetNumSequences())
            Skins.Message(mtError, tr("No editing sequences defined!"));
-        else if (!cCutter::Start(fileName))
+        else if (access(cCutter::EditedFileName(fileName), F_OK) == 0 && !Interface->Confirm(tr("Edited version already exists - overwrite?")))
+           ;
+        else if (!RecordingsHandler.Add(ruCut, fileName))
            Skins.Message(mtError, tr("Can't start editing process!"));
         else
            Skins.Message(mtInfo, tr("Editing process started"));
@@ -4855,12 +5512,10 @@
      if (!m)
         m = marks.GetNext(Current);
      if (m) {
-        if ((m->Index() & 0x01) != 0)
+        if ((m->Index() & 0x01) != 0 && !Setup.SkipEdited) // when skipping edited parts we also need to jump to end marks
            m = marks.Next(m);
-        if (m) {
+        if (m)
            Goto(m->Position() - SecondsToFrames(3, FramesPerSecond()));
-           Play();
-           }
         }
      }
 }
@@ -4931,9 +5586,11 @@
     case kRight:   Forward(); break;
     case kRed:     TimeSearch(); break;
     case kGreen|k_Repeat:
-    case kGreen:   SkipSeconds(-60); break;
+                   SkipSeconds(-Setup.SkipSecondsRepeat); break;
+    case kGreen:   SkipSeconds(-Setup.SkipSeconds); break;
     case kYellow|k_Repeat:
-    case kYellow:  SkipSeconds( 60); break;
+                   SkipSeconds(Setup.SkipSecondsRepeat); break;
+    case kYellow:  SkipSeconds(Setup.SkipSeconds); break;
     case kStop:
     case kBlue:    Hide();
                    Stop();
@@ -4944,17 +5601,29 @@
         // Editing:
         case kMarkToggle:      MarkToggle(); break;
         case kPrev|k_Repeat:
-        case kPrev:
+        case kPrev:            if (Setup.AdaptiveSkipPrevNext) {
+                                  MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false);
+                                  break;
+                                  }
+                               // fall through...
         case kMarkJumpBack|k_Repeat:
         case kMarkJumpBack:    MarkJump(false); break;
         case kNext|k_Repeat:
-        case kNext:
+        case kNext:            if (Setup.AdaptiveSkipPrevNext) {
+                                  MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false);
+                                  break;
+                                  }
+                               // fall through...
         case kMarkJumpForward|k_Repeat:
         case kMarkJumpForward: MarkJump(true); break;
         case kMarkMoveBack|k_Repeat:
-        case kMarkMoveBack:    MarkMove(false); break;
+        case kMarkMoveBack:    MarkMove(-1, true); break;
         case kMarkMoveForward|k_Repeat:
-        case kMarkMoveForward: MarkMove(true); break;
+        case kMarkMoveForward: MarkMove(+1, true); break;
+        case kMarkSkipBack|k_Repeat:
+        case kMarkSkipBack:    MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
+        case kMarkSkipForward|k_Repeat:
+        case kMarkSkipForward: MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
         case kEditCut:         EditCut(); break;
         case kEditTest:        EditTest(); break;
         default: {
@@ -4968,10 +5637,8 @@
                            else
                               Show();
                            break;
-            case kBack:    if (Setup.DelTimeshiftRec) {
-                              cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
-                              return rc && rc->InstantId() ? osEnd : osRecordings;
-                              }
+            case kBack:    Hide();
+                           Stop();
                            return osRecordings;
             default:       return osUnknown;
             }
diff -ruN vdr-2.0.0/menu.h vdr-2.2.0/menu.h
--- vdr-2.0.0/menu.h	2012-12-07 14:44:13.000000000 +0100
+++ vdr-2.2.0/menu.h	2015-02-06 10:47:30.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.h 2.13 2012/12/07 13:44:13 kls Exp $
+ * $Id: menu.h 3.8 2015/02/06 09:47:30 kls Exp $
  */
 
 #ifndef __MENU_H
@@ -38,11 +38,12 @@
   cString dir;
   cOsdItem *firstFolder;
   bool editing;
+  int helpKeys;
   void SetHelpKeys(void);
   void Set(const char *CurrentFolder = NULL);
   void DescendPath(const char *Path);
   eOSState SetFolder(void);
-  eOSState Select(void);
+  eOSState Select(bool Open);
   eOSState New(void);
   eOSState Delete(void);
   eOSState Edit(void);
@@ -107,7 +108,7 @@
   void Set(void);
   bool Update(bool Force = false);
 public:
-  cMenuMain(eOSState State = osUnknown);
+  cMenuMain(eOSState State = osUnknown, bool OpenSubMenus = false);
   virtual eOSState ProcessKey(eKeys Key);
   static cOsdObject *PluginOsdObject(void);
   };
@@ -120,6 +121,8 @@
   cTimeMs lastTime;
   int number;
   bool timeout;
+  int osdState;
+  const cPositioner *positioner;
   cChannel *channel;
   const cEvent *lastPresent;
   const cEvent *lastFollowing;
@@ -189,6 +192,13 @@
 cOsdObject *CamControl(void);
 bool CamMenuActive(void);
 
+class cRecordingFilter {
+public:
+  virtual ~cRecordingFilter(void) {};
+  virtual bool Filter(const cRecording *Recording) const = 0;
+      ///< Returns true if the given Recording shall be displayed in the Recordings menu.
+  };
+
 class cMenuRecordingItem;
 
 class cMenuRecordings : public cOsdMenu {
@@ -197,6 +207,9 @@
   int level;
   int recordingsState;
   int helpKeys;
+  const cRecordingFilter *filter;
+  static cString path;
+  static cString fileName;
   void SetHelpKeys(void);
   void Set(bool Refresh = false);
   bool Open(bool OpenSubMenus = false);
@@ -209,9 +222,11 @@
 protected:
   cString DirectoryName(void);
 public:
-  cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false);
+  cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false, const cRecordingFilter *Filter = NULL);
   ~cMenuRecordings();
   virtual eOSState ProcessKey(eKeys Key);
+  static void SetPath(const char *Path);
+  static void SetRecording(const char *FileName);
   };
 
 class cRecordControl {
@@ -255,9 +270,23 @@
   static bool StateChanged(int &State);
   };
 
+class cAdaptiveSkipper {
+private:
+  int *initialValue;
+  int currentValue;
+  double framesPerSecond;
+  eKeys lastKey;
+  cTimeMs timeout;
+public:
+  cAdaptiveSkipper(void);
+  void Initialize(int *InitialValue, double FramesPerSecond);
+  int GetValue(eKeys Key);
+  };
+
 class cReplayControl : public cDvbPlayerControl {
 private:
   cSkinDisplayReplay *displayReplay;
+  cAdaptiveSkipper adaptiveSkipper;
   cMarks marks;
   bool marksModified;
   bool visible, modeOnly, shown, displayFrames;
@@ -277,7 +306,7 @@
   bool ShowProgress(bool Initial);
   void MarkToggle(void);
   void MarkJump(bool Forward);
-  void MarkMove(bool Forward);
+  void MarkMove(int Frames, bool MarkRequired);
   void EditCut(void);
   void EditTest(void);
 public:
diff -ruN vdr-2.0.0/menuitems.c vdr-2.2.0/menuitems.c
--- vdr-2.0.0/menuitems.c	2013-02-15 15:20:29.000000000 +0100
+++ vdr-2.2.0/menuitems.c	2015-02-09 12:53:10.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menuitems.c 2.16 2013/02/15 14:20:29 kls Exp $
+ * $Id: menuitems.c 3.3 2015/02/09 11:53:10 kls Exp $
  */
 
 #include "menuitems.h"
@@ -51,14 +51,14 @@
   helpDisplayed = false;
 }
 
-bool cMenuEditItem::DisplayHelp(void)
+bool cMenuEditItem::DisplayHelp(bool Current)
 {
   bool HasHelp = helpRed || helpGreen || helpYellow || helpBlue;
-  if (HasHelp && !helpDisplayed) {
+  if (HasHelp && !helpDisplayed && Current) {
      cSkinDisplay::Current()->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
      cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
-     helpDisplayed = true;
      }
+  helpDisplayed = Current;
   return HasHelp;
 }
 
@@ -223,6 +223,50 @@
   return state;
 }
 
+// --- cMenuEditIntxItem -----------------------------------------------------
+
+cMenuEditIntxItem::cMenuEditIntxItem(const char *Name, int *Value, int Min, int Max, int Factor, const char *NegString, const char *PosString)
+:cMenuEditIntItem(Name, Value, Min, Max)
+{
+  factor = ::max(Factor, 1);
+  negString = NegString;
+  posString = PosString;
+  Set();
+}
+
+void cMenuEditIntxItem::SetHelpKeys(void)
+{
+  if (negString && posString)
+     SetHelp(NULL, (*value < 0) ? posString : negString);
+}
+
+void cMenuEditIntxItem::Set(void)
+{
+  const char *s = (*value < 0) ? negString : posString;
+  int v = *value;
+  if (negString && posString)
+     v = abs(v);
+  SetValue(cString::sprintf(s ? "%.*f %s" : "%.*f", factor / 10, double(v) / factor, s));
+  SetHelpKeys();
+}
+
+eOSState cMenuEditIntxItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditIntItem::ProcessKey(Key);
+  if (state == osUnknown) {
+     switch (Key) {
+       case kGreen: if (negString && posString) {
+                       *value = -*value;
+                       Set();
+                       state = osContinue;
+                       }
+                    break;
+       default: ;
+       }
+     }
+  return state;
+}
+
 // --- cMenuEditPrcItem ------------------------------------------------------
 
 cMenuEditPrcItem::cMenuEditPrcItem(const char *Name, double *Value, double Min, double Max, int Decimals)
@@ -443,12 +487,12 @@
   if (InEditMode()) {
      // This is an ugly hack to make editing strings work with the 'skincurses' plugin.
      const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
-     if (!font || font->Width("W") != 1) // all characters have with == 1 in the font used by 'skincurses'
+     if (!font || font->Width("W") != 1) // all characters have width == 1 in the font used by 'skincurses'
         font = cFont::GetFont(fontOsd);
 
      int width = cSkinDisplay::Current()->EditableWidth();
      width -= font->Width("[]");
-     width -= font->Width("<>"); // reserving this anyway make the whole thing simpler
+     width -= font->Width("<>"); // reserving this anyway makes the whole thing simpler
 
      if (pos < offset)
         offset = pos;
@@ -1124,7 +1168,7 @@
 // --- cMenuSetupPage --------------------------------------------------------
 
 cMenuSetupPage::cMenuSetupPage(void)
-:cOsdMenu("", 33)
+:cOsdMenu("", 36)
 {
   SetMenuCategory(mcSetup);
   plugin = NULL;
diff -ruN vdr-2.0.0/menuitems.h vdr-2.2.0/menuitems.h
--- vdr-2.0.0/menuitems.h	2012-03-13 12:19:11.000000000 +0100
+++ vdr-2.2.0/menuitems.h	2013-05-24 12:19:55.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menuitems.h 2.8 2012/03/13 11:19:11 kls Exp $
+ * $Id: menuitems.h 3.1 2013/05/24 10:19:55 kls Exp $
  */
 
 #ifndef __MENUITEMS_H
@@ -27,7 +27,7 @@
   cMenuEditItem(const char *Name);
   ~cMenuEditItem();
   void SetValue(const char *Value);
-  bool DisplayHelp(void);
+  bool DisplayHelp(bool Current);
   };
 
 class cMenuEditIntItem : public cMenuEditItem {
@@ -70,6 +70,17 @@
   virtual eOSState ProcessKey(eKeys Key);
   };
 
+class cMenuEditIntxItem : public cMenuEditIntItem {
+private:
+  int factor;
+  const char *negString, *posString;
+  void SetHelpKeys(void);
+  virtual void Set(void);
+public:
+  cMenuEditIntxItem(const char *Name, int *Value, int Min = INT_MIN, int Max = INT_MAX, int Factor = 1, const char *NegString = NULL, const char *PosString = NULL);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
 class cMenuEditPrcItem : public cMenuEditItem {
 protected:
   double *value;
diff -ruN vdr-2.0.0/newplugin vdr-2.2.0/newplugin
--- vdr-2.0.0/newplugin	2013-01-12 14:46:00.000000000 +0100
+++ vdr-2.2.0/newplugin	2014-01-01 14:29:54.000000000 +0100
@@ -12,7 +12,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: newplugin 2.17 2013/01/12 13:46:00 kls Exp $
+# $Id: newplugin 3.2 2014/01/01 13:29:54 kls Exp $
 
 $PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
 
@@ -75,7 +75,7 @@
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = \$(if \$(VDRDIR),\$(shell pkg-config --variable=\$(1) \$(VDRDIR)/vdr.pc),\$(shell pkg-config --variable=\$(1) vdr || pkg-config --variable=\$(1) ../../../vdr.pc))
+PKGCFG = \$(if \$(VDRDIR),\$(shell pkg-config --variable=\$(1) \$(VDRDIR)/vdr.pc),\$(shell PKG_CONFIG_PATH="\$\$PKG_CONFIG_PATH:../../.." pkg-config --variable=\$(1) vdr))
 LIBDIR = \$(call PKGCFG,libdir)
 LOCDIR = \$(call PKGCFG,locdir)
 PLGCFG = \$(call PKGCFG,plgcfg)
@@ -352,8 +352,8 @@
 
 sub CreateFile
 {
-  my ($Name, $Content) = @_;
-  open(FILE, ">$PLUGINDIR/$Name") || die "$Name:  $!\n";
+  my ($FileName, $Content) = @_;
+  open(FILE, ">$PLUGINDIR/$FileName") || die "$FileName: $!\n";
   print FILE $Content;
   close(FILE);
 }
diff -ruN vdr-2.0.0/nit.c vdr-2.2.0/nit.c
--- vdr-2.0.0/nit.c	2013-03-07 10:42:29.000000000 +0100
+++ vdr-2.2.0/nit.c	2015-02-04 10:13:54.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: nit.c 2.10 2013/03/07 09:42:29 kls Exp $
+ * $Id: nit.c 3.5 2015/02/04 09:13:54 kls Exp $
  */
 
 #include "nit.h"
@@ -19,8 +19,9 @@
 #define DVB_SYSTEM_1 0 // see also dvbdevice.c
 #define DVB_SYSTEM_2 1
 
-cNitFilter::cNitFilter(void)
+cNitFilter::cNitFilter(cSdtFilter *SdtFilter)
 {
+  sdtFilter = SdtFilter;
   numNits = 0;
   networkId = 0;
   Set(0x10, 0x40);  // NIT
@@ -183,6 +184,7 @@
                            }
                        }
                     }
+                 sdtFilter->Trigger(Source);
                  }
                  break;
             case SI::S2SatelliteDeliverySystemDescriptorTag: {
@@ -243,7 +245,7 @@
                            }
                         }
                     if (!found || forceTransponderUpdate) {
-                        for (int n = 0; n < NumFrequencies; n++) {
+                       for (int n = 0; n < NumFrequencies; n++) {
                            cChannel *Channel = new cChannel;
                            Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
                            if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('C')))
@@ -253,6 +255,7 @@
                            }
                        }
                     }
+                 sdtFilter->Trigger(Source);
                  }
                  break;
             case SI::TerrestrialDeliverySystemDescriptorTag: {
@@ -314,8 +317,9 @@
                            else
                               delete Channel;
                            }
-                        }
+                       }
                     }
+                 sdtFilter->Trigger(Source);
                  }
                  break;
             case SI::ExtensionDescriptorTag: {
@@ -329,11 +333,12 @@
                                   SI::T2DeliverySystemDescriptor *td = (SI::T2DeliverySystemDescriptor *)d;
                                   int Frequency = Channel->Frequency();
                                   int SymbolRate = Channel->Srate();
-                                  //int SystemId = td->getSystemId();
                                   cDvbTransponderParameters dtp(Channel->Parameters());
                                   dtp.SetSystem(DVB_SYSTEM_2);
                                   dtp.SetStreamId(td->getPlpId());
+                                  dtp.SetT2SystemId(td->getT2SystemId());
                                   if (td->getExtendedDataFlag()) {
+                                     dtp.SetSisoMiso(td->getSisoMiso());
                                      static int T2Bandwidths[] = { 8000000, 7000000, 6000000, 5000000, 10000000, 1712000, 0, 0 };
                                      dtp.SetBandwidth(T2Bandwidths[td->getBandwidth()]);
                                      static int T2GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4, GUARD_INTERVAL_1_128, GUARD_INTERVAL_19_128, GUARD_INTERVAL_19_256, 0 };
@@ -352,6 +357,42 @@
                    }
                  }
                  break;
+            case SI::LogicalChannelDescriptorTag:
+                 if (Setup.StandardCompliance == STANDARD_NORDIG) {
+                    SI::LogicalChannelDescriptor *lcd = (SI::LogicalChannelDescriptor *)d;
+                    SI::LogicalChannelDescriptor::LogicalChannel LogicalChannel;
+                    for (SI::Loop::Iterator it4; lcd->logicalChannelLoop.getNext(LogicalChannel, it4); ) {
+                        int lcn = LogicalChannel.getLogicalChannelNumber();
+                        int sid = LogicalChannel.getServiceId();
+                        if (LogicalChannel.getVisibleServiceFlag()) {
+                           for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+                               if (!Channel->GroupSep() && Channel->Sid() == sid && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+                                  Channel->SetLcn(lcn);
+                                  break;
+                                  }
+                               }
+                           }
+                        }
+                    }
+                 break;
+            case SI::HdSimulcastLogicalChannelDescriptorTag:
+                 if (Setup.StandardCompliance == STANDARD_NORDIG) {
+                    SI::HdSimulcastLogicalChannelDescriptor *lcd = (SI::HdSimulcastLogicalChannelDescriptor *)d;
+                    SI::HdSimulcastLogicalChannelDescriptor::HdSimulcastLogicalChannel HdSimulcastLogicalChannel;
+                    for (SI::Loop::Iterator it4; lcd->hdSimulcastLogicalChannelLoop.getNext(HdSimulcastLogicalChannel, it4); ) {
+                        int lcn = HdSimulcastLogicalChannel.getLogicalChannelNumber();
+                        int sid = HdSimulcastLogicalChannel.getServiceId();
+                        if (HdSimulcastLogicalChannel.getVisibleServiceFlag()) {
+                           for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+                               if (!Channel->GroupSep() && Channel->Sid() == sid && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+                                  Channel->SetLcn(lcn);
+                                  break;
+                                  }
+                               }
+                           }
+                        }
+                    }
+                 break;
             default: ;
             }
           delete d;
diff -ruN vdr-2.0.0/nit.h vdr-2.2.0/nit.h
--- vdr-2.0.0/nit.h	2007-06-10 10:50:21.000000000 +0200
+++ vdr-2.2.0/nit.h	2014-03-10 15:12:05.000000000 +0100
@@ -4,13 +4,14 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: nit.h 2.0 2007/06/10 08:50:21 kls Exp $
+ * $Id: nit.h 3.1 2014/03/10 14:12:05 kls Exp $
  */
 
 #ifndef __NIT_H
 #define __NIT_H
 
 #include "filter.h"
+#include "sdt.h"
 
 #define MAXNITS 16
 #define MAXNETWORKNAME Utf8BufSize(256)
@@ -26,13 +27,14 @@
     };
 
   cSectionSyncer sectionSyncer;
+  cSdtFilter *sdtFilter;
   cNit nits[MAXNITS];
   u_short networkId;
   int numNits;
 protected:
   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
 public:
-  cNitFilter(void);
+  cNitFilter(cSdtFilter *SdtFilter);
   virtual void SetStatus(bool On);
   };
 
diff -ruN vdr-2.0.0/osd.c vdr-2.2.0/osd.c
--- vdr-2.0.0/osd.c	2013-02-14 16:50:19.000000000 +0100
+++ vdr-2.2.0/osd.c	2015-02-11 10:48:02.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.c 2.38 2013/02/14 15:50:19 kls Exp $
+ * $Id: osd.c 3.5 2015/02/11 09:48:02 kls Exp $
  */
 
 #include "osd.h"
@@ -512,6 +512,17 @@
      }
 }
 
+void cBitmap::Fill(tIndex Index)
+{
+  if (bitmap) {
+     memset(bitmap, Index, width * height);
+     dirtyX1 = 0;
+     dirtyY1 = 0;
+     dirtyX2 = width - 1;
+     dirtyY2 = height - 1;
+     }
+}
+
 void cBitmap::DrawPixel(int x, int y, tColor Color)
 {
   x -= x0;
@@ -639,8 +650,8 @@
     case 8:          cy = y1; rx /= 2; break;
     default: ;
     }
-  int TwoASquare = 2 * rx * rx;
-  int TwoBSquare = 2 * ry * ry;
+  int TwoASquare = max(1, 2 * rx * rx);
+  int TwoBSquare = max(1, 2 * ry * ry);
   int x = rx;
   int y = 0;
   int XChange = ry * ry * (1 - 2 * rx);
@@ -824,7 +835,7 @@
      }
 }
 
-cBitmap *cBitmap::Scaled(double FactorX, double FactorY, bool AntiAlias)
+cBitmap *cBitmap::Scaled(double FactorX, double FactorY, bool AntiAlias) const
 {
   // Fixed point scaling code based on www.inversereality.org/files/bitmapscaling.pdf
   // by deltener@mindtremors.com
@@ -973,12 +984,13 @@
 
 void cPixmap::MarkViewPortDirty(const cRect &Rect)
 {
-  dirtyViewPort.Combine(Rect.Intersected(viewPort));
+  if (layer >= 0)
+     dirtyViewPort.Combine(Rect.Intersected(viewPort));
 }
 
 void cPixmap::MarkViewPortDirty(const cPoint &Point)
 {
-  if (viewPort.Contains(Point))
+  if (layer >= 0 && viewPort.Contains(Point))
      dirtyViewPort.Combine(Point);
 }
 
@@ -1014,11 +1026,18 @@
      esyslog("ERROR: pixmap layer %d limited to %d", Layer, MAXPIXMAPLAYERS - 1);
      Layer = MAXPIXMAPLAYERS - 1;
      }
-  if (Layer != layer) {
-     if (Layer > 0 || layer > 0)
-        MarkViewPortDirty(viewPort);
+  // The sequence here is important, because the view port is only marked as dirty
+  // if the layer is >= 0:
+  if (layer >= 0) {
+     MarkViewPortDirty(viewPort); // the pixmap is visible and may or may not become invisible
      layer = Layer;
      }
+  else if (Layer >= 0) {
+     layer = Layer;
+     MarkViewPortDirty(viewPort); // the pixmap was invisible and has become visible
+     }
+  else
+     layer = Layer; // the pixmap was invisible and remains so
   Unlock();
 }
 
@@ -1130,6 +1149,7 @@
 :cPixmap(Layer, ViewPort, DrawPort)
 {
   data = MALLOC(tColor, this->DrawPort().Width() * this->DrawPort().Height());
+  panning = false;
 }
 
 cPixmapMemory::~cPixmapMemory()
@@ -1380,8 +1400,8 @@
     case 8:          cy = y1; rx /= 2; break;
     default: ;
     }
-  int TwoASquare = 2 * rx * rx;
-  int TwoBSquare = 2 * ry * ry;
+  int TwoASquare = max(1, 2 * rx * rx);
+  int TwoBSquare = max(1, 2 * ry * ry);
   int x = rx;
   int y = 0;
   int XChange = ry * ry * (1 - 2 * rx);
@@ -1703,7 +1723,8 @@
      LOCK_PIXMAPS;
      for (int i = 1; i < pixmaps.Size(); i++) { // begin at 1 - don't let the background pixmap be destroyed!
          if (pixmaps[i] == Pixmap) {
-            pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
+            if (Pixmap->Layer() >= 0)
+               pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
             delete Pixmap;
             pixmaps[i] = NULL;
             return;
@@ -1726,9 +1747,9 @@
   return Pixmap;
 }
 
-cPixmapMemory *cOsd::RenderPixmaps(void)
+cPixmap *cOsd::RenderPixmaps(void)
 {
-  cPixmapMemory *Pixmap = NULL;
+  cPixmap *Pixmap = NULL;
   if (isTrueColor) {
      LOCK_PIXMAPS;
      // Collect overlapping dirty rectangles:
@@ -1751,25 +1772,27 @@
         d.Combine(OldDirty);
         OldDirty = NewDirty;
 #endif
-        Pixmap = new cPixmapMemory(0, d);
-        Pixmap->Clear();
-        // Render the individual pixmaps into the resulting pixmap:
-        for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
-            for (int i = 0; i < pixmaps.Size(); i++) {
-                if (cPixmap *pm = pixmaps[i]) {
-                   if (pm->Layer() == Layer)
-                   Pixmap->DrawPixmap(pm, d);
+        Pixmap = CreatePixmap(-1, d);
+        if (Pixmap) {
+           Pixmap->Clear();
+           // Render the individual pixmaps into the resulting pixmap:
+           for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
+               for (int i = 0; i < pixmaps.Size(); i++) {
+                   if (cPixmap *pm = pixmaps[i]) {
+                      if (pm->Layer() == Layer)
+                         Pixmap->DrawPixmap(pm, d);
+                      }
                    }
-                }
-            }
+               }
 #ifdef DebugDirty
-        cPixmapMemory DirtyIndicator(7, NewDirty);
-        static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
-        static int DirtyIndicatorIndex = 0;
-        DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
-        DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
-        Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
+           cPixmapMemory DirtyIndicator(7, NewDirty);
+           static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
+           static int DirtyIndicatorIndex = 0;
+           DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
+           DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
+           Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
 #endif
+           }
         }
      }
   return Pixmap;
@@ -1906,6 +1929,16 @@
      }
 }
 
+void cOsd::DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias)
+{
+  const cBitmap *b = &Bitmap;
+  if (!DoubleEqual(FactorX, 1.0) || !DoubleEqual(FactorY, 1.0))
+     b = b->Scaled(FactorX, FactorY, AntiAlias);
+  DrawBitmap(x, y, *b);
+  if (b != &Bitmap)
+     delete b;
+}
+
 void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
 {
   if (isTrueColor)
@@ -1957,6 +1990,7 @@
 int cOsdProvider::oldHeight = 0;
 double cOsdProvider::oldAspect = 1.0;
 cImage *cOsdProvider::images[MAXOSDIMAGES] = { NULL };
+int cOsdProvider::osdState = 0;
 
 cOsdProvider::cOsdProvider(void)
 {
@@ -1994,6 +2028,7 @@
   int Width;
   int Height;
   double Aspect;
+  cMutexLock MutexLock(&cOsd::mutex);
   cDevice::PrimaryDevice()->GetOsdSize(Width, Height, Aspect);
   if (Width != oldWidth || Height != oldHeight || !DoubleEqual(Aspect, oldAspect) || Force) {
      Setup.OSDLeft = int(round(Width * Setup.OSDLeftP));
@@ -2011,9 +2046,18 @@
      oldHeight = Height;
      oldAspect = Aspect;
      dsyslog("OSD size changed to %dx%d @ %g", Width, Height, Aspect);
+     osdState++;
      }
 }
 
+bool cOsdProvider::OsdSizeChanged(int &State)
+{
+  cMutexLock MutexLock(&cOsd::mutex);
+  bool Result = osdState != State;
+  State = osdState;
+  return Result;
+}
+
 bool cOsdProvider::SupportsTrueColor(void)
 {
   if (osdProvider) {
diff -ruN vdr-2.0.0/osd.h vdr-2.2.0/osd.h
--- vdr-2.0.0/osd.h	2013-02-12 14:39:08.000000000 +0100
+++ vdr-2.2.0/osd.h	2015-02-11 10:48:02.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.h 2.20 2013/02/12 13:39:08 kls Exp $
+ * $Id: osd.h 3.6 2015/02/11 09:48:02 kls Exp $
  */
 
 #ifndef __OSD_H
@@ -192,6 +192,8 @@
        ///< contents of the bitmap will be lost. If Width and Height are the same
        ///< as the current values, nothing will happen and the bitmap remains
        ///< unchanged.
+  void SetOffset(int X0, int Y0) { x0 = X0; y0 = Y0; }
+       ///< Sets the offset of this bitmap to the given values.
   bool Contains(int x, int y) const;
        ///< Returns true if this bitmap contains the point (x, y).
   bool Covers(int x1, int y1, int x2, int y2) const;
@@ -221,6 +223,8 @@
   void SetIndex(int x, int y, tIndex Index);
        ///< Sets the index at the given coordinates to Index.
        ///< Coordinates are relative to the bitmap's origin.
+  void Fill(tIndex Index);
+       ///< Fills the bitmap data with the given Index.
   void DrawPixel(int x, int y, tColor Color);
        ///< Sets the pixel at the given coordinates to the given Color, which is
        ///< a full 32 bit ARGB value.
@@ -283,7 +287,7 @@
        ///< the 2^NewBpp most frequently used colors as defined in the current palette.
        ///< If NewBpp is not smaller than the bitmap's current color depth,
        ///< or if it is not one of 4bpp or 2bpp, nothing happens.
-  cBitmap *Scaled(double FactorX, double FactorY, bool AntiAlias = false);
+  cBitmap *Scaled(double FactorX, double FactorY, bool AntiAlias = false) const;
        ///< Creates a copy of this bitmap, scaled by the given factors.
        ///< If AntiAlias is true and either of the factors is greater than 1.0,
        ///< anti-aliasing is applied. This will also set the color depth of the
@@ -657,7 +661,7 @@
        ///< covers the entire view port. This may be of advantage if, e.g.,
        ///< there is a draw port that holds, say, 11 lines of text, while the
        ///< view port displays only 10 lines. By Pan()'ing the draw port up one
-       ///< line, an new bottom line can be written into the draw port (without
+       ///< line, a new bottom line can be written into the draw port (without
        ///< being seen through the view port), and later the draw port can be
        ///< shifted smoothly, resulting in a smooth scrolling.
        ///< It is the caller's responsibility to make sure that Source and Dest
@@ -759,12 +763,13 @@
        ///< the pixmap could not be added to the list.
        ///< A derived class that implements its own cPixmap class must call AddPixmap()
        ///< in order to add a newly created pixmap to the OSD's list of pixmaps.
-  cPixmapMemory *RenderPixmaps(void);
+  cPixmap *RenderPixmaps(void);
        ///< Renders the dirty part of all pixmaps into a resulting pixmap that
        ///< shall be displayed on the OSD. The returned pixmap's view port is
        ///< set to the location of the rectangle on the OSD that needs to be
        ///< refreshed; its draw port's origin is at (0, 0), and it has the same
        ///< size as the view port.
+       ///< Only pixmaps with a non-negative layer value are rendered.
        ///< If there are several non-overlapping dirty rectangles from different pixmaps,
        ///< they are returned separately in order to avoid re-rendering large parts
        ///< of the OSD that haven't changed at all. The caller must therefore call
@@ -774,7 +779,7 @@
        ///< by putting a LOCK_PIXMAPS into the scope of the operation).
        ///< If there are no dirty pixmaps, or if this is not a true color OSD,
        ///< this function returns NULL.
-       ///< The caller must delete the returned pixmap after use.
+       ///< The caller must call DestroyPixmap() for the returned pixmap after use.
 public:
   virtual ~cOsd();
        ///< Shuts down the OSD.
@@ -813,6 +818,8 @@
        ///< If this is a true color OSD, a pointer to a dummy bitmap with 8bpp
        ///< is returned. This is done so that skins that call this function
        ///< in order to preset the bitmap's palette won't crash.
+       ///< Use of this function outside of derived classes is deprecated and it
+       ///< may be made 'protected' in a future version.
   virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null);
        ///< Creates a new true color pixmap on this OSD (see cPixmap for details).
        ///< The caller must not delete the returned object, it will be deleted when
@@ -881,6 +888,11 @@
        ///< If Overlay is true, any pixel in Bitmap that has color index 0 will
        ///< not overwrite the corresponding pixel in the target area.
        ///< If this is a true color OSD, ReplacePalette has no meaning.
+  virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias = false);
+       ///< Sets the pixels in the OSD with the data from the given Bitmap, putting
+       ///< the upper left corner of the Bitmap at (x, y) and scaled by the given
+       ///< factors. If AntiAlias is true and either of the factors is greater than
+       ///< 1.0, anti-aliasing is applied.
   virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault);
        ///< Draws the given string at coordinates (x, y) with the given foreground
        ///< and background color and font. If Width and Height are given, the text
@@ -919,13 +931,16 @@
        ///< pixmaps, the Flush() function should basically do something like this:
        ///<
        ///<  LOCK_PIXMAPS;
-       ///<  while (cPixmapMemory *pm = RenderPixmaps()) {
+       ///<  while (cPixmapMemory *pm = dynamic_cast<cPixmapMemory *>(RenderPixmaps())) {
        ///<        int w = pm->ViewPort().Width();
        ///<        int h = pm->ViewPort().Height();
        ///<        int d = w * sizeof(tColor);
        ///<        MyOsdDrawPixmap(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), pm->Data(), w, h, h * d);
-       ///<        delete pm;
+       ///<        DestroyPixmap(pm);
        ///<        }
+       ///<
+       ///< If a plugin uses a derived cPixmap implementation, it needs to use that
+       ///< type instead of cPixmapMemory.
   };
 
 #define MAXOSDIMAGES 64
@@ -938,6 +953,7 @@
   static int oldHeight;
   static double oldAspect;
   static cImage *images[MAXOSDIMAGES];
+  static int osdState;
 protected:
   virtual cOsd *CreateOsd(int Left, int Top, uint Level) = 0;
       ///< Returns a pointer to a newly created cOsd object, which will be located
@@ -974,6 +990,12 @@
       ///< font sizes accordingly. If Force is true, all settings are recalculated,
       ///< even if the video resolution hasn't changed since the last call to
       ///< this function.
+  static bool OsdSizeChanged(int &State);
+      ///< Checks if the OSD size has changed and a currently displayed OSD needs to
+      ///< be redrawn. An internal reference value is incremented on every size change
+      ///< and is compared against State when calling the method.
+      ///< OsdSizeChanged() can be called with an uninitialized State to just get
+      ///< the current value of State.
   static bool SupportsTrueColor(void);
       ///< Returns true if the current OSD provider is able to handle a true color OSD.
   static int StoreImage(const cImage &Image);
diff -ruN vdr-2.0.0/osdbase.c vdr-2.2.0/osdbase.c
--- vdr-2.0.0/osdbase.c	2012-12-07 10:50:47.000000000 +0100
+++ vdr-2.2.0/osdbase.c	2015-01-15 11:11:11.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osdbase.c 2.7 2012/12/07 09:50:47 kls Exp $
+ * $Id: osdbase.c 3.3 2015/01/15 10:11:11 kls Exp $
  */
 
 #include "osdbase.h"
@@ -86,6 +86,7 @@
   displayMenuItems = 0;
   title = NULL;
   menuCategory = mcUnknown;
+  menuSortMode = msmUnknown;
   SetTitle(Title);
   SetCols(c0, c1, c2, c3, c4);
   first = 0;
@@ -114,6 +115,11 @@
   menuCategory = MenuCategory;
 }
 
+void cOsdMenu::SetMenuSortMode(eMenuSortMode MenuSortMode)
+{
+  menuSortMode = MenuSortMode;
+}
+
 void cOsdMenu::SetDisplayMenu(void)
 {
   if (displayMenu) {
@@ -224,6 +230,7 @@
   cStatus::MsgOsdClear();
   if (menuCategory != displayMenu->MenuCategory())
      displayMenu->SetMenuCategory(menuCategory);
+  displayMenu->SetMenuSortMode(menuSortMode);
   displayMenuItems = displayMenu->MaxItems();
   displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
   displayMenu->SetTitle(title);
@@ -286,7 +293,7 @@
      if (!Current)
         item->SetFresh(true); // leaving the current item resets 'fresh'
      if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
-        if (!MenuEditItem->DisplayHelp())
+        if (!MenuEditItem->DisplayHelp(Current))
            DisplayHelp();
         else
            helpDisplayed = false;
@@ -502,12 +509,14 @@
   return osContinue; // convenience return value
 }
 
-eOSState cOsdMenu::CloseSubMenu()
+eOSState cOsdMenu::CloseSubMenu(bool ReDisplay)
 {
   delete subMenu;
   subMenu = NULL;
-  RefreshCurrent();
-  Display();
+  if (ReDisplay) {
+     RefreshCurrent();
+     Display();
+     }
   return osContinue; // convenience return value
 }
 
diff -ruN vdr-2.0.0/osdbase.h vdr-2.2.0/osdbase.h
--- vdr-2.0.0/osdbase.h	2012-12-07 10:49:35.000000000 +0100
+++ vdr-2.2.0/osdbase.h	2015-01-15 11:09:18.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osdbase.h 2.5 2012/12/07 09:49:35 kls Exp $
+ * $Id: osdbase.h 3.2 2015/01/15 10:09:18 kls Exp $
  */
 
 #ifndef __OSDBASE_H
@@ -92,6 +92,7 @@
   int cols[cSkinDisplayMenu::MaxTabs];
   int first, current, marked;
   eMenuCategory menuCategory;
+  eMenuSortMode menuSortMode;
   cOsdMenu *subMenu;
   const char *helpRed, *helpGreen, *helpYellow, *helpBlue;
   bool helpDisplayed;
@@ -119,7 +120,7 @@
   void Mark(void);
   eOSState HotKey(eKeys Key);
   eOSState AddSubMenu(cOsdMenu *SubMenu);
-  eOSState CloseSubMenu();
+  eOSState CloseSubMenu(bool ReDisplay = true);
   bool HasSubMenu(void) { return subMenu; }
   cOsdMenu *SubMenu(void) { return subMenu; }
   void SetStatus(const char *s);
@@ -131,6 +132,7 @@
   virtual ~cOsdMenu();
   virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
   void SetMenuCategory(eMenuCategory MenuCategory);
+  void SetMenuSortMode(eMenuSortMode MenuSortMode);
   int Current(void) const { return current; }
   void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
   void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
diff -ruN vdr-2.0.0/pat.c vdr-2.2.0/pat.c
--- vdr-2.0.0/pat.c	2012-11-25 15:12:21.000000000 +0100
+++ vdr-2.2.0/pat.c	2015-01-04 14:14:01.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: pat.c 2.19 2012/11/25 14:12:21 kls Exp $
+ * $Id: pat.c 3.5 2015/01/04 13:14:01 kls Exp $
  */
 
 #include "pat.h"
@@ -12,15 +12,15 @@
 #include "channels.h"
 #include "libsi/section.h"
 #include "libsi/descriptor.h"
-#include "thread.h"
 
-#define PMT_SCAN_TIMEOUT  10 // seconds
+#define PMT_SCAN_TIMEOUT  1000 // ms
 
 // --- cCaDescriptor ---------------------------------------------------------
 
 class cCaDescriptor : public cListObject {
 private:
   int caSystem;
+  int caPid;
   int esPid;
   int length;
   uchar *data;
@@ -29,6 +29,7 @@
   virtual ~cCaDescriptor();
   bool operator== (const cCaDescriptor &arg) const;
   int CaSystem(void) { return caSystem; }
+  int CaPid(void) { return caPid; }
   int EsPid(void) { return esPid; }
   int Length(void) const { return length; }
   const uchar *Data(void) const { return data; }
@@ -37,6 +38,7 @@
 cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, int EsPid, int Length, const uchar *Data)
 {
   caSystem = CaSystem;
+  caPid = CaPid;
   esPid = EsPid;
   length = Length + 6;
   data = MALLOC(uchar, length);
@@ -67,26 +69,30 @@
   int source;
   int transponder;
   int serviceId;
+  int pmtPid; // needed for OctopusNet - otherwise irrelevant!
   int numCaIds;
   int caIds[MAXCAIDS + 1];
   cList<cCaDescriptor> caDescriptors;
   void AddCaId(int CaId);
 public:
-  cCaDescriptors(int Source, int Transponder, int ServiceId);
+  cCaDescriptors(int Source, int Transponder, int ServiceId, int PmtPid);
   bool operator== (const cCaDescriptors &arg) const;
   bool Is(int Source, int Transponder, int ServiceId);
   bool Is(cCaDescriptors * CaDescriptors);
   bool Empty(void) { return caDescriptors.Count() == 0; }
   void AddCaDescriptor(SI::CaDescriptor *d, int EsPid);
   int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
+  int GetCaPids(const int *CaSystemIds, int BufSize, int *Pids);
+  const int GetPmtPid(void) { return pmtPid; };
   const int *CaIds(void) { return caIds; }
   };
 
-cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId)
+cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId, int PmtPid)
 {
   source = Source;
   transponder = Transponder;
   serviceId = ServiceId;
+  pmtPid = PmtPid;
   numCaIds = 0;
   caIds[0] = 0;
 }
@@ -163,7 +169,7 @@
          if (EsPid < 0 || d->EsPid() == EsPid) {
             const int *caids = CaSystemIds;
             do {
-               if (d->CaSystem() == *caids) {
+               if (*caids == 0xFFFF || d->CaSystem() == *caids) {
                   if (length + d->Length() <= BufSize) {
                      memcpy(Data + length, d->Data(), d->Length());
                      length += d->Length();
@@ -179,6 +185,30 @@
   return -1;
 }
 
+int cCaDescriptors::GetCaPids(const int *CaSystemIds, int BufSize, int *Pids)
+{
+  if (!CaSystemIds || !*CaSystemIds)
+     return 0;
+  if (BufSize > 0 && Pids) {
+     int numPids = 0;
+     for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
+         const int *caids = CaSystemIds;
+         do {
+            if (*caids == 0xFFFF || d->CaSystem() == *caids) {
+               if (numPids + 1 < BufSize) {
+                  Pids[numPids++] = d->CaPid();
+                  Pids[numPids] = 0;
+                  }
+               else
+                  return -1;
+               }
+            } while (*++caids);
+         }
+     return numPids;
+     }
+  return -1;
+}
+
 // --- cCaDescriptorHandler --------------------------------------------------
 
 class cCaDescriptorHandler : public cList<cCaDescriptors> {
@@ -190,6 +220,8 @@
       // 1 if it is an all new descriptor with actual contents,
       // and 2 if an existing descriptor was changed.
   int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
+  int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids);
+  int GetPmtPid(int Source, int Transponder, int ServiceId);
   };
 
 int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
@@ -220,6 +252,26 @@
   return 0;
 }
 
+int cCaDescriptorHandler::GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
+{
+  cMutexLock MutexLock(&mutex);
+  for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
+      if (ca->Is(Source, Transponder, ServiceId))
+         return ca->GetCaPids(CaSystemIds, BufSize, Pids);
+      }
+  return 0;
+}
+
+int cCaDescriptorHandler::GetPmtPid(int Source, int Transponder, int ServiceId)
+{
+  cMutexLock MutexLock(&mutex);
+  for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
+      if (ca->Is(Source, Transponder, ServiceId))
+         return ca->GetPmtPid();
+      }
+  return 0;
+}
+
 cCaDescriptorHandler CaDescriptorHandler;
 
 int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
@@ -227,100 +279,131 @@
   return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, EsPid);
 }
 
+int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
+{
+  return CaDescriptorHandler.GetCaPids(Source, Transponder, ServiceId, CaSystemIds, BufSize, Pids);
+}
+
+int GetPmtPid(int Source, int Transponder, int ServiceId)
+{
+  return CaDescriptorHandler.GetPmtPid(Source, Transponder, ServiceId);
+}
+
 // --- cPatFilter ------------------------------------------------------------
 
+//#define DEBUG_PAT_PMT
+#ifdef DEBUG_PAT_PMT
+#define DBGLOG(a...) { cString s = cString::sprintf(a); fprintf(stderr, "%s\n", *s); dsyslog("%s", *s); }
+#else
+#define DBGLOG(a...)
+#endif
+
 cPatFilter::cPatFilter(void)
 {
-  pmtIndex = 0;
-  pmtPid = 0;
-  pmtSid = 0;
-  lastPmtScan = 0;
-  numPmtEntries = 0;
+  Trigger(0);
   Set(0x00, 0x00);  // PAT
 }
 
 void cPatFilter::SetStatus(bool On)
 {
+  cMutexLock MutexLock(&mutex);
+  DBGLOG("PAT filter set status %d", On);
   cFilter::SetStatus(On);
-  pmtIndex = 0;
-  pmtPid = 0;
-  pmtSid = 0;
-  lastPmtScan = 0;
-  numPmtEntries = 0;
+  Trigger();
 }
 
-void cPatFilter::Trigger(void)
+void cPatFilter::Trigger(int Sid)
 {
+  cMutexLock MutexLock(&mutex);
+  patVersion = -1;
+  pmtIndex = -1;
   numPmtEntries = 0;
+  if (Sid >= 0) {
+     sid = Sid;
+     DBGLOG("PAT filter trigger SID %d", Sid);
+     }
 }
 
-bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version)
+bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion)
 {
-  uint64_t v = Version;
-  v <<= 32;
-  uint64_t id = (PmtPid | (Sid << 16)) & 0x00000000FFFFFFFFLL;
+  int Id = MakePmtId(PmtPid, Sid);
   for (int i = 0; i < numPmtEntries; i++) {
-      if ((pmtVersion[i] & 0x00000000FFFFFFFFLL) == id) {
-         bool Changed = (pmtVersion[i] & 0x000000FF00000000LL) != v;
-         if (Changed)
-            pmtVersion[i] = id | v;
-         return Changed;
+      if (pmtId[i] == Id) {
+         if (pmtVersion[i] != Version) {
+            if (SetNewVersion)
+               pmtVersion[i] = Version;
+            else
+               DBGLOG("PMT %d  %2d %5d %2d -> %2d", Transponder(), i, PmtPid, pmtVersion[i], Version);
+            return true;
+            }
+         break;
          }
       }
-  if (numPmtEntries < MAXPMTENTRIES)
-     pmtVersion[numPmtEntries++] = id | v;
-  return true;
+  return false;
+}
+
+void cPatFilter::SwitchToNextPmtPid(void)
+{
+  if (pmtIndex >= 0) {
+     Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
+     pmtIndex = (pmtIndex + 1) % numPmtEntries;
+     Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
+     }
 }
 
 void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
 {
+  cMutexLock MutexLock(&mutex);
   if (Pid == 0x00) {
-     if (Tid == 0x00) {
-        if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
-           Del(pmtPid, 0x02);
-           pmtPid = 0;
-           pmtIndex++;
-           lastPmtScan = time(NULL);
-           }
-        if (!pmtPid) {
-           SI::PAT pat(Data, false);
-           if (!pat.CheckCRCAndParse())
-              return;
+     if (Tid == SI::TableIdPAT) {
+        SI::PAT pat(Data, false);
+        if (!pat.CheckCRCAndParse())
+           return;
+        if (pat.getVersionNumber() != patVersion) {
+           DBGLOG("PAT %d %d -> %d", Transponder(), patVersion, pat.getVersionNumber());
+           if (pmtIndex >= 0) {
+              Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
+              pmtIndex = -1;
+              }
+           numPmtEntries = 0;
            SI::PAT::Association assoc;
-           int Index = 0;
            for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
-               if (!assoc.isNITPid()) {
-                  if (Index++ >= pmtIndex && Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId())) {
-                     pmtPid = assoc.getPid();
-                     pmtSid = assoc.getServiceId();
-                     Add(pmtPid, 0x02);
-                     break;
+               if (!assoc.isNITPid() && numPmtEntries < MAXPMTENTRIES) {
+                  DBGLOG("    PMT pid %2d %5d  SID %5d", numPmtEntries, assoc.getPid(), assoc.getServiceId());
+                  pmtId[numPmtEntries] = MakePmtId(assoc.getPid(), assoc.getServiceId());
+                  pmtVersion[numPmtEntries] = -1;
+                  if (sid == assoc.getServiceId()) {
+                     pmtIndex = numPmtEntries;
+                     DBGLOG("sid = %d pmtIndex = %d", sid, pmtIndex);
                      }
+                  numPmtEntries++;
                   }
                }
-           if (!pmtPid)
+           if (numPmtEntries > 0 && pmtIndex < 0)
               pmtIndex = 0;
+           Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
+           patVersion = pat.getVersionNumber();
+           timer.Set(PMT_SCAN_TIMEOUT);
            }
         }
      }
-  else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) {
+  else if (Tid == SI::TableIdPMT && Source() && Transponder()) {
+     timer.Set(PMT_SCAN_TIMEOUT);
      SI::PMT pmt(Data, false);
      if (!pmt.CheckCRCAndParse())
         return;
-     if (pmt.getServiceId() != pmtSid)
-        return; // skip broken PMT records
-     if (!PmtVersionChanged(pmtPid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
-        lastPmtScan = 0; // this triggers the next scan
+     if (!PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
+        SwitchToNextPmtPid();
         return;
         }
-     if (!Channels.Lock(true, 10)) {
-        numPmtEntries = 0; // to make sure we try again
+     if (!Channels.Lock(true, 10))
         return;
-        }
+     PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true);
+     SwitchToNextPmtPid();
      cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
      if (Channel) {
         SI::CaDescriptor *d;
-        cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid());
+        cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid(), Pid);
         // Scan the common loop:
         for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
             CaDescriptors->AddCaDescriptor(d, 0);
@@ -552,7 +635,12 @@
            }
         Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
         }
-     lastPmtScan = 0; // this triggers the next scan
      Channels.Unlock();
      }
+  if (timer.TimedOut()) {
+     if (pmtIndex >= 0)
+        DBGLOG("PMT timeout %d", pmtIndex);
+     SwitchToNextPmtPid();
+     timer.Set(PMT_SCAN_TIMEOUT);
+     }
 }
diff -ruN vdr-2.0.0/pat.h vdr-2.2.0/pat.h
--- vdr-2.0.0/pat.h	2013-02-16 16:20:24.000000000 +0100
+++ vdr-2.2.0/pat.h	2015-01-04 14:17:22.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: pat.h 2.3 2013/02/16 15:20:24 kls Exp $
+ * $Id: pat.h 3.4 2015/01/04 13:17:22 kls Exp $
  */
 
 #ifndef __PAT_H
@@ -12,34 +12,51 @@
 
 #include <stdint.h>
 #include "filter.h"
+#include "thread.h"
 
 #define MAXPMTENTRIES 64
 
 class cPatFilter : public cFilter {
 private:
-  time_t lastPmtScan;
+  cMutex mutex;
+  cTimeMs timer;
+  int patVersion;
   int pmtIndex;
-  int pmtPid;
-  int pmtSid;
-  uint64_t pmtVersion[MAXPMTENTRIES];
+  int pmtId[MAXPMTENTRIES];
+  int pmtVersion[MAXPMTENTRIES];
   int numPmtEntries;
-  bool PmtVersionChanged(int PmtPid, int Sid, int Version);
+  int sid;
+  int GetPmtPid(int Index) { return pmtId[Index] & 0x0000FFFF; }
+  int MakePmtId(int PmtPid, int Sid) { return PmtPid | (Sid << 16); }
+  bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false);
+  void SwitchToNextPmtPid(void);
 protected:
   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
 public:
   cPatFilter(void);
   virtual void SetStatus(bool On);
-  void Trigger(void);
+  void Trigger(int Sid = -1);
   };
 
 int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
          ///< Gets all CA descriptors for a given channel.
          ///< Copies all available CA descriptors for the given Source, Transponder and ServiceId
          ///< into the provided buffer at Data (at most BufSize bytes). Only those CA descriptors
-         ///< are copied that match one of the given CA system IDs.
+         ///< are copied that match one of the given CA system IDs (or all of them, if CaSystemIds
+         ///< is 0xFFFF).
          ///< Returns the number of bytes copied into Data (0 if no CA descriptors are
          ///< available), or -1 if BufSize was too small to hold all CA descriptors.
-         ///< The return value tells whether these CA descriptors are to be used
-         ///< for the individual streams.
+
+int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids);
+         ///< Gets all CA pids for a given channel.
+         ///< Copies all available CA pids from the CA descriptors for the given Source, Transponder and ServiceId
+         ///< into the provided buffer at Pids (at most BufSize - 1 entries, the list will be zero-terminated).
+         ///< Only the CA pids of those CA descriptors are copied that match one of the given CA system IDs
+         ///< (or all of them, if CaSystemIds is 0xFFFF).
+         ///< Returns the number of pids copied into Pids (0 if no CA descriptors are
+         ///< available), or -1 if BufSize was too small to hold all CA pids.
+
+int GetPmtPid(int Source, int Transponder, int ServiceId);
+         ///< Gets the Pid of the PMT in which the CA descriptors for this channel are defined.
 
 #endif //__PAT_H
diff -ruN vdr-2.0.0/player.c vdr-2.2.0/player.c
--- vdr-2.0.0/player.c	2012-04-28 13:52:50.000000000 +0200
+++ vdr-2.2.0/player.c	2012-04-28 13:52:50.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: player.c 2.2 2012/04/28 11:52:50 kls Exp $
+ * $Id: player.c 3.0 2012/04/28 11:52:50 kls Exp $
  */
 
 #include "player.h"
diff -ruN vdr-2.0.0/player.h vdr-2.2.0/player.h
--- vdr-2.0.0/player.h	2012-04-28 15:04:17.000000000 +0200
+++ vdr-2.2.0/player.h	2013-12-25 14:25:02.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: player.h 2.6 2012/04/28 13:04:17 kls Exp $
+ * $Id: player.h 3.1 2013/12/25 13:25:02 kls Exp $
  */
 
 #ifndef __PLAYER_H
@@ -27,7 +27,7 @@
   bool DeviceFlush(int TimeoutMs = 0) { return device ? device->Flush(TimeoutMs) : true; }
   bool DeviceHasIBPTrickSpeed(void) { return device ? device->HasIBPTrickSpeed() : false; }
   bool DeviceIsPlayingVideo(void) { return device ? device->IsPlayingVideo() : false; }
-  void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
+  void DeviceTrickSpeed(int Speed, bool Forward) { if (device) device->TrickSpeed(Speed, Forward); }
   void DeviceClear(void) { if (device) device->Clear(); }
   void DevicePlay(void) { if (device) device->Play(); }
   void DeviceFreeze(void) { if (device) device->Freeze(); }
diff -ruN vdr-2.0.0/plugin.c vdr-2.2.0/plugin.c
--- vdr-2.0.0/plugin.c	2012-09-01 15:10:27.000000000 +0200
+++ vdr-2.2.0/plugin.c	2012-09-01 15:10:27.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: plugin.c 2.4 2012/09/01 13:10:27 kls Exp $
+ * $Id: plugin.c 3.0 2012/09/01 13:10:27 kls Exp $
  */
 
 #include "plugin.h"
diff -ruN vdr-2.0.0/plugin.h vdr-2.2.0/plugin.h
--- vdr-2.0.0/plugin.h	2012-09-01 15:08:54.000000000 +0200
+++ vdr-2.2.0/plugin.h	2012-09-01 15:08:54.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: plugin.h 2.2 2012/09/01 13:08:54 kls Exp $
+ * $Id: plugin.h 3.0 2012/09/01 13:08:54 kls Exp $
  */
 
 #ifndef __PLUGIN_H
diff -ruN vdr-2.0.0/po/ar.po vdr-2.2.0/po/ar.po
--- vdr-2.0.0/po/ar.po	2013-03-11 14:10:16.000000000 +0100
+++ vdr-2.2.0/po/ar.po	2015-02-19 10:12:22.263195225 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Osama Alrawab <alrawab@hotmail.com>, 2010, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2008-10-16 11:16-0400\n"
 "Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
 "Language-Team: Arabic <ar@li.org>\n"
@@ -23,11 +23,14 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** قناة خاطئة ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "القناة غير متاحة"
 
 msgid "Can't start Transfer Mode!"
-msgstr "تعذر  بدء طور النقل"
+msgstr "تعذر بدء طور النقل"
 
 msgid "off"
 msgstr "ايقاف"
@@ -80,8 +83,17 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
-msgstr "EPG ا لبدء بالبحث على دليل القنوات الالكترونى "
+msgstr "EPG ا لبدء بالبحث على دليل القنوات الالكترونى"
 
 msgid "Content$Movie/Drama"
 msgstr "Movie/Drama"
@@ -336,7 +348,7 @@
 msgstr "ara"
 
 msgid "Phase 1: Detecting RC code type"
-msgstr "التحقق من مفاتيح  وحدة التحكم"
+msgstr "التحقق من مفاتيح وحدة التحكم"
 
 msgid "Press any key on the RC unit"
 msgstr "الرجاء الضغط على اى زر فى وحدة التحكم"
@@ -502,10 +514,10 @@
 msgstr "المستخدم 4"
 
 msgid "Key$User5"
-msgstr "المستخدم 5 "
+msgstr "المستخدم 5"
 
 msgid "Key$User6"
-msgstr "المستخدم 6 "
+msgstr "المستخدم 6"
 
 msgid "Key$User7"
 msgstr "المستخدم 7"
@@ -567,6 +579,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "اعدادات القناة غبر موحد"
 
@@ -607,8 +625,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "'%c'! اسم المجلد لا يجب ان يحتوى على"
 
-msgid "Button$Select"
-msgstr "الزر"
+msgid "Button$Open"
+msgstr "افتح"
 
 msgid "Delete folder and all sub folders?"
 msgstr "حذف المجلد وكل المجلدات الفرعية"
@@ -712,7 +730,7 @@
 
 #, c-format
 msgid "This event - %s"
-msgstr " %sهذا الحدث"
+msgstr "%sهذا الحدث"
 
 msgid "This event - all channels"
 msgstr "هذا الحدث لكل القنوات"
@@ -727,6 +745,76 @@
 msgid "CAM not responding!"
 msgstr "الكامة لا تستجيب"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "معلومات التسجبل"
 
@@ -739,9 +827,6 @@
 msgid "Recordings"
 msgstr "التسجيلات"
 
-msgid "Button$Open"
-msgstr "افتح"
-
 msgid "Commands"
 msgstr "الاوامر"
 
@@ -773,10 +858,10 @@
 msgstr "اللغة"
 
 msgid "Setup.OSD$Skin"
-msgstr " الثوب"
+msgstr "الثوب"
 
 msgid "Setup.OSD$Theme"
-msgstr " الثيمة"
+msgstr "الثيمة"
 
 msgid "Setup.OSD$Left (%)"
 msgstr "Left (%)"
@@ -818,7 +903,7 @@
 msgstr "Fixed font size (%)"
 
 msgid "Setup.OSD$Channel info position"
-msgstr " موضع معلومات القناة"
+msgstr "موضع معلومات القناة"
 
 msgid "bottom"
 msgstr "اسفل"
@@ -878,7 +963,7 @@
 msgstr "اﻻنتهاء من البحث على الدليل الالكترونى للقنوات"
 
 msgid "Setup.EPG$EPG bugfix level"
-msgstr " معدل اصلاح اخطاء الدليل الالكترونى"
+msgstr "معدل اصلاح اخطاء الدليل الالكترونى"
 
 msgid "Setup.EPG$EPG linger time (min)"
 msgstr "اقل مدة للدليل الالكترونى"
@@ -940,7 +1025,7 @@
 msgstr "Standard compliance"
 
 msgid "Setup.DVB$Video format"
-msgstr "نوع الفيديو  "
+msgstr "نوع الفيديو"
 
 msgid "Setup.DVB$Video display format"
 msgstr "نوع عرض الفديو"
@@ -979,7 +1064,7 @@
 msgstr "الاقط"
 
 msgid "Setup.LNB$Use DiSEqC"
-msgstr " DiSEqC استعمل"
+msgstr "DiSEqC استعمل"
 
 msgid "Setup.LNB$SLOF (MHz)"
 msgstr "التذبذب المحلى للاقط م هرتز"
@@ -997,6 +1082,33 @@
 msgid "Setup.LNB$own"
 msgstr "own"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "اعادة تشغيل الكامة"
 
@@ -1006,8 +1118,18 @@
 msgid "CAM ready"
 msgstr "الكامة جاهزة"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
-msgstr "الكامة "
+msgstr "الكامة"
+
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
 
 msgid "Button$Menu"
 msgstr "القائمة"
@@ -1021,6 +1143,9 @@
 msgid "Can't open CAM menu!"
 msgstr "تعذر فتح قائمة الكامة"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "الكامة مستخدمة الان هل تريد اعادة تشغيلها"
 
@@ -1114,6 +1239,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pause replay when setting mark"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "رقم المواصلة"
 
@@ -1150,6 +1302,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "فعل الصوت"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Channels wrap"
 
@@ -1227,7 +1385,7 @@
 
 #. TRANSLATORS: note the trailing blank!
 msgid "Jump: "
-msgstr "اقفز الى  "
+msgstr "اقفز الى "
 
 msgid "No editing marks defined!"
 msgstr "لاتوجد علامات تعديل معرفة"
@@ -1314,7 +1472,7 @@
 msgstr "الصوت "
 
 msgid "Classic VDR"
-msgstr "VDR تقليدى  "
+msgstr "VDR تقليدى"
 
 msgid "DISK"
 msgstr "قرص"
@@ -1334,8 +1492,12 @@
 msgid "PLAY"
 msgstr "عرض"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
-msgstr "ST:TNG شاشة "
+msgstr "ST:TNG شاشة"
 
 #. TRANSLATORS: the first character of each weekday, beginning with monday
 msgid "MTWTFSS"
@@ -1376,7 +1538,7 @@
 msgstr "ابتداء التسجيل"
 
 msgid "VDR will shut down later - press Power to force"
-msgstr "البرنامج سيقوم بالاغلاق لاحقا اضغط زر  القفل لاتمام  عملية الاغلاق"
+msgstr "البرنامج سيقوم بالاغلاق لاحقا اضغط زر القفل لاتمام عملية الاغلاق"
 
 msgid "Press any key to cancel shutdown"
 msgstr "اضغط اى زر لالغاء اقفال البرنامج"
@@ -1395,7 +1557,7 @@
 
 #, c-format
 msgid "VDR will shut down in %s minutes"
-msgstr "البرنامج سيقوم بالاغلاق بعد%s دقيقة "
+msgstr "البرنامج سيقوم بالاغلاق بعد%s دقيقة"
 
 msgid "Disk"
 msgstr "القرص الصلب"
diff -ruN vdr-2.0.0/po/ca_ES.po vdr-2.2.0/po/ca_ES.po
--- vdr-2.0.0/po/ca_ES.po	2013-03-11 14:10:23.000000000 +0100
+++ vdr-2.2.0/po/ca_ES.po	2015-02-19 10:12:21.874178607 +0100
@@ -1,5 +1,5 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Marc Rovira Vall <tm05462@salleURL.edu>, 2003
 # Ramon Roca <ramon.roca@xcombo.com>, 2003
@@ -8,9 +8,9 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2008-03-02 19:02+0100\n"
 "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
 "Language-Team: Catalan <vdr@linuxtv.org>\n"
@@ -22,6 +22,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal incorrecte ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Canal no disponible!"
 
@@ -79,6 +82,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Iniciant exploraci� EPG"
 
@@ -566,6 +578,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Propietats del canal duplicades!"
 
@@ -606,8 +624,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "El nom de la carpeta no pot contenir '%c'"
 
-msgid "Button$Select"
-msgstr "Seleccionar"
+msgid "Button$Open"
+msgstr "Obrir"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Esborrar carpetas i totes les sub carpetes?"
@@ -726,6 +744,76 @@
 msgid "CAM not responding!"
 msgstr "CAM no respon"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Informaci� de la gravaci�"
 
@@ -738,9 +826,6 @@
 msgid "Recordings"
 msgstr "Veure programes gravats"
 
-msgid "Button$Open"
-msgstr "Obrir"
-
 msgid "Commands"
 msgstr "Ordres"
 
@@ -853,7 +938,7 @@
 msgstr "Sempre ordenar primer carpetes"
 
 msgid "Setup.OSD$Number keys for characters"
-msgstr "Tecles num�riques per a car�cters "
+msgstr "Tecles num�riques per a car�cters"
 
 msgid "Setup.OSD$Color key 0"
 msgstr "Tecla color 0"
@@ -996,6 +1081,33 @@
 msgid "Setup.LNB$own"
 msgstr "propi"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "Reiniciar CAM"
 
@@ -1005,9 +1117,19 @@
 msgid "CAM ready"
 msgstr "CAM preparat"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Men�"
 
@@ -1020,6 +1142,9 @@
 msgid "Can't open CAM menu!"
 msgstr "No puc obrir el men� de la CAM!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM en �s - reiniciar?"
 
@@ -1113,6 +1238,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pausar reproducci� en establir marca"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de Continuar"
 
@@ -1149,6 +1301,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volum inicial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Primer canal despr�s de l'ultim"
 
@@ -1195,7 +1353,7 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr " Cancel�lar l'edici� "
+msgstr " Cancel�lar l'edici�"
 
 msgid "Stop recording?"
 msgstr "Aturar la gravaci�?"
@@ -1333,6 +1491,10 @@
 msgid "PLAY"
 msgstr "REPRODUIR"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Quadres ST:TNG"
 
diff -ruN vdr-2.0.0/po/cs_CZ.po vdr-2.2.0/po/cs_CZ.po
--- vdr-2.0.0/po/cs_CZ.po	2013-03-11 14:10:35.000000000 +0100
+++ vdr-2.2.0/po/cs_CZ.po	2015-02-19 10:12:22.190192082 +0100
@@ -1,16 +1,16 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Vladimír Bárta <vladimir.barta@k2atmitec.cz>, 2006, 2008
 # Jiří Dobrý <jdobry@centrum.cz>, 2008
 # Radek Šťastný <dedkus@gmail.com>, 2010
-# Aleš Juřík <ajurik@quick.cz>, 2013
+# Aleš Juřík <ajurik@quick.cz>, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2010-05-06 11:00+0200\n"
 "Last-Translator: Aleš Juřík <ajurik@quick.cz>\n"
 "Language-Team: Czech <vdr@linuxtv.org>\n"
@@ -22,6 +22,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Neplatný kanál ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktivován"
+
 msgid "Channel not available!"
 msgstr "Kanál není dostupný!"
 
@@ -32,7 +35,7 @@
 msgstr "vyp."
 
 msgid "on"
-msgstr "zap. "
+msgstr "zap."
 
 msgid "auto"
 msgstr "auto"
@@ -79,6 +82,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "Systémový identifikátor T2"
+
+msgid "SISO/MISO"
+msgstr "Mód SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Začíná prohledávání EPG"
 
@@ -566,6 +578,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Nastavení kanálu není jedinečné!"
 
@@ -606,8 +624,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Jméno složky nesmí obsahovat '%c'!"
 
-msgid "Button$Select"
-msgstr "Vybrat"
+msgid "Button$Open"
+msgstr "Otevřít"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Smazat složku včetně podsložek?"
@@ -726,6 +744,76 @@
 msgid "CAM not responding!"
 msgstr "CAM neodpovídá!"
 
+msgid "Edit path"
+msgstr "Editace cesty"
+
+msgid "Folder"
+msgstr "Složka"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Tato složka je používána - změny nejsou možné!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Přesunout vloženou složku obsahující %d nahrávek?"
+
+msgid "Error while moving folder!"
+msgstr "Chyba při přesunu složky!"
+
+msgid "Edit recording"
+msgstr "Editace nahrávky"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Tato nahrávka je nyní používána - změny nejsou možné!"
+
+msgid "Button$Cancel cutting"
+msgstr "Zrušení střihu"
+
+msgid "Button$Stop cutting"
+msgstr "Zastavení střihu"
+
+msgid "Button$Cancel moving"
+msgstr "Zrušení přesunu"
+
+msgid "Button$Stop moving"
+msgstr "Zastavení přesunu"
+
+msgid "Button$Cancel copying"
+msgstr "Zrušení kopírování"
+
+msgid "Button$Stop copying"
+msgstr "Zastavené kopírování"
+
+msgid "Button$Cut"
+msgstr "Střih"
+
+msgid "Button$Delete marks"
+msgstr "Výmaz značek"
+
+msgid "Recording vanished!"
+msgstr "Nahrávka zmizela!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Editovaná verze již existuje - přepsat?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Chyba při vložení nahrávky do fronty pro střih"
+
+msgid "Rename recording to folder name?"
+msgstr "Přejmenovat nahrávku jménem složky?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Samzat editační značky této nahrávky?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Chyba při mazání editačních značek!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Chyba při změně priority/životnosti!"
+
+msgid "Error while changing folder/name!"
+msgstr "Chyba při změně složky/jména!"
+
 msgid "Recording info"
 msgstr "Detail nahrávky"
 
@@ -738,9 +826,6 @@
 msgid "Recordings"
 msgstr "Nahrávky"
 
-msgid "Button$Open"
-msgstr "Otevřít"
-
 msgid "Commands"
 msgstr "Příkazy"
 
@@ -996,6 +1081,33 @@
 msgid "Setup.LNB$own"
 msgstr "vlastní"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Použít anténní pozicionér?"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Zeměpisná šířka místa (stupně)"
+
+msgid "South"
+msgstr "Jih"
+
+msgid "North"
+msgstr "Sever"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Zeměpisná délka místa (stupně)"
+
+msgid "West"
+msgstr "Západ"
+
+msgid "East"
+msgstr "Východ"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. otočení pozicionéru (stupně)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Rychlost pozicionéru (stupně/vteřinu)"
+
 msgid "CAM reset"
 msgstr "Reset CAMu"
 
@@ -1005,9 +1117,19 @@
 msgid "CAM ready"
 msgstr "CAM připraven"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktivuji)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Zrušení aktivace"
+
+msgid "Button$Activate"
+msgstr "Aktivace"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1020,6 +1142,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Menu CAM není dostupné"
 
+msgid "Can't activate CAM!"
+msgstr "Nemohu aktivovat CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM se používá - opravdu restartovat?"
 
@@ -1113,6 +1238,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pozastavit přehrávání při nastavování značky"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pozastavit přehrávání při skoku na značku"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Přeskočit editavané části"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pozastavit přehrávání na poslední značce"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Trvání úvodního času pro adaptivní přeskakování (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Doba pro vynulování adaptivního přeskakování (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Změň chování adaptivního přeskakování"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Použití tlačítek Zpšt/Dále pro adaptivní přeskakování"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Velikost skoku za použití tlačítek Zelená/Žlutá (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Velikost skoku za použití tlačítek Zelená/Žlutá při opakování (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID obnovení"
 
@@ -1149,6 +1301,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hlasitost po spuštění"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Krok hlasitosti"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Linearizace hlasitosti"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Přecházet z konce na začátek seznamu kanálů"
 
@@ -1333,6 +1491,10 @@
 msgid "PLAY"
 msgstr "PŘEHRÁVÁNÍ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Otáčení antény na %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG konzola"
 
diff -ruN vdr-2.0.0/po/da_DK.po vdr-2.2.0/po/da_DK.po
--- vdr-2.0.0/po/da_DK.po	2013-03-11 14:11:19.000000000 +0100
+++ vdr-2.2.0/po/da_DK.po	2015-02-19 10:12:21.957182179 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Mogens Elneff <mogens@elneff.dk>, 2004, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
 "Language-Team: Danish <vdr@linuxtv.org>\n"
@@ -19,6 +19,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Ugyldig kanal! ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal er ikke tilg�ngelig!"
 
@@ -76,6 +79,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Starter EPG skanning"
 
@@ -563,6 +575,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Kanalindstillinger er ikke entydige!"
 
@@ -603,8 +621,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr ""
 
-msgid "Button$Select"
-msgstr ""
+msgid "Button$Open"
+msgstr "�bn"
 
 msgid "Delete folder and all sub folders?"
 msgstr ""
@@ -723,6 +741,76 @@
 msgid "CAM not responding!"
 msgstr "CAM svarer ikke!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Optagelses info"
 
@@ -735,9 +823,6 @@
 msgid "Recordings"
 msgstr "Optagelser"
 
-msgid "Button$Open"
-msgstr "�bn"
-
 msgid "Commands"
 msgstr "Kommandoer"
 
@@ -993,6 +1078,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "CAM nulstil"
 
@@ -1002,9 +1114,19 @@
 msgid "CAM ready"
 msgstr "CAM klar"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1017,6 +1139,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Kan ikke �bne CAM menuen!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM er i brug - virkelig nulstille?"
 
@@ -1110,6 +1235,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Genoptagelses ID"
 
@@ -1146,6 +1298,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Lydstyrke ved opstart"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr ""
 
@@ -1330,6 +1488,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG konsol"
 
diff -ruN vdr-2.0.0/po/de_DE.po vdr-2.2.0/po/de_DE.po
--- vdr-2.0.0/po/de_DE.po	2013-03-11 14:11:24.000000000 +0100
+++ vdr-2.2.0/po/de_DE.po	2015-02-19 10:12:22.401201125 +0100
@@ -1,14 +1,15 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Klaus Schmidinger <vdr@tvdr.de>, 2000-2008
+# Klaus Schmidinger <vdr@tvdr.de>, 2000-2015
+# Albert Danis <a.danis@gmx.de>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2010-01-16 16:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-10 13:45+0100\n"
 "Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
 "Language-Team: German <vdr@linuxtv.org>\n"
 "Language: de\n"
@@ -19,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Ung�ltiger Kanal ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktiviert!"
+
 msgid "Channel not available!"
 msgstr "Kanal nicht verf�gbar!"
 
@@ -74,7 +78,16 @@
 msgstr "Rolloff"
 
 msgid "StreamId"
-msgstr "StreamId"
+msgstr "Stream-ID"
+
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2-System-ID"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
 
 msgid "Starting EPG scan"
 msgstr "Aktualisiere EPG-Daten"
@@ -563,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Kanaleinstellungen sind nicht eindeutig!"
 
@@ -603,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Verzeichnisname darf kein '%c' enthalten!"
 
-msgid "Button$Select"
-msgstr "Ausw�hlen"
+msgid "Button$Open"
+msgstr "�ffnen"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Verzeichnis und alle Unterverzeichnisse l�schen?"
@@ -723,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM antwortet nicht!"
 
+msgid "Edit path"
+msgstr "Pfad editieren"
+
+msgid "Folder"
+msgstr "Ordner"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Dieser Ordner ist zur Zeit in Verwendung - es sind keine �nderungen m�glich!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Ganzen Ordner mit %d Aufnahmen verschieben?"
+
+msgid "Error while moving folder!"
+msgstr "Fehler beim Verschieben des Ordners!"
+
+msgid "Edit recording"
+msgstr "Aufnahme editieren"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Diese Aufnahme ist zur Zeit in Verwendung - es sind keine �nderungen m�glich!"
+
+msgid "Button$Cancel cutting"
+msgstr "Schnitt abbrechen"
+
+msgid "Button$Stop cutting"
+msgstr "Schnitt beenden"
+
+msgid "Button$Cancel moving"
+msgstr "Verschieben abbrechen"
+
+msgid "Button$Stop moving"
+msgstr "Verschieben beenden"
+
+msgid "Button$Cancel copying"
+msgstr "Kopieren abbrechen"
+
+msgid "Button$Stop copying"
+msgstr "Kopieren beenden"
+
+msgid "Button$Cut"
+msgstr "Schneiden"
+
+msgid "Button$Delete marks"
+msgstr "Marken l�schen"
+
+msgid "Recording vanished!"
+msgstr "Aufnahme verschwunden!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Geschnittene Version existiert bereits - �berschreiben?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Fehler beim Hinzuf�gen der Aufnahme zur Schnittwarteschlange"
+
+msgid "Rename recording to folder name?"
+msgstr "Aufnahme in Ordnername umbenennen?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Schnittmarken f�r diese Aufnahme l�schen?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Fehler beim L�schen der Schnittmarken!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Fehler beim �ndern der Priorit�t bzw. Lebensdauer!"
+
+msgid "Error while changing folder/name!"
+msgstr "Fehler beim �ndern des Ordners bzw. Namens!"
+
 msgid "Recording info"
 msgstr "Aufzeichnung"
 
@@ -735,9 +824,6 @@
 msgid "Recordings"
 msgstr "Aufzeichnungen"
 
-msgid "Button$Open"
-msgstr "�ffnen"
-
 msgid "Commands"
 msgstr "Befehle"
 
@@ -745,7 +831,7 @@
 msgstr "Aufzeichnung l�schen?"
 
 msgid "Recording is being edited - really delete?"
-msgstr "Aufzeichnung wird geschnitten - trotzdem l�schen?"
+msgstr "Aufzeichnung wird bearbeitet - trotzdem l�schen?"
 
 msgid "Error while deleting recording!"
 msgstr "Fehler beim L�schen der Aufzeichnung!"
@@ -874,7 +960,7 @@
 msgstr "Zeit bis zur EPG-Aktualisierung (h)"
 
 msgid "Setup.EPG$EPG bugfix level"
-msgstr "EPG-Fehlerbereinigung"
+msgstr "EPG-Fehlerbereinigungsstufe"
 
 msgid "Setup.EPG$EPG linger time (min)"
 msgstr "Alte EPG-Daten anzeigen (min)"
@@ -993,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "eigenes"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Antennen-Positionierer benutzen"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Standort Breitengrad"
+
+msgid "South"
+msgstr "S�d"
+
+msgid "North"
+msgstr "Nord"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Standort L�ngengrad"
+
+msgid "West"
+msgstr "West"
+
+msgid "East"
+msgstr "Ost"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. Positionier-Winkel (Grad)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Max. Positionier-Geschwindigkeit (Grad/s)"
+
 msgid "CAM reset"
 msgstr "CAM zur�ckgesetzt"
 
@@ -1002,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM bereit"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (wird aktiviert)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Aktivierung abbrechen"
+
+msgid "Button$Activate"
+msgstr "Aktivieren"
+
 msgid "Button$Menu"
 msgstr "Men�"
 
@@ -1017,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "CAM-Men� kann nicht ge�ffnet werden!"
 
+msgid "Can't activate CAM!"
+msgstr "CAM kann nicht aktiviert werden!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM wird benutzt - wirklich zur�cksetzen?"
 
@@ -1110,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pause beim Setzen einer Schnittmarke"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pause beim Sprung auf eine Schnittmarke"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Herausgeschnittene Teile �berspringen"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pause an der letzten Schnittmarke"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Anfangswert beim adaptiven Springen (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Zeitlimit beim adaptiven Springen (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Alternatives Verhalten beim adapt. Springen"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Adaptives Springen mit Zur�ck/Vorw�rts-Tasten"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Sprungweite mit Taste Gr�n/Gelb (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Sprungweite mit Taste Gr�n/Gelb bei Wiederh. (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Wiedergabe-ID"
 
@@ -1126,7 +1279,7 @@
 msgstr "SVDRP trennen bei Inaktivit�t (s)"
 
 msgid "Setup.Miscellaneous$Zap timeout (s)"
-msgstr "Mindestzeit f�r Kanalhistorie (s)"
+msgstr "Mindestzeit f�r vorherigen Kanal (s)"
 
 msgid "Setup.Miscellaneous$Channel entry timeout (ms)"
 msgstr "Zeitlimit f�r Kanaleingabe (ms)"
@@ -1146,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Lautst�rke beim Einschalten"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Anzahl Lautst�rke Schritte"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Lautst�rke Kurve korrigieren"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Rundum zappen"
 
@@ -1192,13 +1351,13 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr " Schneiden abbrechen"
+msgstr " Bearbeitung abbrechen"
 
 msgid "Stop recording?"
 msgstr "Aufzeichnung beenden?"
 
 msgid "Cancel editing?"
-msgstr "Schneiden abbrechen?"
+msgstr "Bearbeitung abbrechen?"
 
 msgid "No audio available!"
 msgstr "Kein Audio verf�gbar!"
@@ -1280,7 +1439,7 @@
 msgstr "Ausschalten unm�glich - Option '-s' fehlt!"
 
 msgid "Editing - shut down anyway?"
-msgstr "Schnitt l�uft - trotzdem ausschalten?"
+msgstr "Bearbeitung l�uft - trotzdem ausschalten?"
 
 msgid "Recording - shut down anyway?"
 msgstr "Aufnahme l�uft - trotzdem ausschalten?"
@@ -1297,7 +1456,7 @@
 msgstr "Plugin %s wacht in %ld Min auf, weiter?"
 
 msgid "Editing - restart anyway?"
-msgstr "Schnitt l�uft - trotzdem neu starten?"
+msgstr "Bearbeitung l�uft - trotzdem neu starten?"
 
 msgid "Recording - restart anyway?"
 msgstr "Aufnahme l�uft - trotzdem neu starten?"
@@ -1330,6 +1489,10 @@
 msgid "PLAY"
 msgstr "WIEDERGABE"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Antenne wird auf %.1f gedreht..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG-Konsolen"
 
@@ -1339,7 +1502,7 @@
 
 #. TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!)
 msgid "MonTueWedThuFriSatSun"
-msgstr "MonDieMitDonFreSamSon"
+msgstr "Mo.Di.Mi.Do.Fr.Sa.So."
 
 msgid "Monday"
 msgstr "Montag"
@@ -1372,7 +1535,7 @@
 msgstr "Aufzeichnung gestartet"
 
 msgid "VDR will shut down later - press Power to force"
-msgstr "VDR schaltet sp�ter aus - Power zum erzwingen"
+msgstr "VDR schaltet sp�ter aus - Power zum Erzwingen"
 
 msgid "Press any key to cancel shutdown"
 msgstr "Taste dr�cken, um Ausschalten abzubrechen"
@@ -1381,10 +1544,10 @@
 msgstr "Prim�res Interface wird umgeschaltet..."
 
 msgid "Editing process failed!"
-msgstr "Schnitt gescheitert!"
+msgstr "Bearbeitung gescheitert!"
 
 msgid "Editing process finished"
-msgstr "Schnitt beendet"
+msgstr "Bearbeitung beendet"
 
 msgid "Press any key to cancel restart"
 msgstr "Taste dr�cken, um Neustart abzubrechen"
diff -ruN vdr-2.0.0/po/el_GR.po vdr-2.2.0/po/el_GR.po
--- vdr-2.0.0/po/el_GR.po	2013-03-11 14:11:28.000000000 +0100
+++ vdr-2.2.0/po/el_GR.po	2015-02-19 10:12:22.057186423 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Dimitrios Dimitrakos <mail@dimitrios.de>, 2002, 2006
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
 "Language-Team: Greek <vdr@linuxtv.org>\n"
@@ -19,6 +19,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** ����� ������ ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "�� ������ ��� ����� ���������!"
 
@@ -76,6 +79,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "���� ������ EPG"
 
@@ -563,6 +575,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "�� ��������� ��� �������� ����������������!"
 
@@ -603,8 +621,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr ""
 
-msgid "Button$Select"
-msgstr ""
+msgid "Button$Open"
+msgstr "�������"
 
 msgid "Delete folder and all sub folders?"
 msgstr ""
@@ -723,6 +741,76 @@
 msgid "CAM not responding!"
 msgstr ""
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "����������� E�������"
 
@@ -735,9 +823,6 @@
 msgid "Recordings"
 msgstr "��������"
 
-msgid "Button$Open"
-msgstr "�������"
-
 msgid "Commands"
 msgstr "�������"
 
@@ -993,6 +1078,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr ""
 
@@ -1002,9 +1114,19 @@
 msgid "CAM ready"
 msgstr ""
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "M����"
 
@@ -1017,6 +1139,9 @@
 msgid "Can't open CAM menu!"
 msgstr "������� � �������� ��� CAM �����!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr ""
 
@@ -1110,6 +1235,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID ������������"
 
@@ -1146,6 +1298,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr ""
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr ""
 
@@ -1330,6 +1488,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "������ ST:TNG"
 
diff -ruN vdr-2.0.0/po/es_ES.po vdr-2.2.0/po/es_ES.po
--- vdr-2.0.0/po/es_ES.po	2013-03-11 14:11:35.000000000 +0100
+++ vdr-2.2.0/po/es_ES.po	2015-02-19 10:12:21.902179822 +0100
@@ -1,16 +1,16 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Ruben Nunez Francisco <ruben.nunez@tang-it.com>, 2002, 2006
 # Luca Olivetti <luca@ventoso.org>, 2008, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2008-03-02 19:02+0100\n"
-"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-19 23:00+0100\n"
+"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
 "Language-Team: Spanish <vdr@linuxtv.org>\n"
 "Language: es\n"
 "MIME-Version: 1.0\n"
@@ -18,7 +18,10 @@
 "Content-Transfer-Encoding: 8bit\n"
 
 msgid "*** Invalid Channel ***"
-msgstr "*** Canal no v�lido ***"
+msgstr "*** Canal no valido ***"
+
+msgid "CAM activated!"
+msgstr "CAM activada!"
 
 msgid "Channel not available!"
 msgstr "�Canal no disponible!"
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Piloto"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Iniciando la exploraci�n de EPG"
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "!Propiedades de canal duplicadas!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "�El nombre de la carpeta no puede contener '%c'!"
 
-msgid "Button$Select"
-msgstr "Seleccionar"
+msgid "Button$Open"
+msgstr "Abrir"
 
 msgid "Delete folder and all sub folders?"
 msgstr "�Borrar carpeta y todas sub carpetas?"
@@ -689,7 +707,7 @@
 msgstr "�Qu� hay ahora?"
 
 msgid "What's on next?"
-msgstr "�Qu� hay despu�s?"
+msgstr "�Quiz� hay despu�s?"
 
 msgid "Button$Next"
 msgstr "Despu�s"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "�CAM no responde!"
 
+msgid "Edit path"
+msgstr "Editar path"
+
+msgid "Folder"
+msgstr "Carpeta"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Esta carpeta esta en uso - no se pueden hacer cambios!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Mueve la carpeta entera y su contenido %d grabaciones"
+
+msgid "Error while moving folder!"
+msgstr "Error al mover la carpeta!"
+
+msgid "Edit recording"
+msgstr "Editar grabaci�n"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Esta grabaci�n esta en uso - no se pueden hacer cambios!"
+
+msgid "Button$Cancel cutting"
+msgstr "Cancelar corte"
+
+msgid "Button$Stop cutting"
+msgstr "Stop corte"
+
+msgid "Button$Cancel moving"
+msgstr "Cancelar mover"
+
+msgid "Button$Stop moving"
+msgstr "Stop mover"
+
+msgid "Button$Cancel copying"
+msgstr "Cancelar copiar"
+
+msgid "Button$Stop copying"
+msgstr "Stop copiar"
+
+msgid "Button$Cut"
+msgstr "Cortar"
+
+msgid "Button$Delete marks"
+msgstr "Eliminar marcas"
+
+msgid "Recording vanished!"
+msgstr "Grabaci�n desaparecida"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Se est� editando una versi�n - sobreescribir?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Error mientras la grabaci�n est� en cola!"
+
+msgid "Rename recording to folder name?"
+msgstr "Renombrar la grabaci�n a la carpeta nombre?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Eliminar las marcas de edic�n para esta grabaci�n?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Error mientras se borra las marcas de edici�n!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Error mientras cambias prioridades/directo!"
+
+msgid "Error while changing folder/name!"
+msgstr "Error mientras cambias carpeta/nombre"
+
 msgid "Recording info"
 msgstr "Informaci�n de grabaci�n"
 
@@ -736,11 +824,8 @@
 msgid "Recordings"
 msgstr "Grabaciones"
 
-msgid "Button$Open"
-msgstr "Abrir"
-
 msgid "Commands"
-msgstr "�rdenes"
+msgstr "�rdenes"
 
 msgid "Delete recording?"
 msgstr "�Eliminar grabaci�n?"
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "propio"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Usar el posicionador"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Latitud (grados)"
+
+msgid "South"
+msgstr "Sud"
+
+msgid "North"
+msgstr "Norte"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Longitud (grados)"
+
+msgid "West"
+msgstr "Oeste"
+
+msgid "East"
+msgstr "Este"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. oscilaci�n del posicionador ( grados)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Velocidad Posicionador (grados)"
+
 msgid "CAM reset"
 msgstr "Reset CAM"
 
@@ -1003,11 +1115,21 @@
 msgid "CAM ready"
 msgstr "CAM preparado"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (activando)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Cancelar activaci�n"
+
+msgid "Button$Activate"
+msgstr "Activado"
+
 msgid "Button$Menu"
-msgstr "Men�"
+msgstr "Menu"
 
 msgid "Button$Reset"
 msgstr "Reiniciar"
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "�No se puede abrir el men� CAM!"
 
+msgid "Can't activate CAM!"
+msgstr "No puedes activar CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM en uso - �reiniciar?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pausar reproducci�n al establecer marca"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pausa de reproducci�n al saltar a una marca"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Saltar partes editadas"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Repetici�n Pausa en �ltima marca"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Duraci�n inicial para saltar adaptativo ( s )"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Cambiar el tiempo de espera para saltar adaptativo (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Comportamiento alternativo para saltar adaptativo"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Use teclas Prev/Next para saltar de adaptaci�n"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Saltar distancia con teclas verde/amarillo (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Saltar distancia con teclas verde/amarillo en la repetici�n (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de continuaci�n"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volumen inicial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Volumen saltos"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Volumen lineal"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Primer canal despu�s del �ltimo"
 
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "REPRODUCIR"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Mover a %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Paneles ST:TNG"
 
diff -ruN vdr-2.0.0/po/et_EE.po vdr-2.2.0/po/et_EE.po
--- vdr-2.0.0/po/et_EE.po	2013-03-11 14:11:39.000000000 +0100
+++ vdr-2.2.0/po/et_EE.po	2015-02-19 10:12:21.851177873 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Arthur Konovalov <artlov@gmail.com>, 2004-2013
+# Arthur Konovalov <artlov@gmail.com>, 2004-2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
 "Language-Team: Estonian <vdr@linuxtv.org>\n"
@@ -17,7 +17,10 @@
 "Content-Transfer-Encoding: 8bit\n"
 
 msgid "*** Invalid Channel ***"
-msgstr "*** Vigane kanal ***"
+msgstr "*** Kehtetu kanal ***"
+
+msgid "CAM activated!"
+msgstr "CAM aktiveeritud!"
 
 msgid "Channel not available!"
 msgstr "Kanal ei ole kättesaadav!"
@@ -74,7 +77,16 @@
 msgstr "Rolloff"
 
 msgid "StreamId"
-msgstr "Stream-tunnus"
+msgstr "Vootunnus"
+
+msgid "Pilot"
+msgstr "Piloot"
+
+msgid "T2SystemId"
+msgstr "T2 tunnus"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
 
 msgid "Starting EPG scan"
 msgstr "EPG skaneerimine käivitatud"
@@ -471,7 +483,7 @@
 msgstr "Kanalid"
 
 msgid "Key$Timers"
-msgstr "Taimer"
+msgstr "Taimerid"
 
 msgid "Key$Recordings"
 msgstr "Salvestused"
@@ -563,6 +575,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Kanaliseaded ei ole unikaalsed!"
 
@@ -582,7 +600,7 @@
 msgstr "Märkimine"
 
 msgid "Channel is being used by a timer!"
-msgstr "Kanal on taimeri kasutuses!"
+msgstr "Kanal on taimeris kasutusel!"
 
 msgid "Delete channel?"
 msgstr "Kustutada kanal?"
@@ -603,8 +621,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Kausta nimi ei saa sisaldada '%c' sümbolit!"
 
-msgid "Button$Select"
-msgstr "Vali"
+msgid "Button$Open"
+msgstr "Avada"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Kustutada kaust ja kõik alamkaustad?"
@@ -658,7 +676,7 @@
 msgstr "Kausta valik"
 
 msgid "Timers"
-msgstr "Taimer"
+msgstr "Taimerid"
 
 msgid "Button$On/Off"
 msgstr "On/Off"
@@ -682,7 +700,7 @@
 msgstr "Salvestada"
 
 msgid "Button$Switch"
-msgstr "Valida"
+msgstr "Vali"
 
 msgid "What's on now?"
 msgstr "Hetkel eetris"
@@ -697,14 +715,14 @@
 msgstr "Hetkel"
 
 msgid "Button$Schedule"
-msgstr "Ajakava"
+msgstr "Kava"
 
 msgid "Can't switch channel!"
 msgstr "Kanali vahetus ei ole võimalik!"
 
 #, c-format
 msgid "Schedule - %s"
-msgstr "Ajakava - %s"
+msgstr "Kava - %s"
 
 #, c-format
 msgid "This event - %s"
@@ -723,6 +741,76 @@
 msgid "CAM not responding!"
 msgstr "CAM ei vasta"
 
+msgid "Edit path"
+msgstr "Muuda teed"
+
+msgid "Folder"
+msgstr "Kaust"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Kaust on juba kasutusel - muutmine ei ole võimalik!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Teisaldada kaust mis sisaldab %d salvestust?"
+
+msgid "Error while moving folder!"
+msgstr "Kausta teisaldamise viga!"
+
+msgid "Edit recording"
+msgstr "Salvestuse redigeerimine"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Salvestus hetkel kasutusel - muutmine ei ole võimalik!"
+
+msgid "Button$Cancel cutting"
+msgstr "Lõikamise tühistamine"
+
+msgid "Button$Stop cutting"
+msgstr "Lõikamise peatamine"
+
+msgid "Button$Cancel moving"
+msgstr "Teisaldamise tühistamine"
+
+msgid "Button$Stop moving"
+msgstr "Teisaldamise peatamine"
+
+msgid "Button$Cancel copying"
+msgstr "Kopeerimise tühistamine"
+
+msgid "Button$Stop copying"
+msgstr "Kopeerimise peatamine"
+
+msgid "Button$Cut"
+msgstr "Lõika"
+
+msgid "Button$Delete marks"
+msgstr "Markerite kustutamine"
+
+msgid "Recording vanished!"
+msgstr "Salvestus kadunud!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Redigeeritud versioon juba olemas - kirjutada üle?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Salvestuse lisamine lõikamiseks ebaõnnestus!"
+
+msgid "Rename recording to folder name?"
+msgstr "Nimetada salvestuse nimi kaustaks?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Kustutada selle salvestuse markerid?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Markerite kustutamine ebaõnnestus!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Prioriteedi/eluea muutmine ebaõnnestus!"
+
+msgid "Error while changing folder/name!"
+msgstr "Kausta/nime muutmine ebaõnnestus!"
+
 msgid "Recording info"
 msgstr "Salvestuse info"
 
@@ -735,9 +823,6 @@
 msgid "Recordings"
 msgstr "Salvestused"
 
-msgid "Button$Open"
-msgstr "Avada"
-
 msgid "Commands"
 msgstr "Käsud"
 
@@ -754,7 +839,7 @@
 msgstr "Salvestuse käsud"
 
 msgid "never"
-msgstr "ei"
+msgstr "ealeski"
 
 msgid "skin dependent"
 msgstr "kestast sõltuv"
@@ -763,7 +848,7 @@
 msgstr "alati"
 
 msgid "OSD"
-msgstr "Ekraanimenüü"
+msgstr "Ekraanikuva"
 
 msgid "Setup.OSD$Language"
 msgstr "Keel"
@@ -793,7 +878,7 @@
 msgstr "Väikese fondi kasutus"
 
 msgid "Setup.OSD$Anti-alias"
-msgstr "Fondi silumine"
+msgstr "Fondi sakitõrje"
 
 msgid "Setup.OSD$Default font"
 msgstr "Vaikefont"
@@ -933,7 +1018,7 @@
 msgstr "Esmane DVB seade"
 
 msgid "Setup.DVB$Standard compliance"
-msgstr "Standardivastavavus"
+msgstr "Standardivastavus"
 
 msgid "Setup.DVB$Video format"
 msgstr "TV külgsuhe"
@@ -993,6 +1078,33 @@
 msgid "Setup.LNB$own"
 msgstr "oma"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Antenni positsioneerija kasutamine"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Asukoha laiuskraad (°)"
+
+msgid "South"
+msgstr "lõunasse (S)"
+
+msgid "North"
+msgstr "põhja (N)"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Asukoha pikkuskraad (°)"
+
+msgid "West"
+msgstr "läände (W)"
+
+msgid "East"
+msgstr "itta (E)"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Positsioneerija pöördeulatus (°)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Positsioneerija kiirus (°/s)"
+
 msgid "CAM reset"
 msgstr "CAM taaskäivitamine"
 
@@ -1002,9 +1114,19 @@
 msgid "CAM ready"
 msgstr "CAM töövalmis"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktiveerimine)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Aktiveerimise tühistamine"
+
+msgid "Button$Activate"
+msgstr "Aktiveeri"
+
 msgid "Button$Menu"
 msgstr "Menüü"
 
@@ -1017,6 +1139,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Ei saa avada CAM menüüd!"
 
+msgid "Can't activate CAM!"
+msgstr "Ei saa CAM'i aktiveerida!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM on kasutuses - taaskäivitada?"
 
@@ -1054,7 +1179,7 @@
 msgstr "Salvestuse eluiga (päevi)"
 
 msgid "Setup.Recording$Pause key handling"
-msgstr "Pausi klahvi käsitsemine"
+msgstr "Pausi klahvi käsitlemine"
 
 msgid "Setup.Recording$Pause priority"
 msgstr "Pausi prioriteet"
@@ -1096,7 +1221,7 @@
 msgstr "Taasesitus"
 
 msgid "Setup.Replay$Multi speed mode"
-msgstr "Mitme kiiruse režiim"
+msgstr "Mitmikkiiruse režiim"
 
 msgid "Setup.Replay$Show replay mode"
 msgstr "Korduse režiimi kuvamine"
@@ -1108,10 +1233,37 @@
 msgstr "Edenemiseriba kuvamise aeg (s)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "Taasesituse peatamine markeri seadmisel"
+msgstr "Paus markeri seadmisel"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Paus markerile siirdamisel"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Redigeeritud osade vahelejätmine"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Paus viimasel markeril"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Adaptiivse hüppe pikkuse algväärtus (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Adaptiivse hüppe lähtestamise viide (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Adaptiivse hüppe vaheldumise omadus"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Adaptiivne hüpe klahvidega Eelmine/Järgmine"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Klahvide Roheline/Kollane hüpe (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Klahvide Roheline/Kollane korduv hüpe (s)"
 
 msgid "Setup.Replay$Resume ID"
-msgstr "Jätkamise ID"
+msgstr "Taasesituse tunnus"
 
 msgid "Miscellaneous"
 msgstr "Muud sätted"
@@ -1146,6 +1298,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Helitugevus käivitamisel"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Helitugevuse samm"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Helitugevuse lineaarsus"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kanalite ringkerimine"
 
@@ -1179,7 +1337,7 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Stop replaying"
-msgstr " Lõpetada taasesitamine"
+msgstr " Lõpetada taasesitus"
 
 msgid "Button$Pause"
 msgstr "Paus"
@@ -1188,17 +1346,17 @@
 msgstr "Stopp"
 
 msgid "Button$Resume"
-msgstr "Jätkata"
+msgstr "Jätkamine"
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr " Katkestada redigeerimine"
+msgstr " Tühistada töötlemine"
 
 msgid "Stop recording?"
 msgstr "Lõpetada salvestamine?"
 
 msgid "Cancel editing?"
-msgstr "Tühistada redigeermine?"
+msgstr "Tühistada töötlemine?"
 
 msgid "No audio available!"
 msgstr "Audio kättesaamatu!"
@@ -1216,10 +1374,10 @@
 msgstr "Puudub vaba DVB seade salvestamiseks!"
 
 msgid "Pausing live video..."
-msgstr "Otseülekande peatamine..."
+msgstr "Otseedastuse peatamine..."
 
 msgid "Delete timeshift recording?"
-msgstr "Kustutada ajanihke salvestus?"
+msgstr "Kustutada ajanihke salvestust?"
 
 #. TRANSLATORS: note the trailing blank!
 msgid "Jump: "
@@ -1229,7 +1387,7 @@
 msgstr "Redigeerimise markerid puuduvad!"
 
 msgid "No editing sequences defined!"
-msgstr "Redigeerimise järjestus puudub!"
+msgstr "Redigeerimise järjestus määramata!"
 
 msgid "Can't start editing process!"
 msgstr "Redigeerimise start ebaõnnestus!"
@@ -1280,7 +1438,7 @@
 msgstr "Väljalülitamine ebaõnnestus - '-s' parameeter puudub!"
 
 msgid "Editing - shut down anyway?"
-msgstr "Redigeerimine aktiive - lülitada välja?"
+msgstr "Töötlemine aktiive - lülitada välja?"
 
 msgid "Recording - shut down anyway?"
 msgstr "Salvestamine aktiivne - lülitada välja?"
@@ -1297,7 +1455,7 @@
 msgstr "Laiendusmoodul %s ärkab %ld minuti pärast, jätkata?"
 
 msgid "Editing - restart anyway?"
-msgstr "Redigeerimine aktiivne - restart?"
+msgstr "Töötlemine aktiivne - restart?"
 
 msgid "Recording - restart anyway?"
 msgstr "Salvestamine aktiivne - restart?"
@@ -1330,6 +1488,10 @@
 msgid "PLAY"
 msgstr "ESITUS"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Liikumine positsioonile %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Panels"
 
@@ -1381,10 +1543,10 @@
 msgstr "Esmase DVB seadme ümberlülitus..."
 
 msgid "Editing process failed!"
-msgstr "Redigeerimine ebaõnnestus!"
+msgstr "Töötlemine ebaõnnestus!"
 
 msgid "Editing process finished"
-msgstr "Redigeerimine lõpetatud"
+msgstr "Töötlemine lõpetatud"
 
 msgid "Press any key to cancel restart"
 msgstr "Restardi katkestamiseks vajuta suvalist klahvi"
diff -ruN vdr-2.0.0/po/fi_FI.po vdr-2.2.0/po/fi_FI.po
--- vdr-2.0.0/po/fi_FI.po	2013-03-17 15:07:35.000000000 +0100
+++ vdr-2.2.0/po/fi_FI.po	2015-02-19 10:12:21.984183344 +0100
@@ -1,17 +1,17 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Hannu Savolainen <hannu@opensound.com>, 2002
 # Jaakko Hyvätti <jaakko@hyvatti.iki.fi>, 2002, 2003
 # Niko Tarnanen <niko.tarnanen@hut.fi>, 2003
-# Rolf Ahrenberg <Rolf.Ahrenberg@sci.fi>, 2003-2008, 2010-2013
+# Rolf Ahrenberg <Rolf.Ahrenberg@sci.fi>, 2003-2008, 2010-2013, 2015
 # Matti Lehtimäki <matti.lehtimaki@gmail.com>, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2007-08-15 15:52+0200\n"
 "Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n"
 "Language-Team: Finnish <vdr@linuxtv.org>\n"
@@ -23,6 +23,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Virheellinen kanavavalinta ***"
 
+msgid "CAM activated!"
+msgstr "CA-moduuli aktivoitu!"
+
 msgid "Channel not available!"
 msgstr "Kanava ei ole käytettävissä!"
 
@@ -80,6 +83,15 @@
 msgid "StreamId"
 msgstr "Lähetetunniste"
 
+msgid "Pilot"
+msgstr "Pilotti"
+
+msgid "T2SystemId"
+msgstr "T2-tunniste"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Ohjelmaoppaan päivitys aloitettu"
 
@@ -567,6 +579,12 @@
 msgid "Sid"
 msgstr "Palvelu-ID"
 
+msgid "Nid"
+msgstr "Verkko-ID"
+
+msgid "Tid"
+msgstr "Lähete-ID"
+
 msgid "Channel settings are not unique!"
 msgstr "Kanava-asetukset eivät ole yksilölliset!"
 
@@ -607,8 +625,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Kansion nimessä ei saa olla '%c'-merkkiä!"
 
-msgid "Button$Select"
-msgstr "Valitse"
+msgid "Button$Open"
+msgstr "Avaa"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Poistetaanko kansio alikansioineen?"
@@ -727,6 +745,76 @@
 msgid "CAM not responding!"
 msgstr "CA-moduuli ei vastaa!"
 
+msgid "Edit path"
+msgstr "Muokkaa polkua"
+
+msgid "Folder"
+msgstr "Kansio"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Kansio on käytössä - muokkaukset eivät mahdollisia!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Siirrä koko kansio sisältäen %d tallennetta?"
+
+msgid "Error while moving folder!"
+msgstr "Kansion siirto epäonnistui!"
+
+msgid "Edit recording"
+msgstr "Muokkaa tallennetta"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Tallenne on käytössä - muokkaus ei mahdollista!"
+
+msgid "Button$Cancel cutting"
+msgstr "Peru leikkaus"
+
+msgid "Button$Stop cutting"
+msgstr "Lopeta leikkaus"
+
+msgid "Button$Cancel moving"
+msgstr "Peru siirto"
+
+msgid "Button$Stop moving"
+msgstr "Lopeta siirto"
+
+msgid "Button$Cancel copying"
+msgstr "Peru kopiointi"
+
+msgid "Button$Stop copying"
+msgstr "Lopeta kopiointi"
+
+msgid "Button$Cut"
+msgstr "Leikkaa"
+
+msgid "Button$Delete marks"
+msgstr "Poista merkinnät"
+
+msgid "Recording vanished!"
+msgstr "Tallenne katosi!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Muokattava versio on jo olemassa - ylikirjoitetaanko?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Tallenteen lisääminen leikkausjonoon epäonnistui!"
+
+msgid "Rename recording to folder name?"
+msgstr "Nimeä tallenne kansion mukaan?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Poistetaanko muokkausmerkinnät tallenteelta?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Muokkausmerkintöjen poistaminen epäonnistui!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Prioriteetin/elinajan vaihtaminen epäonnistui!"
+
+msgid "Error while changing folder/name!"
+msgstr "Kansion/nimen vaihtaminen epäonnistui!"
+
 msgid "Recording info"
 msgstr "Tallenteen tiedot"
 
@@ -739,9 +827,6 @@
 msgid "Recordings"
 msgstr "Tallenteet"
 
-msgid "Button$Open"
-msgstr "Avaa"
-
 msgid "Commands"
 msgstr "Komennot"
 
@@ -997,6 +1082,33 @@
 msgid "Setup.LNB$own"
 msgstr "oma"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Käytä kääntömoottoria lautaselle"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Paikkakunnan leveysaste (°)"
+
+msgid "South"
+msgstr "etelään"
+
+msgid "North"
+msgstr "pohjoiseen"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Paikkakunnan pituusaste (°)"
+
+msgid "West"
+msgstr "länteen"
+
+msgid "East"
+msgstr "itään"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Laajin kääntömoottorin pyyhkäisy (°)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Kääntömoottorin nopeus (°/s)"
+
 msgid "CAM reset"
 msgstr "CAM nollaus"
 
@@ -1006,9 +1118,19 @@
 msgid "CAM ready"
 msgstr "CAM valmis"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktivoidaan)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Peruuta aktivointi"
+
+msgid "Button$Activate"
+msgstr "Aktivoi"
+
 msgid "Button$Menu"
 msgstr "Valikko"
 
@@ -1021,6 +1143,9 @@
 msgid "Can't open CAM menu!"
 msgstr "CA-moduulin valikko ei saatavilla"
 
+msgid "Can't activate CAM!"
+msgstr "CA-moduulin aktivointi epäonnistui!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CA-moduuli käytössä - nollataanko?"
 
@@ -1114,6 +1239,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pysäytä toisto asetettaessa merkki"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pysäytä toisto hypätessä merkkiin"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Ohita muokatut kohdat"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pysäytä toisto viimeiseen merkkiin"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Mukautuvan hypyn oletuskesto (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Mukautuvan hypyn nollausviive (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Käytä vaihtoehtoista mukautuvaa hyppyä"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Käytä Ed./Seur. mukautuvaan hyppyyn"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Hypyn kesto värinäppäimillä (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Toistohypyn kesto värinäppäimillä (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Tallenteen paluutunniste"
 
@@ -1150,6 +1302,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Äänenvoimakkuus käynnistettäessä"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Äänenvoimakkuuden askellus"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Äänenvoimakkuuden lineaarisuus"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kanavien rullaus"
 
@@ -1334,6 +1492,10 @@
 msgid "PLAY"
 msgstr "TOISTO"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Käännetään lautasta %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG konsoli"
 
diff -ruN vdr-2.0.0/po/fr_FR.po vdr-2.2.0/po/fr_FR.po
--- vdr-2.0.0/po/fr_FR.po	2013-03-29 11:27:47.000000000 +0100
+++ vdr-2.2.0/po/fr_FR.po	2015-02-19 10:12:22.241194200 +0100
@@ -1,5 +1,5 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Jean-Claude Repetto <jc@repetto.org>, 2001, 2002, 2008
 # Olivier Jacques <jacquesolivier@hotmail.com>, 2003, 2005
@@ -9,17 +9,18 @@
 # Bruno Roussel <bruno.roussel@free.fr>, 2007
 # Michael Nival <mnival@club-internet.fr>, 2007
 # Marc Perrudin <vdr@ekass.net>, 2013
-# Bernard Jaulin <bernard.jaulin@gmail.com>, 2013
+# Bernard Jaulin <bernard.jaulin@gmail.com>, 2013, 2015
 # Peter Münster <pmlists@free.fr>, 2013
 # Dominique Plu <dplu@free.fr>, 2013
+# Régis Bossut <famille.bossut@wanadoo.fr>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-02-24 12:56+0100\n"
-"Last-Translator: Dominique Plu <dplu@free.fr>\n"
+"POT-Creation-Date: 2015-02-11 11:02+0100\n"
+"PO-Revision-Date: 2015-02-18 20:16+0100\n"
+"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
 "Language-Team: French <vdr@linuxtv.org>\n"
 "Language: fr\n"
 "MIME-Version: 1.0\n"
@@ -29,6 +30,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Chaîne invalide ! ***"
 
+msgid "CAM activated!"
+msgstr "CAM activé !"
+
 msgid "Channel not available!"
 msgstr "Chaîne non disponible !"
 
@@ -86,6 +90,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilote"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Mise à jour du guide des programmes"
 
@@ -105,7 +118,7 @@
 msgstr "Comédie"
 
 msgid "Content$Soap/Melodrama/Folkloric"
-msgstr "Soap/Mélodrame/Folklorique"
+msgstr "Feuilleton/Mélodrame/Folklorique"
 
 msgid "Content$Romance"
 msgstr "Romance"
@@ -192,7 +205,7 @@
 msgstr "Programme de divertissement pour les 10-16 ans"
 
 msgid "Content$Informational/Educational/School Programme"
-msgstr "Informationnel/Éducatif/Programme Scolaire "
+msgstr "Informationnel/Éducatif/Programme Scolaire"
 
 msgid "Content$Cartoons/Puppets"
 msgstr "Dessins animés/Marionnettes"
@@ -328,7 +341,7 @@
 
 #, c-format
 msgid "ParentalRating$from %d"
-msgstr "À partir de %d"
+msgstr "%d ans et plus"
 
 msgid "No title"
 msgstr "Sans titre"
@@ -342,7 +355,7 @@
 msgstr "fra"
 
 msgid "Phase 1: Detecting RC code type"
-msgstr "Phase 1: Détection du type de télécommande"
+msgstr "Phase 1 : Détection du type de télécommande"
 
 msgid "Press any key on the RC unit"
 msgstr "Appuyer sur une touche de la télécommande"
@@ -354,7 +367,7 @@
 msgstr "N'appuyer sur aucune touche..."
 
 msgid "Phase 2: Learning specific key codes"
-msgstr "Phase 2: Apprentissage des touches"
+msgstr "Phase 2 : Apprentissage des touches"
 
 #, c-format
 msgid "Press key for '%s'"
@@ -379,7 +392,7 @@
 msgstr "Apprentissage des touches de la télécommande"
 
 msgid "Phase 3: Saving key codes"
-msgstr "Phase 3: Sauvegarde des touches"
+msgstr "Phase 3 : Sauvegarde des touches"
 
 msgid "Press 'Up' to save, 'Down' to cancel"
 msgstr "Appuyer sur 'Haut' pour sauvegarder, 'Bas' pour annuler"
@@ -526,7 +539,7 @@
 msgstr "En clair"
 
 msgid "encrypted"
-msgstr "Crypté"
+msgstr "crypté"
 
 msgid "Edit channel"
 msgstr "Modifier la chaîne"
@@ -573,8 +586,14 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
-msgstr "Cette chaîne n'est pas unique !"
+msgstr "Les paramètres de cette chaîne ne sont pas uniques !"
 
 msgid "Channels"
 msgstr "Chaînes"
@@ -604,7 +623,7 @@
 msgstr "Nouveau dossier"
 
 msgid "Sub folder"
-msgstr "Sous dossier"
+msgstr "Sous-dossier"
 
 msgid "Folder name already exists!"
 msgstr "Ce nom de dossier existe déjà !"
@@ -613,11 +632,11 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Le nom de dossier ne doit pas contenir '%c' !"
 
-msgid "Button$Select"
-msgstr "Sélectionner"
+msgid "Button$Open"
+msgstr "Ouvrir"
 
 msgid "Delete folder and all sub folders?"
-msgstr "Supprimer le dossier et tous les sous dossiers ?"
+msgstr "Supprimer le dossier et tous les sous-dossiers ?"
 
 msgid "Delete folder?"
 msgstr "Supprimer le dossier ?"
@@ -671,7 +690,7 @@
 msgstr "Programmations"
 
 msgid "Button$On/Off"
-msgstr "Marche/Arr"
+msgstr "Marche/Arrêt"
 
 msgid "Button$Info"
 msgstr "Info"
@@ -718,13 +737,13 @@
 
 #, c-format
 msgid "This event - %s"
-msgstr "Cet événement - %s"
+msgstr "Cet évènement - %s"
 
 msgid "This event - all channels"
-msgstr "Cet événement - toutes les chaînes"
+msgstr "Cet évènement - toutes les chaînes"
 
 msgid "All events - all channels"
-msgstr "Tous les événements - toutes les chaînes"
+msgstr "Tous les évènements - toutes les chaînes"
 
 #, c-format
 msgid "Please enter %d digits!"
@@ -733,6 +752,76 @@
 msgid "CAM not responding!"
 msgstr "Pas de réponse du CAM"
 
+msgid "Edit path"
+msgstr "Éditer le chemin"
+
+msgid "Folder"
+msgstr "Dossier"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Ce dossier est en cours d'utilisation - pas de modification possible !"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Déplacer tout le dossier contenant %d enregistrements ?"
+
+msgid "Error while moving folder!"
+msgstr "Erreur lors du déplacement du dossier !"
+
+msgid "Edit recording"
+msgstr "Éditer l'enregistrement"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Cet enregistrement est en cours d'utilisation - pas de modification possible !"
+
+msgid "Button$Cancel cutting"
+msgstr "Annuler la coupe"
+
+msgid "Button$Stop cutting"
+msgstr "Arrêter la coupe"
+
+msgid "Button$Cancel moving"
+msgstr "Annuler le déplacement"
+
+msgid "Button$Stop moving"
+msgstr "Arrêter le déplacement"
+
+msgid "Button$Cancel copying"
+msgstr "Annuler la copie"
+
+msgid "Button$Stop copying"
+msgstr "Arrêter la copie"
+
+msgid "Button$Cut"
+msgstr "Couper"
+
+msgid "Button$Delete marks"
+msgstr "Supprimer les marqueurs"
+
+msgid "Recording vanished!"
+msgstr "L'enregistrement a disparu !"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "La version éditée existe déjà - écraser ?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Erreur lors de la mise en file d'attente pour la coupe !"
+
+msgid "Rename recording to folder name?"
+msgstr "Modifier le nom du dossier des enregistrements ?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Supprimer les marqueurs de montage pour cet enregistrement ?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Erreur pendant la suppression des marqueurs du montage !"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Erreur pendant le changement de priorité/durée de vie !"
+
+msgid "Error while changing folder/name!"
+msgstr "Erreur pendant le changement de dossier/nom !"
+
 msgid "Recording info"
 msgstr "Infos sur l'enregistrement"
 
@@ -745,9 +834,6 @@
 msgid "Recordings"
 msgstr "Enregistrements"
 
-msgid "Button$Open"
-msgstr "Ouvrir"
-
 msgid "Commands"
 msgstr "Commandes"
 
@@ -839,7 +925,7 @@
 msgstr "Infos chaîne lors du changement"
 
 msgid "Setup.OSD$Timeout requested channel info"
-msgstr "Affichage infos chaîne non permanent"
+msgstr "Infos chaîne non permanentes"
 
 msgid "Setup.OSD$Scroll pages"
 msgstr "Défilement par pages"
@@ -1003,6 +1089,33 @@
 msgid "Setup.LNB$own"
 msgstr "Propriétaire"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Utiliser le positionneur de parabole"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Latitude du site (degrés)"
+
+msgid "South"
+msgstr "Sud"
+
+msgid "North"
+msgstr "Nord"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Longitude du site (degrés)"
+
+msgid "West"
+msgstr "Ouest"
+
+msgid "East"
+msgstr "Est"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Course max. du positionneur (degrés)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Vitesse du positionneur (degrés/s)"
+
 msgid "CAM reset"
 msgstr "CAM réinitialisé"
 
@@ -1012,9 +1125,19 @@
 msgid "CAM ready"
 msgstr "CAM prêt"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (activation en cours)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Annuler l'activation"
+
+msgid "Button$Activate"
+msgstr "Activer"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1027,6 +1150,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Impossible d'ouvrir le menu CAM !"
 
+msgid "Can't activate CAM!"
+msgstr "Impossible d'activer le CAM !"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM en cours d'utilisation - confirmer réinitialisation ?"
 
@@ -1067,7 +1193,7 @@
 msgstr "Fonction de la touche Pause"
 
 msgid "Setup.Recording$Pause priority"
-msgstr "Priorité de l'enregistr. mis en pause "
+msgstr "Priorité de l'enregistr. mis en pause"
 
 msgid "Setup.Recording$Pause lifetime (d)"
 msgstr "Expiration enregistr. mis en pause (j)"
@@ -1100,7 +1226,7 @@
 msgstr "Scinder les fichiers édités"
 
 msgid "Setup.Recording$Delete timeshift recording"
-msgstr "Supprimer l'enregistrement timeshift"
+msgstr "Supprimer l'enregistrement du timeshift"
 
 msgid "Replay"
 msgstr "Lecture"
@@ -1118,16 +1244,43 @@
 msgstr "Afficher la barre de progression (s)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "Arrêt lecture si ajout marqueur"
+msgstr "Paude de la lecture pendant l'ajout du marqueur"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pause lecture pendant le saut vers un marqueur"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Sauter les parties éditées"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pause lecture au dernier marqueur"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Durée initiale du saut adaptatif (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Annule la temporisation du saut adaptatif (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Réduire le saut adaptatif lors d'un changement de sens"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Utiliser les touches Préc./Suiv. pour le saut adaptatif"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Durée du saut pour les touches Verte/Jaune (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Durée du saut pour les touches Verte/Jaune en répétition (s)"
 
 msgid "Setup.Replay$Resume ID"
-msgstr "ID de reprise"
+msgstr "ID résumé"
 
 msgid "Miscellaneous"
 msgstr "Divers"
 
 msgid "Setup.Miscellaneous$Min. event timeout (min)"
-msgstr "Temps mini entre événements (min)"
+msgstr "Temps mini entre évènements (min)"
 
 msgid "Setup.Miscellaneous$Min. user inactivity (min)"
 msgstr "Inactivité de l'utilisateur (min)"
@@ -1142,10 +1295,10 @@
 msgstr "Délai de saisie de la chaîne (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
-msgstr "Délai de répétition de la télécommande (ms)"
+msgstr "Délai répétition de la télécommande (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delta (ms)"
-msgstr "Delta de répétition de la télécommande (ms)"
+msgstr "Delta répétition de la télécommande (ms)"
 
 msgid "Setup.Miscellaneous$Initial channel"
 msgstr "Chaîne initiale"
@@ -1156,6 +1309,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volume initial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Pas du changement de volume"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Correction de la courbe"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Affichage circulaire des chaînes"
 
@@ -1236,7 +1395,7 @@
 msgstr "Accès direct : "
 
 msgid "No editing marks defined!"
-msgstr "Pas de marques d'édition définies !"
+msgstr "Pas de marqueurs d'édition définis !"
 
 msgid "No editing sequences defined!"
 msgstr "Aucune séquence d'édition définie !"
@@ -1340,6 +1499,10 @@
 msgid "PLAY"
 msgstr "LECTURE"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Orientation de la parabole vers %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Consoles ST:TNG"
 
diff -ruN vdr-2.0.0/po/hr_HR.po vdr-2.2.0/po/hr_HR.po
--- vdr-2.0.0/po/hr_HR.po	2013-03-11 14:12:04.000000000 +0100
+++ vdr-2.2.0/po/hr_HR.po	2015-02-19 10:12:22.335198304 +0100
@@ -1,5 +1,5 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Drazen Dupor <drazen.dupor@dupor.com>, 2004, 2005
 # Dino Ravnic <dino.ravnic@fer.hr>, 2004
@@ -7,9 +7,9 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2008-03-17 19:00+0100\n"
 "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
 "Language-Team: Croatian <vdr@linuxtv.org>\n"
@@ -21,6 +21,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Neispravan Program ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Program nije dostupan!"
 
@@ -78,6 +81,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Po�injem EPG pretragu"
 
@@ -565,6 +577,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Postavke programa nisu jedinstvene!"
 
@@ -605,8 +623,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr ""
 
-msgid "Button$Select"
-msgstr ""
+msgid "Button$Open"
+msgstr "Otvori"
 
 msgid "Delete folder and all sub folders?"
 msgstr ""
@@ -725,6 +743,76 @@
 msgid "CAM not responding!"
 msgstr "CAM ne odgovara!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Detalji snimanja"
 
@@ -737,9 +825,6 @@
 msgid "Recordings"
 msgstr "Snimke"
 
-msgid "Button$Open"
-msgstr "Otvori"
-
 msgid "Commands"
 msgstr "Naredbe"
 
@@ -995,6 +1080,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "Ponovno pokreni CAM"
 
@@ -1004,9 +1116,19 @@
 msgid "CAM ready"
 msgstr "CAM spreman"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Izbornik"
 
@@ -1019,6 +1141,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Otvaranje CAM izbornika neuspje�no!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM se koristi - ponovno pokrenuti unato�?"
 
@@ -1112,6 +1237,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID nastavka"
 
@@ -1148,6 +1300,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Po�etna ja�ina zvuka"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr ""
 
@@ -1243,10 +1401,10 @@
 msgstr "Ure�ivanje je ve� u tijeku!!"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
-msgstr "abc��d�efghijklmnopqrs�tuvwxyz�0123456789-.,#~\\^$[]|()*+?{}/:%@&"
+msgstr " abc��d�efghijklmnopqrs�tuvwxyz�0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 
 msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr "0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
+msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
 
 msgid "Button$ABC/abc"
 msgstr "ABC/abc"
@@ -1332,6 +1490,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Panele"
 
diff -ruN vdr-2.0.0/po/hu_HU.po vdr-2.2.0/po/hu_HU.po
--- vdr-2.0.0/po/hu_HU.po	2013-03-11 14:12:14.000000000 +0100
+++ vdr-2.2.0/po/hu_HU.po	2015-02-19 10:12:21.930181023 +0100
@@ -1,30 +1,34 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Istvan Koenigsberger <istvnko@hotmail.com>, 2002, 2003, 2006
 # Guido Josten <guido.josten@t-online.de>, 2002, 2003, 2006
 # Thomas Günther <tom@toms-cafe.de>, 2007
-# István Füley <ifuley@tigercomp.ro>, 2007, 2012, 2013
+# István Füley <ifuley@tigercomp.ro>, 2007, 2012, 2013, 2015
+# Albert Danis <a.danis@gmx.de>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-03-01 19:22+0200\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
+"PO-Revision-Date: 2015-02-13 09:36+0200\n"
 "Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
 "Language-Team: Hungarian <vdr@linuxtv.org>\n"
 "Language: hu\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.5\n"
+"X-Generator: Poedit 1.6.11\n"
 
 msgid "*** Invalid Channel ***"
 msgstr "*** Érvénytelen csatorna ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktiválva!"
+
 msgid "Channel not available!"
-msgstr "Az adó nem elérhető"
+msgstr "Csatorna nem elérhető!"
 
 msgid "Can't start Transfer Mode!"
 msgstr "Transfer-Mode nem indítható!"
@@ -51,7 +55,7 @@
 msgstr "Srate"
 
 msgid "Inversion"
-msgstr "Inversion"
+msgstr "Inverzió"
 
 msgid "CoderateH"
 msgstr "CoderateH"
@@ -72,7 +76,7 @@
 msgstr "Guard"
 
 msgid "Hierarchy"
-msgstr "Hierarhia"
+msgstr "Hierarchia"
 
 msgid "Rolloff"
 msgstr "Rolloff"
@@ -80,14 +84,23 @@
 msgid "StreamId"
 msgstr "Stream azonosító"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2 rendszer azonosító"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
-msgstr "EPG adatok aktualizálása"
+msgstr "EPG adatok frissítése"
 
 msgid "Content$Movie/Drama"
 msgstr "Film/Dráma"
 
 msgid "Content$Detective/Thriller"
-msgstr "Nyomozó/Thriller"
+msgstr "Bűnügy/Krimi"
 
 msgid "Content$Adventure/Western/War"
 msgstr "Kaland/Western/Háborús"
@@ -105,7 +118,7 @@
 msgstr "Romantikus film"
 
 msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
-msgstr "Komolyzenei/Klasszikus/Vallási/Történelmi film/Dráma"
+msgstr "Komoly film/Klasszikus/Vallási/Történelmi film/Dráma"
 
 msgid "Content$Adult Movie/Drama"
 msgstr "Felnőtt tartalom/Dráma"
@@ -129,7 +142,7 @@
 msgstr "Show/Játékshow"
 
 msgid "Content$Game Show/Quiz/Contest"
-msgstr "Játék/Kvíz/Verseny"
+msgstr "Játék/Kvíz/Vetélkedő"
 
 msgid "Content$Variety Show"
 msgstr "Varietéműsor"
@@ -147,7 +160,7 @@
 msgstr "Sporthíradó"
 
 msgid "Content$Football/Soccer"
-msgstr "Fotball/Labdarúgás"
+msgstr "Amerikai futball/Labdarúgás"
 
 msgid "Content$Tennis/Squash"
 msgstr "Tenisz/Squash"
@@ -168,13 +181,13 @@
 msgstr "Télisportok"
 
 msgid "Content$Equestrian"
-msgstr "Biliárd"
+msgstr "Lovassport"
 
 msgid "Content$Martial Sports"
-msgstr "Közdősport"
+msgstr "Küzdősport"
 
 msgid "Content$Children's/Youth Programme"
-msgstr "Gyerekprogram/Fiataloknak"
+msgstr "Gyerek/Ifjúsági"
 
 msgid "Content$Pre-school Children's Programme"
 msgstr "Óvodáskorú gyerekeknek"
@@ -216,10 +229,10 @@
 msgstr "Művészet/Kultúra"
 
 msgid "Content$Performing Arts"
-msgstr "Előadás"
+msgstr "Előadóművészet"
 
 msgid "Content$Fine Arts"
-msgstr "Belle arte"
+msgstr "Képzőművészet"
 
 msgid "Content$Religion"
 msgstr "Vallás"
@@ -234,10 +247,10 @@
 msgstr "Film/Mozi"
 
 msgid "Content$Experimental Film/Video"
-msgstr "Amatőr film/video"
+msgstr "Avantgárd/Videó"
 
 msgid "Content$Broadcasting/Press"
-msgstr "Közvetítés/Sajtó"
+msgstr "Hírfolyam/Sajtó"
 
 msgid "Content$New Media"
 msgstr "Új média"
@@ -267,16 +280,16 @@
 msgstr "Természet/Állatok/Környezet"
 
 msgid "Content$Technology/Natural Sciences"
-msgstr "Technológia/Természettudomány"
+msgstr "Technika/Természettudomány"
 
 msgid "Content$Medicine/Physiology/Psychology"
-msgstr "Gyógyászat/Pszichológia/Orvostan"
+msgstr "Gyógyászat/Fiziológia/Pszichológia"
 
 msgid "Content$Foreign Countries/Expeditions"
-msgstr "Idegen országok/Expedíciók"
+msgstr "Külföld/Expedíciók"
 
 msgid "Content$Social/Spiritual Sciences"
-msgstr "Társadalmi/Lélektudomány"
+msgstr "Társadalmi/Lélektudományok"
 
 msgid "Content$Further Education"
 msgstr "Továbbképzés"
@@ -285,7 +298,7 @@
 msgstr "Nyelvek"
 
 msgid "Content$Leisure/Hobbies"
-msgstr "Hobbi"
+msgstr "Szabadidő/Hobbi"
 
 msgid "Content$Tourism/Travel"
 msgstr "Turizmus/Utazás"
@@ -294,13 +307,13 @@
 msgstr "Kézműves"
 
 msgid "Content$Motoring"
-msgstr "Motor"
+msgstr "Autók, Motorok"
 
 msgid "Content$Fitness & Health"
 msgstr "Fitness és egészség"
 
 msgid "Content$Cooking"
-msgstr "Főzőműsor"
+msgstr "Konyha"
 
 msgid "Content$Advertisement/Shopping"
 msgstr "Reklám/Vásárlás"
@@ -325,7 +338,7 @@
 msgstr "%d éven felülieknek"
 
 msgid "No title"
-msgstr "cím nélkül"
+msgstr "Cím nélkül"
 
 #. TRANSLATORS: The name of the language, as written natively
 msgid "LanguageName$English"
@@ -367,16 +380,16 @@
 msgstr "('Le' megnyomása a befejezéshez)"
 
 msgid "(press 'Menu' to skip this key)"
-msgstr "A Menü gombot nyomni ennek a gombnak a kihagyásához"
+msgstr "('Menü' megnyomása ennek a gombnak a kihagyásához)"
 
 msgid "Learning Remote Control Keys"
 msgstr "Távirányító betanítása"
 
 msgid "Phase 3: Saving key codes"
-msgstr "Harmadik lépés: kód mentése"
+msgstr "Harmadik lépés: gomb kódok mentése"
 
 msgid "Press 'Up' to save, 'Down' to cancel"
-msgstr "'Fel' mentés, 'Le' mégse"
+msgstr "'Fel' mentés, 'Le' megszakítás"
 
 msgid "Key$Up"
 msgstr "Fel"
@@ -451,7 +464,7 @@
 msgstr "Csatorna-"
 
 msgid "Key$PrevChannel"
-msgstr "Az előző adás"
+msgstr "Előző csatorna"
 
 msgid "Key$Volume+"
 msgstr "Hangerő+"
@@ -460,7 +473,7 @@
 msgstr "Hangerő-"
 
 msgid "Key$Mute"
-msgstr "Csend"
+msgstr "Néma üzemmód"
 
 msgid "Key$Audio"
 msgstr "Hang"
@@ -523,7 +536,7 @@
 msgstr "Kódolt"
 
 msgid "Edit channel"
-msgstr "Csatornák beállítása"
+msgstr "Csatorna szerkesztése"
 
 msgid "Name"
 msgstr "Név"
@@ -567,26 +580,32 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
-msgstr "Az adóbeállítások nem egyértelműek"
+msgstr "Csatornabeállítások nem egyértelműek!"
 
 msgid "Channels"
 msgstr "Csatornák"
 
 msgid "Button$Edit"
-msgstr "Beállítani"
+msgstr "Szerkesztés"
 
 msgid "Button$New"
 msgstr "Új"
 
 msgid "Button$Delete"
-msgstr "Törölni"
+msgstr "Törlés"
 
 msgid "Button$Mark"
-msgstr "Megjelölni"
+msgstr "Kijelölés"
 
 msgid "Channel is being used by a timer!"
-msgstr "Az adót az időzítő használja!"
+msgstr "Csatornát az időzítő használja!"
 
 msgid "Delete channel?"
 msgstr "Csatorna törlése?"
@@ -605,10 +624,10 @@
 
 #, c-format
 msgid "Folder name must not contain '%c'!"
-msgstr "A könyvtár neve nem tartalmazhatja: '%c'!"
+msgstr "Könyvtár neve nem tartalmazhat '%c'!"
 
-msgid "Button$Select"
-msgstr "Kiválasztás"
+msgid "Button$Open"
+msgstr "Kinyitni"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Valóban töröljem a könyvtárat és az alkönyvtárait?"
@@ -617,10 +636,10 @@
 msgstr "Töröljem a könyvtárat?"
 
 msgid "Edit timer"
-msgstr "Időzítő megváltoztatása"
+msgstr "Időzítő szerkesztése"
 
 msgid "Active"
-msgstr "Aktiv"
+msgstr "Aktív"
 
 msgid "Channel"
 msgstr "Csatorna"
@@ -644,7 +663,7 @@
 msgstr "Élettartam"
 
 msgid "File"
-msgstr "File"
+msgstr "Fájl"
 
 msgid "Button$Folder"
 msgstr "Könyvtár"
@@ -677,7 +696,7 @@
 msgstr "Felvétel folyamatban van - biztos töröljem?"
 
 msgid "Event"
-msgstr "Esemény"
+msgstr "Adás"
 
 msgid "Button$Timer"
 msgstr "Időzítő"
@@ -686,13 +705,13 @@
 msgstr "Felvétel"
 
 msgid "Button$Switch"
-msgstr "Átkapcsolni"
+msgstr "Átváltás"
 
 msgid "What's on now?"
-msgstr "Most adásban"
+msgstr "Adásban"
 
 msgid "What's on next?"
-msgstr "Mi következik?"
+msgstr "Következik"
 
 msgid "Button$Next"
 msgstr "Következő"
@@ -704,7 +723,7 @@
 msgstr "Műsorújság"
 
 msgid "Can't switch channel!"
-msgstr "Az adót nem lehet elkapcsolni"
+msgstr "Csatornaváltás nem lehetséges!"
 
 #, c-format
 msgid "Schedule - %s"
@@ -715,23 +734,93 @@
 msgstr "Ez az adás - %s"
 
 msgid "This event - all channels"
-msgstr "Ez az adás - összes adón"
+msgstr "Ez az adás - összes csatornán"
 
 msgid "All events - all channels"
-msgstr "Az összes adás - az összes adón"
+msgstr "Összes adás - összes csatornán"
 
 #, c-format
 msgid "Please enter %d digits!"
 msgstr "Üssön be %d számot!"
 
 msgid "CAM not responding!"
-msgstr "A CAM nem válaszol!"
+msgstr "CAM nem válaszol!"
+
+msgid "Edit path"
+msgstr "Elérés szerkesztése"
+
+msgid "Folder"
+msgstr "Könyvtár"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Ez a könyvtár használatban van - módosítás nem lehetséges!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "A teljes könytár %d felvételt tartalmaz, áthelyezzem?"
+
+msgid "Error while moving folder!"
+msgstr "Hiba a könyvtár áthelyezése közben!"
+
+msgid "Edit recording"
+msgstr "Felvétel szerkesztése"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Ez a felvétel használatban van - módosítás nem lehetséges!"
+
+msgid "Button$Cancel cutting"
+msgstr "Vágás megszakítása"
+
+msgid "Button$Stop cutting"
+msgstr "Vágás leállítása"
+
+msgid "Button$Cancel moving"
+msgstr "Áthelyezés megszakítása"
+
+msgid "Button$Stop moving"
+msgstr "Áthelyezés leállítása"
+
+msgid "Button$Cancel copying"
+msgstr "Másolás megszakítása"
+
+msgid "Button$Stop copying"
+msgstr "Másolása leállítása"
+
+msgid "Button$Cut"
+msgstr "Vágás"
+
+msgid "Button$Delete marks"
+msgstr "Vágópontok törlése"
+
+msgid "Recording vanished!"
+msgstr "Ez a felvétel eltűnt!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "A szerkesztett változat már létezik - felülírjam?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Hiba a felvétel vágásra előkészítése közben!"
+
+msgid "Rename recording to folder name?"
+msgstr "Felvétel átnevezése a könyvtár nevére?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Töröljem a felvétel vágópontjait?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Hiba a vágópontok törlése közben!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Hiba a prioritás/élettartam változtatása közben!"
+
+msgid "Error while changing folder/name!"
+msgstr "Hiba a könyvtár/név változtatása közben!"
 
 msgid "Recording info"
 msgstr "Felvétel infó"
 
 msgid "Button$Play"
-msgstr "Lejátszani"
+msgstr "Lejátszás"
 
 msgid "Button$Rewind"
 msgstr "Vissza az elejére"
@@ -739,9 +828,6 @@
 msgid "Recordings"
 msgstr "Felvételek"
 
-msgid "Button$Open"
-msgstr "Kinyitni"
-
 msgid "Commands"
 msgstr "Parancsok"
 
@@ -749,13 +835,13 @@
 msgstr "Felvétel törlése?"
 
 msgid "Recording is being edited - really delete?"
-msgstr "A felvétel szerkesztés alatt - biztos törölni?"
+msgstr "Ez a felvétel szerkesztés alatt áll - biztos töröljem?"
 
 msgid "Error while deleting recording!"
-msgstr "Hiba a felvétel törlésénél!"
+msgstr "Hiba a felvétel törlése közben!"
 
 msgid "Recording commands"
-msgstr "Parancsok a felvett filmekhez"
+msgstr "Parancsok a felvételekhez"
 
 msgid "never"
 msgstr "soha"
@@ -791,7 +877,7 @@
 msgstr "Magasság (%)"
 
 msgid "Setup.OSD$Message time (s)"
-msgstr "Információ feltűntetésének időtartama"
+msgstr "Információ feltüntetésének időtartama (mp)"
 
 msgid "Setup.OSD$Use small font"
 msgstr "Kisbetűt használni"
@@ -818,7 +904,7 @@
 msgstr "Kötött betűtipus méret (%)"
 
 msgid "Setup.OSD$Channel info position"
-msgstr "Adásinformáció poziciója"
+msgstr "Csatorna információ pozíciója"
 
 msgid "bottom"
 msgstr "lent"
@@ -827,13 +913,13 @@
 msgstr "Fent"
 
 msgid "Setup.OSD$Channel info time (s)"
-msgstr "Adásinformáció feltűntetésének időtartama (s)"
+msgstr "Csatorna infó időtartama (mp)"
 
 msgid "Setup.OSD$Info on channel switch"
-msgstr "Információ az adó váltásánál"
+msgstr "Információ a csatorna váltásánál"
 
 msgid "Setup.OSD$Timeout requested channel info"
-msgstr "A lekérdezett adásinformáció bezárása"
+msgstr "Lekérdezett adásinformáció bezárása"
 
 msgid "Setup.OSD$Scroll pages"
 msgstr "Oldalanként léptetni"
@@ -842,10 +928,10 @@
 msgstr "Korlátlan léptetés"
 
 msgid "Setup.OSD$Menu key closes"
-msgstr "A menü gomb zár"
+msgstr "Menü gomb zár"
 
 msgid "Setup.OSD$Recording directories"
-msgstr "Felvételek listája"
+msgstr "Felvételek könyvtárai"
 
 msgid "Setup.OSD$Folders in timer menu"
 msgstr "Könyvtárválasztás az időzítő menüben"
@@ -854,7 +940,7 @@
 msgstr "Könyvtárakat rendezd előre"
 
 msgid "Setup.OSD$Number keys for characters"
-msgstr "Számgombok a betűkön"
+msgstr "Betűk a számgombokon"
 
 msgid "Setup.OSD$Color key 0"
 msgstr "Színes gomb 0"
@@ -881,7 +967,7 @@
 msgstr "Hibaelhárítás szintje"
 
 msgid "Setup.EPG$EPG linger time (min)"
-msgstr "Eltelt EPG adatok kijelzése (perc)"
+msgstr "Eltelt EPG adatok kijelzése (p)"
 
 msgid "Setup.EPG$Set system time"
 msgstr "Óra állítása TP időhöz"
@@ -904,7 +990,7 @@
 msgstr "letterbox"
 
 msgid "center cut out"
-msgstr "képközép kivágás"
+msgstr "teljeskitöltés"
 
 msgid "no"
 msgstr "nem"
@@ -913,13 +999,13 @@
 msgstr "kizárólag nevek"
 
 msgid "PIDs only"
-msgstr "kizárólag PIDek"
+msgstr "kizárólag PID-ek"
 
 msgid "names and PIDs"
-msgstr "nevek és PIDek"
+msgstr "nevek és PID-ek"
 
 msgid "add new channels"
-msgstr "új adók hozzáadása"
+msgstr "új csatornák hozzáadása"
 
 msgid "add new transponders"
 msgstr "új transponderek hozzáadása"
@@ -934,22 +1020,22 @@
 msgstr "Feliratok"
 
 msgid "Setup.DVB$Primary DVB interface"
-msgstr "Első DVB interface"
+msgstr "Elsődleges DVB eszköz"
 
 msgid "Setup.DVB$Standard compliance"
 msgstr "Vételi norma"
 
 msgid "Setup.DVB$Video format"
-msgstr "Video formátum"
+msgstr "Videó formátum"
 
 msgid "Setup.DVB$Video display format"
-msgstr "Képernyőformátum"
+msgstr "Megjelenítő formátum"
 
 msgid "Setup.DVB$Use Dolby Digital"
 msgstr "Dolby-Digital hangot használni"
 
 msgid "Setup.DVB$Update channels"
-msgstr "Adók aktualizálása"
+msgstr "Csatornák frissítése"
 
 msgid "Setup.DVB$Audio languages"
 msgstr "Hangsáv nyelvek"
@@ -992,11 +1078,38 @@
 
 #, c-format
 msgid "Setup.LNB$Device %d connected to sat cable"
-msgstr "Fejkábel a %d. tunerhez"
+msgstr "Fejkábel a %d. tunerhez csatlakoztatva"
 
 msgid "Setup.LNB$own"
 msgstr "saját"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Forgató használata"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Telepítés szélességi foka"
+
+msgid "South"
+msgstr "Dél"
+
+msgid "North"
+msgstr "Észak"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Telepítés hosszúsági foka"
+
+msgid "West"
+msgstr "Nyugat"
+
+msgid "East"
+msgstr "Kelet"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Forgató oldalszöge max. (fok)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Forgató sebessége max. (fok/mp)"
+
 msgid "CAM reset"
 msgstr "CAM újraindítás"
 
@@ -1006,26 +1119,39 @@
 msgid "CAM ready"
 msgstr "CAM működik"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktiválás)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Aktiválás megszakítása"
+
+msgid "Button$Activate"
+msgstr "Aktiválás"
+
 msgid "Button$Menu"
 msgstr "Menü"
 
 msgid "Button$Reset"
-msgstr "Reset"
+msgstr "Újraindítás"
 
 msgid "Opening CAM menu..."
-msgstr "A CAM menü nyitása..."
+msgstr "CAM menü nyitása..."
 
 msgid "Can't open CAM menu!"
-msgstr "A CAM menü nem nyitható"
+msgstr "CAM menü nem nyitható!"
+
+msgid "Can't activate CAM!"
+msgstr "CAM aktiválás sikertelen!"
 
 msgid "CAM is in use - really reset?"
-msgstr "CAM használatban - valóban újraindítjuk?"
+msgstr "CAM használatban - valóban újraindítani?"
 
 msgid "Can't reset CAM!"
-msgstr "A CAM újraindítás nem sikerült"
+msgstr "CAM újraindítása nem sikerült!"
 
 msgid "do not pause live video"
 msgstr "élőkép leállítása tiltva"
@@ -1046,52 +1172,52 @@
 msgstr "Felvétel"
 
 msgid "Setup.Recording$Margin at start (min)"
-msgstr "Időeltolás a kezdésnél (perc)"
+msgstr "Időeltolás a kezdésnél (p)"
 
 msgid "Setup.Recording$Margin at stop (min)"
-msgstr "Időeltolás a befejezésnél (perc)"
+msgstr "Időeltolás a befejezésnél (p)"
 
 msgid "Setup.Recording$Default priority"
 msgstr "Alapértelmezett prioritás"
 
 msgid "Setup.Recording$Default lifetime (d)"
-msgstr "Alapértelmezett élettartam"
+msgstr "Alapértelmezett élettartam (n)"
 
 msgid "Setup.Recording$Pause key handling"
-msgstr "A szünet gomb működése"
+msgstr "Szünet gomb működése"
 
 msgid "Setup.Recording$Pause priority"
 msgstr "Időeltolás prioritása"
 
 msgid "Setup.Recording$Pause lifetime (d)"
-msgstr "Időeltolás élettartama"
+msgstr "Időeltolás élettartama (n)"
 
 msgid "Setup.Recording$Use episode name"
-msgstr "Epizódnév"
+msgstr "Epizódnevet használni"
 
 msgid "Setup.Recording$Use VPS"
-msgstr "VPS"
+msgstr "VPS-t használni"
 
 msgid "Setup.Recording$VPS margin (s)"
 msgstr "Időeltolás VPS-nél (mp)"
 
 msgid "Setup.Recording$Mark instant recording"
-msgstr "Direktfelvétel megjelölése"
+msgstr "Egygombos felvétel kijelölése"
 
 msgid "Setup.Recording$Name instant recording"
-msgstr "Direktfelvétel megnevezése"
+msgstr "Egygombos felvétel megnevezése"
 
 msgid "Setup.Recording$Instant rec. time (min)"
-msgstr "Direktfelvétel időtartama (perc)"
+msgstr "Egygombos felvétel időtartama (p)"
 
 msgid "Setup.Recording$present event"
 msgstr "Most futó műsor"
 
 msgid "Setup.Recording$Max. video file size (MB)"
-msgstr "Max. video file méret (MB)"
+msgstr "Max. videó fájlméret (MB)"
 
 msgid "Setup.Recording$Split edited files"
-msgstr "Feldolgozott file-ok felosztása"
+msgstr "Szerkesztett fájlok felosztása"
 
 msgid "Setup.Recording$Delete timeshift recording"
 msgstr "Időeltolásos felvétel törlése"
@@ -1112,10 +1238,37 @@
 msgstr "Lejátszósáv felüntetésének ideje (mp)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "A visszajátszás megállítása vágópont kijelölésnél"
+msgstr "Lejátszás megállítása vágópont kijelölésnél"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Szünet a lejátszásban, ha vágópontra ugrik"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Vágott részek kihagyása"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Utolsó vágópontnál szünet a lejátszásban"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Felezett ugrás kezdeti értéke (mp)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Felezett ugrás időkorlátja (mp)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Felezett ugrás csak irányváltoztatásnál"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Vissza/Előre gombok haszn. a felezett ugráshoz"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Ugrástáv a Zöld/Sárga gombokkal (mp)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Ismételt ugrástáv a Zöld/Sárga gombokkal (mp)"
 
 msgid "Setup.Replay$Resume ID"
-msgstr "Lejátszás ID"
+msgstr "Lejátszás azonosító"
 
 msgid "Miscellaneous"
 msgstr "Egyéb"
@@ -1124,32 +1277,38 @@
 msgstr "VDR leáll ha nem következik felvétel (p)"
 
 msgid "Setup.Miscellaneous$Min. user inactivity (min)"
-msgstr "VDR leáll használat nélkül (perc)"
+msgstr "VDR leáll használat nélkül (p)"
 
 msgid "Setup.Miscellaneous$SVDRP timeout (s)"
-msgstr "SVDRP szétkapcsol használat nélkül(s)"
+msgstr "SVDRP szétkapcsol használat nélkül(mp)"
 
 msgid "Setup.Miscellaneous$Zap timeout (s)"
-msgstr "Adásváltás ideje (s)"
+msgstr "Előző csatorna időkorlátja (mp)"
 
 msgid "Setup.Miscellaneous$Channel entry timeout (ms)"
-msgstr "Csatornaváltás timeout (ms)"
+msgstr "Csatorna beírás időkorlátja (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
 msgstr "Távirányító ismétlés késleltetése (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delta (ms)"
-msgstr "Távirányító ismétlés deltája (ms)"
+msgstr "Távirányító ismétlés intervalluma (ms)"
 
 msgid "Setup.Miscellaneous$Initial channel"
 msgstr "Csatorna induláskor"
 
 msgid "Setup.Miscellaneous$as before"
-msgstr "ahogy az előbb"
+msgstr "Mint legutóbb"
 
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hangerő indulásnál"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Hangerőállítás léptéke"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Hangerő linearizálása"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Csatornalista görgetése"
 
@@ -1160,10 +1319,10 @@
 msgstr "Vészleállítás"
 
 msgid "Plugins"
-msgstr "Plugins"
+msgstr "Pluginok"
 
 msgid "This plugin has no setup parameters!"
-msgstr "Ennek a plugin-nak nincs setup-paramétere!"
+msgstr "Ennek a plugin-nak nincs paramétere!"
 
 msgid "Setup"
 msgstr "Beállítások"
@@ -1199,28 +1358,28 @@
 msgstr " Vágást megszakítani"
 
 msgid "Stop recording?"
-msgstr "Felvétel befejezni?"
+msgstr "Felvételt befejezni?"
 
 msgid "Cancel editing?"
-msgstr "Vágást befejezni?"
+msgstr "Vágást megszakítani?"
 
 msgid "No audio available!"
-msgstr "Hang nem lehetséges!"
+msgstr "Hang nem elérhetö!"
 
 msgid "No subtitles"
 msgstr "Felirat ki"
 
 msgid "No subtitles available!"
-msgstr "A csatornán nincs felirat!"
+msgstr "Ezen a csatornán nincs felirat!"
 
 msgid "Not enough disk space to start recording!"
-msgstr "Nincs elegendő hely a felvételre"
+msgstr "Nincs elegendő hely a felvételre!"
 
 msgid "No free DVB device to record!"
-msgstr "Nincs szabad DVB kártya a felvételhez!"
+msgstr "Nincs szabad DVB eszköz a felvételhez!"
 
 msgid "Pausing live video..."
-msgstr "Az élő adás megállítva..."
+msgstr "Élő adás megállítva..."
 
 msgid "Delete timeshift recording?"
 msgstr "Töröljem az időeltolásos felvételt?"
@@ -1230,19 +1389,19 @@
 msgstr "Ugrás ide: "
 
 msgid "No editing marks defined!"
-msgstr "Nincs vágópont kijelölve"
+msgstr "Nincs vágópont kijelölve!"
 
 msgid "No editing sequences defined!"
-msgstr "Nincs vágórész kijelölve"
+msgstr "Nincs vágórész kijelölve!"
 
 msgid "Can't start editing process!"
-msgstr "A vágás nem indítható!"
+msgstr "Vágás nem indítható!"
 
 msgid "Editing process started"
 msgstr "Vágás elindítva"
 
 msgid "Editing process already active!"
-msgstr "A vágás már aktív!"
+msgstr "Vágás már aktív!"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 msgstr " aábcdeéfghiíjklmnoóöőpqrstuúüűvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
@@ -1254,57 +1413,57 @@
 msgstr "ABC/abc"
 
 msgid "Button$Overwrite"
-msgstr "Átírni"
+msgstr "Felülírás"
 
 msgid "Button$Insert"
-msgstr "Beilleszteni"
+msgstr "Beillesztés"
 
 msgid "Plugin"
 msgstr "Plugin"
 
 msgid "Up/Dn for new location - OK to move"
-msgstr "Fel/Le egy új opcióért - aztán OK"
+msgstr "Fel/Le egy új pozícióért - aztán OK"
 
 msgid "Channel locked (recording)!"
-msgstr "Az adó blokkolva (felvétel)!"
+msgstr "Csatorna blokkolva (felvétel)!"
 
 msgid "Low disk space!"
-msgstr "A merevlemez majdnem tele!"
+msgstr "Adattároló majdnem tele!"
 
 msgid "Regenerating index file"
-msgstr "Az index file újragenerálása"
+msgstr "Index fájl újragenerálása"
 
 msgid "Index file regeneration complete"
-msgstr "Index file újragenerálása kész."
+msgstr "Index fájl újragenerálása kész"
 
 msgid "Index file regeneration failed!"
-msgstr "Index file újragenerálása sikertelen!"
+msgstr "Index fájl újragenerálása sikertelen!"
 
 msgid "Can't shutdown - option '-s' not given!"
-msgstr "A leállítás nem lehetséges - Opció '-s' hiányzik!"
+msgstr "Leállítás nem lehetséges: '-s' opció nincs megadva!"
 
 msgid "Editing - shut down anyway?"
-msgstr "Szerkesztés folyamatban - valóban leállítjuk?"
+msgstr "Szerkesztés folyamatban - mégis leállítani?"
 
 msgid "Recording - shut down anyway?"
-msgstr "Felvétel folyamatban van - mégis kikapcsolni?"
+msgstr "Felvétel folyamatban - mégis leállítani?"
 
 #, c-format
 msgid "Recording in %ld minutes, shut down anyway?"
-msgstr "Felvétel %ld perc mulva kezdődik - mégis kikapcsolni?"
+msgstr "Felvétel %ld perc múlva kezdődik - mégis leállítani?"
 
 msgid "shut down anyway?"
-msgstr "mégis kikapcsolni?"
+msgstr "mégis leállítani?"
 
 #, c-format
 msgid "Plugin %s wakes up in %ld min, continue?"
-msgstr "A(z) %s plugin  ébreszt %ld perc múlva, folytatjuk?"
+msgstr "A(z) %s plugin ébreszt %ld perc múlva, folytatni?"
 
 msgid "Editing - restart anyway?"
-msgstr "Szerkesztés folyamatban - valóban újraindítjuk?"
+msgstr "Szerkesztés folyamatban - mégis újraindítani?"
 
 msgid "Recording - restart anyway?"
-msgstr "Felvétel folyamatban van - mégis újraindítani?"
+msgstr "Felvétel folyamatban - mégis újraindítani?"
 
 msgid "restart anyway?"
 msgstr "mégis újraindítani?"
@@ -1317,7 +1476,7 @@
 msgstr "Klasszikus VDR"
 
 msgid "DISK"
-msgstr "LEMEZ"
+msgstr "ADATTÁROLÓ"
 
 msgid "LOAD"
 msgstr "TERHELÉS"
@@ -1334,6 +1493,10 @@
 msgid "PLAY"
 msgstr "LEJÁTSZÁS"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Antenna forgatása %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Konzol"
 
@@ -1373,16 +1536,16 @@
 msgstr "Megállítsam az élő képet?"
 
 msgid "Recording started"
-msgstr "A felvétel elindítva"
+msgstr "Felvétel elindítva"
 
 msgid "VDR will shut down later - press Power to force"
-msgstr "A VDR később fog leállni - Kikapcs gombbal kényszerített leállítás"
+msgstr "VDR később fog leállni - Kikapcs gombbal kényszerített leállítás"
 
 msgid "Press any key to cancel shutdown"
 msgstr "Nyomj egy gombot a leállás megszakításához"
 
 msgid "Switching primary DVB..."
-msgstr "Elsődleges DVB kártya váltása folyamatban..."
+msgstr "Elsődleges DVB eszköz váltása folyamatban..."
 
 msgid "Editing process failed!"
 msgstr "Vágás sikertelen!"
@@ -1395,10 +1558,10 @@
 
 #, c-format
 msgid "VDR will shut down in %s minutes"
-msgstr "A VDR leáll %s perc múlva"
+msgstr "VDR leáll %s perc múlva"
 
 msgid "Disk"
-msgstr "Lemez"
+msgstr "Adattároló"
 
 msgid "free"
 msgstr "szabad"
diff -ruN vdr-2.0.0/po/it_IT.po vdr-2.2.0/po/it_IT.po
--- vdr-2.0.0/po/it_IT.po	2013-03-11 14:12:16.000000000 +0100
+++ vdr-2.2.0/po/it_IT.po	2015-02-19 10:12:22.005184202 +0100
@@ -1,36 +1,38 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Alberto Carraro <bertocar@tin.it>, 2001
 # Antonio Ospite <ospite@studenti.unina.it>, 2003, 2006
 # Sean Carlos <seanc@libero.it>, 2005
-# Nino Gerbino <ngerb@interfree.it>, 2006
-# Diego Pierotto <vdr-italian@tiscali.it>, 2007-2010, 2012, 2013
+# Nino Gerbino <ngerb@interfree.it>, 2006, 2015
+# Diego Pierotto <vdr-italian@tiscali.it>, 2007-2010, 2012, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-02-11 23:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-12 19:31+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
 "Language-Team: Italian <vdr@linuxtv.org>\n"
 "Language: it\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\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"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Poedit 1.5.4\n"
 
 msgid "*** Invalid Channel ***"
 msgstr "*** Canale NON valido ***"
 
+msgid "CAM activated!"
+msgstr "CAM attivata!"
+
 msgid "Channel not available!"
 msgstr "Canale non disponibile!"
 
 msgid "Can't start Transfer Mode!"
-msgstr "Impossibile avviare mod. trasferimento!"
+msgstr "Impossibile avviare modalità trasferimento!"
 
 msgid "off"
 msgstr "off"
@@ -83,6 +85,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Inizio scansione EPG"
 
@@ -570,6 +581,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Parametri canale non univoci!"
 
@@ -610,8 +627,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Il nome cartella non deve contenere '%c'!"
 
-msgid "Button$Select"
-msgstr "Seleziona"
+msgid "Button$Open"
+msgstr "Apri"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Eliminare la cartella e sue sottocartelle?"
@@ -730,6 +747,76 @@
 msgid "CAM not responding!"
 msgstr "La CAM non risponde!"
 
+msgid "Edit path"
+msgstr "Modifica percorso"
+
+msgid "Folder"
+msgstr "Cartella"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "La cartella è attualmente in uso - nessuna modifica possibile!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Spostare tutta la cartella contenente %d registrazioni?"
+
+msgid "Error while moving folder!"
+msgstr "Errore durante lo spostamento della cartella!"
+
+msgid "Edit recording"
+msgstr "Modifica registrazione"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "La registrazione è attualmente in uso - nessuna modifica possibile!"
+
+msgid "Button$Cancel cutting"
+msgstr "Annulla taglio"
+
+msgid "Button$Stop cutting"
+msgstr "Ferma taglio"
+
+msgid "Button$Cancel moving"
+msgstr "Annulla spostamento"
+
+msgid "Button$Stop moving"
+msgstr "Ferma spostamento"
+
+msgid "Button$Cancel copying"
+msgstr "Annulla copia"
+
+msgid "Button$Stop copying"
+msgstr "Ferma copia"
+
+msgid "Button$Cut"
+msgstr "Taglia"
+
+msgid "Button$Delete marks"
+msgstr "Elimina marcatori"
+
+msgid "Recording vanished!"
+msgstr "Registrazione sparita!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Versione modificata già esistente. Sovrascrivere?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Errore durante l'accodamento della registrazione per il taglio!"
+
+msgid "Rename recording to folder name?"
+msgstr "Rinominare registrazione come nome cartella?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Eliminare i marcatori di modifica della registrazione?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Errore durante la cancellazione dei marcatori di modifica!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Errore durante la modifica priorità/durata!"
+
+msgid "Error while changing folder/name!"
+msgstr "Errore durante la modifica cartella/nome!"
+
 msgid "Recording info"
 msgstr "Info registrazione"
 
@@ -742,9 +829,6 @@
 msgid "Recordings"
 msgstr "Registrazioni"
 
-msgid "Button$Open"
-msgstr "Apri"
-
 msgid "Commands"
 msgstr "Comandi"
 
@@ -1000,6 +1084,33 @@
 msgid "Setup.LNB$own"
 msgstr "propria"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Utilizza motore antenna"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Latitudine attuale (gradi)"
+
+msgid "South"
+msgstr "Sud"
+
+msgid "North"
+msgstr "Nord"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Longitudine attuale (gradi)"
+
+msgid "West"
+msgstr "Ovest"
+
+msgid "East"
+msgstr "Est"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Rotazione massima motore (gradi)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Velocità motore (gradi/s)"
+
 msgid "CAM reset"
 msgstr "Reimposta la CAM"
 
@@ -1009,9 +1120,19 @@
 msgid "CAM ready"
 msgstr "La CAM è pronta"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (attivazione)"
+
 msgid "CAM"
 msgstr "Accesso condizionato CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Annulla attivazione"
+
+msgid "Button$Activate"
+msgstr "Attiva"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1024,6 +1145,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Impossibile aprire il menu CAM!"
 
+msgid "Can't activate CAM!"
+msgstr "Impossibile attivare CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "La CAM è in uso - vuoi reimpostarla?"
 
@@ -1031,7 +1155,7 @@
 msgstr "Impossibile reimpostare il modulo CAM!"
 
 msgid "do not pause live video"
-msgstr "non pausare video dal vivo"
+msgstr "non mettere in pausa il video dal vivo"
 
 msgid "confirm pause live video"
 msgstr "conferma pausa video dal vivo"
@@ -1079,13 +1203,13 @@
 msgstr "Margine VPS (s)"
 
 msgid "Setup.Recording$Mark instant recording"
-msgstr "Segna reg. immediata"
+msgstr "Segna registrazione immediata"
 
 msgid "Setup.Recording$Name instant recording"
-msgstr "Nome reg. immediata"
+msgstr "Nome registrazione immediata"
 
 msgid "Setup.Recording$Instant rec. time (min)"
-msgstr "Durata reg. immediata (min)"
+msgstr "Durata registrazione immediata (min)"
 
 msgid "Setup.Recording$present event"
 msgstr "evento attuale"
@@ -1115,7 +1239,34 @@
 msgstr "Mostra tempo elaborazione (s)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "Pausa riproduzione durante impostazione segni"
+msgstr "Pausa riproduzione durante impostazione marcatori"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pausa riproduzione se si passa ad un marcatore"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Salta parti modificate"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pausa riproduzione all'ultimo marcatore"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Durata iniziale per spostamenti adattivi (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Reimposta scadenza per spostamenti adattivi (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Comportamento alternativo per spostamenti adattivi"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Usa tasti Avanti/Indietro per spostamenti adattivi"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Durata spostamento con tasti Verde/Giallo (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Durata spostamento con tasti Verde/Giallo in sequenza (s)"
 
 msgid "Setup.Replay$Resume ID"
 msgstr "ID di ripristino"
@@ -1153,6 +1304,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volume iniziale"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Volume a tacche"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Volume lineare"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Riavvolgimento canali"
 
@@ -1323,10 +1480,10 @@
 msgstr "DISCO"
 
 msgid "LOAD"
-msgstr "CARICA"
+msgstr "USO"
 
 msgid "TIMERS"
-msgstr "TIMER"
+msgstr "TIMERS"
 
 msgid "DEVICES"
 msgstr "SCHEDE"
@@ -1337,6 +1494,10 @@
 msgid "PLAY"
 msgstr "RIPRODUCI"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Spostamento antenna verso %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Consolle ST:TNG"
 
diff -ruN vdr-2.0.0/po/lt_LT.po vdr-2.2.0/po/lt_LT.po
--- vdr-2.0.0/po/lt_LT.po	2013-03-28 10:01:52.000000000 +0100
+++ vdr-2.2.0/po/lt_LT.po	2015-02-19 10:12:22.079187361 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Valdemaras Pipiras <varas@ambernet.lt>, 2009, 2010, 2013.
+# Valdemaras Pipiras <varas@ambernet.lt>, 2009, 2010, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2010-10-30 11:55+0200\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-11 14:02+0200\n"
 "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
 "Language-Team: Lithuanian <vdr@linuxtv.org>\n"
 "Language: lt\n"
@@ -19,6 +19,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Blogi kanalo nustatymai ***"
 
+msgid "CAM activated!"
+msgstr "Dekodavimo modulis aktyvuotas!"
+
 msgid "Channel not available!"
 msgstr "Kanalas neegzistuoja!"
 
@@ -76,6 +79,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilotas"
+
+msgid "T2SystemId"
+msgstr "T2 Sistemos ID"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Pradedamas EPG skanavimas"
 
@@ -561,7 +573,13 @@
 msgstr "CA (dekodavimo sistema)"
 
 msgid "Sid"
-msgstr "Serviso id"
+msgstr "Serviso id (Sid)"
+
+msgid "Nid"
+msgstr "Tinklo id (Nid)"
+
+msgid "Tid"
+msgstr "Tid"
 
 msgid "Channel settings are not unique!"
 msgstr "Kanalų nustatymai neunikalūs!"
@@ -603,8 +621,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Katalogo pavadinimas turi būti sudarytas iš '%c'!"
 
-msgid "Button$Select"
-msgstr "Pasirinkti"
+msgid "Button$Open"
+msgstr "Atidaryti"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Ištrinti katalogą ir jo visus pakatalogius?"
@@ -723,6 +741,76 @@
 msgid "CAM not responding!"
 msgstr "Dekodavimo modulis (CAM) neveikia!"
 
+msgid "Edit path"
+msgstr "Koreguoti kelią"
+
+msgid "Folder"
+msgstr "Katalogas"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Šis katalogas šiuo metu naudojamas, tad šiuo metu negalimi jokie pakeitimai!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Perkelti šį katalogą kurio viduje yra %d įrašų?"
+
+msgid "Error while moving folder!"
+msgstr "Klaida perkeliant katalogą!"
+
+msgid "Edit recording"
+msgstr "Koreguoti įrašus"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Šis įrašas šiuo metu naudojamas, tad šiuo metu negalimi jokie pakeitimai!"
+
+msgid "Button$Cancel cutting"
+msgstr "Atšaukti karpymą"
+
+msgid "Button$Stop cutting"
+msgstr "Sustabdyti karpymą"
+
+msgid "Button$Cancel moving"
+msgstr "Atšaukti perkėlimą"
+
+msgid "Button$Stop moving"
+msgstr "Sustabdyti perkėlimą"
+
+msgid "Button$Cancel copying"
+msgstr "Atšaukti kopijavimą"
+
+msgid "Button$Stop copying"
+msgstr "Sustabdyti kopijavimą"
+
+msgid "Button$Cut"
+msgstr "Iškirpti"
+
+msgid "Button$Delete marks"
+msgstr "Ištrinti žymes"
+
+msgid "Recording vanished!"
+msgstr "Įrašai išvalyti!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Tokia koreguota versija jau yra, vistiek perašyti?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Klaida sustatant įrašus į karpymo eilę!"
+
+msgid "Rename recording to folder name?"
+msgstr "Priskirti įrašui katalogo pavadinimą?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Ar tikrai ištrinti šio įrašo koregavimo žymes?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Klaida trinant koregavimo žymes!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Klaida keičiant prioritetą/galiojimo laiką!"
+
+msgid "Error while changing folder/name!"
+msgstr "Klaida keičiant katalogą/pavadinimą!"
+
 msgid "Recording info"
 msgstr "Informacija apie įrašus"
 
@@ -735,9 +823,6 @@
 msgid "Recordings"
 msgstr "Įrašai"
 
-msgid "Button$Open"
-msgstr "Atidaryti"
-
 msgid "Commands"
 msgstr "Komandos"
 
@@ -805,7 +890,7 @@
 msgstr "Nustatyto dydžio šriftas"
 
 msgid "Setup.OSD$Default font size (%)"
-msgstr "Numatyto šrifto dydis  (%)"
+msgstr "Numatyto šrifto dydis (%)"
 
 msgid "Setup.OSD$Small font size (%)"
 msgstr "Mažo šrifto dydis (%)"
@@ -993,6 +1078,33 @@
 msgid "Setup.LNB$own"
 msgstr "savas"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Naudoti pozicionierių"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Vitovės platuma (laipsniais)"
+
+msgid "South"
+msgstr "Pietūs"
+
+msgid "North"
+msgstr "Šiaurė"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Vietovės ilgima (laipsniais)"
+
+msgid "West"
+msgstr "Vakarai"
+
+msgid "East"
+msgstr "Rytai"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Maksimalus pozicionieriaus pasukimas (laipsniais)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Pozicionieriaus kreitis (laipsniais/s)"
+
 msgid "CAM reset"
 msgstr "Dekodavimo modulis (CAM) perkrautas"
 
@@ -1002,9 +1114,19 @@
 msgid "CAM ready"
 msgstr "Dekodavimo modulis (CAM) paruoštas darbui"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktyvuojama)"
+
 msgid "CAM"
 msgstr "Dekodavimo modulis (CAM)"
 
+msgid "Button$Cancel activation"
+msgstr "Atšaukti aktyvaciją"
+
+msgid "Button$Activate"
+msgstr "Aktyvuoti"
+
 msgid "Button$Menu"
 msgstr "Meniu"
 
@@ -1017,6 +1139,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Negali atidaryti dekodavimo modulio meniu!"
 
+msgid "Can't activate CAM!"
+msgstr "Negali aktyvuoti dekodavimo modulio!"
+
 msgid "CAM is in use - really reset?"
 msgstr "Dekodavimo modulis šiuo metu naudojamas - tikrai perkrauti?"
 
@@ -1110,6 +1235,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pristabdyti pakartojimą kai vyksta žymėjimas"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pristabdyti pakartojim1 kol pereinama prie žymės"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Praleisti koreguotas dalis"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pristabdyti pakartojimą pasiekus paskutinę žymę"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Adaptyvaus peršokimo trukmė (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Adaptyvaus peršokimo perkrovimo užlaikymas(s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Adaptyvaus peršokimo galima elgsena"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Adaptyviam peršokimui naudoti Atgal/Pirmyn mygtukus"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Praleisti atkarpą (s) su Žaliu/Geltonu mygtukais"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Praleisti atkarpą pakartojime (s) su Žaliu/Geltonu mygtukais"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Kūrinio ID"
 
@@ -1146,6 +1298,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Garsas įjungimo metu"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Garso reguliavimo žingsniai"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Garso lygiavimas"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kanalų pridengimas"
 
@@ -1210,7 +1368,7 @@
 msgstr "Subtitrai nepateikiami!"
 
 msgid "Not enough disk space to start recording!"
-msgstr "Nepakanka vietos diske,  kad būtų galima įrašinėti!"
+msgstr "Nepakanka vietos diske, kad būtų galima įrašinėti!"
 
 msgid "No free DVB device to record!"
 msgstr "Nėra laisvų DVB įrenginių, kad būtų galima įrašinėti!"
@@ -1330,6 +1488,10 @@
 msgid "PLAY"
 msgstr "GROTI"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Lėkštė sukama į %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Skydeliai"
 
diff -ruN vdr-2.0.0/po/mk_MK.po vdr-2.2.0/po/mk_MK.po
--- vdr-2.0.0/po/mk_MK.po	2013-03-11 14:12:47.000000000 +0100
+++ vdr-2.2.0/po/mk_MK.po	2015-02-19 10:12:22.215193128 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Dimitar Petrovski <dimeptr@gmail.com>, 2009, 2012, 2013
+# Dimitar Petrovski <dimeptr@gmail.com>, 2009, 2012, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2012-11-19 15:18+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-08 15:18+0100\n"
 "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
 "Language-Team: Macedonian <en@li.org>\n"
 "Language: mk\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Невалиден Канал ***"
 
+msgid "CAM activated!"
+msgstr "CAM активиран!"
+
 msgid "Channel not available!"
 msgstr "Каналот е недостапен!"
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Пилот"
+
+msgid "T2SystemId"
+msgstr "Т2СистемИд"
+
+msgid "SISO/MISO"
+msgstr "СИСО/МИСО"
+
 msgid "Starting EPG scan"
 msgstr "Започнувам скенирање на EPG"
 
@@ -333,7 +345,7 @@
 msgstr "mkd"
 
 msgid "Phase 1: Detecting RC code type"
-msgstr "Фаза 1: Детекција на типот на далечински управувач "
+msgstr "Фаза 1: Детекција на типот на далечински управувач"
 
 msgid "Press any key on the RC unit"
 msgstr "Притиснете копче на далечинскиот управувач"
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Уредбите на каналот не се уникатни!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Името на директориумот не смее да содржи '%c'"
 
-msgid "Button$Select"
-msgstr "Избери"
+msgid "Button$Open"
+msgstr "Отвори"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Избриши директориум со сите поддиректориуми?"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM не одговара!"
 
+msgid "Edit path"
+msgstr "Уреди патека"
+
+msgid "Folder"
+msgstr "Папка"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Оваа папка е моментално во употреба - промени не се возможни!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Помести цела папка со %d снимки?"
+
+msgid "Error while moving folder!"
+msgstr "Грешка при поместување папка!"
+
+msgid "Edit recording"
+msgstr "Уреди снимка"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Оваа снимка е моментално во употреба - промени не се возможни!"
+
+msgid "Button$Cancel cutting"
+msgstr "Откажи сечење"
+
+msgid "Button$Stop cutting"
+msgstr "Запри сечење"
+
+msgid "Button$Cancel moving"
+msgstr "Откажи преместување"
+
+msgid "Button$Stop moving"
+msgstr "Запри преместување"
+
+msgid "Button$Cancel copying"
+msgstr "Откажи копирање"
+
+msgid "Button$Stop copying"
+msgstr "Запри копирање"
+
+msgid "Button$Cut"
+msgstr "Сечи"
+
+msgid "Button$Delete marks"
+msgstr "Избриши ознаки"
+
+msgid "Recording vanished!"
+msgstr "Снимката исчезна!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Уредена верзија веќе постои - пребриши?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Грешка при редење на снимка за сечење!"
+
+msgid "Rename recording to folder name?"
+msgstr "Преименувај ја снимката како името на папката?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Бриши ознаки за уредување за оваа снимка?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Грешка при бришење на ознака за уредување!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Грешка при промена на приоритет/траење!"
+
+msgid "Error while changing folder/name!"
+msgstr "Грешка при промена на папка/име!"
+
 msgid "Recording info"
 msgstr "Детали на снимката"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Снимки"
 
-msgid "Button$Open"
-msgstr "Отвори"
-
 msgid "Commands"
 msgstr "Наредби"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "свој"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Користи позиционер за чинија"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Латидута на локацијата (степени)"
+
+msgid "South"
+msgstr "Југ"
+
+msgid "North"
+msgstr "Север"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Лонгитуда на локацијата (степени)"
+
+msgid "West"
+msgstr "Запад"
+
+msgid "East"
+msgstr "Исток"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Максимален од на позиционерот (степени)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Брзина на позиционерот (степени/сек)"
+
 msgid "CAM reset"
 msgstr "Рестартирај CAM"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM спремен"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (активирање)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Откажи активација"
+
+msgid "Button$Activate"
+msgstr "Активирај"
+
 msgid "Button$Menu"
 msgstr "Мени"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Неуспешно отварање на CAM менито!"
 
+msgid "Can't activate CAM!"
+msgstr "Неуспешно активирање на CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM е во употреба - рестартирај?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Паузирај репродукција кога се внесува ознака"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Паузирај репродукција кога се скока до ознака"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Прескокни ги уредуваните делови"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Паузирај репродукција на последанта ознака"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Почетно траење на адаптабилно скокање (сек)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Време на ресетирање за адаптабилно скокање (сек)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Алтернативно однесување за адаптабилно скокање"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Користи копчиња Претходно/Следно за адаптабилно скокање"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Дистанца на скокање со Зелено/Жолто копче (сек)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Дистанца на скокање со Зелено/Жолто копче при повторување (сек)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID на продолжеток"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Почетна јачина на звук"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Чекори на јачина на звук"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Линеаризациај на јачина на звук"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Премотување канали"
 
@@ -1242,10 +1400,10 @@
 msgstr "Уредувањето е веќе во тек!!"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
-msgstr "абвгдѓежзѕијклљмнњопрстќуфхцчџш0123456789-.,#~\\^$[]|()*+?{}/:%@&"
+msgstr " абвгдѓежзѕијклљмнњопрстќуфхцчџш0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 
 msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr "0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabcабвг2\tdefдѓеж3\tghiзѕиј4\tjklклљм5\tmnoнњоп6\tpqrsрстќ7\ttuvуфхц8\twxyzчџш9"
+msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabcабвг2\tdefдѓеж3\tghiзѕиј4\tjklклљм5\tmnoнњоп6\tpqrsрстќ7\ttuvуфхц8\twxyzчџш9"
 
 msgid "Button$ABC/abc"
 msgstr "АБВ/абв"
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "ПУШТИ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Движење на чинијата кон %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Панели"
 
diff -ruN vdr-2.0.0/po/nl_NL.po vdr-2.2.0/po/nl_NL.po
--- vdr-2.0.0/po/nl_NL.po	2013-03-11 14:12:56.000000000 +0100
+++ vdr-2.2.0/po/nl_NL.po	2015-02-19 10:12:22.029185229 +0100
@@ -1,5 +1,5 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>, 2001
 # Hans Dingemans <hans.dingemans@tacticalops.nl>, 2003, 2005
@@ -7,14 +7,15 @@
 # Carel Willemse <carel@nekanali.nl>, 2008, 2013
 # Johan Schuring <johan.schuring@vetteblei.nl>, 2008
 # Cedric Dewijs <cedric.dewijs@telfort.nl>, 2013
+# Erik Oomen <oomen.e@gmail.com>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2008-02-26 17:20+0100\n"
-"Last-Translator: Cedric Dewijs <cedric.dewijs@telfort.nl>\n"
+"POT-Creation-Date: 2015-02-11 10:51+0100\n"
+"PO-Revision-Date: 2015-02-10 19:43+0100\n"
+"Last-Translator: Erik Oomen <oomen.e@gmail.com>\n"
 "Language-Team: Dutch <vdr@linuxtv.org>\n"
 "Language: nl\n"
 "MIME-Version: 1.0\n"
@@ -24,6 +25,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Ongeldig kanaal ***"
 
+msgid "CAM activated!"
+msgstr "CAM geactiveerd!"
+
 msgid "Channel not available!"
 msgstr "Kanaal niet beschikbaar"
 
@@ -81,6 +85,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Piloot"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Bezig met starten EPG scan"
 
@@ -568,6 +581,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Kanaalinstellingen zijn niet uniek!"
 
@@ -608,8 +627,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Map mag geen karakter '%c' bevatten!"
 
-msgid "Button$Select"
-msgstr "Kies"
+msgid "Button$Open"
+msgstr "Openen"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Map en alle submappen verwijderen?"
@@ -728,6 +747,76 @@
 msgid "CAM not responding!"
 msgstr "CAM reageert niet!"
 
+msgid "Edit path"
+msgstr "Pad bewerken"
+
+msgid "Folder"
+msgstr "Map"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Deze map is momenteel in gebruik - geen wijzigingen zijn mogelijk!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Verplaats hele map met %d -opnames?"
+
+msgid "Error while moving folder!"
+msgstr "Fout tijdens het verplaatsen van de map!"
+
+msgid "Edit recording"
+msgstr "Opname bewerken"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Deze opname is momenteel in gebruik - geen wijzigingen zijn mogelijk!"
+
+msgid "Button$Cancel cutting"
+msgstr "Knippen afbreken"
+
+msgid "Button$Stop cutting"
+msgstr "Knippen stoppen"
+
+msgid "Button$Cancel moving"
+msgstr "Verplaatsen afbreken"
+
+msgid "Button$Stop moving"
+msgstr "Stoppen met verplaatsen"
+
+msgid "Button$Cancel copying"
+msgstr "Kopi�ren afbreken"
+
+msgid "Button$Stop copying"
+msgstr "Stop kopi�ren"
+
+msgid "Button$Cut"
+msgstr "Knip"
+
+msgid "Button$Delete marks"
+msgstr "Verwijder markeringen"
+
+msgid "Recording vanished!"
+msgstr "Opname verdwenen!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Bewerkte versie bestaat al - overschrijven?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Kan opname voor knippen niet in wachtrij plaatsen!"
+
+msgid "Rename recording to folder name?"
+msgstr "Opname hernoemen naar mapnaam?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Verwijder bewerkings markeringen voor deze opnamen?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Fout bij het verwijderen van alle bewerkings markeringen!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Fout tijdens het wijzigen prioriteit / duur!"
+
+msgid "Error while changing folder/name!"
+msgstr "Fout tijdens het wijzigen map / naam!"
+
 msgid "Recording info"
 msgstr "Opname info"
 
@@ -740,9 +829,6 @@
 msgid "Recordings"
 msgstr "Opnames"
 
-msgid "Button$Open"
-msgstr "Openen"
-
 msgid "Commands"
 msgstr "Commando's"
 
@@ -834,7 +920,7 @@
 msgstr "Kanaal info tonen"
 
 msgid "Setup.OSD$Timeout requested channel info"
-msgstr "Tijdsduur opgevraagde kanaalinfo."
+msgstr "Timout opgevraagde kanaalinfo"
 
 msgid "Setup.OSD$Scroll pages"
 msgstr "Scrollen per pagina"
@@ -998,6 +1084,33 @@
 msgid "Setup.LNB$own"
 msgstr "eigen"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Gebruik schotel positioner"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Plaats breedtegraad (graden)"
+
+msgid "South"
+msgstr "Zuiden"
+
+msgid "North"
+msgstr "Noorden"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Plaats lengtegraad (graden)"
+
+msgid "West"
+msgstr "Westen"
+
+msgid "East"
+msgstr "Oosten"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. positioner swing (graden)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Positioner snelheid (graden / s)"
+
 msgid "CAM reset"
 msgstr "CAM herstarten"
 
@@ -1007,9 +1120,19 @@
 msgid "CAM ready"
 msgstr "CAM gereed"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (Activeren)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Anuleer activering"
+
+msgid "Button$Activate"
+msgstr "Activeer"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1022,6 +1145,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Kan CAM-menu niet openen!"
 
+msgid "Can't activate CAM!"
+msgstr "Kan CAM niet te activeren!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM wordt gebruikt - werkelijk herstarten?"
 
@@ -1115,6 +1241,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pauzeer wanneer markering wordt geplaatst"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pauzeer bij het springen naar een markering"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Sla bewerkte onderdelen over"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pauzeer bij laatste markering"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Initi�le duur bij adaptief overslaan"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Reset Time-out voor adaptief overslaan"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Alternatief gedrag voor adaptief overslaan"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Gebruik Vorige/Volgende toetsen voor adaptief overslaan"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Spoel sprong met groene en gele toetsen (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Spoel sprong met groene en gele toesten in repeteerstand (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Hervattings ID"
 
@@ -1151,6 +1304,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Opstartvolume"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Volume stappen"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Volume lineariseren"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Doorscrollen kanalenlijst"
 
@@ -1335,6 +1494,10 @@
 msgid "PLAY"
 msgstr "AFSPELEN"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Schotel verplaatsen naar% .1f ..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Consoles"
 
diff -ruN vdr-2.0.0/po/nn_NO.po vdr-2.2.0/po/nn_NO.po
--- vdr-2.0.0/po/nn_NO.po	2013-03-11 14:13:05.000000000 +0100
+++ vdr-2.2.0/po/nn_NO.po	2015-02-19 10:12:22.310197240 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # J�rgen Tvedt <pjtvedt@online.no>, 2001
 # Truls Slevigen <truls@slevigen.no>, 2002
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
 "Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Ugyldig Kanal! ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr ""
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr ""
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr ""
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr ""
 
-msgid "Button$Select"
-msgstr ""
+msgid "Button$Open"
+msgstr "�pne"
 
 msgid "Delete folder and all sub folders?"
 msgstr ""
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr ""
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr ""
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Opptak"
 
-msgid "Button$Open"
-msgstr "�pne"
-
 msgid "Commands"
 msgstr "Kommandoer"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr ""
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr ""
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Meny"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr ""
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr ""
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Resume ID"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr ""
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr ""
 
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr ""
 
diff -ruN vdr-2.0.0/po/pl_PL.po vdr-2.2.0/po/pl_PL.po
--- vdr-2.0.0/po/pl_PL.po	2013-03-11 14:13:08.000000000 +0100
+++ vdr-2.2.0/po/pl_PL.po	2015-02-19 10:12:21.765173924 +0100
@@ -1,17 +1,18 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Michael Rakowski <mrak@gmx.de>, 2002, 2003, 2008
 # Jaroslaw Swierczynski <swiergot@gmail.com>, 2006
 # Marek Nazarko <mnazarko@gmail.com>, 2013
+# Tomasz Maciej Nowak <tmn505@gmail.com>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2008-03-09 12:59+0100\n"
-"Last-Translator: Marek Nazarko <mnazarko@gmail.com>\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-12 00:59+0100\n"
+"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
 "Language-Team: Polish <vdr@linuxtv.org>\n"
 "Language: pl\n"
 "MIME-Version: 1.0\n"
@@ -21,6 +22,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Niepoprawny kana� ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktywowany!"
+
 msgid "Channel not available!"
 msgstr "Kana� nie jest dost�pny!"
 
@@ -78,6 +82,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Rozpoczynam skanowanie EPG"
 
@@ -565,6 +578,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Ustawienia kana�u nie s� unikalne!"
 
@@ -605,8 +624,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Folder nie mo�e zawiera� '%c'!"
 
-msgid "Button$Select"
-msgstr "Wybierz"
+msgid "Button$Open"
+msgstr "Otw�rz"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Czy skasowa� katalog i wszystkie podkatalogi?"
@@ -725,6 +744,76 @@
 msgid "CAM not responding!"
 msgstr "CAM nie reaguje!"
 
+msgid "Edit path"
+msgstr "Edytuj �cie�k�"
+
+msgid "Folder"
+msgstr "Katalog"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Ten katalog jest w u�yciu - zmiany nie s� mo�liwe!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Przenie�� ca�y katalog zawiraj�cy %d nagra�?"
+
+msgid "Error while moving folder!"
+msgstr "B��d podczas przenoszenia katalogu!"
+
+msgid "Edit recording"
+msgstr "Edytuj nagranie"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "To nagranie jest w u�yciu - zmiany nie s� mo�liwe!"
+
+msgid "Button$Cancel cutting"
+msgstr "Anuluj przycinanie"
+
+msgid "Button$Stop cutting"
+msgstr "Zatrzymaj przycinanie"
+
+msgid "Button$Cancel moving"
+msgstr "Anuluj przenoszenie"
+
+msgid "Button$Stop moving"
+msgstr "Zatrzymaj przenoszenie"
+
+msgid "Button$Cancel copying"
+msgstr "Anuluj kopiowanie"
+
+msgid "Button$Stop copying"
+msgstr "Zatrzymaj kopiowanie"
+
+msgid "Button$Cut"
+msgstr "Wytnij"
+
+msgid "Button$Delete marks"
+msgstr "Usu� zaznaczone"
+
+msgid "Recording vanished!"
+msgstr "Nagranie znikn�o!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Edytowana wersja ju� istnieje - nadpisa�?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "B��d podczas zakolejkowania nagrania do przycinania!"
+
+msgid "Rename recording to folder name?"
+msgstr "Zmieni� nazw� nagrania na nazw� katalogu?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Usun�� zaznaczenia z edycji dla tego nagrania?"
+
+msgid "Error while deleting editing marks!"
+msgstr "B��d podczas usuwania zaznacze� edycji!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "B��d podczas zmiany priorytetu/czasu �ycia!"
+
+msgid "Error while changing folder/name!"
+msgstr "B��d podczas zmiany katalogu/nazwy!"
+
 msgid "Recording info"
 msgstr "Informacje o nagraniu"
 
@@ -737,9 +826,6 @@
 msgid "Recordings"
 msgstr "Nagrania"
 
-msgid "Button$Open"
-msgstr "Otw�rz"
-
 msgid "Commands"
 msgstr "Polecenia"
 
@@ -798,7 +884,7 @@
 msgstr "Wyg�adzanie"
 
 msgid "Setup.OSD$Default font"
-msgstr "Standardowa  czcionka"
+msgstr "Standardowa czcionka"
 
 msgid "Setup.OSD$Small font"
 msgstr "Ma�a czcionka"
@@ -995,6 +1081,33 @@
 msgid "Setup.LNB$own"
 msgstr "W�asny"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "U�ywaj obrotnicy"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Szeroko�� geograficzna stopnie"
+
+msgid "South"
+msgstr "Po�udnie"
+
+msgid "North"
+msgstr "P�noc"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "D�ugo�� geograficzna (stopnie)"
+
+msgid "West"
+msgstr "Zach�d"
+
+msgid "East"
+msgstr "Wsch�d"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. obr�t pozycjonera (stopnie)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Pr�dko�� pozycjonera (stopnie/s)"
+
 msgid "CAM reset"
 msgstr "CAM zresetowany"
 
@@ -1004,9 +1117,19 @@
 msgid "CAM ready"
 msgstr "CAM gotowy"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktywuj�)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Anuluj aktywacj�"
+
+msgid "Button$Activate"
+msgstr "Aktywuj"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1019,6 +1142,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Nie mo�na otworzy� menu CAM!"
 
+msgid "Can't activate CAM!"
+msgstr "Nie mo�na aktywowa� CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM jest w u�yciu - naprawd� zresetowa�?"
 
@@ -1112,6 +1238,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Wstrzymaj odtwarzanie podczas ustawiania zaznaczania"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Wstrzymaj odtwarzanie przy przeskoku do zaznaczenia"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Przeskocz edytowan� cz��"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Wstrzymaj odtwarzanie przy ostatnim zaznaczeniu"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Pocz�tkowy czas trwania dla przeskoku adaptacyjnego (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Czas oczekiwania na wyzerowanie dla przeskoku adaptacyjnego (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Zmie� zachowanie dla przeskoku adaptacyjnego"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "U�yj klawiszy Poprzedni/Nast�pny dla przeskoku adaptacyjnego"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Pomi� dystans klawiszami Zielony/��ty (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Pomi� dystans klawiszami Zielony/��ty w powt�rce (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID wznowienia"
 
@@ -1148,6 +1301,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Pocz�tkowa g�o�no��"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Przeskok g�o�no�ci"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Linearyzacja g�o�no�ci"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Zawijanie kana��w"
 
@@ -1282,7 +1441,7 @@
 msgstr "Nie mo�na wy��czy� - nie podano opcji '-s'!"
 
 msgid "Editing - shut down anyway?"
-msgstr "Monta� w trakcie  - Wy��czy� mimo to?"
+msgstr "Monta� w trakcie - Wy��czy� mimo to?"
 
 msgid "Recording - shut down anyway?"
 msgstr "Trwa nagrywanie - wy��czy� mimo to?"
@@ -1299,7 +1458,7 @@
 msgstr "Wtyczka %s obudzi si� za %ld min, kontynuowa�?"
 
 msgid "Editing - restart anyway?"
-msgstr "Monta� w trakcie  - Zrestartowa� mimo to?"
+msgstr "Monta� w trakcie - Zrestartowa� mimo to?"
 
 msgid "Recording - restart anyway?"
 msgstr "Trwa nagrywanie - zrestartowa� mimo to?"
@@ -1332,6 +1491,10 @@
 msgid "PLAY"
 msgstr "ODTWARZA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Obracam talerz do %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Panel ST:TNG"
 
diff -ruN vdr-2.0.0/po/pt_PT.po vdr-2.2.0/po/pt_PT.po
--- vdr-2.0.0/po/pt_PT.po	2013-03-11 14:13:14.000000000 +0100
+++ vdr-2.2.0/po/pt_PT.po	2015-02-19 10:12:21.738172785 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Paulo Lopes <pmml@netvita.pt>, 2001
 # Cris Silva <hudokkow@gmail.com>, 2010
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2010-03-28 22:49+0100\n"
 "Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
 "Language-Team: Portuguese <vdr@linuxtv.org>\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal inv�lido ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Canal indispon�vel!"
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "A iniciar a busca do EPG"
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Par�metros do canal n�o s�o �nicos!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "O nome da pasta n�o pode conter '%c'!"
 
-msgid "Button$Select"
-msgstr "Seleccionar"
+msgid "Button$Open"
+msgstr "Abrir"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Apagar pasta e todas as subpastas?"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "A CAM n�o responde!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Informa��o da grava��o"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Grava��es"
 
-msgid "Button$Open"
-msgstr "Abrir"
-
 msgid "Commands"
 msgstr "Comandos"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "Reiniciar CAM"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM pronta"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Imposs�vel abrir menu da CAM!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM em uso - reiniciar mesmo?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de resumo"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volume inicial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Retroceder canais"
 
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Consola ST:TNG"
 
@@ -1373,7 +1535,7 @@
 msgstr "Grava��o iniciada"
 
 msgid "VDR will shut down later - press Power to force"
-msgstr "O VDR vai desligar mais tarde  - pressione On/Off para for�ar"
+msgstr "O VDR vai desligar mais tarde - pressione On/Off para for�ar"
 
 msgid "Press any key to cancel shutdown"
 msgstr "Pressione qualquer tecla para cancelar o encerramento"
diff -ruN vdr-2.0.0/po/ro_RO.po vdr-2.2.0/po/ro_RO.po
--- vdr-2.0.0/po/ro_RO.po	2013-03-11 14:13:23.000000000 +0100
+++ vdr-2.2.0/po/ro_RO.po	2015-02-19 10:12:21.820176167 +0100
@@ -1,26 +1,29 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Paul Lacatus <paul@campina.iiruc.ro>, 2002
-# Lucian Muresan <lucianm@users.sourceforge.net>, 2004-2006, 2008, 2010-2013
+# Lucian Muresan <lucianm@users.sourceforge.net>, 2004-2006, 2008, 2010-2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-02-09 23:01+0100\n"
-"Last-Translator: Lucian Muresan <lucianm@users.sorceforge.net>\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-11 22:26+0100\n"
+"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
 "Language-Team: Romanian <vdr@linuxtv.org>\n"
 "Language: ro\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.5\n"
+"X-Generator: Poedit 1.6.9\n"
 
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal invalid ***"
 
+msgid "CAM activated!"
+msgstr "CAM activat!"
+
 msgid "Channel not available!"
 msgstr "Canal indisponibil"
 
@@ -78,6 +81,15 @@
 msgid "StreamId"
 msgstr "Identificator Stream"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "ID sistem T2"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Pornesc achiziţia EPG"
 
@@ -565,6 +577,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Parametrii canalului nu sunt univoci!"
 
@@ -605,8 +623,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Numele directorului nu poate să conţină '%c'!"
 
-msgid "Button$Select"
-msgstr "Selectează"
+msgid "Button$Open"
+msgstr "Deschide"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Şterg directorul şi toate sub-directoarele?"
@@ -725,6 +743,76 @@
 msgid "CAM not responding!"
 msgstr "CAM-ul nu reacţionează!"
 
+msgid "Edit path"
+msgstr "Editează calea"
+
+msgid "Folder"
+msgstr "Director"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Acest director tocmai este accesat - nu sunt posibile modificări!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Mut întregul director conținând %d înregistrări?"
+
+msgid "Error while moving folder!"
+msgstr "Eroare la mutarea directorului!"
+
+msgid "Edit recording"
+msgstr "Editează înregistrarea"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Această înregistrare tocmai este accesată - nu sunt posibile modificări!"
+
+msgid "Button$Cancel cutting"
+msgstr "Anulează tăierea"
+
+msgid "Button$Stop cutting"
+msgstr "Oprește tăierea"
+
+msgid "Button$Cancel moving"
+msgstr "Anuleaza mutarea"
+
+msgid "Button$Stop moving"
+msgstr "Oprește mutarea"
+
+msgid "Button$Cancel copying"
+msgstr "Anulează copierea"
+
+msgid "Button$Stop copying"
+msgstr "Oprește copierea"
+
+msgid "Button$Cut"
+msgstr "Taie"
+
+msgid "Button$Delete marks"
+msgstr "Șterge marcajele"
+
+msgid "Recording vanished!"
+msgstr "Înregistrarea a dispărut!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Deja există o versiune editată - o suprascriu?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Eroare la punerea în coada pentru tăiere!"
+
+msgid "Rename recording to folder name?"
+msgstr "Redenumesc înregistrarea ca pe director?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Șterg marcajele de editare pentru această înregistrare?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Eroare la ștergerea marcajelor de editare!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Eroare la schimbarea priorității/timpului de păstrare!"
+
+msgid "Error while changing folder/name!"
+msgstr "Eroare la schimbarea directorului/numelui!"
+
 msgid "Recording info"
 msgstr "Detaliile înregistrării"
 
@@ -737,9 +825,6 @@
 msgid "Recordings"
 msgstr "Înregistrări"
 
-msgid "Button$Open"
-msgstr "Deschide"
-
 msgid "Commands"
 msgstr "Comenzi"
 
@@ -747,7 +832,7 @@
 msgstr "Şterg înregistrarea?"
 
 msgid "Recording is being edited - really delete?"
-msgstr "Montajul înregistrării e în curs de desfășurare - șterg totuși?"
+msgstr "Editarea înregistrării e în curs de desfășurare - șterg totuși?"
 
 msgid "Error while deleting recording!"
 msgstr "Eroare la ştergerea înregistrării!"
@@ -995,6 +1080,33 @@
 msgid "Setup.LNB$own"
 msgstr "propriu"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Folosește actuator de poziționare"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Latitudinea locului (grade)"
+
+msgid "South"
+msgstr "Sud"
+
+msgid "North"
+msgstr "Nord"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Longitudinea locului (grade)"
+
+msgid "West"
+msgstr "Vest"
+
+msgid "East"
+msgstr "Est"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Unghi maxim al actuatorului (grade)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Viteza actuatorului (grade/s)"
+
 msgid "CAM reset"
 msgstr "Resetare CAM"
 
@@ -1004,9 +1116,19 @@
 msgid "CAM ready"
 msgstr "CAM pregătit"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (activez)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Anulează activarea"
+
+msgid "Button$Activate"
+msgstr "Activează"
+
 msgid "Button$Menu"
 msgstr "Meniu"
 
@@ -1019,6 +1141,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Nu pot deschide meniul CAM"
 
+msgid "Can't activate CAM!"
+msgstr "Nu pot activa CAM-ul!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM-ul este in folosinţă - totuşi resetez?"
 
@@ -1089,7 +1214,7 @@
 msgstr "Dimensiune maximă a fişierului video (MB)"
 
 msgid "Setup.Recording$Split edited files"
-msgstr "Separare fişiere montate"
+msgstr "Separare fişiere editate"
 
 msgid "Setup.Recording$Delete timeshift recording"
 msgstr "Şterge înregistrarea pentru vizionare decalată"
@@ -1107,10 +1232,37 @@
 msgstr "Arată timpul rămas"
 
 msgid "Setup.Replay$Progress display time (s)"
-msgstr "Durata afișării indicatorului de progres (s) "
+msgstr "Durata afișării indicatorului de progres (s)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "Pauză la punerea marcajului de montaj"
+msgstr "Pauză la punerea marcajului de editare"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pauză după săritura la marcaj"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Săritură peste părțile editate"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pauză la ultimul marcaj"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Durata inițială (s) pentru săritul adaptiv al marcajelor"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Timeout (s) de resetare pentru săritul adaptiv al marcajelor"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Strategie alternativă pentru săritul adaptiv al marcajelor"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Folosește tastele Anterior/Următor pentru săritul adaptiv"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Durata sărită cu tastele Verde/Galben (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Durata sărită cu tastele Verde/Galben la repetiție (s)"
 
 msgid "Setup.Replay$Resume ID"
 msgstr "Identificator continuare"
@@ -1148,6 +1300,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volumul la pornire"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Numărul de pași ai reglajului de volum"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Liniarizează caracteristica de volum"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Lista de canale în buclă"
 
@@ -1194,13 +1352,13 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr " Opreşte montajul înregistrării"
+msgstr " Anulează editarea înregistrării"
 
 msgid "Stop recording?"
 msgstr "Opresc înregistrarea?"
 
 msgid "Cancel editing?"
-msgstr "Opresc montajul înregistrării?"
+msgstr "Anulez editarea înregistrării?"
 
 msgid "No audio available!"
 msgstr "Lipseşte sunetul!"
@@ -1228,19 +1386,19 @@
 msgstr "Salt la: "
 
 msgid "No editing marks defined!"
-msgstr "Nu s-au pus marcaje de montaj pentru această înregistrare"
+msgstr "Nu s-au pus marcaje de editare pentru această înregistrare"
 
 msgid "No editing sequences defined!"
-msgstr "Nu s-au definit secvențe pentru montaj!"
+msgstr "Nu s-au definit secvențe pentru editare!"
 
 msgid "Can't start editing process!"
-msgstr "Nu pot porni montajul înregistrării!"
+msgstr "Nu pot porni editarea înregistrării!"
 
 msgid "Editing process started"
-msgstr "Montajul înregistrării a început"
+msgstr "Editarea înregistrării a început"
 
 msgid "Editing process already active!"
-msgstr "Montajul înregistrării este deja activ!"
+msgstr "Editarea înregistrării este deja activă!"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 msgstr " aăâbcdefghiîjklmnopqrsştţuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
@@ -1282,7 +1440,7 @@
 msgstr "Nu pot închide - vezi opţiunea '-s'"
 
 msgid "Editing - shut down anyway?"
-msgstr "Montajul tocmai se efectuează - închid, totuşi?"
+msgstr "Editarea tocmai se efectuează - închid, totuşi?"
 
 msgid "Recording - shut down anyway?"
 msgstr "Tocmai se înregistrează - închid, totuşi?"
@@ -1299,7 +1457,7 @@
 msgstr "Plugin-ul %s se va trezi +n %ld min, continui?"
 
 msgid "Editing - restart anyway?"
-msgstr "Montajul tocmai se efectuează - repornesc, totuşi?"
+msgstr "Editarea tocmai se efectuează - repornesc, totuşi?"
 
 msgid "Recording - restart anyway?"
 msgstr "Tocmai se înregistrează - repornesc, totuşi?"
@@ -1332,6 +1490,10 @@
 msgid "PLAY"
 msgstr "REDARE"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Rotesc antena la %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Cons. ST:TNG"
 
@@ -1383,10 +1545,10 @@
 msgstr "Comut dispozitiv DVB primar..."
 
 msgid "Editing process failed!"
-msgstr "Montajul înregistrării a eşuat"
+msgstr "Editarea înregistrării a eşuat"
 
 msgid "Editing process finished"
-msgstr "Montajul înregistrării s-a terminat"
+msgstr "Editarea înregistrării s-a încheiat"
 
 msgid "Press any key to cancel restart"
 msgstr "Apăsaţi orice tastă pentru a anula repornirea"
diff -ruN vdr-2.0.0/po/ru_RU.po vdr-2.2.0/po/ru_RU.po
--- vdr-2.0.0/po/ru_RU.po	2013-03-11 14:13:26.000000000 +0100
+++ vdr-2.2.0/po/ru_RU.po	2015-02-19 10:12:22.359199335 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Vyacheslav Dikonov <sdiconov@mail.ru>, 2004, 2005
 # Oleg Roitburd <oroitburd@gmail.com>, 2005-2008, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2013-03-10 17:13+0100\n"
 "Last-Translator: Oleg Roitburd <oroitburd@gmail.com>\n"
 "Language-Team: Russian <vdr@linuxtv.org>\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** ������������ ����� ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "����� ����������!"
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "������� EPG-������������"
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "��������� ������ �� ���������!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "��� ���������� �� ������ ��������� '%c'!"
 
-msgid "Button$Select"
-msgstr "�������"
+msgid "Button$Open"
+msgstr "�������"
 
 msgid "Delete folder and all sub folders?"
 msgstr "������� ���������� � ��� �������������?"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM �� ��������"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "���� � ������"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "������"
 
-msgid "Button$Open"
-msgstr "�������"
-
 msgid "Commands"
 msgstr "�������"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "����"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "CAM ����������"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM �����"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "�������� ������"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "����"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "���� CAM-������ ����������!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM ������������ - ������������� �����������?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "����� ��� ��������� ����������"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID ���������������"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "��������� ��� ���������"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "��������� �������"
 
@@ -1275,7 +1433,7 @@
 msgstr "����������� ���������� ����� ��������"
 
 msgid "Index file regeneration failed!"
-msgstr "������ ����������� ���������� ����� "
+msgstr "������ ����������� ���������� �����"
 
 msgid "Can't shutdown - option '-s' not given!"
 msgstr "���������� ���������� - �� ����� �������� '-s'!"
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "���������������"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG ������"
 
diff -ruN vdr-2.0.0/po/sk_SK.po vdr-2.2.0/po/sk_SK.po
--- vdr-2.0.0/po/sk_SK.po	2013-03-11 14:13:32.000000000 +0100
+++ vdr-2.2.0/po/sk_SK.po	2015-02-19 10:12:22.287196240 +0100
@@ -1,24 +1,28 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Milan Hrala <hrala.milan@gmail.com>, 2011, 2013
+# Milan Hrala <hrala.milan@gmail.com>, 2011, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
-"PO-Revision-Date: 2013-03-04 21:24+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-17 18:59+0100\n"
 "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
 "Language-Team: Slovak <vdr@linuxtv.org>\n"
 "Language: sk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ISO-8859-2\n"
 "Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
 
 msgid "*** Invalid Channel ***"
 msgstr "*** Neplatn� kan�l ***"
 
+msgid "CAM activated!"
+msgstr "CAM Aktivovan�!"
+
 msgid "Channel not available!"
 msgstr "Kan�l nie je dostupn�!"
 
@@ -44,16 +48,16 @@
 msgstr "syst�m"
 
 msgid "Srate"
-msgstr "Srate"
+msgstr "Symbolov� r�chlos�"
 
 msgid "Inversion"
-msgstr "Inversion"
+msgstr "Inverzia"
 
 msgid "CoderateH"
-msgstr "CoderateH"
+msgstr "Konvolu�n�K�d-vysok� prijorita"
 
 msgid "CoderateL"
-msgstr "CoderateL"
+msgstr "Konvolu�n�K�d-n�zka prijorita"
 
 msgid "Modulation"
 msgstr "Modul�cia"
@@ -62,10 +66,10 @@
 msgstr "��rka p�sma"
 
 msgid "Transmission"
-msgstr "Prenos"
+msgstr "Vysielac� m�d"
 
 msgid "Guard"
-msgstr "Ochrana"
+msgstr "Ochrann� interval"
 
 msgid "Hierarchy"
 msgstr "Hierarchia"
@@ -76,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Sp���a sa sn�manie EPG"
 
@@ -332,7 +345,7 @@
 msgstr "slk"
 
 msgid "Phase 1: Detecting RC code type"
-msgstr "F�za 1: Detekcia typu dia�kov�ho ovl�dania"
+msgstr "Krok 1: Detekcia typu dia�kov�ho ovl�dania"
 
 msgid "Press any key on the RC unit"
 msgstr "Stla�te �ubovoln� tla�idlo ovl�dania"
@@ -344,23 +357,23 @@
 msgstr "Nestl��ajte �iadne kl�vesy..."
 
 msgid "Phase 2: Learning specific key codes"
-msgstr "F�za 2: Ur�enie funkcie tla�idiel"
+msgstr "Krok 2: Ur�enie funkcie tla�idiel"
 
 #, c-format
 msgid "Press key for '%s'"
-msgstr "Stla�te tla�idlo pre funkciu  '%s'"
+msgstr "Stla�te tla�idlo pre funkciu '%s'"
 
 msgid "Press 'Up' to confirm"
 msgstr "Pre potvrdenie stla�te tla�idlo 'Hore'"
 
 msgid "Press 'Down' to continue"
-msgstr "Pokra�ujete stla�en�m tla�idla 'Dole' "
+msgstr "Pokra�ujete stla�en�m tla�idla 'Dole'"
 
 msgid "(press 'Up' to go back)"
 msgstr "(Vr�tite sa stla�en�m tla�idla 'hore')"
 
 msgid "(press 'Down' to end key definition)"
-msgstr "(Definovanie tla�idiel ukon��te stla�en�m tla�idla 'dole')"
+msgstr "(Stla�en�m tla�idla 'dole' ukon��te definovanie tla�idiel)"
 
 msgid "(press 'Menu' to skip this key)"
 msgstr "(stla�en�m 'Menu' presko��te defin�ciu tla�idla)"
@@ -369,7 +382,7 @@
 msgstr "Programovanie dia�kov�ho ovl�dania"
 
 msgid "Phase 3: Saving key codes"
-msgstr "F�za 3: Ulo�enie k�du tla�idiel"
+msgstr "Krok 3: Ulo�enie k�du tla�idiel"
 
 msgid "Press 'Up' to save, 'Down' to cancel"
 msgstr "Nastavenie stla�en�m tla�idla 'hore' ulo��te, 'Dole' zru��te"
@@ -531,31 +544,31 @@
 msgstr "Frekvencia"
 
 msgid "Vpid"
-msgstr "Vpid"
+msgstr "Video pid"
 
 msgid "Ppid"
-msgstr "Ppid"
+msgstr "PCR pid"
 
 msgid "Apid1"
-msgstr "Apid1"
+msgstr "Audio pid1"
 
 msgid "Apid2"
-msgstr "Apid2"
+msgstr "Audio pid2"
 
 msgid "Dpid1"
-msgstr "Dpid1"
+msgstr "Dolby pid1"
 
 msgid "Dpid2"
-msgstr "Dpid2"
+msgstr "Dolby pid2"
 
 msgid "Spid1"
-msgstr "Spid1"
+msgstr "Spid1 (titulky)"
 
 msgid "Spid2"
-msgstr "Spid2"
+msgstr "Spid2 (titulky)"
 
 msgid "Tpid"
-msgstr "Tpid"
+msgstr "Teletext pid"
 
 msgid "CA"
 msgstr "CA"
@@ -563,8 +576,14 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Network id"
+
+msgid "Tid"
+msgstr "Transponder id"
+
 msgid "Channel settings are not unique!"
-msgstr "Nastavenia kan�lu nie s� v�nimo�n�!"
+msgstr "Parametre kan�lu s� duplicitn�!"
 
 msgid "Channels"
 msgstr "Kan�ly"
@@ -582,7 +601,7 @@
 msgstr "Ozna�i�"
 
 msgid "Channel is being used by a timer!"
-msgstr "Pl�n nahr�vania pr�ve pou��va kan�l!"
+msgstr "U� je napl�novan� nahr�vanie pre tento kan�l!"
 
 msgid "Delete channel?"
 msgstr "Odstr�ni� kan�l?"
@@ -603,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "N�zov zlo�ky nesmie obsahova� '%c'!"
 
-msgid "Button$Select"
-msgstr "Vybra�"
+msgid "Button$Open"
+msgstr "Otvori�"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Vymaza� zlo�ku a v�etky pod zlo�ky?"
@@ -723,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM neodpoved�!"
 
+msgid "Edit path"
+msgstr "Upravi� umiestnenie"
+
+msgid "Folder"
+msgstr "Prie�inok"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Tento prie�inok sa pr�ve pou��va - �iadne zmeny nie s� mo�n�!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Presun�� cel� prie�inok obsahuj�ci %d nahr�vky?"
+
+msgid "Error while moving folder!"
+msgstr "Chyba pri presune prie�inku!"
+
+msgid "Edit recording"
+msgstr "Upravi� nahr�vku"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "T�to nahr�vka sa v s��asnej dobe pou��va - �iadne zmeny nie s� mo�n�!"
+
+msgid "Button$Cancel cutting"
+msgstr "Zru�i� strih"
+
+msgid "Button$Stop cutting"
+msgstr "Zastavi� strih"
+
+msgid "Button$Cancel moving"
+msgstr "Zru�i� presun"
+
+msgid "Button$Stop moving"
+msgstr "Zastavi� presun"
+
+msgid "Button$Cancel copying"
+msgstr "Zru�i� kop�rovanie"
+
+msgid "Button$Stop copying"
+msgstr "Zastavi� kop�rovanie"
+
+msgid "Button$Cut"
+msgstr "Vystrihn��"
+
+msgid "Button$Delete marks"
+msgstr "Vymaza� zna�ky"
+
+msgid "Recording vanished!"
+msgstr "Z�znam sa stratil!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Upraven� verzia u� existuje - prep�sa�?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Chyba v porad� pri zostrihu nahr�vky!"
+
+msgid "Rename recording to folder name?"
+msgstr "Premenova� meno prie�inku nahr�vky?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Odstr�ni� strihov� zna�ky tejto nahr�vky?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Chyba pri mazan� zna�iek!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Chyba pri zmene priority/�ivotnos�i!"
+
+msgid "Error while changing folder/name!"
+msgstr "Chyba pri zmene prie�inka/n�zvu!"
+
 msgid "Recording info"
 msgstr "Podrobnosti nahr�vky"
 
@@ -735,9 +824,6 @@
 msgid "Recordings"
 msgstr "Nahr�vky"
 
-msgid "Button$Open"
-msgstr "Otvori�"
-
 msgid "Commands"
 msgstr "Pr�kazy"
 
@@ -802,7 +888,7 @@
 msgstr "Mal� p�smo"
 
 msgid "Setup.OSD$Fixed font"
-msgstr "Fixn� p�smo"
+msgstr "P�smo s kon�tantnou ��rkou"
 
 msgid "Setup.OSD$Default font size (%)"
 msgstr "Ve�kos� predvolen�ho p�sma (%)"
@@ -811,7 +897,7 @@
 msgstr "Ve�kos� mal�ho p�sma (%)"
 
 msgid "Setup.OSD$Fixed font size (%)"
-msgstr "Ve�kos� fixn�ho p�sma (%)"
+msgstr "Ve�kos� p�sma kon�tantnej ��rky (%)"
 
 msgid "Setup.OSD$Channel info position"
 msgstr "Poz�cia inform�ci� o kan�le"
@@ -993,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "vlastn�"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Pou�i� polohovanie paraboly"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Zemepisn� ��rka (stup�ov)"
+
+msgid "South"
+msgstr "Ju�ne"
+
+msgid "North"
+msgstr "Severne"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Zemepisn� d�ka (stup�ov)"
+
+msgid "West"
+msgstr "Z�padne"
+
+msgid "East"
+msgstr "V�chodne"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Max. rozsah polohovania (stup�ov)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "R�chlos� polohovania (stup�ov/s)"
+
 msgid "CAM reset"
 msgstr "Resetnutie CAMu"
 
@@ -1002,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM pripraven�"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktivovan�)"
+
 msgid "CAM"
 msgstr "CAM (modul podmienen�ho pr�stupu)"
 
+msgid "Button$Cancel activation"
+msgstr "Zru�i� aktiv�ciu"
+
+msgid "Button$Activate"
+msgstr "Aktivova�"
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1017,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Menu CAM nie je dostupn�"
 
+msgid "Can't activate CAM!"
+msgstr "CAM nie je mo�n� aktivova�!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM sa pou��va - naozaj re�tartova�?"
 
@@ -1110,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pozastavi� prehr�vanie pri stanoven� zna�ky"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pozastavi� prehr�vanie pri skoku na zna�ku"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Presko�i� upraven� �asti"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pozastavi� prehr�vanie na poslednej zna�ke"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Po�iato�n� doba pre adapt�vny skok (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Obnovi� �asov� limit pre adapt�vne preskakovanie (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Alternat�vne spr�vanie pre adapt�vne preskakovanie"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Pou�i� tla�idl� Predo�l�/Nasleduj�ci pre adapt�vne preskakovanie"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Presko�i� �sek so zelen�m/�lt�m tla�idlom (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Opakovan� presko�enie �seku so Zelen�m/�lt�m tla�idlom (s)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ident. ��slo obnovenia prehr�vania"
 
@@ -1132,7 +1285,7 @@
 msgstr "Limit pri ��selnej vo�be kan�la (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
-msgstr "Zdr�anie opakovan�ho stla�enia  tla�idla (ms)"
+msgstr "Zdr�anie opakovan�ho stla�enia tla�idla (ms)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delta (ms)"
 msgstr "Peri�da opakovania stla�en�ho tla�idla (ms)"
@@ -1146,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hlasitos� po spusten�"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Krok zmeny hlasitosti"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Zarovna� zmenu hlasitosti"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kan�ly cyklova� pri prepnut�"
 
@@ -1172,7 +1331,7 @@
 
 #. TRANSLATORS: note the leading and trailing blanks!
 msgid " Stop recording "
-msgstr " Zastavi� nahr�vanie"
+msgstr " Zastavi� nahr�vanie "
 
 msgid "Schedule"
 msgstr "TV program"
@@ -1307,7 +1466,7 @@
 
 #. TRANSLATORS: note the trailing blank!
 msgid "Volume "
-msgstr "Hlasitos�"
+msgstr "Hlasitos� "
 
 msgid "Classic VDR"
 msgstr "Klasick� VDR"
@@ -1330,6 +1489,10 @@
 msgid "PLAY"
 msgstr "PREHR�VA SA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Pohyb paraboly na %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG panely"
 
@@ -1387,7 +1550,7 @@
 msgstr "Proces zostrih�vania dokon�en�"
 
 msgid "Press any key to cancel restart"
-msgstr "ktor�ko�vek  kl�vesa zru�� re�tart"
+msgstr "ktor�ko�vek kl�vesa zru�� re�tart"
 
 #, c-format
 msgid "VDR will shut down in %s minutes"
diff -ruN vdr-2.0.0/po/sl_SI.po vdr-2.2.0/po/sl_SI.po
--- vdr-2.0.0/po/sl_SI.po	2013-03-11 14:13:47.000000000 +0100
+++ vdr-2.2.0/po/sl_SI.po	2015-02-19 10:12:22.384200393 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Miha Setina <mihasetina@softhome.net>, 2000
 # Matjaz Thaler <matjaz.thaler@guest.arnes.si>, 2003, 2005, 2006, 2008, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2013-03-04 12:46+0100\n"
 "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
 "Language-Team: Slovenian <vdr@linuxtv.org>\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Neznan kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal ni razpolo�ljiv!"
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Pri�enjam EPG-scan"
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Nastavitve kanala niso edinstvene!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Direkotrij ne sme vsebobati '%c'!"
 
-msgid "Button$Select"
-msgstr "Izberi"
+msgid "Button$Open"
+msgstr "Odpri"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Izbri�i direktorij in vse pod direktorije"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM se ne odziva!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Podatki o snemanju"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Posnetki"
 
-msgid "Button$Open"
-msgstr "Odpri"
-
 msgid "Commands"
 msgstr "Ukazi"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "lastni"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "Reset CAM-a"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM pripravljen"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Meni"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Ne morem odpreti CAM menija!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM je v uporabi - zares resetiraj?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pavza predvajanja pri postavitvi zan�ke"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID za predvajanje"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Privzeta glasnost"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Menjava kanala"
 
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "PREDVAJAJ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Konsola"
 
diff -ruN vdr-2.0.0/po/sr_RS.po vdr-2.2.0/po/sr_RS.po
--- vdr-2.0.0/po/sr_RS.po	2013-03-16 16:09:13.000000000 +0100
+++ vdr-2.2.0/po/sr_RS.po	2015-02-19 10:12:21.793175106 +0100
@@ -1,14 +1,14 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Milan Cvijanovi� <elcom_cvijo@hotmail.com>, 2010
 # Zoran Turalija <zoran.turalija@gmail.com>, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2013-03-16 15:05+0100\n"
 "Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n"
 "Language-Team: Serbian <vdr@linuxtv.org>\n"
@@ -20,6 +20,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Neispravan Kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal nije dostupan!"
 
@@ -77,6 +80,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Po�inje EPG pretra�ivanje"
 
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Postavke kanala nisu jedinstvene!"
 
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Naziv direktorijuma ne sme da sadr�i '%c'!"
 
-msgid "Button$Select"
-msgstr "Izaberi"
+msgid "Button$Open"
+msgstr "Otvori"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Obri�i direktorijum i sve poddirektorijume?"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM ne reaguje!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Detalji snimanja"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Snimci"
 
-msgid "Button$Open"
-msgstr "Otvori"
-
 msgid "Commands"
 msgstr "Komande"
 
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "sopstven"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "Ponovno pokreni CAM"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM spreman"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Meni"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Otvaranje CAM menija neuspe�no!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM u upotrebi - stvarno ponovno pokrenuti?"
 
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pauziraj reprodukciju prilikom obele�avanja"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID reprodukcije"
 
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Po�etna ja�ina tona"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Prelazak sa kraja na po�etak liste kanala"
 
@@ -1193,7 +1351,7 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr "Odustani od izmena"
+msgstr " Odustani od izmena"
 
 msgid "Stop recording?"
 msgstr "Zaustavi snimanje?"
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "REPRODUKCIJA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG paneli"
 
diff -ruN vdr-2.0.0/po/sv_SE.po vdr-2.2.0/po/sv_SE.po
--- vdr-2.0.0/po/sv_SE.po	2013-03-11 14:13:53.000000000 +0100
+++ vdr-2.2.0/po/sv_SE.po	2015-02-19 10:12:22.140189963 +0100
@@ -1,19 +1,20 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Tomas Prybil <tomas@prybil.se>, 2002, 2003, 2005, 2006
 # Jan Ekholm <chakie@infa.abo.fi>, 2003
 # Tomas Berglund <tomber@telia.com>, 2008
 # Magnus Andersson <svankan@bahnhof.se>, 2008
 # Richard Lithvall <r-vdr@boomer.se>, 2013
+# Magnus Sirvi� <sirwio@hotmail.com>, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-02-18 17:04+0100\n"
-"Last-Translator: Richard Lithvall <r-vdr@boomer.se>\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-12 21:58+0100\n"
+"Last-Translator: Magnus Sirvi� <sirwio@hotmail.com>\n"
 "Language-Team: Swedish <vdr@linuxtv.org>\n"
 "Language: sv\n"
 "MIME-Version: 1.0\n"
@@ -23,6 +24,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Felaktig kanal ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktiverad!"
+
 msgid "Channel not available!"
 msgstr "Kanalen �r inte tillg�nglig!"
 
@@ -72,7 +76,7 @@
 msgstr "Guard"
 
 msgid "Hierarchy"
-msgstr "Hierarchy"
+msgstr "Hierarki"
 
 msgid "Rolloff"
 msgstr "Rolloff"
@@ -80,6 +84,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilotavsnitt"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "P�b�rjar EPG-avs�kning"
 
@@ -108,7 +121,7 @@
 msgstr "Seri�s/Klassisk/Relig�s/Historisk/Drama"
 
 msgid "Content$Adult Movie/Drama"
-msgstr "Vuxen/Drama"
+msgstr "Vuxenfilm/Drama"
 
 msgid "Content$News/Current Affairs"
 msgstr "Nyheter/Journalistik"
@@ -567,6 +580,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Kanalinst�llningarna �r ej unika!"
 
@@ -607,8 +626,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Mappnamnet f�r inte inneh�lla '%c'!"
 
-msgid "Button$Select"
-msgstr "V�lj"
+msgid "Button$Open"
+msgstr "�ppna"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Radera mapp och alla undermappar?"
@@ -727,6 +746,76 @@
 msgid "CAM not responding!"
 msgstr "CAM svarar inte!"
 
+msgid "Edit path"
+msgstr "Redigera s�kv�g"
+
+msgid "Folder"
+msgstr "Mapp"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Den h�r mappen anv�nds just nu - inga f�r�ndringar �r m�jliga!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Flytta hela mappen inneh�llandes %d inspelningar?"
+
+msgid "Error while moving folder!"
+msgstr "Ett fel upstod n�r mappen skulle flyttas!"
+
+msgid "Edit recording"
+msgstr "Redigera inspelning"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Den h�r inspelningen anv�nds just nu - inga f�r�ndringar �r m�jliga!"
+
+msgid "Button$Cancel cutting"
+msgstr "Avbryta klippning"
+
+msgid "Button$Stop cutting"
+msgstr "Avsluta klippning"
+
+msgid "Button$Cancel moving"
+msgstr "Avbryta flytt"
+
+msgid "Button$Stop moving"
+msgstr "Avsluta flytt"
+
+msgid "Button$Cancel copying"
+msgstr "Avbryta kopiering"
+
+msgid "Button$Stop copying"
+msgstr "Avsluta kopiering"
+
+msgid "Button$Cut"
+msgstr "Redigera"
+
+msgid "Button$Delete marks"
+msgstr "Ta bort redigeringsm�rke"
+
+msgid "Recording vanished!"
+msgstr "Inspelningen f�rsvann!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "En redigerad version finns redan - skall den ers�ttas?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Ett fel upstod n�r inspelningen k�ades f�r klippning!"
+
+msgid "Rename recording to folder name?"
+msgstr "Byta namn p� inspelning till mappnamnet?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Ta bort redigeringsm�rken f�r den h�r inspelningen?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Ett fel uppstod n�r redigeringsm�rken skulle tas bort!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Ett fel upstod n�r prioritet/livstid skulle �ndras!"
+
+msgid "Error while changing folder/name!"
+msgstr "Ett fel uppstod n�r mapp/namn skulle �ndras!"
+
 msgid "Recording info"
 msgstr "Inspelningsinformation"
 
@@ -739,9 +828,6 @@
 msgid "Recordings"
 msgstr "Inspelningar"
 
-msgid "Button$Open"
-msgstr "�ppna"
-
 msgid "Commands"
 msgstr "Kommandon"
 
@@ -995,7 +1081,34 @@
 msgstr "Enhet %d ansluten till satellitkabel"
 
 msgid "Setup.LNB$own"
-msgstr "�gare"
+msgstr "egen"
+
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Anv�nda parabolstyrning"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Latitude (grader)"
+
+msgid "South"
+msgstr "S�der"
+
+msgid "North"
+msgstr "Norr"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Longitud (grader)"
+
+msgid "West"
+msgstr "V�ster"
+
+msgid "East"
+msgstr "�ster"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Maximal positonerings sv�ngning (grader)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Positoneringshastighet (grader/s)"
 
 msgid "CAM reset"
 msgstr "CAM omstart"
@@ -1006,9 +1119,19 @@
 msgid "CAM ready"
 msgstr "CAM klar"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (aktiverar)"
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr "Avbryta aktivering"
+
+msgid "Button$Activate"
+msgstr "Aktivera"
+
 msgid "Button$Menu"
 msgstr "Meny"
 
@@ -1021,6 +1144,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Det g�r inte att �ppna CAM menyn!"
 
+msgid "Can't activate CAM!"
+msgstr "Kan inte aktivera CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM upptagen, vill du verkligen �terst�lla?"
 
@@ -1112,7 +1238,34 @@
 msgstr "Tid f�r visning av f�rloppsindikatorn (sek)"
 
 msgid "Setup.Replay$Pause replay when setting mark"
-msgstr "Pausa uppspelningen vid s�ttande av redigeringsm�rke"
+msgstr "Pausa uppspelning vid s�ttande av redigeringsm�rke"
+
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Pausa uppspelning vid hopp till redigeringsm�rke"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Hoppa �ver redigerade delar"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Pausa uppspelning p� sista redigeringsm�rket"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Initialt v�rde p� intelligent hopp i inspelning (s)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Tidsgr�ns vid intelligenta hopp i inspellning (s)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Alternativt beteende p� intelligenta hopp i inspelning"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Anv�nda F�reg�ende/N�sta knappar f�r intelligenta hopp i inspelning"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Tid f�r hopp med Gr�n/Gul knapp (s)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Tid f�r hopp med Gr�n/Gul knapp vid �teruppspelning (s)"
 
 msgid "Setup.Replay$Resume ID"
 msgstr "�terupptagnings-ID"
@@ -1150,6 +1303,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Ljudstyrka vid uppstart"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Volymsteg"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Volymlinj�risering"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Cirkul�r kanallista"
 
@@ -1248,7 +1407,7 @@
 msgstr " abcdefghijklmnopqrstuvwxyz���0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 
 msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc��2\tdef3\tghi4\tjkl5\tmno�6\tpqrs7\ttuv8\twxyz"
+msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc��2\tdef3\tghi4\tjkl5\tmno�6\tpqrs7\ttuv8\twxyz9"
 
 msgid "Button$ABC/abc"
 msgstr "ABC/abc"
@@ -1334,6 +1493,10 @@
 msgid "PLAY"
 msgstr "SPELA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Flyttar parabol till %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG"
 
diff -ruN vdr-2.0.0/po/tr_TR.po vdr-2.2.0/po/tr_TR.po
--- vdr-2.0.0/po/tr_TR.po	2013-03-11 14:13:56.000000000 +0100
+++ vdr-2.2.0/po/tr_TR.po	2015-02-19 10:12:22.432202449 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Oktay Yolge�en <oktay_73@yahoo.de>, 2007, 2008
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2008-02-28 00:33+0100\n"
 "Last-Translator: Oktay Yolge�en <oktay_73@yahoo.de>\n"
 "Language-Team: Turkish <vdr@linuxtv.org>\n"
@@ -19,6 +19,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "*** Ge�ersiz kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal kullan�lam�yor!"
 
@@ -76,6 +79,15 @@
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "EPG tarama ba�l�yor"
 
@@ -563,6 +575,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Kanal ayarlar� belli de��l!"
 
@@ -603,8 +621,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr ""
 
-msgid "Button$Select"
-msgstr ""
+msgid "Button$Open"
+msgstr "A�"
 
 msgid "Delete folder and all sub folders?"
 msgstr ""
@@ -723,6 +741,76 @@
 msgid "CAM not responding!"
 msgstr "CAM yan�t vermiyor!"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "Kay�t bilgisi"
 
@@ -735,9 +823,6 @@
 msgid "Recordings"
 msgstr "Kay�tlar"
 
-msgid "Button$Open"
-msgstr "A�"
-
 msgid "Commands"
 msgstr "Komutlar"
 
@@ -993,6 +1078,33 @@
 msgid "Setup.LNB$own"
 msgstr ""
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "CAM s�f�rland�"
 
@@ -1002,9 +1114,19 @@
 msgid "CAM ready"
 msgstr "CAM haz�r"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Men�"
 
@@ -1017,6 +1139,9 @@
 msgid "Can't open CAM menu!"
 msgstr "CAM men�s� a��lam�yor!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM kullan�l�yor - ger�ekden s�f�rla?"
 
@@ -1110,6 +1235,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr ""
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "G�steri� ID'si"
 
@@ -1123,7 +1275,7 @@
 msgstr "Minimum kullan�c� hareketsizli�i (dak)"
 
 msgid "Setup.Miscellaneous$SVDRP timeout (s)"
-msgstr "SVDRP zaman a��m� (sn) "
+msgstr "SVDRP zaman a��m� (sn)"
 
 msgid "Setup.Miscellaneous$Zap timeout (s)"
 msgstr "Zaping zaman a��m� (sn)"
@@ -1146,6 +1298,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "A��l��daki ses"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr ""
 
@@ -1330,6 +1488,10 @@
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG paneli"
 
diff -ruN vdr-2.0.0/po/uk_UA.po vdr-2.2.0/po/uk_UA.po
--- vdr-2.0.0/po/uk_UA.po	2013-03-11 14:13:58.000000000 +0100
+++ vdr-2.2.0/po/uk_UA.po	2015-02-19 10:12:22.113188803 +0100
@@ -1,25 +1,28 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
-# Yarema Aka Knedlyk <yupadmin@gmail.com>, 2007-2010, 2013
+# Yarema Aka Knedlyk <yupadmin@gmail.com>, 2007-2010, 2013, 2015
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
-"PO-Revision-Date: 2013-02-09 16:00+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
+"PO-Revision-Date: 2015-02-13 18:14+0100\n"
 "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
 "Language-Team: Ukrainian <vdr@linuxtv.org>\n"
 "Language: uk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.4\n"
+"X-Generator: Poedit 1.7.4\n"
 
 msgid "*** Invalid Channel ***"
 msgstr "*** Неправильний канал ***"
 
+msgid "CAM activated!"
+msgstr "CAM активовано!"
+
 msgid "Channel not available!"
 msgstr "Канал недоступний!"
 
@@ -75,10 +78,19 @@
 msgstr "Крен"
 
 msgid "StreamId"
-msgstr "StreamId"
+msgstr "Id потоку"
+
+msgid "Pilot"
+msgstr "Пілот"
+
+msgid "T2SystemId"
+msgstr "T2SystemId"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
 
 msgid "Starting EPG scan"
-msgstr "Починаю EPG-сканування"
+msgstr "Початок сканування EPG"
 
 msgid "Content$Movie/Drama"
 msgstr "Фільм/Драма"
@@ -237,7 +249,7 @@
 msgstr "Трансляція/Преса"
 
 msgid "Content$New Media"
-msgstr "Нові медія"
+msgstr "Нові медіа"
 
 msgid "Content$Arts/Culture Magazine"
 msgstr "Мистецтво/Культурний журнал"
@@ -412,7 +424,7 @@
 msgstr "Інфо"
 
 msgid "Key$Play/Pause"
-msgstr "Програвати/Пауза"
+msgstr "Програвання/Пауза"
 
 msgid "Key$Play"
 msgstr "Програвання"
@@ -564,6 +576,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr "Nid"
+
+msgid "Tid"
+msgstr "Tid"
+
 msgid "Channel settings are not unique!"
 msgstr "Настройки каналу не єдині!"
 
@@ -586,7 +604,7 @@
 msgstr "Канал зайнятий таймером!"
 
 msgid "Delete channel?"
-msgstr "Видалити канал?"
+msgstr "Вилучити канал?"
 
 msgid "Edit folder"
 msgstr "Редагувати теку"
@@ -604,8 +622,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "Назва теки не повинна містити '%c'!"
 
-msgid "Button$Select"
-msgstr "Вибрати"
+msgid "Button$Open"
+msgstr "Відкрити"
 
 msgid "Delete folder and all sub folders?"
 msgstr "Видалити теку і всі підтеки?"
@@ -701,7 +719,7 @@
 msgstr "Програма"
 
 msgid "Can't switch channel!"
-msgstr "Неможливо переключити канал!"
+msgstr "Неможливо перемкнути канал!"
 
 #, c-format
 msgid "Schedule - %s"
@@ -724,6 +742,76 @@
 msgid "CAM not responding!"
 msgstr "CAM не відповідає!"
 
+msgid "Edit path"
+msgstr "Редагувати шлях"
+
+msgid "Folder"
+msgstr "Тека"
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr "Ця тека зараз використовується - не дозволено жодних змін!"
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr "Перемістити цілу теку, що містить %d записів?"
+
+msgid "Error while moving folder!"
+msgstr "Помилка перенесення теки!"
+
+msgid "Edit recording"
+msgstr "Редагувати запис"
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr "Цей запис зараз використовується - не дозволено жодних змін!"
+
+msgid "Button$Cancel cutting"
+msgstr "Відмінити обрізання"
+
+msgid "Button$Stop cutting"
+msgstr "Зупинити обрізання"
+
+msgid "Button$Cancel moving"
+msgstr "Відмінити перенесення"
+
+msgid "Button$Stop moving"
+msgstr "Зупинити перенесення"
+
+msgid "Button$Cancel copying"
+msgstr "Відмінити копіювання"
+
+msgid "Button$Stop copying"
+msgstr "Зупинити копіювання"
+
+msgid "Button$Cut"
+msgstr "Обрізання"
+
+msgid "Button$Delete marks"
+msgstr "Вилучити мітки"
+
+msgid "Recording vanished!"
+msgstr "Запис зник!"
+
+msgid "Edited version already exists - overwrite?"
+msgstr "Вже існує редагована версія - перезаписати?"
+
+msgid "Error while queueing recording for cutting!"
+msgstr "Помилка перешуковування запису для обрізання!"
+
+msgid "Rename recording to folder name?"
+msgstr "Назвати запис назвою теки?"
+
+msgid "Delete editing marks for this recording?"
+msgstr "Вилучити мітки редагування для цього запису?"
+
+msgid "Error while deleting editing marks!"
+msgstr "Помилка вилучення міток редагування!"
+
+msgid "Error while changing priority/lifetime!"
+msgstr "Помилка при зміні пріоритету/тривалості!"
+
+msgid "Error while changing folder/name!"
+msgstr "Помилка при зміні теки/назви!"
+
 msgid "Recording info"
 msgstr "Про запис"
 
@@ -736,9 +824,6 @@
 msgid "Recordings"
 msgstr "Записи"
 
-msgid "Button$Open"
-msgstr "Відкрити"
-
 msgid "Commands"
 msgstr "Команди"
 
@@ -749,7 +834,7 @@
 msgstr "Запис редагується - дійсно вилучити?"
 
 msgid "Error while deleting recording!"
-msgstr "Помилка видалення запису!"
+msgstr "Помилка вилучення запису!"
 
 msgid "Recording commands"
 msgstr "Команди запису"
@@ -758,7 +843,7 @@
 msgstr "ніколи"
 
 msgid "skin dependent"
-msgstr "згідно з стилем"
+msgstr "згідно зі стилем"
 
 msgid "always"
 msgstr "завжди"
@@ -994,6 +1079,33 @@
 msgid "Setup.LNB$own"
 msgstr "власне"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Використання позиціонеру тарілки"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Широта (градуси)"
+
+msgid "South"
+msgstr "Південь"
+
+msgid "North"
+msgstr "Північ"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Довгота (градуси)"
+
+msgid "West"
+msgstr "Захід"
+
+msgid "East"
+msgstr "Схід"
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr "Макс. кут позиціонера (градуси)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Швидкість позиціонера (градуси/с)"
+
 msgid "CAM reset"
 msgstr "Перезавантаження CAM"
 
@@ -1003,9 +1115,19 @@
 msgid "CAM ready"
 msgstr "CAM готовий"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr " (активування)"
+
 msgid "CAM"
 msgstr "CAM (Умовний доступ)"
 
+msgid "Button$Cancel activation"
+msgstr "Відмінити активацію"
+
+msgid "Button$Activate"
+msgstr "Активувати"
+
 msgid "Button$Menu"
 msgstr "Меню"
 
@@ -1018,6 +1140,9 @@
 msgid "Can't open CAM menu!"
 msgstr "Меню CAM-модуля недоступне!"
 
+msgid "Can't activate CAM!"
+msgstr "Не можу активувати CAM!"
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM використовується - дійсно перезапустити?"
 
@@ -1100,10 +1225,10 @@
 msgstr "Багатошвидкісний режим"
 
 msgid "Setup.Replay$Show replay mode"
-msgstr "Віображати режим перегляду"
+msgstr "Показати режим перегляду"
 
 msgid "Setup.Replay$Show remaining time"
-msgstr "Показати час що зашився"
+msgstr "Показати час що залишився"
 
 msgid "Setup.Replay$Progress display time (s)"
 msgstr "Час показу індикатора (с)"
@@ -1111,6 +1236,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Пауза програвання при встановленні мітки"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr "Призупинити програвання при перескакуванні до мітки"
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr "Пропустити редаговані частини"
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr "Призупинити програвання на останній мітці"
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr "Початкова тривалість для адаптивного пропуску (ів)"
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr "Скинути тайм-аут для адаптивного пропуску (ів)"
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr "Альтернативна поведінка для адаптивного пропуску (ів)"
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr "Викор кнопки Попер./Наст. для адаптивного пропуску"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr "Інтервал пропуску з Зеленою/Жовтою кнопкою (ми)"
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr "Інтервал пропуску з Зеленою/Жовтою кнопкою в повторенні (ях)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID продовження"
 
@@ -1124,13 +1276,13 @@
 msgstr "Мін. час очікування вводу (хв)"
 
 msgid "Setup.Miscellaneous$SVDRP timeout (s)"
-msgstr "Затримкка обриву з'єднання SVDRP (сек)"
+msgstr "Затримка обриву з'єднання SVDRP (сек)"
 
 msgid "Setup.Miscellaneous$Zap timeout (s)"
 msgstr "Затримка переключання каналу (сек)"
 
 msgid "Setup.Miscellaneous$Channel entry timeout (ms)"
-msgstr "Затримка часу для впровадження каналу (ms)"
+msgstr "Затримка часу для впровадження каналу (мс)"
 
 msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
 msgstr "Затримка повтору кнопок пульта (мс)"
@@ -1147,6 +1299,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Гучність при включенні"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Кроки гучності"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Лінеаризована гучність"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Кінець каналів"
 
@@ -1220,7 +1378,7 @@
 msgstr "Режим відкладеного перегляду..."
 
 msgid "Delete timeshift recording?"
-msgstr "Видалити запис з часовим зсувом?"
+msgstr "Вилучити запис з часовим зсувом?"
 
 #. TRANSLATORS: note the trailing blank!
 msgid "Jump: "
@@ -1242,10 +1400,10 @@
 msgstr "Процес відеомонтажу вже запущений!"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
-msgstr " abcdefghijklmnopqrstuvwxyzабвгдеєёжзиіїйклмнопрстуфхцчшщъыьюя0123456789-.#~,/_@"
+msgstr " abcdefghijklmnopqrstuvwxyzабвгдеєёжзиіїйклмнопрстуфхцчшщъыьюя0123456789-.#~,/@"
 
 msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr " 0\t-.#~,/_@1\tabcабвг2\tdefдеєёжз3\tghiиіїйкл4\tjklмно5\tmnoпрс6\tpqrsтуфх7\ttuvцчшщъ8\twxyzыьэюя9"
+msgstr " 0\t-.#~,/@1\tabcабвг2\tdefдеєёжз3\tghiиіїйкл4\tjklмно5\tmnoпрс6\tpqrsтуфх7\ttuvцчшщъ8\twxyzыьэюя9"
 
 msgid "Button$ABC/abc"
 msgstr "АБВ/абв"
@@ -1331,6 +1489,10 @@
 msgid "PLAY"
 msgstr "ПРОГРАВАННЯ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Повертання тарілки до %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG панелі"
 
diff -ruN vdr-2.0.0/po/zh_CN.po vdr-2.2.0/po/zh_CN.po
--- vdr-2.0.0/po/zh_CN.po	2013-03-11 14:14:04.000000000 +0100
+++ vdr-2.2.0/po/zh_CN.po	2015-02-19 10:12:22.166191070 +0100
@@ -1,13 +1,13 @@
 # VDR language source file.
-# Copyright (C) 2013 Klaus Schmidinger <vdr@tvdr.de>
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
 # This file is distributed under the same license as the VDR package.
 # Nan Feng <nfvdr@live.com>, 2008, 2013
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: VDR 2.0.0\n"
+"Project-Id-Version: VDR 2.2.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
+"POT-Creation-Date: 2015-02-10 13:40+0100\n"
 "PO-Revision-Date: 2013-03-04 14:52+0800\n"
 "Last-Translator: NFVDR <nfvdr@live.com>\n"
 "Language-Team: Chinese (simplified) <nfvdr@live.com>\n"
@@ -21,6 +21,9 @@
 msgid "*** Invalid Channel ***"
 msgstr "***无效的频道 ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "频道不可用!"
 
@@ -78,6 +81,15 @@
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "开始节目单扫描"
 
@@ -343,7 +355,7 @@
 msgstr "发现RC代码!"
 
 msgid "Do not press any key..."
-msgstr "不要按下任何键... "
+msgstr "不要按下任何键..."
 
 msgid "Phase 2: Learning specific key codes"
 msgstr "阶段 2: 学习特殊的按键代码"
@@ -565,6 +577,12 @@
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "频道设置不是唯一的！"
 
@@ -605,8 +623,8 @@
 msgid "Folder name must not contain '%c'!"
 msgstr "文件夹名称不能包含 '%c'!"
 
-msgid "Button$Select"
-msgstr "选择"
+msgid "Button$Open"
+msgstr "打开"
 
 msgid "Delete folder and all sub folders?"
 msgstr "删除文件夹和所有子文件夹吗？"
@@ -672,7 +690,7 @@
 msgstr "是否删除时间？"
 
 msgid "Timer still recording - really delete?"
-msgstr "录像中-是否的删除？ "
+msgstr "录像中-是否的删除？"
 
 msgid "Event"
 msgstr "事件"
@@ -725,6 +743,76 @@
 msgid "CAM not responding!"
 msgstr "CAM 没有响应！"
 
+msgid "Edit path"
+msgstr ""
+
+msgid "Folder"
+msgstr ""
+
+msgid "This folder is currently in use - no changes are possible!"
+msgstr ""
+
+#, c-format
+msgid "Move entire folder containing %d recordings?"
+msgstr ""
+
+msgid "Error while moving folder!"
+msgstr ""
+
+msgid "Edit recording"
+msgstr ""
+
+msgid "This recording is currently in use - no changes are possible!"
+msgstr ""
+
+msgid "Button$Cancel cutting"
+msgstr ""
+
+msgid "Button$Stop cutting"
+msgstr ""
+
+msgid "Button$Cancel moving"
+msgstr ""
+
+msgid "Button$Stop moving"
+msgstr ""
+
+msgid "Button$Cancel copying"
+msgstr ""
+
+msgid "Button$Stop copying"
+msgstr ""
+
+msgid "Button$Cut"
+msgstr ""
+
+msgid "Button$Delete marks"
+msgstr ""
+
+msgid "Recording vanished!"
+msgstr ""
+
+msgid "Edited version already exists - overwrite?"
+msgstr ""
+
+msgid "Error while queueing recording for cutting!"
+msgstr ""
+
+msgid "Rename recording to folder name?"
+msgstr ""
+
+msgid "Delete editing marks for this recording?"
+msgstr ""
+
+msgid "Error while deleting editing marks!"
+msgstr ""
+
+msgid "Error while changing priority/lifetime!"
+msgstr ""
+
+msgid "Error while changing folder/name!"
+msgstr ""
+
 msgid "Recording info"
 msgstr "录像信息"
 
@@ -737,9 +825,6 @@
 msgid "Recordings"
 msgstr "录像回放列表"
 
-msgid "Button$Open"
-msgstr "打开"
-
 msgid "Commands"
 msgstr "常用操作命令"
 
@@ -889,7 +974,7 @@
 
 #. TRANSLATORS: note the plural!
 msgid "Setup.EPG$Preferred languages"
-msgstr "首选语种 "
+msgstr "首选语种"
 
 #. TRANSLATORS: note the singular!
 msgid "Setup.EPG$Preferred language"
@@ -995,6 +1080,33 @@
 msgid "Setup.LNB$own"
 msgstr "所有"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr ""
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr ""
+
+msgid "South"
+msgstr ""
+
+msgid "North"
+msgstr ""
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr ""
+
+msgid "West"
+msgstr ""
+
+msgid "East"
+msgstr ""
+
+msgid "Setup.LNB$Max. positioner swing (degrees)"
+msgstr ""
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr ""
+
 msgid "CAM reset"
 msgstr "CAM重置"
 
@@ -1004,9 +1116,19 @@
 msgid "CAM ready"
 msgstr "CAM准备"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM设置"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "菜单"
 
@@ -1019,6 +1141,9 @@
 msgid "Can't open CAM menu!"
 msgstr "不能打开CAM菜单"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM正在使用-是否重启？"
 
@@ -1112,6 +1237,33 @@
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "暂停回放时，设置标志"
 
+msgid "Setup.Replay$Pause replay when jumping to a mark"
+msgstr ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
+msgstr ""
+
+msgid "Setup.Replay$Alternate behavior for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
+msgstr ""
+
+msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "恢复 ID"
 
@@ -1148,6 +1300,12 @@
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "初始化声音"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "频道排序"
 
@@ -1167,21 +1325,21 @@
 msgstr "VDR系统设置"
 
 msgid "Restart"
-msgstr " 重启VDR系统"
+msgstr "重启VDR系统"
 
 msgid "Really restart?"
 msgstr "是否重启？"
 
 #. TRANSLATORS: note the leading and trailing blanks!
 msgid " Stop recording "
-msgstr "是否停止录像?"
+msgstr " 是否停止录像? "
 
 msgid "Schedule"
 msgstr "任务列表"
 
 #. TRANSLATORS: note the leading blank!
 msgid " Stop replaying"
-msgstr "停止回放"
+msgstr " 停止回放"
 
 msgid "Button$Pause"
 msgstr "暂停"
@@ -1194,7 +1352,7 @@
 
 #. TRANSLATORS: note the leading blank!
 msgid " Cancel editing"
-msgstr "取消编辑"
+msgstr " 取消编辑"
 
 msgid "Stop recording?"
 msgstr "停止录像"
@@ -1243,10 +1401,10 @@
 msgstr "编辑处理已经激活"
 
 msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
-msgstr "abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
+msgstr " abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
 
 msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr "0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
+msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
 
 msgid "Button$ABC/abc"
 msgstr "ABC/abc"
@@ -1309,7 +1467,7 @@
 
 #. TRANSLATORS: note the trailing blank!
 msgid "Volume "
-msgstr "音量调整"
+msgstr "音量调整 "
 
 msgid "Classic VDR"
 msgstr "经典VDR面板"
@@ -1332,6 +1490,10 @@
 msgid "PLAY"
 msgstr "播放"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG 面板"
 
diff -ruN vdr-2.0.0/positioner.c vdr-2.2.0/positioner.c
--- vdr-2.0.0/positioner.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/positioner.c	2015-02-14 12:54:31.000000000 +0100
@@ -0,0 +1,141 @@
+/*
+ * positioner.c: Steerable dish positioning
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * For an explanation (in German) of the theory behind the calculations see
+ * http://www.vdr-portal.de/board17-developer/board97-vdr-core/p1154305-grundlagen-und-winkelberechnungen-f%C3%BCr-h-h-diseqc-motor-antennenanlagen
+ * by Albert Danis.
+ *
+ * $Id: positioner.c 3.5 2015/02/14 11:54:31 kls Exp $
+ */
+
+#include "positioner.h"
+#include <math.h>
+#include "config.h"
+
+#define SAT_EARTH_RATIO    0.1513 // the Earth's radius, divided by the distance from the Earth's center to the satellite
+#define SAT_VISIBILITY_LAT 812    // the absolute latitude beyond which no satellite can be seen (degrees * 10)
+
+#define RAD(x) ((x) * M_PI / 1800)
+#define DEG(x) ((x) * 1800 / M_PI)
+
+cPositioner *cPositioner::positioner = NULL;
+
+cPositioner::cPositioner(void)
+{
+  capabilities = pcCanNothing;
+  frontend = -1;
+  targetLongitude = lastLongitude = Setup.PositionerLastLon;
+  targetHourAngle = lastHourAngle = CalcHourAngle(lastLongitude);
+  swingTime = 0;
+  delete positioner;
+  positioner = this;
+}
+
+cPositioner::~cPositioner()
+{
+  positioner = NULL;
+}
+
+int cPositioner::NormalizeAngle(int Angle)
+{
+  while (Angle < -1800)
+        Angle += 3600;
+  while (Angle > 1800)
+        Angle -= 3600;
+  return Angle;
+}
+
+int cPositioner::CalcHourAngle(int Longitude)
+{
+  double Alpha = RAD(Longitude - Setup.SiteLon);
+  double Lat = RAD(Setup.SiteLat);
+  int Sign = Setup.SiteLat >= 0 ? -1 : 1; // angles to the right are positive, angles to the left are negative
+  return Sign * round(DEG(atan2(sin(Alpha), cos(Alpha) - cos(Lat) * SAT_EARTH_RATIO)));
+}
+
+int cPositioner::CalcLongitude(int HourAngle)
+{
+  double Lat = RAD(Setup.SiteLat);
+  double Lon = RAD(Setup.SiteLon);
+  double Delta = RAD(HourAngle);
+  double Alpha = Delta - asin(sin(M_PI - Delta) * cos(Lat) * SAT_EARTH_RATIO);
+  int Sign = Setup.SiteLat >= 0 ? 1 : -1;
+  return NormalizeAngle(round(DEG(Lon - Sign * Alpha)));
+}
+
+int cPositioner::HorizonLongitude(ePositionerDirection Direction)
+{
+  double Delta;
+  if (abs(Setup.SiteLat) <= SAT_VISIBILITY_LAT)
+     Delta = acos(SAT_EARTH_RATIO / cos(RAD(Setup.SiteLat)));
+  else
+     Delta = 0;
+  if ((Setup.SiteLat >= 0) != (Direction == pdLeft))
+     Delta = -Delta;
+  return NormalizeAngle(round(DEG(RAD(Setup.SiteLon) + Delta)));
+}
+
+int cPositioner::HardLimitLongitude(ePositionerDirection Direction) const
+{
+  return CalcLongitude(Direction == pdLeft ? -Setup.PositionerSwing : Setup.PositionerSwing);
+}
+
+void cPositioner::StartMovementTimer(int Longitude)
+{
+  if (Setup.PositionerSpeed <= 0)
+     return;
+  cMutexLock MutexLock(&mutex);
+  lastLongitude = CurrentLongitude(); // in case the dish was already in motion
+  targetLongitude = Longitude;
+  lastHourAngle = CalcHourAngle(lastLongitude);
+  targetHourAngle = CalcHourAngle(targetLongitude);
+  swingTime = abs(targetHourAngle - lastHourAngle) * 1000 / Setup.PositionerSpeed; // time (ms) it takes to move the dish from lastHourAngle to targetHourAngle
+  movementStart.Set();
+  Setup.PositionerLastLon = targetLongitude;
+}
+
+void cPositioner::GotoPosition(uint Number, int Longitude)
+{
+  if (Longitude != targetLongitude)
+     dsyslog("moving positioner to position %d, longitude %d", Number, Longitude);
+  StartMovementTimer(Longitude);
+}
+
+void cPositioner::GotoAngle(int Longitude)
+{
+  if (Longitude != targetLongitude)
+     dsyslog("moving positioner to longitude %d", Longitude);
+  StartMovementTimer(Longitude);
+}
+
+int cPositioner::CurrentLongitude(void) const
+{
+  cMutexLock MutexLock(&mutex);
+  if (targetLongitude != lastLongitude) {
+     int Elapsed = movementStart.Elapsed(); // it's important to make this 'int', otherwise the expression below yields funny results
+     if (swingTime <= Elapsed)
+        lastLongitude = targetLongitude;
+     else
+        return CalcLongitude(lastHourAngle + (targetHourAngle - lastHourAngle) * Elapsed / swingTime);
+     }
+  return lastLongitude;
+}
+
+bool cPositioner::IsMoving(void) const
+{
+  cMutexLock MutexLock(&mutex);
+  return CurrentLongitude() != targetLongitude;
+}
+
+cPositioner *cPositioner::GetPositioner(void)
+{
+  return positioner;
+}
+
+void cPositioner::DestroyPositioner(void)
+{
+  delete positioner;
+}
diff -ruN vdr-2.0.0/positioner.h vdr-2.2.0/positioner.h
--- vdr-2.0.0/positioner.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-2.2.0/positioner.h	2013-12-28 12:15:56.000000000 +0100
@@ -0,0 +1,171 @@
+/*
+ * positioner.h: Steerable dish positioning
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: positioner.h 3.3 2013/12/28 11:15:56 kls Exp $
+ */
+
+#ifndef __POSITIONER_H
+#define __POSITIONER_H
+
+#include "thread.h"
+#include "tools.h"
+
+/// A steerable satellite dish generally points to the south on the northern hemisphere,
+/// and to the north on the southern hemisphere (unless you're located directly on the
+/// equator, in which case the general direction is "up"). Therefore, moving the dish
+/// "east" or "west" means something different on either hemisphere. From the local dish
+/// motor's point of view, it makes more sense to speak of turning the dish "left" or
+/// "right", which is independent of the actual hemisphere the dish is located in.
+/// In the cPositioner class context, when a dish on the northern hemisphere moves "east",
+/// it is considered to be moving "left". Imagine standing behind the dish and looking
+/// towards the satellites, and clearly "east" is "left". On the southern hemisphere
+/// the same move to the "left" would go to the "west". So on the hardware level it is
+/// clear what "left" and "right" means. The user interface may present different labels
+/// to the viewer, depending on the hemisphere the dish is on.
+/// All angles in this context are given in "degrees * 10", which allows for an angular
+/// resolution of 0.1 degrees.
+
+class cPositioner {
+private:
+  mutable cMutex mutex;
+  static cPositioner *positioner;
+  int capabilities;
+  int frontend; // file descriptor of the DVB frontend
+  mutable int lastLongitude; // the longitude the dish has last been moved to
+  int targetLongitude; // the longitude the dish is supposed to be moved to
+  mutable int lastHourAngle; // the hour angle the positioner has last been moved to
+  int targetHourAngle; // the hour angle the positioner is supposed to be moved to
+  int swingTime;
+  cTimeMs movementStart;
+protected:
+  cPositioner(void);
+  virtual ~cPositioner();
+  void SetCapabilities(int Capabilities) { capabilities = Capabilities; }
+      ///< A derived class shall call this function in its constructor to set the
+      ///< capability flags it supports.
+  int Frontend(void) const { return frontend; }
+      ///< Returns the file descriptor of the DVB frontend the positioner is
+      ///< connected to. If the positioner is not connected to any DVB device,
+      ///< -1 will be returned.
+  static int CalcHourAngle(int Longitude);
+      ///< Takes the longitude and latitude of the dish location from the system
+      ///< setup and the given Longitude to calculate the "hour angle" to which to move
+      ///< the dish to in order to point to the satellite at orbital position Longitude.
+      ///< An hour angle of zero means the dish shall point directly towards the
+      ///< celestial equator (which is south on the northern hemisphere, and north on
+      ///< the southern hemisphere). Negative values mean that the dish needs to be
+      ///< moved to the left (as seen from behind the dish), while positive values
+      ///< require a movement to the right.
+  static int CalcLongitude(int HourAngle);
+      ///< Returns the longitude of the satellite position the dish points at when the
+      ///< positioner is moved to the given HourAngle.
+  void StartMovementTimer(int Longitude);
+      ///< Starts a timer that estimates how long it will take to move the dish from
+      ///< the current position to the one given by Longitude. The default implementation
+      ///< of CurrentLongitude() uses this timer.
+public:
+  enum ePositionerCapabilities {
+    pcCanNothing         = 0x0000,
+    pcCanDrive           = 0x0001,
+    pcCanStep            = 0x0002,
+    pcCanHalt            = 0x0004,
+    pcCanSetLimits       = 0x0008,
+    pcCanDisableLimits   = 0x0010,
+    pcCanEnableLimits    = 0x0020,
+    pcCanStorePosition   = 0x0040,
+    pcCanRecalcPositions = 0x0080,
+    pcCanGotoPosition    = 0x0100,
+    pcCanGotoAngle       = 0x0200,
+    };
+  enum ePositionerDirection { pdLeft, pdRight };
+  static int NormalizeAngle(int Angle);
+          ///< Normalizes the given Angle into the range -1800...1800.
+  int Capabilities(void) const { return capabilities; }
+          ///< Returns a flag word defining all the things this positioner is
+          ///< capable of.
+  void SetFrontend(int Frontend) { frontend = Frontend; }
+          ///< This function is called whenever the positioner is connected to
+          ///< a DVB frontend.
+  static int HorizonLongitude(ePositionerDirection Direction);
+          ///< Returns the longitude of the satellite position that is just at the
+          ///< horizon when looking in the given Direction. Note that this function
+          ///< only delivers reasonable values for site latitudes between +/-81 degrees.
+          ///< Beyond these limits (i.e. near the north or south pole) a constant value
+          ///< of 0 will be returned.
+  int HardLimitLongitude(ePositionerDirection Direction) const;
+          ///< Returns the longitude of the positioner's hard limit in the given
+          ///< Direction. Note that the value returned here may be larger (or smaller,
+          ///< depending on the Direction) than that returned by HorizonLongitude(),
+          ///< which would mean that it lies below that horizon.
+  int LastLongitude(void) const { return lastLongitude; }
+          ///< Returns the longitude the dish has last been moved to.
+  int TargetLongitude(void) const { return targetLongitude; }
+          ///< Returns the longitude the dish is supposed to be moved to. Once the target
+          ///< longitude has been reached, this is the same as the value returned by
+          ///< CurrentLongitude().
+  virtual cString Error(void) const { return NULL; }
+          ///< Returns a short, single line string indicating an error condition (if
+          ///< the positioner is able to report any errors).
+          ///< NULL means there is no error.
+  virtual void Drive(ePositionerDirection Direction) {}
+          ///< Continuously move the dish to the given Direction until Halt() is
+          ///< called or it hits the soft or hard limit.
+  virtual void Step(ePositionerDirection Direction, uint Steps = 1) {}
+          ///< Move the dish the given number of Steps in the given Direction.
+          ///< The maximum number of steps a particular positioner can do in a single
+          ///< call may be limited.
+          ///< A "step" is the smallest possible movement the positioner can make, which
+          ///< is typically 0.1 degrees.
+  virtual void Halt(void) {}
+          ///< Stop any ongoing motion of the dish.
+  virtual void SetLimit(ePositionerDirection Direction) {}
+          ///< Set the soft limit of the dish movement in the given Direction to the
+          ///< current position.
+  virtual void DisableLimits(void) {}
+          ///< Disables the soft limits for the dish movement.
+  virtual void EnableLimits(void) {}
+          ///< Enables the soft limits for the dish movement.
+  virtual void StorePosition(uint Number) {}
+          ///< Store the current position as a satellite position with the given Number.
+          ///< Number can be in the range 1...255. However, a particular positioner
+          ///< may only have a limited number of satellite positions it can store.
+  virtual void RecalcPositions(uint Number) {}
+          ///< Take the difference between the current actual position of the dish and
+          ///< the position stored with the given Number, and apply the difference to
+          ///< all stored positions.
+  virtual void GotoPosition(uint Number, int Longitude);
+          ///< Move the dish to the satellite position stored under the given Number.
+          ///< Number must be one of the values previously used with StorePosition().
+          ///< The special value 0 shall move the dish to a "reference position",
+          ///< which usually is due south (or north, if you're on the southern hemisphere).
+          ///< Longitude will be used to calculate how long it takes to move the dish
+          ///< from its current position to the given Longitude.
+          ///< A derived class must call the base class function to have the target
+          ///< longitude stored.
+  virtual void GotoAngle(int Longitude);
+          ///< Move the dish to the given angular position. Longitude can be in the range
+          ///< -1800...+1800. A positive sign indicates a position east of Greenwich,
+          ///< while western positions have a negative sign. The absolute value is in
+          ///< "degrees * 10", which allows for a resolution of 1/10 of a degree.
+          ///< A derived class must call the base class function to have the target
+          ///< longitude stored.
+  virtual int CurrentLongitude(void) const;
+          ///< Returns the longitude the dish currently points to. If the dish is in motion,
+          ///< this may be an estimate based on the angular speed of the positioner.
+          ///< The default implementation takes the last and target longitude as well as
+          ///< the rotation speed of the positioner to calculate the estimated current
+          ///< longitude the dish points to.
+  virtual bool IsMoving(void) const;
+          ///< Returns true if the dish is currently moving as a result of a call to
+          ///< GotoPosition() or GotoAngle().
+  static cPositioner *GetPositioner(void);
+          ///< Returns a previously created positioner. If no plugin has created
+          ///< a positioner, there will always be the default DiSEqC positioner.
+  static void DestroyPositioner(void);
+          ///< Destroys a previously created positioner.
+  };
+
+#endif //__POSITIONER_H
diff -ruN vdr-2.0.0/receiver.c vdr-2.2.0/receiver.c
--- vdr-2.0.0/receiver.c	2012-06-02 15:20:38.000000000 +0200
+++ vdr-2.2.0/receiver.c	2015-01-12 15:04:31.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: receiver.c 2.7 2012/06/02 13:20:38 kls Exp $
+ * $Id: receiver.c 3.3 2015/01/12 14:04:31 kls Exp $
  */
 
 #include "receiver.h"
@@ -14,7 +14,7 @@
 cReceiver::cReceiver(const cChannel *Channel, int Priority)
 {
   device = NULL;
-  priority = constrain(Priority, MINPRIORITY, MAXPRIORITY);
+  SetPriority(Priority);
   numPids = 0;
   SetPids(Channel);
 }
@@ -22,13 +22,18 @@
 cReceiver::~cReceiver()
 {
   if (device) {
-     const char *msg = "ERROR: cReceiver has not been detached yet! This is a design fault and VDR will segfault now!";
+     const char *msg = "ERROR: cReceiver has not been detached yet! This is a design fault and VDR will abort now!";
      esyslog("%s", msg);
      fprintf(stderr, "%s\n", msg);
-     *(char *)0 = 0; // cause a segfault
+     abort();
      }
 }
 
+void cReceiver::SetPriority(int Priority)
+{
+  priority = constrain(Priority, MINPRIORITY, MAXPRIORITY);
+}
+
 bool cReceiver::AddPid(int Pid)
 {
   if (Pid) {
@@ -72,6 +77,28 @@
   return true;
 }
 
+void cReceiver::DelPid(int Pid)
+{
+  if (Pid) {
+     for (int i = 0; i < numPids; i++) {
+         if (pids[i] == Pid) {
+            for ( ; i < numPids; i++) // we also copy the terminating 0!
+                pids[i] = pids[i + 1];
+            numPids--;
+            return;
+            }
+         }
+     }
+}
+
+void cReceiver::DelPids(const int *Pids)
+{
+  if (Pids) {
+     while (*Pids)
+           DelPid(*Pids++);
+     }
+}
+
 bool cReceiver::WantsPid(int Pid)
 {
   if (Pid) {
diff -ruN vdr-2.0.0/receiver.h vdr-2.2.0/receiver.h
--- vdr-2.0.0/receiver.h	2012-09-02 11:27:20.000000000 +0200
+++ vdr-2.2.0/receiver.h	2015-01-12 15:03:22.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: receiver.h 2.9 2012/09/02 09:27:20 kls Exp $
+ * $Id: receiver.h 3.3 2015/01/12 14:03:22 kls Exp $
  */
 
 #ifndef __RECEIVER_H
@@ -24,6 +24,7 @@
   int numPids;
   bool WantsPid(int Pid);
 protected:
+  cDevice *Device(void) { return device; }
   void Detach(void);
   virtual void Activate(bool On) {}
                ///< This function is called just before the cReceiver gets attached to
@@ -48,6 +49,8 @@
                ///< that this cReceiver may be detached at any time in favor of a timer recording
                ///< or live viewing (without blocking the cDevice it is attached to).
   virtual ~cReceiver();
+  int Priority(void) { return priority; }
+  void SetPriority(int Priority);
   bool AddPid(int Pid);
                ///< Adds the given Pid to the list of PIDs of this receiver.
   bool AddPids(const int *Pids);
@@ -64,7 +67,13 @@
                ///< through ChannelID(). The ChannelID is necessary to allow the device
                ///< that will be used for this receiver to detect and store whether the
                ///< channel can be decrypted in case this is an encrypted channel.
+  void DelPid(int Pid);
+               ///< Deletes the given Pid from the list of PIDs of this receiver.
+  void DelPids(const int *Pids);
+               ///< Deletes the given zero terminated list of Pids from the list of PIDs of this
+               ///< receiver.
   tChannelID ChannelID(void) { return channelID; }
+  int NumPids(void) const { return numPids; }
   bool IsAttached(void) { return device != NULL; }
                ///< Returns true if this receiver is (still) attached to a device.
                ///< A receiver may be automatically detached from its device in
diff -ruN vdr-2.0.0/recorder.c vdr-2.2.0/recorder.c
--- vdr-2.0.0/recorder.c	2012-09-22 13:53:57.000000000 +0200
+++ vdr-2.2.0/recorder.c	2014-02-21 10:19:52.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recorder.c 2.17 2012/09/22 11:53:57 kls Exp $
+ * $Id: recorder.c 3.3 2014/02/21 09:19:52 kls Exp $
  */
 
 #include "recorder.h"
@@ -14,7 +14,7 @@
 
 // The maximum time we wait before assuming that a recorded video data stream
 // is broken:
-#define MAXBROKENTIMEOUT 30 // seconds
+#define MAXBROKENTIMEOUT 30000 // milliseconds
 
 #define MINFREEDISKSPACE    (512) // MB
 #define DISKCHECKINTERVAL   100 // seconds
@@ -117,7 +117,7 @@
 
 void cRecorder::Action(void)
 {
-  time_t t = time(NULL);
+  cTimeMs t(MAXBROKENTIMEOUT);
   bool InfoWritten = false;
   bool FirstIframeSeen = false;
   while (Running()) {
@@ -139,6 +139,7 @@
                           }
                        }
                     InfoWritten = true;
+                    cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName);
                     }
                  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
                     FirstIframeSeen = true; // start recording with the first I-frame
@@ -154,22 +155,22 @@
                              recordFile->Write(pmt, TS_SIZE);
                              fileSize += TS_SIZE;
                              }
+                       t.Set(MAXBROKENTIMEOUT);
                        }
                     if (recordFile->Write(b, Count) < 0) {
                        LOG_ERROR_STR(fileName->Name());
                        break;
                        }
                     fileSize += Count;
-                    t = time(NULL);
                     }
                  }
               ringBuffer->Del(Count);
               }
            }
-        if (time(NULL) - t > MAXBROKENTIMEOUT) {
+        if (t.TimedOut()) {
            esyslog("ERROR: video data stream broken");
            ShutdownHandler.RequestEmergencyExit();
-           t = time(NULL);
+           t.Set(MAXBROKENTIMEOUT);
            }
         }
 }
diff -ruN vdr-2.0.0/recorder.h vdr-2.2.0/recorder.h
--- vdr-2.0.0/recorder.h	2010-12-27 12:17:04.000000000 +0100
+++ vdr-2.2.0/recorder.h	2015-01-15 15:27:02.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recorder.h 2.3 2010/12/27 11:17:04 kls Exp $
+ * $Id: recorder.h 3.1 2015/01/15 14:27:02 kls Exp $
  */
 
 #ifndef __RECORDER_H
@@ -31,12 +31,16 @@
   bool NextFile(void);
 protected:
   virtual void Activate(bool On);
+       ///< If you override Activate() you need to call Detach() (which is a
+       ///< member of the cReceiver class) from your own destructor in order
+       ///< to properly get a call to Activate(false) when your object is
+       ///< destroyed.
   virtual void Receive(uchar *Data, int Length);
   virtual void Action(void);
 public:
   cRecorder(const char *FileName, const cChannel *Channel, int Priority);
-               // Creates a new recorder for the given Channel and
-               // the given Priority that will record into the file FileName.
+       ///< Creates a new recorder for the given Channel and
+       ///< the given Priority that will record into the file FileName.
   virtual ~cRecorder();
   };
 
diff -ruN vdr-2.0.0/recording.c vdr-2.2.0/recording.c
--- vdr-2.0.0/recording.c	2013-03-11 11:34:41.000000000 +0100
+++ vdr-2.2.0/recording.c	2015-02-16 08:49:14.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.c 2.91 2013/03/11 10:34:41 kls Exp $
+ * $Id: recording.c 3.28 2015/02/16 07:49:14 kls Exp $
  */
 
 #include "recording.h"
@@ -20,8 +20,10 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include "channels.h"
+#include "cutter.h"
 #include "i18n.h"
 #include "interface.h"
+#include "menu.h"
 #include "remux.h"
 #include "ringbuffer.h"
 #include "skins.h"
@@ -63,15 +65,19 @@
 #define REMOVELATENCY      10 // seconds to wait until next check after removing a file
 #define MARKSUPDATEDELTA   10 // seconds between checks for updating editing marks
 #define MININDEXAGE      3600 // seconds before an index file is considered no longer to be written
+#define MAXREMOVETIME      10 // seconds after which to return from removing deleted recordings
 
 #define MAX_LINK_LEVEL  6
 
+#define LIMIT_SECS_PER_MB_RADIO 5 // radio recordings typically have more than this
+
 int DirectoryPathMax = PATH_MAX - 1;
 int DirectoryNameMax = NAME_MAX;
 bool DirectoryEncoding = false;
 int InstanceId = 0;
 
 cRecordings DeletedRecordings(true);
+static cRecordings VanishedRecordings;
 
 // --- cRemoveDeletedRecordingsThread ----------------------------------------
 
@@ -90,13 +96,18 @@
 void cRemoveDeletedRecordingsThread::Action(void)
 {
   // Make sure only one instance of VDR does this:
-  cLockFile LockFile(VideoDirectory);
+  cLockFile LockFile(cVideoDirectory::Name());
   if (LockFile.Lock()) {
+     time_t StartTime = time(NULL);
      bool deleted = false;
      cThreadLock DeletedRecordingsLock(&DeletedRecordings);
      for (cRecording *r = DeletedRecordings.First(); r; ) {
          if (cIoThrottle::Engaged())
             return;
+         if (time(NULL) - StartTime > MAXREMOVETIME)
+            return; // don't stay here too long
+         if (cRemote::HasKeys())
+            return; // react immediately on user input
          if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
             cRecording *next = DeletedRecordings.Next(r);
             r->Remove();
@@ -109,7 +120,7 @@
          }
      if (deleted) {
         const char *IgnoreFiles[] = { SORTMODEFILE, NULL };
-        RemoveEmptyVideoDirectories(IgnoreFiles);
+        cVideoDirectory::RemoveEmptyVideoDirectories(IgnoreFiles);
         }
      }
 }
@@ -145,9 +156,9 @@
   static time_t LastFreeDiskCheck = 0;
   int Factor = (Priority == -1) ? 10 : 1;
   if (Force || time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA / Factor) {
-     if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
+     if (!cVideoDirectory::VideoFileSpaceAvailable(MINDISKSPACE)) {
         // Make sure only one instance of VDR does this:
-        cLockFile LockFile(VideoDirectory);
+        cLockFile LockFile(cVideoDirectory::Name());
         if (!LockFile.Lock())
            return;
         // Remove the oldest file that has been "deleted":
@@ -178,40 +189,52 @@
               return; // the next call will actually remove it
            }
         // No "deleted" files to remove, so let's see if we can delete a recording:
-        isyslog("...no deleted recording found, trying to delete an old recording...");
-        cThreadLock RecordingsLock(&Recordings);
-        if (Recordings.Count()) {
-           cRecording *r = Recordings.First();
-           cRecording *r0 = NULL;
-           while (r) {
-                 if (r->IsOnVideoDirectoryFileSystem()) { // only delete recordings that will actually increase the free video disk space
-                    if (!r->IsEdited() && r->Lifetime() < MAXLIFETIME) { // edited recordings and recordings with MAXLIFETIME live forever
-                       if ((r->Lifetime() == 0 && Priority > r->Priority()) || // the recording has no guaranteed lifetime and the new recording has higher priority
-                           (r->Lifetime() > 0 && (time(NULL) - r->Start()) / SECSINDAY >= r->Lifetime())) { // the recording's guaranteed lifetime has expired
-                          if (r0) {
-                             if (r->Priority() < r0->Priority() || (r->Priority() == r0->Priority() && r->Start() < r0->Start()))
-                                r0 = r; // in any case we delete the one with the lowest priority (or the older one in case of equal priorities)
+        if (Priority > 0) {
+           isyslog("...no deleted recording found, trying to delete an old recording...");
+           cThreadLock RecordingsLock(&Recordings);
+           if (Recordings.Count()) {
+              cRecording *r = Recordings.First();
+              cRecording *r0 = NULL;
+              while (r) {
+                    if (r->IsOnVideoDirectoryFileSystem()) { // only delete recordings that will actually increase the free video disk space
+                       if (!r->IsEdited() && r->Lifetime() < MAXLIFETIME) { // edited recordings and recordings with MAXLIFETIME live forever
+                          if ((r->Lifetime() == 0 && Priority > r->Priority()) || // the recording has no guaranteed lifetime and the new recording has higher priority
+                              (r->Lifetime() > 0 && (time(NULL) - r->Start()) / SECSINDAY >= r->Lifetime())) { // the recording's guaranteed lifetime has expired
+                             if (r0) {
+                                if (r->Priority() < r0->Priority() || (r->Priority() == r0->Priority() && r->Start() < r0->Start()))
+                                   r0 = r; // in any case we delete the one with the lowest priority (or the older one in case of equal priorities)
+                                }
+                             else
+                                r0 = r;
                              }
-                          else
-                             r0 = r;
                           }
                        }
+                    r = Recordings.Next(r);
                     }
-                 r = Recordings.Next(r);
+              if (r0 && r0->Delete()) {
+                 Recordings.Del(r0);
+                 return;
                  }
-           if (r0 && r0->Delete()) {
-              Recordings.Del(r0);
-              return;
               }
+           // Unable to free disk space, but there's nothing we can do about that...
+           isyslog("...no old recording found, giving up");
            }
-        // Unable to free disk space, but there's nothing we can do about that...
-        isyslog("...no old recording found, giving up");
+        else
+           isyslog("...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
         Skins.QueueMessage(mtWarning, tr("Low disk space!"), 5, -1);
         }
      LastFreeDiskCheck = time(NULL);
      }
 }
 
+// --- Clear vanished recordings ---------------------------------------------
+
+void ClearVanishedRecordings(void)
+{
+  cThreadLock RecordingsLock(&Recordings); // yes, it *is* Recordings!
+  VanishedRecordings.Clear();
+}
+
 // --- cResumeFile -----------------------------------------------------------
 
 cResumeFile::cResumeFile(const char *FileName, bool IsPesRecording)
@@ -418,6 +441,13 @@
   framesPerSecond = FramesPerSecond;
 }
 
+void cRecordingInfo::SetFileName(const char *FileName)
+{
+  bool IsPesRecording = fileName && endswith(fileName, ".vdr");
+  free(fileName);
+  fileName = strdup(cString::sprintf("%s%s", FileName, IsPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX));
+}
+
 bool cRecordingInfo::Read(FILE *f)
 {
   if (ownEvent) {
@@ -561,6 +591,7 @@
                      // characters that can be mapped to other characters:
                      case ' ': *p = '_'; break;
                      case FOLDERDELIMCHAR: *p = '/'; break;
+                     case '/': *p = FOLDERDELIMCHAR; break;
                      // characters that have to be encoded:
                      default:
                        if (NeedsConversion(p)) {
@@ -583,6 +614,7 @@
               switch (*p) {
                 // mapped characters:
                 case '_': *p = ' '; break;
+                case FOLDERDELIMCHAR: *p = '/'; break;
                 case '/': *p = FOLDERDELIMCHAR; break;
                 // encoded characters:
                 case '#': {
@@ -763,7 +795,7 @@
   else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
      name = strdup(Timer->File());
   else
-     name = strdup(cString::sprintf("%s~%s", Timer->File(), Subtitle));
+     name = strdup(cString::sprintf("%s%c%s", Timer->File(), FOLDERDELIMCHAR, Subtitle));
   // substitute characters that would cause problems in file names:
   strreplace(name, '\n', ' ');
   start = Timer->StartTime();
@@ -794,8 +826,8 @@
   FileName = fileName = strdup(FileName);
   if (*(fileName + strlen(fileName) - 1) == '/')
      *(fileName + strlen(fileName) - 1) = 0;
-  if (strstr(FileName, VideoDirectory) == FileName)
-     FileName += strlen(VideoDirectory) + 1;
+  if (strstr(FileName, cVideoDirectory::Name()) == FileName)
+     FileName += strlen(cVideoDirectory::Name()) + 1;
   const char *p = strrchr(FileName, '/');
 
   name = NULL;
@@ -943,22 +975,31 @@
 {
   char **sb = (RecordingsSortMode == rsmName) ? &sortBufferName : &sortBufferTime;
   if (!*sb) {
-     char *s = strdup(FileName() + strlen(VideoDirectory));
-     if (RecordingsSortMode != rsmName || Setup.AlwaysSortFoldersFirst)
-        s = StripEpisodeName(s, RecordingsSortMode != rsmName);
-     strreplace(s, '/', '0'); // some locales ignore '/' when sorting
-     int l = strxfrm(NULL, s, 0) + 1;
-     *sb = MALLOC(char, l);
-     strxfrm(*sb, s, l);
-     free(s);
+     if (RecordingsSortMode == rsmTime && !Setup.RecordingDirs) {
+        char buf[32];
+        struct tm tm_r;
+        strftime(buf, sizeof(buf), "%Y%m%d%H%I", localtime_r(&start, &tm_r));
+        *sb = strdup(buf);
+        }
+     else {
+        char *s = strdup(FileName() + strlen(cVideoDirectory::Name()));
+        if (RecordingsSortMode != rsmName || Setup.AlwaysSortFoldersFirst)
+           s = StripEpisodeName(s, RecordingsSortMode != rsmName);
+        strreplace(s, '/', '0'); // some locales ignore '/' when sorting
+        int l = strxfrm(NULL, s, 0) + 1;
+        *sb = MALLOC(char, l);
+        strxfrm(*sb, s, l);
+        free(s);
+        }
      }
   return *sb;
 }
 
 void cRecording::ClearSortName(void)
 {
-  DELETENULL(sortBufferName);
-  DELETENULL(sortBufferTime);
+  free(sortBufferName);
+  free(sortBufferTime);
+  sortBufferName = sortBufferTime = NULL;
 }
 
 int cRecording::GetResume(void) const
@@ -976,6 +1017,28 @@
   return strcasecmp(SortName(), r->SortName());
 }
 
+bool cRecording::IsInPath(const char *Path)
+{
+  if (isempty(Path))
+     return true;
+  int l = strlen(Path);
+  return strncmp(Path, name, l) == 0 && (name[l] == FOLDERDELIMCHAR);
+}
+
+cString cRecording::Folder(void) const
+{
+  if (char *s = strrchr(name, FOLDERDELIMCHAR))
+     return cString(name, s);
+  return "";
+}
+
+cString cRecording::BaseName(void) const
+{
+  if (char *s = strrchr(name, FOLDERDELIMCHAR))
+     return cString(s + 1);
+  return name;
+}
+
 const char *cRecording::FileName(void) const
 {
   if (!fileName) {
@@ -984,11 +1047,11 @@
      const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS;
      int ch = isPesRecording ? priority : channel;
      int ri = isPesRecording ? lifetime : instanceId;
-     char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(VideoDirectory) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve
+     char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(cVideoDirectory::Name()) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve
      if (strcmp(Name, name) != 0)
         dsyslog("recording file name '%s' truncated to '%s'", name, Name);
      Name = ExchangeChars(Name, true);
-     fileName = strdup(cString::sprintf(fmt, VideoDirectory, Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
+     fileName = strdup(cString::sprintf(fmt, cVideoDirectory::Name(), Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
      free(Name);
      }
   return fileName;
@@ -1057,7 +1120,7 @@
 
 const char *cRecording::PrefixFileName(char Prefix)
 {
-  cString p = PrefixVideoFileName(FileName(), Prefix);
+  cString p = cVideoDirectory::PrefixVideoFileName(FileName(), Prefix);
   if (*p) {
      free(fileName);
      fileName = strdup(p);
@@ -1087,10 +1150,26 @@
 bool cRecording::IsOnVideoDirectoryFileSystem(void) const
 {
   if (isOnVideoDirectoryFileSystem < 0)
-     isOnVideoDirectoryFileSystem = ::IsOnVideoDirectoryFileSystem(FileName());
+     isOnVideoDirectoryFileSystem = cVideoDirectory::IsOnVideoDirectoryFileSystem(FileName());
   return isOnVideoDirectoryFileSystem;
 }
 
+bool cRecording::HasMarks(void)
+{
+  return access(cMarks::MarksFileName(this), F_OK) == 0;
+}
+
+bool cRecording::DeleteMarks(void)
+{
+  if (remove(cMarks::MarksFileName(this)) < 0) {
+     if (errno != ENOENT) {
+        LOG_ERROR_STR(fileName);
+        return false;
+        }
+     }
+  return true;
+}
+
 void cRecording::ReadInfo(void)
 {
   info->Read();
@@ -1099,13 +1178,13 @@
   framesPerSecond = info->framesPerSecond;
 }
 
-bool cRecording::WriteInfo(void)
+bool cRecording::WriteInfo(const char *OtherFileName)
 {
-  cString InfoFileName = cString::sprintf("%s%s", fileName, isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX);
-  FILE *f = fopen(InfoFileName, "w");
-  if (f) {
+  cString InfoFileName = cString::sprintf("%s%s", OtherFileName ? OtherFileName : FileName(), isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX);
+  cSafeFile f(InfoFileName);
+  if (f.Open()) {
      info->Write(f);
-     fclose(f);
+     f.Close();
      }
   else
      LOG_ERROR_STR(*InfoFileName);
@@ -1119,6 +1198,59 @@
   fileName = NULL;
 }
 
+bool cRecording::ChangePriorityLifetime(int NewPriority, int NewLifetime)
+{
+  if (NewPriority != Priority() || NewLifetime != Lifetime()) {
+     dsyslog("changing priority/lifetime of '%s' to %d/%d", Name(), NewPriority, NewLifetime);
+     if (IsPesRecording()) {
+        cString OldFileName = FileName();
+        priority = NewPriority;
+        lifetime = NewLifetime;
+        free(fileName);
+        fileName = NULL;
+        cString NewFileName = FileName();
+        if (!cVideoDirectory::RenameVideoFile(OldFileName, NewFileName))
+           return false;
+        info->SetFileName(NewFileName);
+        }
+     else {
+        priority = info->priority = NewPriority;
+        lifetime = info->lifetime = NewLifetime;
+        if (!WriteInfo())
+           return false;
+        }
+     Recordings.ChangeState();
+     Recordings.TouchUpdate();
+     }
+  return true;
+}
+
+bool cRecording::ChangeName(const char *NewName)
+{
+  if (strcmp(NewName, Name())) {
+     dsyslog("changing name of '%s' to '%s'", Name(), NewName);
+     cString OldName = Name();
+     cString OldFileName = FileName();
+     free(fileName);
+     fileName = NULL;
+     free(name);
+     name = strdup(NewName);
+     cString NewFileName = FileName();
+     if (!(MakeDirs(NewFileName, true) && cVideoDirectory::MoveVideoFile(OldFileName, NewFileName))) {
+        free(name);
+        name = strdup(OldName);
+        free(fileName);
+        fileName = strdup(OldFileName);
+        return false;
+        }
+     isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
+     ClearSortName();
+     Recordings.ChangeState();
+     Recordings.TouchUpdate();
+     }
+  return true;
+}
+
 bool cRecording::Delete(void)
 {
   bool result = true;
@@ -1129,11 +1261,11 @@
      if (access(NewName, F_OK) == 0) {
         // the new name already exists, so let's remove that one first:
         isyslog("removing recording '%s'", NewName);
-        RemoveVideoFile(NewName);
+        cVideoDirectory::RemoveVideoFile(NewName);
         }
      isyslog("deleting recording '%s'", FileName());
      if (access(FileName(), F_OK) == 0) {
-        result = RenameVideoFile(FileName(), NewName);
+        result = cVideoDirectory::RenameVideoFile(FileName(), NewName);
         cRecordingUserCommand::InvokeCommand(RUC_DELETERECORDING, NewName);
         }
      else {
@@ -1153,7 +1285,7 @@
      return false;
      }
   isyslog("removing recording %s", FileName());
-  return RemoveVideoFile(FileName());
+  return cVideoDirectory::RemoveVideoFile(FileName());
 }
 
 bool cRecording::Undelete(void)
@@ -1171,7 +1303,7 @@
      else {
         isyslog("undeleting recording '%s'", FileName());
         if (access(FileName(), F_OK) == 0)
-           result = RenameVideoFile(FileName(), NewName);
+           result = cVideoDirectory::RenameVideoFile(FileName(), NewName);
         else {
            isyslog("deleted recording '%s' vanished", FileName());
            result = false;
@@ -1182,6 +1314,17 @@
   return result;
 }
 
+int cRecording::IsInUse(void) const
+{
+  int Use = ruNone;
+  if (cRecordControls::GetRecordControl(FileName()))
+     Use |= ruTimer;
+  if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName()) == 0)
+     Use |= ruReplay;
+  Use |= RecordingsHandler.GetUsage(FileName());
+  return Use;
+}
+
 void cRecording::ResetResume(void) const
 {
   resume = RESUME_NOT_INITIALIZED;
@@ -1227,6 +1370,7 @@
 :cThread("video directory scanner")
 {
   deleted = Deleted;
+  initial = true;
   lastUpdate = 0;
   state = 0;
 }
@@ -1244,22 +1388,27 @@
 const char *cRecordings::UpdateFileName(void)
 {
   if (!updateFileName)
-     updateFileName = strdup(AddDirectory(VideoDirectory, ".update"));
+     updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update"));
   return updateFileName;
 }
 
 void cRecordings::Refresh(bool Foreground)
 {
   lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
-  Lock();
-  Clear();
-  ChangeState();
-  Unlock();
-  ScanVideoDir(VideoDirectory, Foreground);
+  initial = Count() == 0; // no name checking if the list is initially empty
+  if (deleted) {
+     Lock();
+     Clear();
+     ChangeState();
+     Unlock();
+     }
+  ScanVideoDir(cVideoDirectory::Name(), Foreground);
 }
 
-void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel)
+bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel, int DirLevel)
 {
+  bool DoChangeState = false;
+  // Find any new recordings:
   cReadDir d(DirName);
   struct dirent *e;
   while ((Foreground || Running()) && (e = d.Next()) != NULL) {
@@ -1278,25 +1427,48 @@
               }
            if (S_ISDIR(st.st_mode)) {
               if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
-                 cRecording *r = new cRecording(buffer);
-                 if (r->Name()) {
-                    r->NumFrames(); // initializes the numFrames member
-                    r->FileSizeMB(); // initializes the fileSizeMB member
-                    if (deleted)
-                       r->deleted = time(NULL);
-                    Lock();
-                    Add(r);
-                    ChangeState();
-                    Unlock();
+                 if (deleted || initial || !GetByName(buffer)) {
+                    cRecording *r = new cRecording(buffer);
+                    if (r->Name()) {
+                       r->NumFrames(); // initializes the numFrames member
+                       r->FileSizeMB(); // initializes the fileSizeMB member
+                       r->IsOnVideoDirectoryFileSystem(); // initializes the isOnVideoDirectoryFileSystem member
+                       if (deleted)
+                          r->deleted = time(NULL);
+                       Lock();
+                       Add(r);
+                       if (initial)
+                          ChangeState();
+                       else
+                          DoChangeState = true;
+                       Unlock();
+                       }
+                    else
+                       delete r;
                     }
-                 else
-                    delete r;
                  }
               else
-                 ScanVideoDir(buffer, Foreground, LinkLevel + Link);
+                 DoChangeState |= ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
               }
            }
         }
+  // Handle any vanished recordings:
+  if (!deleted && !initial && DirLevel == 0) {
+     for (cRecording *recording = First(); recording; ) {
+         cRecording *r = recording;
+         recording = Next(recording);
+         if (access(r->FileName(), F_OK) != 0) {
+            Lock();
+            Del(r, false);
+            VanishedRecordings.Add(r);
+            DoChangeState = true;
+            Unlock();
+            }
+         }
+     }
+  if (DoChangeState && DirLevel == 0)
+     ChangeState();
+  return DoChangeState;
 }
 
 bool cRecordings::StateChanged(int &State)
@@ -1337,6 +1509,7 @@
 cRecording *cRecordings::GetByName(const char *FileName)
 {
   if (FileName) {
+     LOCK_THREAD;
      for (cRecording *recording = First(); recording; recording = Next(recording)) {
          if (strcmp(recording->FileName(), FileName) == 0)
             return recording;
@@ -1362,22 +1535,24 @@
 {
   LOCK_THREAD;
   cRecording *recording = GetByName(FileName);
-  if (recording) {
-     cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+  cRecording *dummy = NULL;
+  if (!recording)
+     recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list
+  cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+  if (!dummy)
      Del(recording, false);
-     char *ext = strrchr(recording->fileName, '.');
-     if (ext) {
-        strncpy(ext, DELEXT, strlen(ext));
-        if (access(recording->FileName(), F_OK) == 0) {
-           recording->deleted = time(NULL);
-           DeletedRecordings.Add(recording);
-           recording = NULL; // to prevent it from being deleted below
-           }
+  char *ext = strrchr(recording->fileName, '.');
+  if (ext) {
+     strncpy(ext, DELEXT, strlen(ext));
+     if (access(recording->FileName(), F_OK) == 0) {
+        recording->deleted = time(NULL);
+        DeletedRecordings.Add(recording);
+        recording = NULL; // to prevent it from being deleted below
         }
-     delete recording;
-     ChangeState();
-     TouchUpdate();
      }
+  delete recording;
+  ChangeState();
+  TouchUpdate();
 }
 
 void cRecordings::UpdateByName(const char *FileName)
@@ -1411,8 +1586,10 @@
          if (FileSizeMB > 0) {
             int LengthInSeconds = recording->LengthInSeconds();
             if (LengthInSeconds > 0) {
-               size += FileSizeMB;
-               length += LengthInSeconds;
+               if (LengthInSeconds / FileSizeMB < LIMIT_SECS_PER_MB_RADIO) { // don't count radio recordings
+                  size += FileSizeMB;
+                  length += LengthInSeconds;
+                  }
                }
             }
          }
@@ -1420,6 +1597,46 @@
   return (size && length) ? double(size) * 60 / length : -1;
 }
 
+int cRecordings::PathIsInUse(const char *Path)
+{
+  LOCK_THREAD;
+  int Use = ruNone;
+  for (cRecording *recording = First(); recording; recording = Next(recording)) {
+      if (recording->IsInPath(Path))
+         Use |= recording->IsInUse();
+      }
+  return Use;
+}
+
+int cRecordings::GetNumRecordingsInPath(const char *Path)
+{
+  LOCK_THREAD;
+  int n = 0;
+  for (cRecording *recording = First(); recording; recording = Next(recording)) {
+      if (recording->IsInPath(Path))
+         n++;
+      }
+  return n;
+}
+
+bool cRecordings::MoveRecordings(const char *OldPath, const char *NewPath)
+{
+  if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
+     LOCK_THREAD;
+     dsyslog("moving '%s' to '%s'", OldPath, NewPath);
+     for (cRecording *recording = First(); recording; recording = Next(recording)) {
+         if (recording->IsInPath(OldPath)) {
+            const char *p = recording->Name() + strlen(OldPath);
+            cString NewName = cString::sprintf("%s%s", NewPath, p);
+            if (!recording->ChangeName(NewName))
+               return false;
+            ChangeState();
+            }
+         }
+     }
+  return true;
+}
+
 void cRecordings::ResetResume(const char *ResumeFileName)
 {
   LOCK_THREAD;
@@ -1437,6 +1654,362 @@
       recording->ClearSortName();
 }
 
+// --- cDirCopier ------------------------------------------------------------
+
+class cDirCopier : public cThread {
+private:
+  cString dirNameSrc;
+  cString dirNameDst;
+  bool error;
+  bool suspensionLogged;
+  bool Throttled(void);
+  virtual void Action(void);
+public:
+  cDirCopier(const char *DirNameSrc, const char *DirNameDst);
+  virtual ~cDirCopier();
+  void Stop(void);
+  bool Error(void) { return error; }
+  };
+
+cDirCopier::cDirCopier(const char *DirNameSrc, const char *DirNameDst)
+:cThread("file copier", true)
+{
+  dirNameSrc = DirNameSrc;
+  dirNameDst = DirNameDst;
+  error = true; // prepare for the worst!
+  suspensionLogged = false;
+}
+
+cDirCopier::~cDirCopier()
+{
+  Stop();
+}
+
+bool cDirCopier::Throttled(void)
+{
+  if (cIoThrottle::Engaged()) {
+     if (!suspensionLogged) {
+        dsyslog("suspending copy thread");
+        suspensionLogged = true;
+        }
+     return true;
+     }
+  else if (suspensionLogged) {
+     dsyslog("resuming copy thread");
+     suspensionLogged = false;
+     }
+  return false;
+}
+
+void cDirCopier::Action(void)
+{
+  if (DirectoryOk(dirNameDst, true)) {
+     cReadDir d(dirNameSrc);
+     if (d.Ok()) {
+        dsyslog("copying directory '%s' to '%s'", *dirNameSrc, *dirNameDst);
+        dirent *e = NULL;
+        cString FileNameSrc;
+        cString FileNameDst;
+        int From = -1;
+        int To = -1;
+        size_t BufferSize = BUFSIZ;
+        while (Running()) {
+              // Suspend cutting if we have severe throughput problems:
+              if (Throttled()) {
+                 cCondWait::SleepMs(100);
+                 continue;
+                 }
+              // Copy all files in the source directory to the destination directory:
+              if (e) {
+                 // We're currently copying a file:
+                 uchar Buffer[BufferSize];
+                 size_t Read = safe_read(From, Buffer, sizeof(Buffer));
+                 if (Read > 0) {
+                    size_t Written = safe_write(To, Buffer, Read);
+                    if (Written != Read) {
+                       esyslog("ERROR: can't write to destination file '%s': %m", *FileNameDst);
+                       break;
+                       }
+                    }
+                 else if (Read == 0) { // EOF on From
+                    e = NULL; // triggers switch to next entry
+                    if (fsync(To) < 0) {
+                       esyslog("ERROR: can't sync destination file '%s': %m", *FileNameDst);
+                       break;
+                       }
+                    if (close(From) < 0) {
+                       esyslog("ERROR: can't close source file '%s': %m", *FileNameSrc);
+                       break;
+                       }
+                    if (close(To) < 0) {
+                       esyslog("ERROR: can't close destination file '%s': %m", *FileNameDst);
+                       break;
+                       }
+                    // Plausibility check:
+                    off_t FileSizeSrc = FileSize(FileNameSrc);
+                    off_t FileSizeDst = FileSize(FileNameDst);
+                    if (FileSizeSrc != FileSizeDst) {
+                       esyslog("ERROR: file size discrepancy: %" PRId64 " != %" PRId64, FileSizeSrc, FileSizeDst);
+                       break;
+                       }
+                    }
+                 else {
+                    esyslog("ERROR: can't read from source file '%s': %m", *FileNameSrc);
+                    break;
+                    }
+                 }
+              else if ((e = d.Next()) != NULL) {
+                 // We're switching to the next directory entry:
+                 FileNameSrc = AddDirectory(dirNameSrc, e->d_name);
+                 FileNameDst = AddDirectory(dirNameDst, e->d_name);
+                 struct stat st;
+                 if (stat(FileNameSrc, &st) < 0) {
+                    esyslog("ERROR: can't access source file '%s': %m", *FileNameSrc);
+                    break;
+                    }
+                 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
+                    esyslog("ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
+                    break;
+                    }
+                 dsyslog("copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
+                 BufferSize = max(size_t(st.st_blksize * 10), size_t(BUFSIZ));
+                 if (access(FileNameDst, F_OK) == 0) {
+                    esyslog("ERROR: destination file '%s' already exists", *FileNameDst);
+                    break;
+                    }
+                 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
+                    esyslog("ERROR: can't open source file '%s': %m", *FileNameSrc);
+                    break;
+                    }
+                 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
+                    esyslog("ERROR: can't open destination file '%s': %m", *FileNameDst);
+                    close(From);
+                    break;
+                    }
+                 }
+              else {
+                 // We're done:
+                 dsyslog("done copying directory '%s' to '%s'", *dirNameSrc, *dirNameDst);
+                 error = false;
+                 return;
+                 }
+              }
+        close(From); // just to be absolutely sure
+        close(To);
+        esyslog("ERROR: copying directory '%s' to '%s' ended prematurely", *dirNameSrc, *dirNameDst);
+        }
+     else
+        esyslog("ERROR: can't open '%s'", *dirNameSrc);
+     }
+  else
+     esyslog("ERROR: can't access '%s'", *dirNameDst);
+}
+
+void cDirCopier::Stop(void)
+{
+  Cancel(3);
+  if (error) {
+     cVideoDirectory::RemoveVideoFile(dirNameDst);
+     Recordings.AddByName(dirNameSrc);
+     Recordings.DelByName(dirNameDst);
+     }
+}
+
+// --- cRecordingsHandlerEntry -----------------------------------------------
+
+class cRecordingsHandlerEntry : public cListObject {
+private:
+  int usage;
+  cString fileNameSrc;
+  cString fileNameDst;
+  cCutter *cutter;
+  cDirCopier *copier;
+  void ClearPending(void) { usage &= ~ruPending; }
+public:
+  cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst);
+  ~cRecordingsHandlerEntry();
+  int Usage(const char *FileName = NULL) const;
+  const char *FileNameSrc(void) const { return fileNameSrc; }
+  const char *FileNameDst(void) const { return fileNameDst; }
+  bool Active(bool &Error);
+  };
+
+cRecordingsHandlerEntry::cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst)
+{
+  usage = Usage;
+  fileNameSrc = FileNameSrc;
+  fileNameDst = FileNameDst;
+  cutter = NULL;
+  copier = NULL;
+}
+
+cRecordingsHandlerEntry::~cRecordingsHandlerEntry()
+{
+  delete cutter;
+  delete copier;
+}
+
+int cRecordingsHandlerEntry::Usage(const char *FileName) const
+{
+  int u = usage;
+  if (FileName && *FileName) {
+     if (strcmp(FileName, fileNameSrc) == 0)
+        u |= ruSrc;
+     else if (strcmp(FileName, fileNameDst) == 0)
+        u |= ruDst;
+     }
+  return u;
+}
+
+bool cRecordingsHandlerEntry::Active(bool &Error)
+{
+  bool CopierFinishedOk = false;
+  // First test whether there is an ongoing operation:
+  if (cutter) {
+     if (cutter->Active())
+        return true;
+     Error |= cutter->Error();
+     delete cutter;
+     cutter = NULL;
+     }
+  else if (copier) {
+     if (copier->Active())
+        return true;
+     Error |= copier->Error();
+     CopierFinishedOk = !copier->Error();
+     delete copier;
+     copier = NULL;
+     }
+  // Now check if there is something to start:
+  if ((Usage() & ruPending) != 0) {
+     if ((Usage() & ruCut) != 0) {
+        cutter = new cCutter(FileNameSrc());
+        cutter->Start();
+        }
+     else if ((Usage() & (ruMove | ruCopy)) != 0) {
+        copier = new cDirCopier(FileNameSrc(), FileNameDst());
+        copier->Start();
+        }
+     ClearPending();
+     Recordings.ChangeState();
+     return true;
+     }
+  // Clean up:
+  if (CopierFinishedOk && (Usage() & ruMove) != 0) {
+     cRecording Recording(FileNameSrc());
+     if (Recording.Delete())
+        Recordings.DelByName(Recording.FileName());
+     }
+  Recordings.ChangeState();
+  Recordings.TouchUpdate();
+  return false;
+}
+
+// --- cRecordingsHandler ----------------------------------------------------
+
+cRecordingsHandler RecordingsHandler;
+
+cRecordingsHandler::cRecordingsHandler(void)
+{
+  finished = true;
+  error = false;
+}
+
+cRecordingsHandler::~cRecordingsHandler()
+{
+}
+
+cRecordingsHandlerEntry *cRecordingsHandler::Get(const char *FileName)
+{
+  if (FileName && *FileName) {
+     for (cRecordingsHandlerEntry *r = operations.First(); r; r = operations.Next(r)) {
+         if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
+            return r;
+         }
+     }
+  return NULL;
+}
+
+bool cRecordingsHandler::Add(int Usage, const char *FileNameSrc, const char *FileNameDst)
+{
+  dsyslog("recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
+  cMutexLock MutexLock(&mutex);
+  if (Usage == ruCut || Usage == ruMove || Usage == ruCopy) {
+     if (FileNameSrc && *FileNameSrc) {
+        if (Usage == ruCut || FileNameDst && *FileNameDst) {
+           cString fnd;
+           if (Usage == ruCut && !FileNameDst)
+              FileNameDst = fnd = cCutter::EditedFileName(FileNameSrc);
+           if (!Get(FileNameSrc) && !Get(FileNameDst)) {
+              Usage |= ruPending;
+              operations.Add(new cRecordingsHandlerEntry(Usage, FileNameSrc, FileNameDst));
+              finished = false;
+              Active(); // start it right away if possible
+              Recordings.ChangeState();
+              return true;
+              }
+           else
+              esyslog("ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
+           }
+        else
+           esyslog("ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
+        }
+     else
+        esyslog("ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
+     }
+  else
+     esyslog("ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
+  return false;
+}
+
+void cRecordingsHandler::Del(const char *FileName)
+{
+  cMutexLock MutexLock(&mutex);
+  if (cRecordingsHandlerEntry *r = Get(FileName)) {
+     operations.Del(r);
+     Recordings.ChangeState();
+     }
+}
+
+void cRecordingsHandler::DelAll(void)
+{
+  cMutexLock MutexLock(&mutex);
+  operations.Clear();
+  Recordings.ChangeState();
+}
+
+int cRecordingsHandler::GetUsage(const char *FileName)
+{
+  cMutexLock MutexLock(&mutex);
+  if (cRecordingsHandlerEntry *r = Get(FileName))
+     return r->Usage(FileName);
+  return ruNone;
+}
+
+bool cRecordingsHandler::Active(void)
+{
+  cMutexLock MutexLock(&mutex);
+  while (cRecordingsHandlerEntry *r = operations.First()) {
+        if (r->Active(error))
+           return true;
+        else
+           operations.Del(r);
+        }
+  return false;
+}
+
+bool cRecordingsHandler::Finished(bool &Error)
+{
+  cMutexLock MutexLock(&mutex);
+  if (!finished && operations.Count() == 0) {
+     finished = true;
+     Error = error;
+     error = false;
+     return true;
+     }
+  return false;
+}
+
 // --- cMark -----------------------------------------------------------------
 
 double MarkFramesPerSecond = DEFAULTFRAMESPERSECOND;
@@ -1479,8 +2052,14 @@
 
 // --- cMarks ----------------------------------------------------------------
 
+cString cMarks::MarksFileName(const cRecording *Recording)
+{
+  return AddDirectory(Recording->FileName(), Recording->IsPesRecording() ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
+}
+
 bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording)
 {
+  cMutexLock MutexLock(this);
   recordingFileName = RecordingFileName;
   fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
   framesPerSecond = FramesPerSecond;
@@ -1493,8 +2072,9 @@
 
 bool cMarks::Update(void)
 {
+  cMutexLock MutexLock(this);
   time_t t = time(NULL);
-  if (t > nextUpdate) {
+  if (t > nextUpdate && *fileName) {
      time_t LastModified = LastModifiedTime(fileName);
      if (LastModified != lastFileTime) // change detected, or first run
         lastChange = LastModified > 0 ? LastModified : t;
@@ -1524,6 +2104,7 @@
 
 bool cMarks::Save(void)
 {
+  cMutexLock MutexLock(this);
   if (cConfig<cMark>::Save()) {
      lastFileTime = LastModifiedTime(fileName);
      return true;
@@ -1533,6 +2114,7 @@
 
 void cMarks::Align(void)
 {
+  cMutexLock MutexLock(this);
   cIndexFile IndexFile(recordingFileName, false, isPesRecording);
   for (cMark *m = First(); m; m = Next(m)) {
       int p = IndexFile.GetClosestIFrame(m->Position());
@@ -1545,6 +2127,7 @@
 
 void cMarks::Sort(void)
 {
+  cMutexLock MutexLock(this);
   for (cMark *m1 = First(); m1; m1 = Next(m1)) {
       for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
           if (m2->Position() < m1->Position()) {
@@ -1557,6 +2140,7 @@
 
 void cMarks::Add(int Position)
 {
+  cMutexLock MutexLock(this);
   cConfig<cMark>::Add(new cMark(Position, NULL, framesPerSecond));
   Sort();
 }
@@ -1591,7 +2175,7 @@
 cMark *cMarks::GetNextBegin(cMark *EndMark)
 {
   cMark *BeginMark = EndMark ? Next(EndMark) : First();
-  if (BeginMark) {
+  if (BeginMark && EndMark && BeginMark->Position() == EndMark->Position()) {
      while (cMark *NextMark = Next(BeginMark)) {
            if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position
               if (!(BeginMark = Next(NextMark)))
@@ -1609,7 +2193,7 @@
   if (!BeginMark)
      return NULL;
   cMark *EndMark = Next(BeginMark);
-  if (EndMark) {
+  if (EndMark && BeginMark && BeginMark->Position() == EndMark->Position()) {
      while (cMark *NextMark = Next(EndMark)) {
            if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position
               if (!(EndMark = Next(NextMark)))
@@ -1624,6 +2208,7 @@
 
 int cMarks::GetNumSequences(void)
 {
+  cMutexLock MutexLock(this);
   int NumSequences = 0;
   if (cMark *BeginMark = GetNextBegin()) {
      while (cMark *EndMark = GetNextEnd(BeginMark)) {
@@ -1663,17 +2248,19 @@
 class cIndexFileGenerator : public cThread {
 private:
   cString recordingName;
+  bool update;
 protected:
   virtual void Action(void);
 public:
-  cIndexFileGenerator(const char *RecordingName);
+  cIndexFileGenerator(const char *RecordingName, bool Update = false);
   ~cIndexFileGenerator();
   };
 
-cIndexFileGenerator::cIndexFileGenerator(const char *RecordingName)
+cIndexFileGenerator::cIndexFileGenerator(const char *RecordingName, bool Update)
 :cThread("index file generator")
 ,recordingName(RecordingName)
 {
+  update = Update;
   Start();
 }
 
@@ -1692,15 +2279,34 @@
   cRingBufferLinear Buffer(IFG_BUFFER_SIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE);
   cPatPmtParser PatPmtParser;
   cFrameDetector FrameDetector;
-  cIndexFile IndexFile(recordingName, true);
+  cIndexFile IndexFile(recordingName, true, false, false, true);
   int BufferChunks = KILOBYTE(1); // no need to read a lot at the beginning when parsing PAT/PMT
   off_t FileSize = 0;
   off_t FrameOffset = -1;
+  uint16_t FileNumber = 1;
+  off_t FileOffset = 0;
+  int Last = -1;
+  if (update) {
+     // Look for current index and position to end of it if present:
+     bool Independent;
+     int Length;
+     Last = IndexFile.Last();
+     if (Last >= 0 && !IndexFile.Get(Last, &FileNumber, &FileOffset, &Independent, &Length))
+        Last = -1; // reset Last if an error occurred
+     if (Last >= 0) {
+        Rewind = true;
+        isyslog("updating index file");
+        }
+     else
+        isyslog("generating index file");
+     }
   Skins.QueueMessage(mtInfo, tr("Regenerating index file"));
+  bool Stuffed = false;
   while (Running()) {
         // Rewind input file:
         if (Rewind) {
-           ReplayFile = FileName.SetOffset(1);
+           ReplayFile = FileName.SetOffset(FileNumber, FileOffset);
+           FileSize = FileOffset;
            Buffer.Clear();
            Rewind = false;
            }
@@ -1715,7 +2321,8 @@
               int Processed = FrameDetector.Analyze(Data, Length);
               if (Processed > 0) {
                  if (FrameDetector.NewFrame()) {
-                    IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize);
+                    if (IndexFileWritten || Last < 0) // check for first frame and do not write if in update mode
+                       IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize);
                     FrameOffset = -1;
                     IndexFileWritten = true;
                     }
@@ -1760,10 +2367,25 @@
         else if (ReplayFile) {
            int Result = Buffer.Read(ReplayFile, BufferChunks);
            if (Result == 0) { // EOF
-              ReplayFile = FileName.NextFile();
-              FileSize = 0;
-              FrameOffset = -1;
-              Buffer.Clear();
+              if (Buffer.Available() > 0 && !Stuffed) {
+                 // So the last call to Buffer.Get() returned NULL, but there is still
+                 // data in the buffer, and we're at the end of the current TS file.
+                 // The remaining data in the buffer is less than what's needed for the
+                 // frame detector to analyze frames, so we need to put some stuffing
+                 // packets into the buffer to flush out the rest of the data (otherwise
+                 // any frames within the remaining data would not be seen here):
+                 uchar StuffingPacket[TS_SIZE] = { TS_SYNC_BYTE, 0xFF };
+                 for (int i = 0; i <= MIN_TS_PACKETS_FOR_FRAME_DETECTOR; i++)
+                     Buffer.Put(StuffingPacket, sizeof(StuffingPacket));
+                 Stuffed = true;
+                 }
+              else {
+                 ReplayFile = FileName.NextFile();
+                 FileSize = 0;
+                 FrameOffset = -1;
+                 Buffer.Clear();
+                 Stuffed = false;
+                 }
               }
            }
         // Recording has been processed:
@@ -1825,7 +2447,7 @@
 #define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
 #define INDEXFILETESTINTERVAL   10 // ms between tests for the size of the index file in case of pausing live video
 
-cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, bool PauseLive)
+cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, bool PauseLive, bool Update)
 :resumeFile(FileName, IsPesRecording)
 {
   f = -1;
@@ -1861,10 +2483,10 @@
            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);
+              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) {
+           if ((!Record || Update) && last >= 0) {
               size = last + 1;
               index = MALLOC(tIndexTs, size);
               if (index) {
@@ -2126,7 +2748,7 @@
   return -1;
 }
 
-bool cIndexFile::IsStillRecording()
+bool cIndexFile::IsStillRecording(void)
 {
   return f >= 0;
 }
@@ -2152,15 +2774,16 @@
   return -1;
 }
 
-bool GenerateIndex(const char *FileName)
+bool GenerateIndex(const char *FileName, bool Update)
 {
   if (DirectoryOk(FileName)) {
      cRecording Recording(FileName);
      if (Recording.Name()) {
         if (!Recording.IsPesRecording()) {
            cString IndexFileName = AddDirectory(FileName, INDEXFILESUFFIX);
-           unlink(IndexFileName);
-           cIndexFileGenerator *IndexFileGenerator = new cIndexFileGenerator(FileName);
+           if (!Update)
+              unlink(IndexFileName);
+           cIndexFileGenerator *IndexFileGenerator = new cIndexFileGenerator(FileName, Update);
            while (IndexFileGenerator->Active())
                  cCondWait::SleepMs(INDEXFILECHECKINTERVAL);
            if (access(IndexFileName, R_OK) == 0)
@@ -2268,7 +2891,7 @@
      int BlockingFlag = blocking ? 0 : O_NONBLOCK;
      if (record) {
         dsyslog("recording to '%s'", fileName);
-        file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_LARGEFILE | BlockingFlag);
+        file = cVideoDirectory::OpenVideoFile(fileName, O_RDWR | O_CREAT | O_LARGEFILE | BlockingFlag);
         if (!file)
            LOG_ERROR_STR(fileName);
         }
@@ -2289,8 +2912,9 @@
 void cFileName::Close(void)
 {
   if (file) {
-     if (CloseVideoFile(file) < 0)
+     if (file->Close() < 0)
         LOG_ERROR_STR(fileName);
+     delete file;
      file = NULL;
      }
 }
@@ -2352,7 +2976,7 @@
      Sign = "-";
      }
   double Seconds;
-  int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond + 1);
+  int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
   int s = int(Seconds);
   int m = s / 60 % 60;
   int h = s / 3600;
@@ -2362,12 +2986,12 @@
 
 int HMSFToIndex(const char *HMSF, double FramesPerSecond)
 {
-  int h, m, s, f = 1;
+  int h, m, s, f = 0;
   int n = sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f);
   if (n == 1)
-     return h - 1; // plain frame number
+     return h; // plain frame number
   if (n >= 3)
-     return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f - 1;
+     return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
   return 0;
 }
 
@@ -2403,6 +3027,7 @@
 
 void GetRecordingsSortMode(const char *Directory)
 {
+  RecordingsSortMode = rsmName;
   if (FILE *f = fopen(AddDirectory(Directory, SORTMODEFILE), "r")) {
      char buf[8];
      if (fgets(buf, sizeof(buf), f))
diff -ruN vdr-2.0.0/recording.h vdr-2.2.0/recording.h
--- vdr-2.0.0/recording.h	2013-03-04 15:01:23.000000000 +0100
+++ vdr-2.2.0/recording.h	2015-02-07 15:29:14.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.h 2.46 2013/03/04 14:01:23 kls Exp $
+ * $Id: recording.h 3.8 2015/02/07 14:29:14 kls Exp $
  */
 
 #ifndef __RECORDING_H
@@ -25,7 +25,23 @@
 extern bool DirectoryEncoding;
 extern int InstanceId;
 
+enum eRecordingUsage {
+  ruNone     = 0x0000, // the recording is currently unused
+  ruTimer    = 0x0001, // the recording is currently written to by a timer
+  ruReplay   = 0x0002, // the recording is being replayed
+  // mutually exclusive:
+  ruCut      = 0x0004, // the recording is being cut
+  ruMove     = 0x0008, // the recording is being moved
+  ruCopy     = 0x0010, // the recording is being copied
+  // mutually exclusive:
+  ruSrc      = 0x0020, // the recording is the source of a cut, move or copy process
+  ruDst      = 0x0040, // the recording is the destination of a cut, move or copy process
+  //
+  ruPending  = 0x0080, // the recording is pending a cut, move or copy process
+  };
+
 void RemoveDeletedRecordings(void);
+void ClearVanishedRecordings(void);
 void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
      ///< The special Priority value -1 means that we shall get rid of any
      ///< deleted recordings faster than normal (because we're cutting).
@@ -73,6 +89,7 @@
   const char *Aux(void) const { return aux; }
   double FramesPerSecond(void) const { return framesPerSecond; }
   void SetFramesPerSecond(double FramesPerSecond);
+  void SetFileName(const char *FileName);
   bool Write(FILE *f, const char *Prefix = "") const;
   bool Read(void);
   bool Write(void) const;
@@ -100,7 +117,6 @@
   static char *StripEpisodeName(char *s, bool Strip);
   char *SortName(void) const;
   void ClearSortName(void);
-  int GetResume(void) const;
   time_t start;
   int priority;
   int lifetime;
@@ -114,8 +130,21 @@
   int Lifetime(void) const { return lifetime; }
   time_t Deleted(void) const { return deleted; }
   virtual int Compare(const cListObject &ListObject) const;
+  bool IsInPath(const char *Path);
+       ///< Returns true if this recording is stored anywhere under the given Path.
+       ///< If Path is NULL or an empty string, the entire video directory is checked.
+  cString Folder(void) const;
+       ///< Returns the name of the folder this recording is stored in (without the
+       ///< video directory). For use in menus etc.
+  cString BaseName(void) const;
+       ///< Returns the base name of this recording (without the
+       ///< video directory and folder). For use in menus etc.
   const char *Name(void) const { return name; }
+       ///< Returns the full name of the recording (without the video directory.
+       ///< For use in menus etc.
   const char *FileName(void) const;
+       ///< Returns the full path name to the recording directory, including the
+       ///< video directory and the actual '*.rec'. For disk file access use.
   const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
   const cRecordingInfo *Info(void) const { return info; }
   const char *PrefixFileName(char Prefix);
@@ -130,12 +159,24 @@
   int FileSizeMB(void) const;
        ///< Returns the total file size of this recording (in MB), or -1 if the file
        ///< size is unknown.
+  int GetResume(void) const;
+       ///< Returns the index of the frame where replay of this recording shall
+       ///< be resumed, or -1 in case of an error.
   bool IsNew(void) const { return GetResume() <= 0; }
   bool IsEdited(void) const;
   bool IsPesRecording(void) const { return isPesRecording; }
   bool IsOnVideoDirectoryFileSystem(void) const;
+  bool HasMarks(void);
+       ///< Returns true if this recording has any editing marks.
+  bool DeleteMarks(void);
+       ///< Deletes the editing marks from this recording (if any).
+       ///< Returns true if the operation was successful. If there is no marks file
+       ///< for this recording, it also returns true.
   void ReadInfo(void);
-  bool WriteInfo(void);
+  bool WriteInfo(const char *OtherFileName = NULL);
+       ///< Writes in info file of this recording. If OtherFileName is given, the info
+       ///< file will be written under that recording file name instead of this
+       ///< recording's file name.
   void SetStartTime(time_t Start);
        ///< Sets the start time of this recording to the given value.
        ///< If a filename has already been set for this recording, it will be
@@ -144,6 +185,17 @@
        ///< Use this function with care - it does not check whether a recording with
        ///< this new name already exists, and if there is one, results may be
        ///< unexpected!
+  bool ChangePriorityLifetime(int NewPriority, int NewLifetime);
+       ///< Changes the priority and lifetime of this recording to the given values.
+       ///< If the new values are the same as the old ones, nothing happens.
+       ///< Returns false in case of error.
+  bool ChangeName(const char *NewName);
+       ///< Changes the name of this recording to the given value. NewName is in the
+       ///< same format as the one returned by Name(), i.e. without the video directory
+       ///< and the actual '*.rec' part, and using FOLDERDELIMCHAR as the directory
+       ///< delimiter.
+       ///< If the new name is the same as the old one, nothing happens.
+       ///< Returns false in case of error.
   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
@@ -154,17 +206,26 @@
        ///< 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
+  int IsInUse(void) const;
+       ///< Checks whether this recording is currently in use and therefore shall not
+       ///< be tampered with. Returns 0 (ruNone) if the recording is not in use.
+       ///< The return value may consist of several or'd eRecordingUsage flags. If the
+       ///< caller is just interested in whether the recording is in use or not, the
+       ///< return value can be used like a boolean value.
+       ///< A recording may be in use for several reasons (like being recorded and replayed,
+       ///< as in time-shift).
   };
 
 class cRecordings : public cList<cRecording>, public cThread {
 private:
   static char *updateFileName;
   bool deleted;
+  bool initial;
   time_t lastUpdate;
   int state;
   const char *UpdateFileName(void);
   void Refresh(bool Foreground = false);
-  void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
+  bool ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0, int DirLevel = 0);
 protected:
   void Action(void);
 public:
@@ -197,11 +258,78 @@
   double MBperMinute(void);
        ///< Returns the average data rate (in MB/min) of all recordings, or -1 if
        ///< this value is unknown.
+  int PathIsInUse(const char *Path);
+       ///< Checks whether any recording in the given Path is currently in use and therefore
+       ///< the whole Path shall not be tampered with. Returns 0 (ruNone) if no recording
+       ///< is in use.
+       ///< See cRecording::IsInUse() for details about the possible non-zero return values.
+       ///< If several recordings in the Path are currently in use, the return value will
+       ///< be the combination of all individual recordings' flags.
+       ///< If Path is NULL or an empty string, the entire video directory is checked.
+  int GetNumRecordingsInPath(const char *Path);
+       ///< Returns the total number of recordings in the given Path, including all
+       ///< sub-folders of Path.
+       ///< If Path is NULL or an empty string, the entire video directory is checked.
+  bool MoveRecordings(const char *OldPath, const char *NewPath);
+       ///< Moves all recordings in OldPath to NewPath.
+       ///< Returns true if all recordings were successfully moved.
+       ///< As soon as the operation fails for one recording, the whole
+       ///< action is aborted and false will be returned. Any recordings that
+       ///< have been successfully moved thus far will keep their new name.
+       ///< If OldPath and NewPath are on different file systems, the recordings
+       ///< will be moved in a background process and this function returns true
+       ///< if all recordings have been successfully added to the RecordingsHandler.
   };
 
+/// Any access to Recordings that loops through the list of recordings
+/// needs to hold a thread lock on this object!
 extern cRecordings Recordings;
 extern cRecordings DeletedRecordings;
 
+class cRecordingsHandlerEntry;
+
+class cRecordingsHandler {
+private:
+  cMutex mutex;
+  cList<cRecordingsHandlerEntry> operations;
+  bool finished;
+  bool error;
+  cRecordingsHandlerEntry *Get(const char *FileName);
+public:
+  cRecordingsHandler(void);
+  ~cRecordingsHandler();
+  bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst = NULL);
+       ///< Adds the given FileNameSrc to the recordings handler for (later)
+       ///< processing. Usage can be either ruCut, ruMove or ruCopy. FileNameDst
+       ///< is only applicable for ruMove and ruCopy.
+       ///< At any given time there can be only one operation for any FileNameSrc
+       ///< or FileNameDst in the list. An attempt to add a file name twice will
+       ///< result in an error.
+       ///< Returns true if the operation was successfully added to the list.
+  void Del(const char *FileName);
+       ///< Deletes the given FileName from the list of operations.
+       ///< If an action is already in progress, it will be terminated.
+       ///< FileName can be either the FileNameSrc or FileNameDst (if applicable)
+       ///< that was given when the operation was added with Add().
+  void DelAll(void);
+       ///< Deletes/terminates all operations.
+  int GetUsage(const char *FileName);
+       ///< Returns the usage type for the given FileName.
+  bool Active(void);
+       ///< Checks whether there is currently any operation running and starts
+       ///> the next one form the list if the previous one has finished.
+       ///< This function must be called regularly to trigger switching to the
+       ///< next operation in the list.
+       ///< Returns true if there are any operations in the list.
+  bool Finished(bool &Error);
+       ///< Returns true if all operations in the list have been finished.
+       ///< If there have been any errors, Errors will be set to true.
+       ///< This function will only return true once if the list of operations
+       ///< has actually become empty since the last call.
+  };
+
+extern cRecordingsHandler RecordingsHandler;
+
 #define DEFAULTFRAMESPERSECOND 25.0
 
 class cMark : public cListObject {
@@ -222,7 +350,7 @@
   bool Save(FILE *f);
   };
 
-class cMarks : public cConfig<cMark> {
+class cMarks : public cConfig<cMark>, public cMutex {
 private:
   cString recordingFileName;
   cString fileName;
@@ -232,12 +360,20 @@
   time_t lastFileTime;
   time_t lastChange;
 public:
+  static cString MarksFileName(const cRecording *Recording);
+       ///< Returns the marks file name for the given Recording (regardless whether such
+       ///< a file actually exists).
   bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false);
   bool Update(void);
   bool Save(void);
   void Align(void);
   void Sort(void);
   void Add(int Position);
+       ///< If this cMarks object is used by multiple threads, the caller must Lock()
+       ///< it before calling Add() and Unlock() it afterwards. The same applies to
+       ///< calls to Del(), or any of the functions that return a "cMark *", in case
+       ///< an other thread might modifiy the list while the returned pointer is
+       ///< considered valid.
   cMark *Get(int Position);
   cMark *GetPrev(int Position);
   cMark *GetNext(int Position);
@@ -257,6 +393,7 @@
   };
 
 #define RUC_BEFORERECORDING "before"
+#define RUC_STARTRECORDING  "started"
 #define RUC_AFTERRECORDING  "after"
 #define RUC_EDITEDRECORDING "edited"
 #define RUC_DELETERECORDING "deleted"
@@ -299,7 +436,7 @@
   void ConvertToPes(tIndexTs *IndexTs, int Count);
   bool CatchUp(int Index = -1);
 public:
-  cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false);
+  cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false, bool Update = false);
   ~cIndexFile();
   bool Ok(void) { return index != NULL; }
   bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
@@ -358,7 +495,11 @@
       // be modified and may be reallocated if more space is needed. The return
       // value points to the resulting string, which may be different from s.
 
-bool GenerateIndex(const char *FileName);
+bool GenerateIndex(const char *FileName, bool Update = false);
+       ///< Generates the index of the existing recording with the given FileName.
+       ///< If Update is true, an existing index file will be checked whether it is
+       ///< complete, and will be updated if it isn't. Otherwise an existing index
+       ///< file will be removed before a new one is generated.
 
 enum eRecordingsSortMode { rsmName, rsmTime };
 extern eRecordingsSortMode RecordingsSortMode;
diff -ruN vdr-2.0.0/remote.c vdr-2.2.0/remote.c
--- vdr-2.0.0/remote.c	2013-02-03 16:44:55.000000000 +0100
+++ vdr-2.2.0/remote.c	2015-01-20 15:53:57.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remote.c 2.8 2013/02/03 15:44:55 kls Exp $
+ * $Id: remote.c 3.3 2015/01/20 14:53:57 kls Exp $
  */
 
 #include "remote.h"
@@ -124,7 +124,7 @@
 bool cRemote::Put(uint64_t Code, bool Repeat, bool Release)
 {
   char buffer[32];
-  snprintf(buffer, sizeof(buffer), "%016"PRIX64, Code);
+  snprintf(buffer, sizeof(buffer), "%016" PRIX64, Code);
   return Put(buffer, Repeat, Release);
 }
 
@@ -260,6 +260,7 @@
      tcsetattr(STDIN_FILENO, TCSANOW, &tm);
      }
   kbdAvailable = true;
+  systemIsUtf8 = !cCharSetConv::SystemCharacterTable() || strcmp(cCharSetConv::SystemCharacterTable(), "UTF-8") == 0;
   Start();
 }
 
@@ -297,7 +298,7 @@
 
 void cKbdRemote::PutKey(uint64_t Code, bool Repeat, bool Release)
 {
-  if (rawMode || !Put(Code, Repeat, Release)) {
+  if (rawMode || (!Put(Code, Repeat, Release) && !IsLearning())) {
      if (int func = MapCodeToFunc(Code))
         Put(KBDKEY(func), Repeat, Release);
      }
@@ -324,7 +325,23 @@
 
   if ((key1 = ReadKey()) >= 0) {
      k = key1;
-     if (key1 == 0x1B) {
+     if (systemIsUtf8 && (key1 & 0xC0) == 0xC0) {
+        char bytes[4] = { 0 };
+        bytes[0] = key1;
+        int bytescount = 1;
+        if ((key1 & 0xF0) == 0xF0)
+           bytescount = 3;
+        else if ((key1 & 0xE0) == 0xE0)
+           bytescount = 2;
+        for (int i = 0; i < bytescount; i++) {
+            if ((key1 = ReadKey()) >= 0)
+               bytes[i + 1] = key1;
+            }
+        k = Utf8CharGet(bytes);
+        if (k > 0xFF)
+           k = 0;
+        }
+     else if (key1 == 0x1B) {
         // Start of escape sequence
         if ((key1 = ReadKey()) >= 0) {
            k <<= 8;
diff -ruN vdr-2.0.0/remote.h vdr-2.2.0/remote.h
--- vdr-2.0.0/remote.h	2013-02-03 15:34:56.000000000 +0100
+++ vdr-2.2.0/remote.h	2013-12-25 13:32:44.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remote.h 2.1 2013/02/03 14:34:56 kls Exp $
+ * $Id: remote.h 3.1 2013/12/25 12:32:44 kls Exp $
  */
 
 #ifndef __REMOTE_H
@@ -106,6 +106,7 @@
 private:
   static bool kbdAvailable;
   static bool rawMode;
+  bool systemIsUtf8;
   struct termios savedTm;
   virtual void Action(void);
   int ReadKey(void);
diff -ruN vdr-2.0.0/remux.c vdr-2.2.0/remux.c
--- vdr-2.0.0/remux.c	2013-03-03 11:37:58.000000000 +0100
+++ vdr-2.2.0/remux.c	2015-01-14 10:57:09.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remux.c 2.75 2013/03/03 10:37:58 kls Exp $
+ * $Id: remux.c 3.9 2015/01/14 09:57:09 kls Exp $
  */
 
 #include "remux.h"
@@ -23,6 +23,10 @@
 #define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
 #define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
 
+#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION 6
+#define WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION (MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION / 2)
+#define WRN_TS_PACKETS_FOR_FRAME_DETECTOR (MIN_TS_PACKETS_FOR_FRAME_DETECTOR / 2)
+
 #define EMPTY_SCANNER (0xFFFFFFFF)
 
 ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
@@ -231,7 +235,7 @@
   data = NULL;
   length = 0;
   pid = -1;
-  index = 0;
+  Reset();
 }
 
 cTsPayload::cTsPayload(uchar *Data, int Length, int Pid)
@@ -239,12 +243,25 @@
   Setup(Data, Length, Pid);
 }
 
+uchar cTsPayload::SetEof(void)
+{
+  length = index; // triggers EOF
+  return 0x00;
+}
+
+void cTsPayload::Reset(void)
+{
+  index = 0;
+  numPacketsPid = 0;
+  numPacketsOther = 0;
+}
+
 void cTsPayload::Setup(uchar *Data, int Length, int Pid)
 {
   data = Data;
   length = Length;
   pid = Pid >= 0 ? Pid : TsPid(Data);
-  index = 0;
+  Reset();
 }
 
 uchar cTsPayload::GetByte(void)
@@ -255,20 +272,22 @@
             if (data[index] == TS_SYNC_BYTE && index + TS_SIZE <= length) { // to make sure we are at a TS header start and drop incomplete TS packets at the end
                uchar *p = data + index;
                if (TsPid(p) == pid) { // only handle TS packets for the initial PID
+                  if (++numPacketsPid > MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION)
+                     return SetEof();
                   if (TsHasPayload(p)) {
-                     if (index > 0 && TsPayloadStart(p)) { // checking index to not skip the very first TS packet
-                        length = index; // triggers EOF
-                        return 0x00;
-                        }
+                     if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
+                        return SetEof();
                      index += TsPayloadOffset(p);
                      break;
                      }
                   }
+               else if (TsPid(p) == PATPID)
+                  return SetEof(); // caller must see PAT packets in case of index regeneration
+               else
+                  numPacketsOther++;
                }
-            else {
-               length = index; // triggers EOF
-               return 0x00;
-               }
+            else
+               return SetEof();
            }
         }
      return data[index++];
@@ -302,6 +321,8 @@
 bool cTsPayload::Find(uint32_t Code)
 {
   int OldIndex = index;
+  int OldNumPacketsPid = numPacketsPid;
+  int OldNumPacketsOther = numPacketsOther;
   uint32_t Scanner = EMPTY_SCANNER;
   while (!Eof()) {
         Scanner = (Scanner << 8) | GetByte();
@@ -309,9 +330,19 @@
            return true;
         }
   index = OldIndex;
+  numPacketsPid = OldNumPacketsPid;
+  numPacketsOther = OldNumPacketsOther;
   return false;
 }
 
+void cTsPayload::Statistics(void) const
+{
+  if (numPacketsPid + numPacketsOther > WRN_TS_PACKETS_FOR_FRAME_DETECTOR)
+     dsyslog("WARNING: required (%d+%d) TS packets to determine frame type", numPacketsOther, numPacketsPid);
+  if (numPacketsPid > WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION)
+     dsyslog("WARNING: required %d video TS packets to determine frame type", numPacketsPid);
+}
+
 // --- cPatPmtGenerator ------------------------------------------------------
 
 cPatPmtGenerator::cPatPmtGenerator(const cChannel *Channel)
@@ -791,9 +822,12 @@
                          }
                       }
                       break;
-           case 0x81: // STREAMTYPE_USER_PRIVATE
+           case 0x81: // STREAMTYPE_USER_PRIVATE - AC3 audio for ATSC and BD
+           case 0x82: // STREAMTYPE_USER_PRIVATE - DTS audio for BD
                       {
-                      dbgpatpmt(" AC3");
+                      dbgpatpmt(" %s",
+                          stream.getStreamType() == 0x81 ? "AC3" :
+                          stream.getStreamType() == 0x82 ? "DTS" : "");
                       char lang[MAXLANGCODE1] = { 0 };
                       SI::Descriptor *d;
                       for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
@@ -819,6 +853,36 @@
                          }
                       }
                       break;
+           case 0x90: // PGS subtitles for BD
+                      {
+                      dbgpatpmt(" subtitling");
+                      char lang[MAXLANGCODE1] = { 0 };
+                      SI::Descriptor *d;
+                      for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
+                          switch (d->getDescriptorTag()) {
+                            case SI::ISO639LanguageDescriptorTag: {
+                                 SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+                                 dbgpatpmt(" '%s'", ld->languageCode);
+                                 strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
+                                 if (NumSpids < MAXSPIDS) {
+                                    spids[NumSpids] = stream.getPid();
+                                    *slangs[NumSpids] = 0;
+                                    subtitlingTypes[NumSpids] = 0;
+                                    compositionPageIds[NumSpids] = 0;
+                                    ancillaryPageIds[NumSpids] = 0;
+                                    if (updatePrimaryDevice)
+                                       cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), lang);
+                                    NumSpids++;
+                                    spids[NumSpids] = 0;
+                                    }
+                                 }
+                                 break;
+                            default: ;
+                            }
+                          delete d;
+                          }
+                      }
+                      break;
            default: ;
            }
          dbgpatpmt("\n");
@@ -1004,6 +1068,7 @@
   bool debug;
   bool newFrame;
   bool independentFrame;
+  int iFrameTemporalReferenceOffset;
 public:
   cFrameParser(void);
   virtual ~cFrameParser() {};
@@ -1017,6 +1082,7 @@
   void SetDebug(bool Debug) { debug = Debug; }
   bool NewFrame(void) { return newFrame; }
   bool IndependentFrame(void) { return independentFrame; }
+  int IFrameTemporalReferenceOffset(void) { return iFrameTemporalReferenceOffset; }
   };
 
 cFrameParser::cFrameParser(void)
@@ -1024,6 +1090,7 @@
   debug = true;
   newFrame = false;
   independentFrame = false;
+  iFrameTemporalReferenceOffset = 0;
 }
 
 // --- cAudioParser ----------------------------------------------------------
@@ -1056,6 +1123,7 @@
 private:
   uint32_t scanner;
   bool seenIndependentFrame;
+  int lastIFrameTemporalReference;
 public:
   cMpeg2Parser(void);
   virtual int Parse(const uchar *Data, int Length, int Pid);
@@ -1065,6 +1133,7 @@
 {
   scanner = EMPTY_SCANNER;
   seenIndependentFrame = false;
+  lastIFrameTemporalReference = -1; // invalid
 }
 
 int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
@@ -1089,10 +1158,25 @@
             scanner = OldScanner;
             return tsPayload.Used() - TS_SIZE;
             }
+         uchar b1 = tsPayload.GetByte();
+         uchar b2 = tsPayload.GetByte();
+         int TemporalReference = (b1 << 2 ) + ((b2 & 0xC0) >> 6);
+         uchar FrameType = (b2 >> 3) & 0x07;
+         if (tsPayload.Find(0x000001B5)) { // Extension start code
+            if (((tsPayload.GetByte() & 0xF0) >> 4) == 0x08) { // Picture coding extension
+               tsPayload.GetByte();
+               uchar PictureStructure = tsPayload.GetByte() & 0x03;
+               if (PictureStructure == 0x02) // bottom field
+                  break;
+               }
+            }
          newFrame = true;
-         tsPayload.GetByte();
-         uchar FrameType = (tsPayload.GetByte() >> 3) & 0x07;
          independentFrame = FrameType == 1; // I-Frame
+         if (independentFrame) {
+            if (lastIFrameTemporalReference >= 0)
+               iFrameTemporalReferenceOffset = TemporalReference - lastIFrameTemporalReference;
+            lastIFrameTemporalReference = TemporalReference;
+            }
          if (debug) {
             seenIndependentFrame |= independentFrame;
             if (seenIndependentFrame) {
@@ -1100,11 +1184,11 @@
                dbgframes("%c", FrameTypes[FrameType]);
                }
             }
+         tsPayload.Statistics();
          break;
          }
       if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary
-         || (tsPayload.Available() < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit...
-            && (tsPayload.Available() <= 0 || tsPayload.AtTsStart()))) // ...but only if there is no more data at all, or if we are at a TS boundary
+         || tsPayload.Eof()) // or if we're out of data
          break;
       }
   return tsPayload.Used();
@@ -1203,7 +1287,7 @@
 uint32_t cH264Parser::GetGolombUe(void)
 {
   int z = -1;
-  for (int b = 0; !b; z++)
+  for (int b = 0; !b && z < 32; z++) // limiting z to no get stuck if GetBit() always returns 0
       b = GetBit();
   return (1 << z) - 1 + GetBits(z);
 }
@@ -1239,13 +1323,17 @@
            case nutAccessUnitDelimiter:  ParseAccessUnitDelimiter();
                                          gotAccessUnitDelimiter = true;
                                          break;
-           case nutSequenceParameterSet: ParseSequenceParameterSet();
-                                         gotSequenceParameterSet = true;
+           case nutSequenceParameterSet: if (gotAccessUnitDelimiter) {
+                                            ParseSequenceParameterSet();
+                                            gotSequenceParameterSet = true;
+                                            }
                                          break;
            case nutCodedSliceNonIdr:
            case nutCodedSliceIdr:        if (gotAccessUnitDelimiter && gotSequenceParameterSet) {
                                             ParseSliceHeader();
                                             gotAccessUnitDelimiter = false;
+                                            if (newFrame)
+                                               tsPayload.Statistics();
                                             return tsPayload.Used();
                                             }
                                          break;
@@ -1253,8 +1341,7 @@
            }
          }
       if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary
-         || (tsPayload.Available() < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit...
-            && (tsPayload.Available() <= 0 || tsPayload.AtTsStart()))) // ...but only if there is no more data at all, or if we are at a TS boundary
+         || tsPayload.Eof()) // or if we're out of data
          break;
       }
   return tsPayload.Used();
@@ -1457,7 +1544,12 @@
                        for (int i = 0; i < numPtsValues; i++)
                            ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
                        qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
-                       uint32_t Delta = ptsValues[0] / framesPerPayloadUnit;
+                       int Div = framesPerPayloadUnit;
+                       if (framesPerPayloadUnit > 1)
+                          Div += parser->IFrameTemporalReferenceOffset();
+                       if (Div <= 0)
+                          Div = 1;
+                       uint32_t Delta = ptsValues[0] / Div;
                        // determine frame info:
                        if (isVideo) {
                           if (abs(Delta - 3600) <= 1)
@@ -1475,7 +1567,7 @@
                           }
                        else // audio
                           framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
-                       dbgframes("\nDelta = %d  FPS = %5.2f  FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1);
+                       dbgframes("\nDelta = %d  FPS = %5.2f  FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
                        synced = true;
                        parser->SetDebug(false);
                        }
diff -ruN vdr-2.0.0/remux.h vdr-2.2.0/remux.h
--- vdr-2.0.0/remux.h	2013-01-20 12:43:59.000000000 +0100
+++ vdr-2.2.0/remux.h	2014-03-22 15:58:24.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remux.h 2.37 2013/01/20 11:43:59 kls Exp $
+ * $Id: remux.h 3.4 2014/03/22 14:58:24 kls Exp $
  */
 
 #ifndef __REMUX_H
@@ -50,6 +50,7 @@
 #define TS_ADAPT_EXTENSION    0x01
 
 #define PATPID 0x0000 // PAT PID (constant 0)
+#define CATPID 0x0001 // CAT PID (constant 1)
 #define MAXPID 0x2000 // for arrays that use a PID as the index
 
 #define PTSTICKS  90000 // number of PTS ticks per second
@@ -217,8 +218,11 @@
   int length;
   int pid;
   int index; // points to the next byte to process
+  int numPacketsPid; // the number of TS packets with the given PID (for statistical purposes)
+  int numPacketsOther; // the number of TS packets with other PIDs (for statistical purposes)
+  uchar SetEof(void);
 protected:
-  void Reset(void) { index = 0; }
+  void Reset(void);
 public:
   cTsPayload(void);
   cTsPayload(uchar *Data, int Length, int Pid = -1);
@@ -246,6 +250,10 @@
        ///< is counted with its full size.
   bool Eof(void) const { return index >= length; }
        ///< Returns true if all available bytes of the TS payload have been processed.
+  void Statistics(void) const;
+       ///< May be called after a new frame has been detected, and will log a warning
+       ///< if the number of TS packets required to determine the frame type exceeded
+       ///< some safety limits.
   uchar GetByte(void);
        ///< Gets the next byte of the TS payload, skipping any intermediate TS header data.
   bool SkipBytes(int Bytes);
@@ -462,7 +470,7 @@
 
 // Frame detector:
 
-#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 5
+#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 100
 
 class cFrameParser;
 
@@ -476,7 +484,6 @@
   bool independentFrame;
   uint32_t ptsValues[MaxPtsValues]; // 32 bit is enough - we only need the delta
   int numPtsValues;
-  int numFrames;
   int numIFrames;
   bool isVideo;
   double framesPerSecond;
diff -ruN vdr-2.0.0/ringbuffer.c vdr-2.2.0/ringbuffer.c
--- vdr-2.0.0/ringbuffer.c	2012-09-22 13:26:49.000000000 +0200
+++ vdr-2.2.0/ringbuffer.c	2012-09-22 13:26:49.000000000 +0200
@@ -7,7 +7,7 @@
  * Parts of this file were inspired by the 'ringbuffy.c' from the
  * LinuxDVB driver (see linuxtv.org).
  *
- * $Id: ringbuffer.c 2.5 2012/09/22 11:26:49 kls Exp $
+ * $Id: ringbuffer.c 3.0 2012/09/22 11:26:49 kls Exp $
  */
 
 #include "ringbuffer.h"
diff -ruN vdr-2.0.0/ringbuffer.h vdr-2.2.0/ringbuffer.h
--- vdr-2.0.0/ringbuffer.h	2013-02-16 16:20:37.000000000 +0100
+++ vdr-2.2.0/ringbuffer.h	2013-02-16 16:20:37.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: ringbuffer.h 2.5 2013/02/16 15:20:37 kls Exp $
+ * $Id: ringbuffer.h 3.0 2013/02/16 15:20:37 kls Exp $
  */
 
 #ifndef __RINGBUFFER_H
diff -ruN vdr-2.0.0/runvdr.template vdr-2.2.0/runvdr.template
--- vdr-2.0.0/runvdr.template	2011-04-17 14:34:30.000000000 +0200
+++ vdr-2.2.0/runvdr.template	2015-02-05 11:28:53.000000000 +0100
@@ -20,7 +20,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: runvdr.template 2.2 2011/04/17 12:34:30 kls Exp $
+# $Id: runvdr.template 3.1 2015/02/05 10:28:53 kls Exp $
 
 VDRPRG="./vdr"
 
@@ -43,19 +43,21 @@
 
 # Detect whether the DVB driver is already loaded
 # and return 0 if it *is* loaded, 1 if not:
-function DriverLoaded()
+DriverLoaded()
 {
   return 1
 }
 
 # Load all DVB driver modules needed for your hardware:
-function LoadDriver()
+LoadDriver()
 {
+  return 0
 }
 
 # Unload all DVB driver modules loaded in LoadDriver():
-function UnloadDriver()
+UnloadDriver()
 {
+  return 0
 }
 
 # Load driver if it hasn't been loaded already:
diff -ruN vdr-2.0.0/scr.conf vdr-2.2.0/scr.conf
--- vdr-2.0.0/scr.conf	2011-09-17 14:58:39.000000000 +0200
+++ vdr-2.2.0/scr.conf	2015-01-26 12:59:51.000000000 +0100
@@ -14,11 +14,17 @@
 #
 # Examples:
 
-# 0 1284
-# 1 1400
-# 2 1516
-# 3 1632
-# 4 1748
-# 5 1864
-# 6 1980
-# 7 2096
+# EN50494 & EN50607 ("JESS")
+# 0 974
+# 1 1076
+# 2 1178
+# 3 1280
+# 4 1382
+# 5 1484
+# 6 1586
+# 7 1688
+# EN50607 ("JESS") only
+# 8 1790
+# 9 1892
+# 10 1994
+# 11 2096
diff -ruN vdr-2.0.0/sdt.c vdr-2.2.0/sdt.c
--- vdr-2.0.0/sdt.c	2010-05-16 16:23:21.000000000 +0200
+++ vdr-2.2.0/sdt.c	2015-01-04 15:33:35.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sdt.c 2.5 2010/05/16 14:23:21 kls Exp $
+ * $Id: sdt.c 3.4 2015/01/04 14:33:35 kls Exp $
  */
 
 #include "sdt.h"
@@ -17,19 +17,30 @@
 
 cSdtFilter::cSdtFilter(cPatFilter *PatFilter)
 {
+  source = cSource::stNone;
   patFilter = PatFilter;
   Set(0x11, 0x42);  // SDT
 }
 
 void cSdtFilter::SetStatus(bool On)
 {
+  cMutexLock MutexLock(&mutex);
   cFilter::SetStatus(On);
   sectionSyncer.Reset();
+  if (!On)
+     source = cSource::stNone;
+}
+
+void cSdtFilter::Trigger(int Source)
+{
+  cMutexLock MutexLock(&mutex);
+  source = Source;
 }
 
 void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
 {
-  if (!(Source() && Transponder()))
+  cMutexLock MutexLock(&mutex);
+  if (!(source && Transponder()))
      return;
   SI::SDT sdt(Data, false);
   if (!sdt.CheckCRCAndParse())
@@ -40,9 +51,11 @@
      return;
   SI::SDT::Service SiSdtService;
   for (SI::Loop::Iterator it; sdt.serviceLoop.getNext(SiSdtService, it); ) {
-      cChannel *channel = Channels.GetByChannelID(tChannelID(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()));
+      cChannel *channel = Channels.GetByChannelID(tChannelID(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()));
       if (!channel)
-         channel = Channels.GetByChannelID(tChannelID(Source(), 0, Transponder(), SiSdtService.getServiceId()));
+         channel = Channels.GetByChannelID(tChannelID(source, 0, Transponder(), SiSdtService.getServiceId()));
+      if (channel)
+         channel->SetSeen();
 
       cLinkChannels *LinkChannels = NULL;
       SI::Descriptor *d;
@@ -64,7 +77,7 @@
                         sd->serviceName.getText(NameBuf, ShortNameBuf, sizeof(NameBuf), sizeof(ShortNameBuf));
                         char *pn = compactspace(NameBuf);
                         char *ps = compactspace(ShortNameBuf);
-                        if (!*ps && cSource::IsCable(Source())) {
+                        if (!*ps && cSource::IsCable(source)) {
                            // Some cable providers don't mark short channel names according to the
                            // standard, but rather go their own way and use "name>short name":
                            char *p = strchr(pn, '>'); // fix for UPC Wien
@@ -92,7 +105,7 @@
                            }
                         else if (*pn && Setup.UpdateChannels >= 4) {
                            channel = Channels.NewChannel(Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId());
-                           patFilter->Trigger();
+                           patFilter->Trigger(SiSdtService.getServiceId());
                            }
                         }
                    default: ;
@@ -115,10 +128,10 @@
                  SI::NVODReferenceDescriptor *nrd = (SI::NVODReferenceDescriptor *)d;
                  SI::NVODReferenceDescriptor::Service Service;
                  for (SI::Loop::Iterator it; nrd->serviceLoop.getNext(Service, it); ) {
-                     cChannel *link = Channels.GetByChannelID(tChannelID(Source(), Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()));
+                     cChannel *link = Channels.GetByChannelID(tChannelID(source, Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()));
                      if (!link && Setup.UpdateChannels >= 4) {
                         link = Channels.NewChannel(Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId());
-                        patFilter->Trigger();
+                        patFilter->Trigger(Service.getServiceId());
                         }
                      if (link) {
                         if (!LinkChannels)
@@ -139,5 +152,9 @@
             delete LinkChannels;
          }
       }
+  if (sdt.getSectionNumber() == sdt.getLastSectionNumber()) {
+     if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3)
+        Channels.MarkObsoleteChannels(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId());
+     }
   Channels.Unlock();
 }
diff -ruN vdr-2.0.0/sdt.h vdr-2.2.0/sdt.h
--- vdr-2.0.0/sdt.h	2004-01-05 15:30:14.000000000 +0100
+++ vdr-2.2.0/sdt.h	2014-03-10 15:40:54.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sdt.h 2.0 2004/01/05 14:30:14 kls Exp $
+ * $Id: sdt.h 3.1 2014/03/10 14:40:54 kls Exp $
  */
 
 #ifndef __SDT_H
@@ -15,13 +15,16 @@
 
 class cSdtFilter : public cFilter {
 private:
+  cMutex mutex;
   cSectionSyncer sectionSyncer;
+  int source;
   cPatFilter *patFilter;
 protected:
   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
 public:
   cSdtFilter(cPatFilter *PatFilter);
   virtual void SetStatus(bool On);
+  void Trigger(int Source);
   };
 
 #endif //__SDT_H
diff -ruN vdr-2.0.0/sections.c vdr-2.2.0/sections.c
--- vdr-2.0.0/sections.c	2012-10-04 14:21:59.000000000 +0200
+++ vdr-2.2.0/sections.c	2015-01-14 12:35:53.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sections.c 2.2 2012/10/04 12:21:59 kls Exp $
+ * $Id: sections.c 3.1 2015/01/14 11:35:53 kls Exp $
  */
 
 #include "sections.h"
@@ -40,10 +40,11 @@
 // --- cSectionHandler -------------------------------------------------------
 
 cSectionHandler::cSectionHandler(cDevice *Device)
-:cThread("section handler", true)
+:cThread(NULL, true)
 {
   shp = new cSectionHandlerPrivate;
   device = Device;
+  SetDescription("device %d section handler", device->CardIndex() + 1);
   statusCount = 0;
   on = false;
   waitForLock = false;
diff -ruN vdr-2.0.0/sections.h vdr-2.2.0/sections.h
--- vdr-2.0.0/sections.h	2005-08-13 13:23:55.000000000 +0200
+++ vdr-2.2.0/sections.h	2005-08-13 13:23:55.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sections.h 2.0 2005/08/13 11:23:55 kls Exp $
+ * $Id: sections.h 3.0 2005/08/13 11:23:55 kls Exp $
  */
 
 #ifndef __SECTIONS_H
diff -ruN vdr-2.0.0/shutdown.c vdr-2.2.0/shutdown.c
--- vdr-2.0.0/shutdown.c	2013-02-18 11:33:26.000000000 +0100
+++ vdr-2.2.0/shutdown.c	2013-10-02 11:02:01.000000000 +0200
@@ -6,7 +6,7 @@
  *
  * Original version written by Udo Richter <udo_richter@gmx.de>.
  *
- * $Id: shutdown.c 2.1 2013/02/18 10:33:26 kls Exp $
+ * $Id: shutdown.c 3.1 2013/10/02 09:02:01 kls Exp $
  */
 
 #include "shutdown.h"
@@ -16,11 +16,11 @@
 #include <sys/wait.h>
 #include "channels.h"
 #include "config.h"
-#include "cutter.h"
 #include "i18n.h"
 #include "interface.h"
 #include "menu.h"
 #include "plugin.h"
+#include "recording.h"
 #include "timers.h"
 #include "tools.h"
 
@@ -167,7 +167,7 @@
         Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
      return false;
      }
-  if (cCutter::Active()) {
+  if (RecordingsHandler.Active()) {
      if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?")))
         return false;
      }
@@ -210,7 +210,7 @@
 
 bool cShutdownHandler::ConfirmRestart(bool Interactive)
 {
-  if (cCutter::Active()) {
+  if (RecordingsHandler.Active()) {
      if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?")))
         return false;
      }
diff -ruN vdr-2.0.0/shutdown.h vdr-2.2.0/shutdown.h
--- vdr-2.0.0/shutdown.h	2013-02-18 11:35:27.000000000 +0100
+++ vdr-2.2.0/shutdown.h	2013-02-18 11:35:27.000000000 +0100
@@ -6,7 +6,7 @@
  *
  * Original version written by Udo Richter <udo_richter@gmx.de>.
  *
- * $Id: shutdown.h 2.1 2013/02/18 10:35:27 kls Exp $
+ * $Id: shutdown.h 3.0 2013/02/18 10:35:27 kls Exp $
  */
 
 #ifndef __SHUTDOWN_H
diff -ruN vdr-2.0.0/skinclassic.c vdr-2.2.0/skinclassic.c
--- vdr-2.0.0/skinclassic.c	2013-03-03 16:26:09.000000000 +0100
+++ vdr-2.2.0/skinclassic.c	2013-03-03 16:26:09.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinclassic.c 2.10 2013/03/03 15:26:09 kls Exp $
+ * $Id: skinclassic.c 3.0 2013/03/03 15:26:09 kls Exp $
  */
 
 #include "skinclassic.h"
diff -ruN vdr-2.0.0/skinclassic.h vdr-2.2.0/skinclassic.h
--- vdr-2.0.0/skinclassic.h	2005-01-02 15:38:56.000000000 +0100
+++ vdr-2.2.0/skinclassic.h	2005-01-02 15:38:56.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinclassic.h 2.0 2005/01/02 14:38:56 kls Exp $
+ * $Id: skinclassic.h 3.0 2005/01/02 14:38:56 kls Exp $
  */
 
 #ifndef __SKINCLASSIC_H
diff -ruN vdr-2.0.0/skinlcars.c vdr-2.2.0/skinlcars.c
--- vdr-2.0.0/skinlcars.c	2013-03-09 11:43:34.000000000 +0100
+++ vdr-2.2.0/skinlcars.c	2014-06-12 10:48:15.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinlcars.c 2.21 2013/03/09 10:43:34 kls Exp $
+ * $Id: skinlcars.c 3.8 2014/06/12 08:48:15 kls Exp $
  */
 
 // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures,
@@ -27,6 +27,7 @@
 #include "font.h"
 #include "menu.h"
 #include "osd.h"
+#include "positioner.h"
 #include "themes.h"
 #include "videodir.h"
 
@@ -299,6 +300,44 @@
      }
 }
 
+static void DrawDevicePosition(cOsd *Osd, const cPositioner *Positioner, int x0, int y0, int x1, int y1, int &LastCurrent)
+{
+  int HorizonLeft = Positioner->HorizonLongitude(cPositioner::pdLeft);
+  int HorizonRight = Positioner->HorizonLongitude(cPositioner::pdRight);
+  int HardLimitLeft = cPositioner::NormalizeAngle(HorizonLeft - Positioner->HardLimitLongitude(cPositioner::pdLeft));
+  int HardLimitRight = cPositioner::NormalizeAngle(Positioner->HardLimitLongitude(cPositioner::pdRight) - HorizonRight);
+  int HorizonDelta = cPositioner::NormalizeAngle(HorizonLeft - HorizonRight);
+  int Current = cPositioner::NormalizeAngle(HorizonLeft - Positioner->CurrentLongitude());
+  int Target = cPositioner::NormalizeAngle(HorizonLeft - Positioner->TargetLongitude());
+  int d = (y1 - y0) / 2;
+  int w = x1 - x0 - 2 * d;
+  int l = max(x0 + d, x0 + d + w * HardLimitLeft / HorizonDelta);
+  int r = min(x1 - d, x1 - d - w * HardLimitRight / HorizonDelta) - 1;
+  int c = constrain(x0 + d + w * Current / HorizonDelta, l, r);
+  int t = constrain(x0 + d + w * Target / HorizonDelta, l, r);
+  if (c == LastCurrent)
+     return;
+  if (c > t)
+     swap(c, t);
+  tColor ColorRange, ColorMove;
+  if (TwoColors) {
+     ColorRange = Theme.Color(clrChannelFrameBg);
+     ColorMove = Theme.Color(clrBackground);
+     }
+  else {
+     ColorRange = Theme.Color(clrChannelFrameBg);
+     ColorMove = Theme.Color(clrDeviceBg);
+     }
+  Osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, Theme.Color(clrBackground));
+  Osd->DrawEllipse(l - d, y0, l, y1 - 1, ColorRange, 7);
+  Osd->DrawRectangle(l, y0, r, y1 - 1, ColorRange);
+  Osd->DrawEllipse(r, y0, r + d, y1 - 1, ColorRange, 5);
+  Osd->DrawEllipse(c - d, y0, c, y1 - 1, ColorMove, 7);
+  Osd->DrawRectangle(c, y0, t, y1 - 1, ColorMove);
+  Osd->DrawEllipse(t, y0, t + d, y1 - 1, ColorMove, 5);
+  LastCurrent = c;
+}
+
 // --- cSkinLCARSDisplayChannel ----------------------------------------------
 
 class cSkinLCARSDisplayChannel : public cSkinDisplayChannel {
@@ -317,6 +356,7 @@
   bool initial;
   cString lastDate;
   int lastSeen;
+  int lastCurrentPosition;
   int lastDeviceNumber;
   cString lastDeviceType;
   cCamSlot *lastCamSlot;
@@ -336,6 +376,7 @@
   virtual void SetChannel(const cChannel *Channel, int Number);
   virtual void SetEvents(const cEvent *Present, const cEvent *Following);
   virtual void SetMessage(eMessageType Type, const char *Text);
+  virtual void SetPositioner(const cPositioner *Positioner);
   virtual void Flush(void);
   };
 
@@ -352,6 +393,7 @@
   initial = true;
   present = NULL;
   lastSeen = -1;
+  lastCurrentPosition = -1;
   lastDeviceNumber = -1;
   lastCamSlot = NULL;
   lastSignalStrength = -1;
@@ -440,7 +482,7 @@
 void cSkinLCARSDisplayChannel::DrawDate(void)
 {
   cString s = DayDateTime();
-  if (initial || strcmp(s, lastDate)) {
+  if (initial || !*lastDate || strcmp(s, lastDate)) {
      osd->DrawText(xc12, yc11, s, Theme.Color(clrDateFg), Theme.Color(clrDateBg), cFont::GetFont(fontOsd), xc13 - xc12, lineHeight, taRight | taBorder);
      lastDate = s;
      }
@@ -450,7 +492,7 @@
 {
   cDevice *Device = cDevice::PrimaryDevice();
   const tTrackId *Track = Device->GetTrack(Device->GetCurrentAudioTrack());
-  if (!Track && *lastTrackId.description || Track && strcmp(lastTrackId.description, Track->description)) {
+  if (Track ? strcmp(lastTrackId.description, Track->description) : *lastTrackId.description) {
      osd->DrawText(xc03, yc07, Track ? Track->description : "", Theme.Color(clrTrackName), Theme.Color(clrBackground), cFont::GetFont(fontOsd), xc07 - xc03);
      strn0cpy(lastTrackId.description, Track ? Track->description : "", sizeof(lastTrackId.description));
      }
@@ -458,6 +500,8 @@
 
 void cSkinLCARSDisplayChannel::DrawSeen(int Current, int Total)
 {
+  if (lastCurrentPosition >= 0)
+     return; // to not interfere with SetPositioner()
   int Seen = (Total > 0) ? min(xc07 - xc06, int((xc07 - xc06) * double(Current) / Total)) : 0;
   if (initial || Seen != lastSeen) {
      int y0 = yc11 - ShowSeenExtent;
@@ -532,8 +576,13 @@
   osd->DrawText(xc00, yc00, ChNumber, Theme.Color(clrChannelFrameFg), frameColor, tallFont, xc02 - xc00, yc02 - yc00, taTop | taRight | taBorder);
   osd->DrawText(xc03, yc00, ChName, Theme.Color(clrChannelName), Theme.Color(clrBackground), tallFont, xi - xc03 - lineHeight, 0, taTop | taLeft);
   lastSignalDisplay = 0;
-  if (withInfo)
+  if (withInfo) {
+     if (Channel) {
+        int x = xc00 + (yc10 - yc09); // compensate for the arc
+        osd->DrawText(x, yc07, cSource::ToString(Channel->Source()), Theme.Color(clrChannelFrameFg), frameColor, cFont::GetFont(fontOsd), xc02 - x, yc10 - yc07, taTop | taRight | taBorder);
+        }
      DrawDevice();
+     }
 }
 
 void cSkinLCARSDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Following)
@@ -587,6 +636,20 @@
      }
 }
 
+void cSkinLCARSDisplayChannel::SetPositioner(const cPositioner *Positioner)
+{
+  if (Positioner) {
+     int y0 = yc11 - ShowSeenExtent;
+     int y1 = yc11 + lineHeight / 2 - Gap / 2;
+     DrawDevicePosition(osd, Positioner, xc06, y0, xc07, y1, lastCurrentPosition);
+     }
+  else {
+     lastCurrentPosition = -1;
+     initial = true; // to have DrawSeen() refresh the progress bar
+     }
+  return;
+}
+
 void cSkinLCARSDisplayChannel::Flush(void)
 {
   if (withInfo) {
@@ -837,6 +900,15 @@
   ys03 = ys04 - Gap;
   ys05 = yb15;
 
+  // The item area (just to have them initialized, actual setting will be done in SetMenuCategory():
+
+  xi00 = 0;
+  xi01 = 0;
+  xi02 = 0;
+  xi03 = 1;
+  yi00 = 0;
+  yi01 = 1;
+
   // The color buttons in submenus:
   xb00 = xa06;
   xb15 = xa07;
@@ -854,7 +926,7 @@
   xb12 = xb08 + w;
   xb11 = xb12 - Gap;
   xb13 = xb12 + lineHeight / 2;
-  xb14 = xb13 + Gap;;
+  xb14 = xb13 + Gap;
 
   // The color buttons in the main menu:
   int r = lineHeight;
@@ -998,7 +1070,7 @@
 void cSkinLCARSDisplayMenu::DrawDate(void)
 {
   cString s = DayDateTime();
-  if (initial || strcmp(s, lastDate)) {
+  if (initial || !*lastDate || strcmp(s, lastDate)) {
      const cFont *font = cFont::GetFont(fontOsd);
      tColor ColorFg = Theme.Color(clrDateFg);
      tColor ColorBg = Theme.Color(clrDateBg);
@@ -1289,7 +1361,7 @@
   if (initial || y != lastLiveIndicatorY || Transferring != lastLiveIndicatorTransferring) {
      if (lastLiveIndicatorY >= 0)
         osd->DrawRectangle(xs12, lastLiveIndicatorY, xs13 - 1, lastLiveIndicatorY + lineHeight - 1, Theme.Color(clrBackground));
-     if (y >= 0) {
+     if (y > 0) {
         tColor ColorBg = Theme.Color(clrChannelFrameBg);
         osd->DrawRectangle(xs12, y, xs12 + lineHeight / 2 - 1, y + lineHeight - 1, ColorBg);
         osd->DrawEllipse  (xs12 + lineHeight / 2, y, xs13 - 1, y + lineHeight - 1, ColorBg, 5);
@@ -1340,9 +1412,13 @@
      DrawMainFrameUpper(Theme.Color(clrChannelFrameBg));
      osd->DrawText(xd00, yd00, tr("LIVE"), Theme.Color(clrChannelFrameBg), Theme.Color(clrBackground), tallFont, xd07 - xd00, yd01 - yd00, taTop | taRight | taBorder);
      }
+  if (!Channel)
+     return;
   if (initial || Channel != lastChannel) {
      osd->DrawText(xa00, yt00, itoa(Channel->Number()), Theme.Color(clrChannelFrameFg), Theme.Color(clrChannelFrameBg), tallFont, xa02 - xa00, yt02 - yt00, taTop | taRight | taBorder);
      osd->DrawText(xa03, yt00, Channel->Name(), Theme.Color(clrChannelName), Theme.Color(clrBackground), tallFont, xd00 - xa03, yd01 - yd00, taTop | taLeft);
+     int x = xa00 + (yc03 - yc02); // compensate for the arc
+     osd->DrawText(x, yc00, cSource::ToString(Channel->Source()), Theme.Color(clrChannelFrameFg), Theme.Color(clrChannelFrameBg), cFont::GetFont(fontOsd), xa02 - x, yc03 - yc00, taTop | taRight | taBorder);
      lastChannel = Channel;
      DrawSeen(0, 0);
      }
@@ -1708,6 +1784,7 @@
   frameColor = Theme.Color(clrReplayFrameBg);
   lastCurrentWidth = 0;
   lastTotalWidth = 0;
+  memset(&lastTrackId, 0, sizeof(lastTrackId));
   int d = 5 * lineHeight;
   xp00 = 0;
   xp01 = xp00 + d / 2;
@@ -1781,7 +1858,7 @@
 {
   cDevice *Device = cDevice::PrimaryDevice();
   const tTrackId *Track = Device->GetTrack(Device->GetCurrentAudioTrack());
-  if (!Track && *lastTrackId.description || Track && strcmp(lastTrackId.description, Track->description)) {
+  if (Track ? strcmp(lastTrackId.description, Track->description) : *lastTrackId.description) {
      osd->DrawText(xp03, yp04, Track ? Track->description : "", Theme.Color(clrTrackName), Theme.Color(clrBackground), cFont::GetFont(fontOsd), xp07 - xp03);
      strn0cpy(lastTrackId.description, Track ? Track->description : "", sizeof(lastTrackId.description));
      }
diff -ruN vdr-2.0.0/skinlcars.h vdr-2.2.0/skinlcars.h
--- vdr-2.0.0/skinlcars.h	2012-04-15 15:17:35.000000000 +0200
+++ vdr-2.2.0/skinlcars.h	2012-04-15 15:17:35.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinlcars.h 2.1 2012/04/15 13:17:35 kls Exp $
+ * $Id: skinlcars.h 3.0 2012/04/15 13:17:35 kls Exp $
  */
 
 #ifndef __SKINLCARS_H
diff -ruN vdr-2.0.0/skins.c vdr-2.2.0/skins.c
--- vdr-2.0.0/skins.c	2012-06-02 13:44:14.000000000 +0200
+++ vdr-2.2.0/skins.c	2013-08-18 14:07:22.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skins.c 2.10 2012/06/02 11:44:14 kls Exp $
+ * $Id: skins.c 3.1 2013/08/18 12:07:22 kls Exp $
  */
 
 #include "skins.h"
@@ -63,6 +63,22 @@
   current = NULL;
 }
 
+// --- cSkinDisplayChannel ---------------------------------------------------
+
+cSkinDisplayChannel::cSkinDisplayChannel(void)
+{
+  positioner = NULL;
+}
+
+void cSkinDisplayChannel::SetPositioner(const cPositioner *Positioner)
+{
+  if (positioner && Positioner != positioner)
+     SetMessage(mtInfo, NULL);
+  positioner = Positioner;
+  if (positioner)
+     SetMessage(mtInfo, cString::sprintf(tr("Moving dish to %.1f..."), double(positioner->TargetLongitude()) / 10));
+}
+
 // --- cSkinDisplayMenu ------------------------------------------------------
 
 cSkinDisplayMenu::cSkinDisplayMenu(void)
diff -ruN vdr-2.0.0/skins.h vdr-2.2.0/skins.h
--- vdr-2.0.0/skins.h	2012-12-21 12:09:13.000000000 +0100
+++ vdr-2.2.0/skins.h	2015-01-15 11:45:47.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skins.h 2.9 2012/12/21 11:09:13 kls Exp $
+ * $Id: skins.h 3.4 2015/01/15 10:45:47 kls Exp $
  */
 
 #ifndef __SKINS_H
@@ -14,6 +14,7 @@
 #include "epg.h"
 #include "keys.h"
 #include "osd.h"
+#include "positioner.h"
 #include "recording.h"
 #include "themes.h"
 #include "thread.h"
@@ -52,7 +53,10 @@
        ///< This class is used to display the current channel, together with
        ///< the present and following EPG event. How and to what extent this
        ///< is done is totally up to the derived class.
+private:
+  const cPositioner *positioner;
 public:
+  cSkinDisplayChannel(void);
   virtual void SetChannel(const cChannel *Channel, int Number) = 0;
        ///< Sets the current channel to Channel. If Number is not 0, the
        ///< user is in the process of entering a channel number, which must
@@ -65,6 +69,17 @@
        ///< to determine, e.g., the colors for displaying the Text.
        ///< If Text is NULL, any previously displayed message must be removed, and
        ///< any previous contents overwritten by the message must be restored.
+  virtual void SetPositioner(const cPositioner *Positioner);
+       ///< Sets the Positioner used to move the satellite dish. The skin may use the
+       ///< data provided by Positioner to implement some form of progress display,
+       ///< since moving the dish may take a while. This function will only be called
+       ///< if the device receiving the current live channel actually uses a positioner,
+       ///< and it will be called with NULL once the dish has reached its target
+       ///< position (or the user switches to a channel that doesn't require positioning
+       ///< the dish). While the dish is moving, SetPositioner() is called repeatedly,
+       ///< so the skin has a chance to update the progress display.
+       ///< The default implementation calls SetMessage() with a text that indicates
+       ///< that the dish is being moved to a new position.
   /*TODO
   SetButtons
     Red    = Video options
@@ -86,6 +101,7 @@
   mcTimerEdit,
   mcRecording,
   mcRecordingInfo,
+  mcRecordingEdit,
   mcPlugin,
   mcPluginSetup,
   mcSetup,
@@ -105,6 +121,14 @@
   mcCam
   };
 
+enum eMenuSortMode {
+  msmUnknown = 0,
+  msmNumber,
+  msmName,
+  msmTime,
+  msmProvider
+  };
+
 class cSkinDisplayMenu : public cSkinDisplay {
        ///< This class implements the general purpose menu display, which is
        ///< used throughout the program to display information and let the
@@ -151,6 +175,10 @@
   virtual void SetTabs(int Tab1, int Tab2 = 0, int Tab3 = 0, int Tab4 = 0, int Tab5 = 0);
        ///< Sets the tab columns to the given values, which are the number of
        ///< characters in each column.
+  virtual void SetMenuSortMode(eMenuSortMode MenuSortMode) {}
+       ///< Sets the mode by which the items in this menu are sorted.
+       ///< This is purely informative and may be used by a skin to display the
+       ///< current sort mode by means of some text or symbol.
   virtual void Scroll(bool Up, bool Page);
        ///< If this menu contains a text area that can be scrolled, this function
        ///< will be called to actually scroll the text. Up indicates whether the
@@ -264,7 +292,7 @@
   class cProgressBar : public cBitmap {
   protected:
     int total;
-    int Pos(int p) { return p * Width() / total; }
+    int Pos(int p) { return int(int64_t(p) * Width() / total); }
     void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent);
   public:
     cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent);
diff -ruN vdr-2.0.0/skinsttng.c vdr-2.2.0/skinsttng.c
--- vdr-2.0.0/skinsttng.c	2013-03-03 16:29:28.000000000 +0100
+++ vdr-2.2.0/skinsttng.c	2013-11-15 16:33:14.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinsttng.c 2.17 2013/03/03 15:29:28 kls Exp $
+ * $Id: skinsttng.c 3.1 2013/11/15 15:33:14 kls Exp $
  */
 
 // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures
@@ -338,7 +338,7 @@
            }
         cDevice *Device = cDevice::PrimaryDevice();
         const tTrackId *Track = Device->GetTrack(Device->GetCurrentAudioTrack());
-        if (!Track && *lastTrackId.description || Track && strcmp(lastTrackId.description, Track->description)) {
+        if (Track ? strcmp(lastTrackId.description, Track->description) : *lastTrackId.description) {
            osd->DrawText(x3 + TextFrame, y6, Track ? Track->description : "", Theme.Color(clrChannelName), frameColor, font, x4 - x3 - w - 2 * TextFrame);
            strn0cpy(lastTrackId.description, Track ? Track->description : "", sizeof(lastTrackId.description));
            }
diff -ruN vdr-2.0.0/skinsttng.h vdr-2.2.0/skinsttng.h
--- vdr-2.0.0/skinsttng.h	2005-01-02 15:39:29.000000000 +0100
+++ vdr-2.2.0/skinsttng.h	2005-01-02 15:39:29.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skinsttng.h 2.0 2005/01/02 14:39:29 kls Exp $
+ * $Id: skinsttng.h 3.0 2005/01/02 14:39:29 kls Exp $
  */
 
 #ifndef __SKINSTTNG_H
diff -ruN vdr-2.0.0/sourceparams.c vdr-2.2.0/sourceparams.c
--- vdr-2.0.0/sourceparams.c	2010-03-06 12:13:39.000000000 +0100
+++ vdr-2.2.0/sourceparams.c	2014-03-09 13:03:09.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sourceparams.c 1.2 2010/03/06 11:13:39 kls Exp $
+ * $Id: sourceparams.c 3.1 2014/03/09 12:03:09 kls Exp $
  */
 
 #include "sourceparams.h"
@@ -21,8 +21,8 @@
         return;
         }
      SourceParams.Add(this);
-     if (!strchr("ACST", Source)) // no, it's not "ATSC" ;-)
-        Sources.Add(new cSource(Source, Description));
+     if (!Sources.ContainsSourceType(source))
+        Sources.Add(new cSource(source, Description));
      dsyslog("registered source parameters for '%c - %s'", source, Description);
      }
   else
diff -ruN vdr-2.0.0/sourceparams.h vdr-2.2.0/sourceparams.h
--- vdr-2.0.0/sourceparams.h	2010-02-28 12:58:03.000000000 +0100
+++ vdr-2.2.0/sourceparams.h	2010-02-28 12:58:03.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sourceparams.h 1.1 2010/02/28 11:58:03 kls Exp $
+ * $Id: sourceparams.h 3.0 2010/02/28 11:58:03 kls Exp $
  */
 
 #ifndef __SOURCEPARAMS_H
diff -ruN vdr-2.0.0/sources.c vdr-2.2.0/sources.c
--- vdr-2.0.0/sources.c	2010-02-28 16:15:39.000000000 +0100
+++ vdr-2.2.0/sources.c	2014-03-09 13:05:42.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sources.c 2.2 2010/02/28 15:15:39 kls Exp $
+ * $Id: sources.c 3.6 2014/03/09 12:05:42 kls Exp $
  */
 
 #include "sources.h"
@@ -31,23 +31,35 @@
 bool cSource::Parse(const char *s)
 {
   char *codeBuf = NULL;
-  if (2 == sscanf(s, "%a[^ ] %a[^\n]", &codeBuf, &description))
+  if (2 == sscanf(s, "%m[^ ] %m[^\n]", &codeBuf, &description))
      code = FromString(codeBuf);
   free(codeBuf);
   return code != stNone && description && *description;
 }
 
+bool cSource::Matches(int Code1, int Code2)
+{
+  if (Code1 == (stSat | st_Any))
+     return IsSat(Code2);
+  return Code1 == Code2;
+}
+
+int cSource::Position(int Code)
+{
+  int n = (Code & st_Pos);
+  if (n > 0x00007FFF)
+     n |= 0xFFFF0000;
+  return n;
+}
+
 cString cSource::ToString(int Code)
 {
   char buffer[16];
   char *q = buffer;
   *q++ = (Code & st_Mask) >> 24;
-  int n = (Code & st_Pos);
-  if (n > 0x00007FFF)
-     n |= 0xFFFF0000;
-  if (n) {
+  if (int n = Position(Code)) {
      q += snprintf(q, sizeof(buffer) - 2, "%u.%u", abs(n) / 10, abs(n) % 10); // can't simply use "%g" here since the silly 'locale' messes up the decimal point
-     *q++ = (n < 0) ? 'E' : 'W';
+     *q++ = (n < 0) ? 'W' : 'E';
      }
   *q = 0;
   return buffer;
@@ -69,8 +81,8 @@
                                      break;
                    case '.':         dot = true;
                                      break;
-                   case 'E':         neg = true; // fall through to 'W'
-                   case 'W':         if (!dot)
+                   case 'W':         neg = true; // fall through to 'E'
+                   case 'E':         if (!dot)
                                         pos *= 10;
                                      break;
                    default: esyslog("ERROR: unknown source character '%c'", *s);
@@ -84,7 +96,7 @@
         return code;
         }
      else
-       esyslog("ERROR: unknown source key '%c'", *s);
+        esyslog("ERROR: unknown source key '%c'", *s);
      }
   return stNone;
 }
@@ -93,9 +105,9 @@
 {
   int code = SourceType;
   if (SourceType == stSat) {
-     if (East)
+     if (!East)
         Position = -Position;
-     code |= (Position & st_Pos);;
+     code |= (Position & st_Pos);
      }
   return code;
 }
@@ -112,3 +124,12 @@
       }
   return NULL;
 }
+
+bool cSources::ContainsSourceType(char SourceType)
+{
+  for (cSource *p = First(); p; p = Next(p)) {
+      if (cSource::ToChar(p->Code()) == SourceType)
+         return true;
+      }
+  return false;
+}
diff -ruN vdr-2.0.0/sources.conf vdr-2.2.0/sources.conf
--- vdr-2.0.0/sources.conf	2013-03-11 12:24:07.000000000 +0100
+++ vdr-2.2.0/sources.conf	2015-02-18 10:33:19.000000000 +0100
@@ -19,57 +19,60 @@
 
 # Europe
 
-S3E     Eutelsat 3A & Rascom 1R
-S4E     Eurobird 4A
+S3E     Eutelsat 3A/3D & Rascom 1R
+S4E     Eutelsat 4B
 S4.8E   Astra 4A
-S7E     Eutelsat W3A
-S9E     Eurobird 9A
-S10E    Eutelsat W2A
-S13E    Hotbird 6/8/9
-S16E    Eutelsat W2M & Eurobird 16A
-S19.2E  Astra 1H/1KR/1L/1M/2C
-S21.6E  Eutelsat W6
-S23.5E  Astra 3A/3B
-S25.5E  Eurobird 2
+S5E     SES 5
+S7E     Eutelsat 7A
+S9E     Eutelsat 9A/Ka-Sat 9A
+S10E    Eutelsat 10A
+S13E    Eutelsat Hot Bird 13B/13C/13D
+S16E    Eutelsat 16A/16B
+S17E    Amos 5
+S19.2E  Astra 1KR/1L/1M/2C
+S21.6E  Eutelsat 21B
+S23.5E  Astra 3B
+S25.5E  Eutelsat 25B
 S26E    Badr 4/5/6
-S28.2E  Astra 2A/2B/2D
-S28.5E  Eurobird 1
+S28.2E  Astra 1N/2A/2F
+S28.5E  Eutelsat 28A
 S30.5E  Arabsat 5A
 S31.5E  Astra 1G
-S33E    Eurobird 3 & Intelsat New Dawn
-S36E    Eutelsat W4/W7
-S38E    Paksat 1
+S33E    Eutelsat 33A & Intelsat 28
+S36E    Eutelsat 36A/36B
+S38E    Paksat 1R
 S39E    Hellas Sat 2
-S40E    Express AM1
 S42E    Turksat 2A/3A
 S45E    Intelsat 12
+S46E    Azerspace-1
+S47.5E  Intelsat 10
 S49E    Yamal 202
+S52.5E  Yahsat 1A
 S53E    Express AM22
-S55E    Insat 3E
-S56E    Bonum 1
+S56E    DirecTV 1R
 S57E    NSS 12
 S60E    Intelsat 904
 S62E    Intelsat 902
 S64E    Intelsat 906
 S66E    Intelsat 17
 S68.5E  Intelsat 7/10
-S70.5E  Eutelsat W5
-S72E    Intelsat 709
+S70.5E  Eutelsat 70B
+S72E    Intelsat 22
 
 # Asia
 
 S74E    Insat 3C/4CR
 S75E    ABS 1A
-S76.5E  Apstar 2R
-S78.5E  Thaicom 5
-S80E    Express AM2/MD1
-S83E    Insat 2E/4A
-S85.2E  Intelsat 15
-S87.5E  Chinasat 5A
-S88E    ST 1/2
-S90E    Yamal 201
+S76.5E  Apstar 7
+S78.5E  Thaicom 5/6A
+S80E    Express AM2
+S83E    Insat 4A
+S85.2E  Intelsat 15 & Horizons 2
+S87.5E  ChinaSat 12
+S88E    ST 2
+S90E    Yamal 201/300K
 S91.5E  Measat 3/3A
-S92.2E  Chinasat 9
+S92.2E  ChinaSat 9
 S93.5E  Insat 3A/4B
 S95E    NSS 6
 S96.5E  Express AM33
@@ -77,21 +80,22 @@
 S103E   Express A2
 S105.5E Asiasat 3S
 S108.2E Telkom 1 & NSS 11 & SES 7
-S110E   N-Sat 110 & BSAT 2C/3A
-S110.5E Chinasat 10
+S110E   N-Sat 110 & BSAT 3A/3C
+S110.5E ChinaSat 10
 S113E   Palapa D & Koreasat 5
-S116E   Koreasat 6
+S115.5E ChinaSat 6B
+S116E   ABS 7 & Koreasat 6
 S118E   Telkom 2
+S119.5E Thaicom 4
 S122.2E Asiasat 4
-S124E   JCSAT 4A
-S125E   Chinasat 6A
-S128E   JCSAT RA
-S132E   Vinasat 1 & JCSAT5A
+S124E   JCSAT 4B
+S125E   ChinaSat 6A
+S128E   JCSAT 3A
+S132E   Vinasat 1/2 & JCSAT 5A
 S134E   Apstar 6
 S138E   Telstar 18
 S140E   Express AM3
 S144E   Superbird C2
-S146E   ABS 5
 S150E   JCSAT 1B
 S152E   Optus D2
 S154E   JCSAT 2A
@@ -99,73 +103,74 @@
 S160E   Optus D1
 S162E   Superbird B2
 S164E   Optus B3
-S166E   Intelsat 8
-S169E   Intelsat 5
-S172E   GE 23
-S180E   Intelsat 701
+S166E   Intelsat 19
+S169E   Intelsat 8
+S172E   Eutelsat 172A
+S180E   Intelsat 18
 S177W   NSS 9
 
 # Atlantic
 
-S1W     Thor 5/6 & Intelsat 10-02
+S0.8W   Intelsat 10-02
+S1W     Thor 5/6
 S4W     Amos 2/3
-S5W     Atlantic Bird 3
-S7W     Nilesat 101/102/201 & Atlantic Bird 4A
-S8W     Telecom 2D & Atlantic Bird 2
+S5W     Eutelsat 5 West A
+S7W     Nilesat 101/201 & Eutelsat 7 West A
+S8W     Eutelsat 8 West A/C
 S11W    Express AM44
-S12.5W  Atlantic Bird 1
+S12.5W  Eutelsat 12 West A
 S14W    Express A4
 S15W    Telstar 12
 S18W    Intelsat 901
-S20W    NSS 5
-S22W    NSS 7
+S20W    NSS 7
+S22W    SES 4
 S24.5W  Intelsat 905
 S27.5W  Intelsat 907
-S30W    Hispasat 1C/1D/1E
+S30W    Hispasat 1D/1E
 S31.5W  Intelsat 25
 S34.5W  Intelsat 903
 S37.5W  NSS 10 & Telstar 11N
-S40.5W  NSS 806
+S40.5W  SES 6
 S43W    Intelsat 11
 S45W    Intelsat 14
 S50W    Intelsat 1R
-S53W    Intelsat 707
+S53W    Intelsat 23
 S55.5W  Intelsat 805
-S58W    Intelsat 9/16
-S61W    Amazonas 1/2
+S58W    Intelsat 21
+S61W    Amazonas 2/3
 
 # America
 
-S61.5W  Echostar 12/15
+S61.5W  Echostar 16
 S63W    Telstar 14R
 S65W    Star One C1
+S67W    AMC 4
 S70W    Star One C2
 S72W    AMC 6
-S72.5W  DirecTV 1R & Nimiq 5
-S74W    Horizons 2
-S77W    Echostar 1/8
-S79W    AMC 2/5
+S72.7W  Nimiq 5
+S75W    Star One C3
+S77W    QuetzSat 1
 S82W    Nimiq 4
 S83W    AMC 9
 S84W    Brasilsat B4
 S85W    AMC 16
 S85.1W  XM 3
-S87W    AMC 3
+S87W    SES 2
 S89W    Galaxy 28
-S91W    Galaxy 17 & Nimiq 1
-S93W    Galaxy 25
+S91W    Galaxy 17 & Nimiq 6
+S93.1W  Galaxy 25
 S95W    Galaxy 3C
 S97W    Galaxy 19
-S99W    Galaxy 16
-S99.2W  Spaceway 2 & DirecTV 11
+S99.2W  Galaxy 16
 S101W   DirecTV 4S/8 & SES 1
 S103W   AMC 1
 S105W   AMC 15/18
-S107.3W Anik F1/F1R
+S107.3W Anik F1R/G1
 S110W   DirecTV 5 & Echostar 10/11
 S111.1W Anik F2
 S113W   SatMex 6
-S116.8W SatMex 5
+S114.9W SatMex 5
+S116.8W SatMex 8
 S118.8W Anik F3
 S119W   Echostar 14 & DirecTV 7S
 S121W   Echostar 9/Galaxy 23
@@ -174,11 +179,13 @@
 S127W   Galaxy 13/Horizons 1
 S129W   Ciel 2
 S131W   AMC 11
-S133W   Galaxy 13/15
+S133W   Galaxy 15
 S135W   AMC 10
 S137W   AMC 7
 S139W   AMC 8
 
+S360E   Any satellite
+
 # Cable
 
 C       DVB-C
diff -ruN vdr-2.0.0/sources.h vdr-2.2.0/sources.h
--- vdr-2.0.0/sources.h	2012-06-17 13:19:23.000000000 +0200
+++ vdr-2.2.0/sources.h	2014-03-09 12:59:49.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: sources.h 2.4 2012/06/17 11:19:23 kls Exp $
+ * $Id: sources.h 3.3 2014/03/09 11:59:49 kls Exp $
  */
 
 #ifndef __SOURCES_H
@@ -22,6 +22,7 @@
     stTerr  = ('T' << 24),
     st_Mask = 0xFF000000,
     st_Pos  = 0x0000FFFF,
+    st_Any  = 0x00000E10, // 3600 - special value indicating "any position"
     };
 private:
   int code;
@@ -31,8 +32,22 @@
   cSource(char Source, const char *Description);
   ~cSource();
   int Code(void) const { return code; }
+  int Position(void) { return Position(code); }
+      ///< Returns the orbital position of the satellite in case this is a DVB-S
+      ///< source (zero otherwise). The returned value is in the range -1800...+1800,
+      ///< except for the special value 3600, which indicates "any position". This is
+      ///< used with positioners that can move the dish to any requested satellite
+      ///< within their range.
+      ///< A positive sign indicates a position east of Greenwich, while western
+      ///< positions have a negative sign. The absolute value is in "degrees * 10",
+      ///< which allows for a resolution of 1/10 of a degree.
   const char *Description(void) const { return description; }
   bool Parse(const char *s);
+  static bool Matches(int Code1, int Code2);
+      ///< Returns true if Code2 matches Code1. This is simply a check whether the
+      ///< two codes are equal, except for the special case that Code1 is stSat|st_Any,
+      ///< in which case it matches any Code2 that is stSat.
+  static int Position(int Code);
   static char ToChar(int Code) { return (Code & st_Mask) >> 24; }
   static cString ToString(int Code);
   static int FromString(const char *s);
@@ -47,6 +62,7 @@
 class cSources : public cConfig<cSource> {
 public:
   cSource *Get(int Code);
+  bool ContainsSourceType(char SourceType);
   };
 
 extern cSources Sources;
diff -ruN vdr-2.0.0/spu.c vdr-2.2.0/spu.c
--- vdr-2.0.0/spu.c	2008-02-10 15:06:48.000000000 +0100
+++ vdr-2.2.0/spu.c	2008-02-10 15:06:48.000000000 +0100
@@ -6,7 +6,7 @@
  * This code is distributed under the terms and conditions of the
  * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
  *
- * $Id: spu.c 2.0 2008/02/10 14:06:48 kls Exp $
+ * $Id: spu.c 3.0 2008/02/10 14:06:48 kls Exp $
  */
 
 #include "spu.h"
diff -ruN vdr-2.0.0/spu.h vdr-2.2.0/spu.h
--- vdr-2.0.0/spu.h	2006-04-17 14:48:55.000000000 +0200
+++ vdr-2.2.0/spu.h	2006-04-17 14:48:55.000000000 +0200
@@ -6,7 +6,7 @@
  * This code is distributed under the terms and conditions of the
  * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
  *
- * $Id: spu.h 2.0 2006/04/17 12:48:55 kls Exp $
+ * $Id: spu.h 3.0 2006/04/17 12:48:55 kls Exp $
  */
 
 #ifndef __SPU_VDR_H
diff -ruN vdr-2.0.0/status.c vdr-2.2.0/status.c
--- vdr-2.0.0/status.c	2012-03-07 15:17:24.000000000 +0100
+++ vdr-2.2.0/status.c	2014-01-25 11:47:39.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: status.c 2.1 2012/03/07 14:17:24 kls Exp $
+ * $Id: status.c 3.1 2014/01/25 10:47:39 kls Exp $
  */
 
 #include "status.h"
@@ -23,6 +23,12 @@
   statusMonitors.Del(this, false);
 }
 
+void cStatus::MsgChannelChange(const cChannel *Channel)
+{
+  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+      sm->ChannelChange(Channel);
+}
+
 void cStatus::MsgTimerChange(const cTimer *Timer, eTimerChange Change)
 {
   for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
diff -ruN vdr-2.0.0/status.h vdr-2.2.0/status.h
--- vdr-2.0.0/status.h	2012-03-07 15:16:57.000000000 +0100
+++ vdr-2.2.0/status.h	2014-01-25 11:47:39.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: status.h 2.1 2012/03/07 14:16:57 kls Exp $
+ * $Id: status.h 3.1 2014/01/25 10:47:39 kls Exp $
  */
 
 #ifndef __STATUS_H
@@ -24,6 +24,9 @@
   static cList<cStatus> statusMonitors;
 protected:
   // These functions can be implemented by derived classes to receive status information:
+  virtual void ChannelChange(const cChannel *Channel) {}
+               // Indicates a change in the parameters of the given Channel that may
+               // require a retune.
   virtual void TimerChange(const cTimer *Timer, eTimerChange Change) {}
                // Indicates a change in the timer settings.
                // If Change is tcAdd or tcDel, Timer points to the timer that has
@@ -85,6 +88,7 @@
   cStatus(void);
   virtual ~cStatus();
   // These functions are called whenever the related status information changes:
+  static void MsgChannelChange(const cChannel *Channel);
   static void MsgTimerChange(const cTimer *Timer, eTimerChange Change);
   static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
   static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On);
diff -ruN vdr-2.0.0/summary2info vdr-2.2.0/summary2info
--- vdr-2.0.0/summary2info	2011-12-04 15:17:35.000000000 +0100
+++ vdr-2.2.0/summary2info	2011-12-04 15:17:35.000000000 +0100
@@ -10,7 +10,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: summary2info 2.1 2011/12/04 14:17:35 kls Exp $
+# $Id: summary2info 3.0 2011/12/04 14:17:35 kls Exp $
 
 $VideoDir = $ARGV[0] || die "please provide the name of the video directory\n";
 
diff -ruN vdr-2.0.0/svdrp.c vdr-2.2.0/svdrp.c
--- vdr-2.0.0/svdrp.c	2013-02-17 14:18:01.000000000 +0100
+++ vdr-2.2.0/svdrp.c	2015-01-12 12:16:27.000000000 +0100
@@ -10,7 +10,7 @@
  * and interact with the Video Disk Recorder - or write a full featured
  * graphical interface that sits on top of an SVDRP connection.
  *
- * $Id: svdrp.c 2.24 2013/02/17 13:18:01 kls Exp $
+ * $Id: svdrp.c 3.6 2015/01/12 11:16:27 kls Exp $
  */
 
 #include "svdrp.h"
@@ -28,7 +28,6 @@
 #include <unistd.h>
 #include "channels.h"
 #include "config.h"
-#include "cutter.h"
 #include "device.h"
 #include "eitscan.h"
 #include "keys.h"
@@ -258,6 +257,11 @@
   "    used to easily activate or deactivate a timer.",
   "MOVC <number> <to>\n"
   "    Move a channel to a new position.",
+  "MOVR <number> <new name>\n"
+  "    Move the recording with the given number. Before a recording can be\n"
+  "    moved, an LSTR command must have been executed in order to retrieve\n"
+  "    the recording numbers. The numbers don't change during subsequent MOVR\n"
+  "    commands.\n",
   "NEWC <settings>\n"
   "    Create a new channel. Settings must be in the same format as returned\n"
   "    by the LSTC command.",
@@ -659,27 +663,38 @@
      Reply(501, "Missing channel number");
 }
 
+static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecording *Recording)
+{
+  cRecordControl *rc;
+  if ((Reason & ruTimer) != 0 && (rc = cRecordControls::GetRecordControl(Recording->FileName())) != NULL)
+     return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Index() + 1);
+  else if ((Reason & ruReplay) != 0)
+     return cString::sprintf("Recording \"%s\" is being replayed", RecordingId);
+  else if ((Reason & ruCut) != 0)
+     return cString::sprintf("Recording \"%s\" is being edited", RecordingId);
+  else if ((Reason & (ruMove | ruCopy)) != 0)
+     return cString::sprintf("Recording \"%s\" is being copied/moved", RecordingId);
+  else if (Reason)
+     return cString::sprintf("Recording \"%s\" is in use", RecordingId);
+  return NULL;
+}
+
 void cSVDRP::CmdDELR(const char *Option)
 {
   if (*Option) {
      if (isnumber(Option)) {
         cRecording *recording = recordings.Get(strtol(Option, NULL, 10) - 1);
         if (recording) {
-           cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
-           if (!rc) {
-              if (!cCutter::Active(recording->FileName())) {
-                 if (recording->Delete()) {
-                    Reply(250, "Recording \"%s\" deleted", Option);
-                    Recordings.DelByName(recording->FileName());
-                    }
-                 else
-                    Reply(554, "Error while deleting recording!");
+           if (int RecordingInUse = recording->IsInUse())
+              Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, recording));
+           else {
+              if (recording->Delete()) {
+                 Reply(250, "Recording \"%s\" deleted", Option);
+                 Recordings.DelByName(recording->FileName());
                  }
               else
-                 Reply(550, "Recording \"%s\" is being edited", Option);
+                 Reply(554, "Error while deleting recording!");
               }
-           else
-              Reply(550, "Recording \"%s\" is in use by timer %d", Option, rc->Timer()->Index() + 1);
            }
         else
            Reply(550, "Recording \"%s\" not found%s", Option, recordings.Count() ? "" : " (use LSTR before deleting)");
@@ -728,14 +743,10 @@
         if (recording) {
            cMarks Marks;
            if (Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && Marks.Count()) {
-              if (!cCutter::Active()) {
-                 if (cCutter::Start(recording->FileName()))
-                    Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title());
-                 else
-                    Reply(554, "Can't start editing process");
-                 }
+              if (RecordingsHandler.Add(ruCut, recording->FileName()))
+                 Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title());
               else
-                 Reply(554, "Editing process already active");
+                 Reply(554, "Can't start editing process");
               }
            else
               Reply(554, "No editing marks defined");
@@ -1320,6 +1331,46 @@
      Reply(501, "Missing channel number");
 }
 
+void cSVDRP::CmdMOVR(const char *Option)
+{
+  if (*Option) {
+     char *opt = strdup(Option);
+     char *num = skipspace(opt);
+     char *option = num;
+     while (*option && !isspace(*option))
+           option++;
+     char c = *option;
+     *option = 0;
+     if (isnumber(num)) {
+        cRecording *recording = recordings.Get(strtol(num, NULL, 10) - 1);
+        if (recording) {
+           if (int RecordingInUse = recording->IsInUse())
+              Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, recording));
+           else {
+              if (c)
+                 option = skipspace(++option);
+              if (*option) {
+                 cString oldName = recording->Name();
+                 if ((recording = Recordings.GetByName(recording->FileName())) != NULL && recording->ChangeName(option))
+                    Reply(250, "Recording \"%s\" moved to \"%s\"", *oldName, recording->Name());
+                 else
+                    Reply(554, "Error while moving recording \"%s\" to \"%s\"!", *oldName, option);
+                 }
+              else
+                 Reply(501, "Missing new recording name");
+              }
+           }
+        else
+           Reply(550, "Recording \"%s\" not found%s", num, recordings.Count() ? "" : " (use LSTR before moving)");
+        }
+     else
+        Reply(501, "Error in recording number \"%s\"", num);
+     free(opt);
+     }
+  else
+     Reply(501, "Missing recording number");
+}
+
 void cSVDRP::CmdNEWC(const char *Option)
 {
   if (*Option) {
@@ -1550,7 +1601,7 @@
   if (*Option) {
      if (strcasecmp(Option, "DISK") == 0) {
         int FreeMB, UsedMB;
-        int Percent = VideoDiskSpace(&FreeMB, &UsedMB);
+        int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB, &UsedMB);
         Reply(250, "%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
         }
      else
@@ -1659,6 +1710,7 @@
   else if (CMD("MODC"))  CmdMODC(s);
   else if (CMD("MODT"))  CmdMODT(s);
   else if (CMD("MOVC"))  CmdMOVC(s);
+  else if (CMD("MOVR"))  CmdMOVR(s);
   else if (CMD("NEWC"))  CmdNEWC(s);
   else if (CMD("NEWT"))  CmdNEWT(s);
   else if (CMD("NEXT"))  CmdNEXT(s);
diff -ruN vdr-2.0.0/svdrp.h vdr-2.2.0/svdrp.h
--- vdr-2.0.0/svdrp.h	2012-04-26 12:30:06.000000000 +0200
+++ vdr-2.2.0/svdrp.h	2013-10-21 09:42:03.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: svdrp.h 2.3 2012/04/26 10:30:06 kls Exp $
+ * $Id: svdrp.h 3.2 2013/10/21 07:42:03 kls Exp $
  */
 
 #ifndef __SVDRP_H
@@ -71,6 +71,7 @@
   void CmdMODC(const char *Option);
   void CmdMODT(const char *Option);
   void CmdMOVC(const char *Option);
+  void CmdMOVR(const char *Option);
   void CmdNEWC(const char *Option);
   void CmdNEWT(const char *Option);
   void CmdNEXT(const char *Option);
diff -ruN vdr-2.0.0/svdrpsend.1 vdr-2.2.0/svdrpsend.1
--- vdr-2.0.0/svdrpsend.1	2013-03-11 14:16:39.000000000 +0100
+++ vdr-2.2.0/svdrpsend.1	2015-02-17 14:44:09.000000000 +0100
@@ -1,4 +1,4 @@
-.TH "svdrpsend" "1" "31 Mar 2013" "2.0" "Video Disk Recorder"
+.TH "svdrpsend" "1" "19 Feb 2015" "2.2" "Video Disk Recorder"
 .SH NAME
 .LP
 svdrpsend \- sends commands to VDR
diff -ruN vdr-2.0.0/themes.c vdr-2.2.0/themes.c
--- vdr-2.0.0/themes.c	2012-02-17 14:57:32.000000000 +0100
+++ vdr-2.2.0/themes.c	2012-02-17 14:57:32.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: themes.c 2.2 2012/02/17 13:57:32 kls Exp $
+ * $Id: themes.c 3.0 2012/02/17 13:57:32 kls Exp $
  */
 
 #include "themes.h"
diff -ruN vdr-2.0.0/themes.h vdr-2.2.0/themes.h
--- vdr-2.0.0/themes.h	2012-10-07 13:11:43.000000000 +0200
+++ vdr-2.2.0/themes.h	2012-10-07 13:11:43.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: themes.h 2.1 2012/10/07 11:11:43 kls Exp $
+ * $Id: themes.h 3.0 2012/10/07 11:11:43 kls Exp $
  */
 
 #ifndef __THEMES_H
diff -ruN vdr-2.0.0/thread.c vdr-2.2.0/thread.c
--- vdr-2.0.0/thread.c	2013-02-22 15:52:49.000000000 +0100
+++ vdr-2.2.0/thread.c	2013-12-29 16:26:33.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: thread.c 2.7 2013/02/22 14:52:49 kls Exp $
+ * $Id: thread.c 3.2 2013/12/29 15:26:33 kls Exp $
  */
 
 #include "thread.h"
@@ -229,7 +229,7 @@
 
 void cThread::SetIOPriority(int Priority)
 {
-  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
+  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0) // idle class
      LOG_ERROR;
 }
 
@@ -494,7 +494,6 @@
   else { // child process
      int iofd = STDOUT_FILENO;
      if (strcmp(Mode, "w") == 0) {
-        mode = "r";
         iopipe = 1;
         iofd = STDIN_FILENO;
         }
diff -ruN vdr-2.0.0/thread.h vdr-2.2.0/thread.h
--- vdr-2.0.0/thread.h	2013-02-16 16:20:44.000000000 +0100
+++ vdr-2.2.0/thread.h	2015-01-14 12:39:55.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: thread.h 2.4 2013/02/16 15:20:44 kls Exp $
+ * $Id: thread.h 3.2 2015/01/14 11:39:55 kls Exp $
  */
 
 #ifndef __THREAD_H
@@ -110,12 +110,16 @@
   cThread(const char *Description = NULL, bool LowPriority = false);
        ///< Creates a new thread.
        ///< If Description is present, a log file entry will be made when
-       ///< the thread starts and stops. The Start() function must be called
-       ///< to actually start the thread.
+       ///< the thread starts and stops (see SetDescription()).
+       ///< The Start() function must be called to actually start the thread.
        ///< LowPriority can be set to true to make this thread run at a lower
        ///< priority.
   virtual ~cThread();
   void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
+       ///< Sets the description of this thread, which will be used when logging
+       ///< starting or stopping of the thread. Make sure any important information
+       ///< is within the first 15 characters of Description, because only these
+       ///< may be displayed in thread listings (like 'htop', for instance).
   bool Start(void);
        ///< Actually starts the thread.
        ///< If the thread is already running, nothing happens.
@@ -182,7 +186,6 @@
        ///< Returns true if any I/O throttling object is currently active.
   };
 
-
 // cPipe implements a pipe that closes all unnecessary file descriptors in
 // the child process.
 
diff -ruN vdr-2.0.0/timers.c vdr-2.2.0/timers.c
--- vdr-2.0.0/timers.c	2013-03-29 16:37:16.000000000 +0100
+++ vdr-2.2.0/timers.c	2013-12-28 12:33:08.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.c 2.18 2013/03/29 15:37:16 kls Exp $
+ * $Id: timers.c 3.1 2013/12/28 11:33:08 kls Exp $
  */
 
 #include "timers.h"
@@ -296,7 +296,7 @@
   char *filebuffer = NULL;
   free(aux);
   aux = NULL;
-  //XXX Apparently sscanf() doesn't work correctly if the last %a argument
+  //XXX Apparently sscanf() doesn't work correctly if the last %m argument
   //XXX results in an empty string (this first occurred when the EIT gathering
   //XXX was put into a separate thread - don't know why this happens...
   //XXX As a cure we copy the original string and add a blank.
@@ -312,7 +312,7 @@
      s = s2;
      }
   bool result = false;
-  if (8 <= sscanf(s, "%u :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) {
+  if (8 <= sscanf(s, "%u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^:\n]:%m[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) {
      ClrFlags(tfRecording);
      if (aux && !*skipspace(aux)) {
         free(aux);
diff -ruN vdr-2.0.0/timers.h vdr-2.2.0/timers.h
--- vdr-2.0.0/timers.h	2013-03-11 11:35:53.000000000 +0100
+++ vdr-2.2.0/timers.h	2013-03-11 11:35:53.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.h 2.7 2013/03/11 10:35:53 kls Exp $
+ * $Id: timers.h 3.0 2013/03/11 10:35:53 kls Exp $
  */
 
 #ifndef __TIMERS_H
diff -ruN vdr-2.0.0/tools.c vdr-2.2.0/tools.c
--- vdr-2.0.0/tools.c	2012-12-08 12:16:30.000000000 +0100
+++ vdr-2.2.0/tools.c	2015-02-07 16:09:17.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: tools.c 2.29 2012/12/08 11:16:30 kls Exp $
+ * $Id: tools.c 3.4 2015/02/07 15:09:17 kls Exp $
  */
 
 #include "tools.h"
@@ -173,6 +173,31 @@
   return s;
 }
 
+const char *strchrn(const char *s, char c, size_t n)
+{
+  if (n == 0)
+     return s;
+  if (s) {
+     for ( ; *s; s++) {
+         if (*s == c && --n == 0)
+            return s;
+         }
+     }
+  return NULL;
+}
+
+int strcountchr(const char *s, char c)
+{
+  int n = 0;
+  if (s && c) {
+     for ( ; *s; s++) {
+         if (*s == c)
+            n++;
+         }
+     }
+  return n;
+}
+
 char *stripspace(char *s)
 {
   if (s && *s) {
@@ -202,6 +227,30 @@
   return s;
 }
 
+char *compactchars(char *s, char c)
+{
+  if (s && *s && c) {
+     char *t = s;
+     char *p = s;
+     int n = 0;
+     while (*p) {
+           if (*p != c) {
+              *t++ = *p;
+              n = 0;
+              }
+           else if (t != s && n == 0) {
+              *t++ = *p;
+              n++;
+              }
+           p++;
+           }
+     if (n)
+        t--; // the last character was c
+     *t = 0;
+     }
+  return s;
+}
+
 cString strescape(const char *s, const char *chars)
 {
   char *buffer;
@@ -355,7 +404,7 @@
      }
   else
      LOG_ERROR_STR(File1);
-  return false;
+  return true; // we only return false if both files actually exist and are in different file systems!
 }
 
 int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
@@ -560,7 +609,7 @@
            }
      return size;
      }
-  else
+  else if (errno != ENOENT)
      LOG_ERROR_STR(DirName);
   return -1;
 }
@@ -691,12 +740,12 @@
   begin = Now() + Ms;
 }
 
-bool cTimeMs::TimedOut(void)
+bool cTimeMs::TimedOut(void) const
 {
   return Now() >= begin;
 }
 
-uint64_t cTimeMs::Elapsed(void)
+uint64_t cTimeMs::Elapsed(void) const
 {
   return Now() - begin;
 }
@@ -970,6 +1019,20 @@
   s = TakePointer ? (char *)S : S ? strdup(S) : NULL;
 }
 
+cString::cString(const char *S, const char *To)
+{
+  if (!S)
+     s = NULL;
+  else if (!To)
+     s = strdup(S);
+  else {
+     int l = To - S;
+     s = MALLOC(char, l + 1);
+     strncpy(s, S, l);
+     s[l] = 0;
+     }
+}
+
 cString::cString(const cString &String)
 {
   s = String.s ? strdup(String.s) : NULL;
@@ -1008,6 +1071,12 @@
   return *this;
 }
 
+cString &cString::CompactChars(char c)
+{
+  compactchars(s, c);
+  return *this;
+}
+
 cString cString::sprintf(const char *fmt, ...)
 {
   va_list ap;
diff -ruN vdr-2.0.0/tools.h vdr-2.2.0/tools.h
--- vdr-2.0.0/tools.h	2013-02-17 14:18:06.000000000 +0100
+++ vdr-2.2.0/tools.h	2015-02-07 16:12:26.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: tools.h 2.24 2013/02/17 13:18:06 kls Exp $
+ * $Id: tools.h 3.7 2015/02/07 15:12:26 kls Exp $
  */
 
 #ifndef __TOOLS_H
@@ -31,9 +31,9 @@
 
 extern int SysLogLevel;
 
-#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR, a) : void() )
-#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_ERR, a) : void() )
-#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_ERR, a) : void() )
+#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR,   a) : void() )
+#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_INFO,  a) : void() )
+#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_DEBUG, a) : void() )
 
 #define LOG_ERROR         esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
 #define LOG_ERROR_STR(s)  esyslog("ERROR (%s,%d): %s: %m", __FILE__, __LINE__, s)
@@ -64,6 +64,8 @@
 #define BCDCHARTOINT(x) (10 * ((x & 0xF0) >> 4) + (x & 0xF))
 int BCD2INT(int x);
 
+#define IsBitSet(v, b) ((v) & (1 << (b))) // checks if the bit at index b is set in v, where the least significant bit has index 0
+
 // Unfortunately there are no platform independent macros for unaligned
 // access, so we do it this way:
 
@@ -168,6 +170,7 @@
   char *s;
 public:
   cString(const char *S = NULL, bool TakePointer = false);
+  cString(const char *S, const char *To); ///< Copies S up to To (exclusive). To must be a valid pointer into S. If To is NULL, everything is copied.
   cString(const cString &String);
   virtual ~cString();
   operator const void * () const { return s; } // to catch cases where operator*() should be used
@@ -176,6 +179,7 @@
   cString &operator=(const cString &String);
   cString &operator=(const char *String);
   cString &Truncate(int Index); ///< Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
+  cString &CompactChars(char c); ///< Compact any sequence of characters 'c' to a single character, and strip all of them from the beginning and end of this string.
   static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
   static cString vsprintf(const char *fmt, va_list &ap);
   };
@@ -191,6 +195,8 @@
 char *strn0cpy(char *dest, const char *src, size_t n);
 char *strreplace(char *s, char c1, char c2);
 char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary!
+const char *strchrn(const char *s, char c, size_t n); ///< returns a pointer to the n'th occurrence (counting from 1) of c in s, or NULL if no such character was found. If n is 0, s is returned.
+int strcountchr(const char *s, char c); ///< returns the number of occurrences of 'c' in 's'.
 inline char *skipspace(const char *s)
 {
   if ((uchar)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible
@@ -201,6 +207,7 @@
 }
 char *stripspace(char *s);
 char *compactspace(char *s);
+char *compactchars(char *s, char c); ///< removes all occurrences of 'c' from the beginning an end of 's' and replaces sequences of multiple 'c's with a single 'c'.
 cString strescape(const char *s, const char *chars);
 bool startswith(const char *s, const char *p);
 bool endswith(const char *s, const char *p);
@@ -227,6 +234,9 @@
 cString itoa(int n);
 cString AddDirectory(const char *DirName, const char *FileName);
 bool EntriesOnSameFileSystem(const char *File1, const char *File2);
+    ///< Checks whether the given files are on the same file system. If either of the
+    ///< files doesn't exist, this function returns *true* to avoid any actions that might be
+    ///< triggered if files are on different file system.
 int FreeDiskSpaceMB(const char *Directory, int *UsedMB = NULL);
 bool DirectoryOk(const char *DirName, bool LogErrors = false);
 bool MakeDirs(const char *FileName, bool IsDirectory = false);
@@ -330,8 +340,8 @@
       ///< time.
   static uint64_t Now(void);
   void Set(int Ms = 0);
-  bool TimedOut(void);
-  uint64_t Elapsed(void);
+  bool TimedOut(void) const;
+  uint64_t Elapsed(void) const;
   };
 
 class cReadLine {
@@ -530,6 +540,14 @@
   {
     return At(Index);
   }
+  int IndexOf(const T &Data) // returns the index of Data, or -1 if not found
+  {
+    for (int i = 0; i < size; i++) {
+        if (data[i] == Data)
+           return i;
+        }
+    return -1;
+  }
   int Size(void) const { return size; }
   virtual void Insert(T Data, int Before = 0)
   {
@@ -542,18 +560,45 @@
     else
        Append(Data);
   }
+  bool InsertUnique(T Data, int Before = 0)
+  {
+    if (IndexOf(Data) < 0) {
+       Insert(Data, Before);
+       return true;
+       }
+    return false;
+  }
   virtual void Append(T Data)
   {
     if (size >= allocated)
        Realloc(allocated * 3 / 2); // increase size by 50%
     data[size++] = Data;
   }
+  bool AppendUnique(T Data)
+  {
+    if (IndexOf(Data) < 0) {
+       Append(Data);
+       return true;
+       }
+    return false;
+  }
   virtual void Remove(int Index)
   {
+    if (Index < 0)
+       return; // prevents out-of-bounds access
     if (Index < size - 1)
        memmove(&data[Index], &data[Index + 1], (size - Index) * sizeof(T));
     size--;
   }
+  bool RemoveElement(const T &Data)
+  {
+    int i = IndexOf(Data);
+    if (i >= 0) {
+       Remove(i);
+       return true;
+       }
+    return false;
+  }
   virtual void Clear(void)
   {
     for (int i = 0; i < size; i++)
diff -ruN vdr-2.0.0/transfer.c vdr-2.2.0/transfer.c
--- vdr-2.0.0/transfer.c	2013-03-01 10:50:15.000000000 +0100
+++ vdr-2.2.0/transfer.c	2013-08-22 14:33:02.000000000 +0200
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: transfer.c 2.8 2013/03/01 09:50:15 kls Exp $
+ * $Id: transfer.c 3.1 2013/08/22 12:33:02 kls Exp $
  */
 
 #include "transfer.h"
@@ -35,7 +35,7 @@
      cPlayer::Detach();
 }
 
-#define MAXRETRIES     5 // max. number of retries for a single TS packet
+#define MAXRETRIES    20 // max. number of retries for a single TS packet
 #define RETRYWAIT      5 // time (in ms) between two retries
 
 void cTransfer::Receive(uchar *Data, int Length)
diff -ruN vdr-2.0.0/transfer.h vdr-2.2.0/transfer.h
--- vdr-2.0.0/transfer.h	2013-03-01 10:49:46.000000000 +0100
+++ vdr-2.2.0/transfer.h	2013-03-01 10:49:46.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: transfer.h 2.4 2013/03/01 09:49:46 kls Exp $
+ * $Id: transfer.h 3.0 2013/03/01 09:49:46 kls Exp $
  */
 
 #ifndef __TRANSFER_H
diff -ruN vdr-2.0.0/vdr.1 vdr-2.2.0/vdr.1
--- vdr-2.0.0/vdr.1	2013-03-15 11:44:54.000000000 +0100
+++ vdr-2.2.0/vdr.1	2015-02-17 14:43:24.000000000 +0100
@@ -8,9 +8,9 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.1 2.17 2013/03/15 10:44:54 kls Exp $
+.\" $Id: vdr.1 3.6 2015/02/17 13:43:24 kls Exp $
 .\"
-.TH vdr 1 "31 Mar 2013" "2.0" "Video Disk Recorder"
+.TH vdr 1 "19 Feb 2015" "2.2" "Video Disk Recorder"
 .SH NAME
 vdr \- the Video Disk Recorder
 .SH SYNOPSIS
@@ -49,6 +49,11 @@
 Save cache files in \fIdir\fR
 (default is to save them in the video directory).
 .TP
+.BI \-\-chartab= character_table
+Set the character table to use for strings in the DVB data stream that don't begin
+with a character table indicator, but don't use the standard default character table
+(for instance ISO-8859-9).
+.TP
 .BI \-c\  dir ,\ \-\-config= dir
 Read config files from directory \fIdir\fR
 (default is to read them from the video directory).
@@ -178,6 +183,10 @@
 Read resource files from \fIdir\fR
 (default is to read them from the config directory).
 .TP
+.BI \-\-showargs[= dir ]
+Read command line arguments from \fIdir\fR (default is \fI/etc/vdr/conf.d\fR),
+display them to the console and exit.
+.TP
 .BI \-s\  cmd ,\ \-\-shutdown= cmd
 Call \fIcmd\fR to shutdown the computer. See the file \fIINSTALL\fR for more
 information.
@@ -197,11 +206,23 @@
 vdr can switch to a lesser privileged user id during normal
 operation.
 .TP
+.BI \-\-updindex= rec
+Update the index file for the given recording.
+\fIrec\fR must be the full path name of an existing recording.
+The recording must be in TS format.
+If the recording already has an index file, it will be checked
+whether it is complete, and will be updated if it isn't. If
+there is no index file yet, a new one will be generated.
+The program will return immediately after updating the index.
+Note that using this option while another instance of VDR is
+currently replaying the given recording, or if the recording
+has not been finished yet, may lead to unexpected results.
+.TP
 .BI \-\-userdump
 Allow coredumps if -u is given (only for debugging).
 .TP
 .BI \-\-vfat
-For backwards compatibility (same as \-\-dirnames= 250,40,1.
+For backwards compatibility (same as \-\-dirnames= 250,40,1).
 .TP
 .BI \-v\  dir ,\ \-\-video= dir
 Use \fIdir\fR as video directory.
@@ -213,6 +234,10 @@
 .BI \-w\  sec ,\ \-\-watchdog= sec
 Activate the watchdog timer with a timeout of \fIsec\fR seconds.
 A value of \fB0\fR (default) disables the watchdog.
+.P
+If started without any options, vdr tries to read command line options
+from files named '*.conf' in the directory /etc/vdr/conf.d. Files are
+read in alphabetical order. See vdr(5) for details.
 .SH SIGNALS
 .TP
 .B SIGINT, SIGTERM
@@ -279,7 +304,7 @@
 If this file is present in the video directory, its last modification time will
 be used to trigger an update of the list of recordings in the "Recordings" menu.
 .SH SEE ALSO
-.BR vdr (5), svdrp(1)
+.BR vdr (5), svdrpsend (1)
 .SH AUTHOR
 Written by Klaus Schmidinger, with contributions from many others.
 See the file \fICONTRIBUTORS\fR in the \fBvdr\fR source distribution.
diff -ruN vdr-2.0.0/vdr.5 vdr-2.2.0/vdr.5
--- vdr-2.0.0/vdr.5	2013-03-29 11:25:56.000000000 +0100
+++ vdr-2.2.0/vdr.5	2015-02-17 14:43:53.000000000 +0100
@@ -8,9 +8,9 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.5 2.36 2013/03/29 10:25:56 kls Exp $
+.\" $Id: vdr.5 3.4 2015/02/17 13:43:53 kls Exp $
 .\"
-.TH vdr 5 "31 Mar 2013" "2.0" "Video Disk Recorder Files"
+.TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files"
 .SH NAME
 vdr_files \- the Video Disk Recorder Files
 .SH DESCRIPTION
@@ -99,12 +99,15 @@
 \fBI\fR@Inversion (0, 1)
 \fBL\fR@Left circular polarization
 \fBM\fR@Modulation (2, 5, 6, 7, 10, 11, 12, 16, 32, 64, 128, 256, 999)
+\fBN\fR@pilot mode (0, 1, 999)
 \fBO\fR@rollOff (0, 20, 25, 35)
 \fBP\fR@stream id (0-255)
+\fBQ\fR@t2 system id (0-65535)
 \fBR\fR@Right circular polarization
 \fBS\fR@delivery System (0, 1)
 \fBT\fR@Transmission mode (1, 2, 4, 8, 16, 32)
 \fBV\fR@Vertical polarization
+\fBX\fR@siso/miso mode (0, 1)
 \fBY\fR@hierarchY (0, 1, 2, 4)
 .TE
 
@@ -139,6 +142,8 @@
 \fB256\fR@QAM256 (DVB-C, DVB-T2)
 .TE
 
+\fBPilot mode:\fR The pilot mode (0 = "off", 1 = "on", 999 = "auto") for DVB-S2 multiplex (DVB-S2 only).
+
 \fBRolloff:\fR The Nyquist filter rolloff factor for DVB-S (\fB35\fR) and DVB-S2 (\fB35\fR, 25, 20),
 35 = 0.35, 25 = 0.25, 20 = 0.20, DVB-S/DVB-S2 default value is 0.35
 
@@ -146,8 +151,12 @@
 Physical Layer Pipe (PLP) id (\fB0\fR-255) for DVB-T2 multiplex (DVB-S2/DVB-T2 only,
 with devices that support "multi streaming").
 
+\fBT2 System id:\fR Unique identifier (\fB0\fR-65535) of T2 system within the DVB network (DVB-T2).
+
 \fBTransmission mode:\fR Number of DVB-T OFDM carriers, 32 = 32k, 16 = 16k, 8 = 8k, 4 = 4k, 2 = 2k, 1 = 1k. If in doubt, try 8k.
 
+\fBSISO/MISO mode:\fR Specifies the Single-Input/Multiple-Input Single-Output mode (\fB0\fR = SISO, 1 = MISO) (DVB-T2).
+
 \fBHierarchy:\fR If set to 1, this transponder uses two streams, high priority and low priority.
 If in doubt, try 0 (off). (DVB-T/DVB-T2 only).
 
@@ -483,6 +492,7 @@
 \fBV\fR@voltage high (18V)
 \fBA\fR@mini A
 \fBB\fR@mini B
+\fBPn\fR@use positioner to move dish to satellite position n (or to the satellite's orbital position, if no position number is given)
 \fBSn\fR@Satellite channel routing code sequence for bank n follows
 \fBWnn\fR@wait nn milliseconds (nn may be any positive integer number)
 \fB[xx ...]\fR@hex code sequence (max. 6)
@@ -889,6 +899,33 @@
 just 16 bit wide. The internal representation in VDR allows for 32 bit to
 be used, so that external tools can generate EPG data that is guaranteed
 not to collide with the ids of existing data.
+.SS COMMANDLINE OPTIONS
+If started without any options, vdr tries to read any files in the directory
+/etc/vdr/conf.d with names that do not begin with a '.' and that end with '.conf'.
+These files are read in alphabetical order. The format of these files is
+
+# comment
+.br
+[name]
+.br
+-a
+.br
+-b 123
+.br
+--long
+.br
+--longarg=123
+.br
+
+Any lines that begin with '#' as the first non-whitespace character are considered
+comments and are ignored.
+A command line option file consists of one or more sections, indicated by '[name]',
+where 'name' is either the fixed word 'vdr' (if this section contains options for
+the main VDR program) or the name of the plugin this section applies to.
+Each option must be written on a separate line, including the leading '-' (for
+a short option) or '--' (for a long option). If the option has additional arguments,
+they have to be written on the same line as the option itself, separated from the
+option with a blank (short option) or equal sign (long option).
 .SH SEE ALSO
 .BR vdr (1)
 .SH AUTHOR
diff -ruN vdr-2.0.0/vdr.c vdr-2.2.0/vdr.c
--- vdr-2.0.0/vdr.c	2013-03-15 11:44:54.000000000 +0100
+++ vdr-2.2.0/vdr.c	2015-02-10 15:13:12.000000000 +0100
@@ -22,7 +22,7 @@
  *
  * The project's page is at http://www.tvdr.de
  *
- * $Id: vdr.c 2.57 2013/03/15 10:44:54 kls Exp $
+ * $Id: vdr.c 3.16 2015/02/10 14:13:12 kls Exp $
  */
 
 #include <getopt.h>
@@ -34,8 +34,12 @@
 #include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
+#ifdef SDNOTIFY
+#include <systemd/sd-daemon.h>
+#endif
 #include <termios.h>
 #include <unistd.h>
+#include "args.h"
 #include "audio.h"
 #include "channels.h"
 #include "config.h"
@@ -60,6 +64,7 @@
 #include "skinsttng.h"
 #include "sourceparams.h"
 #include "sources.h"
+#include "status.h"
 #include "themes.h"
 #include "timers.h"
 #include "tools.h"
@@ -186,6 +191,7 @@
 #define DEFAULTWATCHDOG     0 // seconds
 #define DEFAULTVIDEODIR VIDEODIR
 #define DEFAULTCONFDIR dd(CONFDIR, VideoDirectory)
+#define DEFAULTARGSDIR dd(ARGSDIR, "/etc/vdr/conf.d")
 #define DEFAULTCACHEDIR dd(CACHEDIR, VideoDirectory)
 #define DEFAULTRESDIR dd(RESDIR, ConfigDirectory)
 #define DEFAULTPLUGINDIR PLUGINDIR
@@ -210,6 +216,12 @@
   bool MuteAudio = false;
   int WatchdogTimeout = DEFAULTWATCHDOG;
   const char *Terminal = NULL;
+  const char *OverrideCharacterTable = NULL;
+#define DEPRECATED_VDR_CHARSET_OVERRIDE
+#ifdef DEPRECATED_VDR_CHARSET_OVERRIDE
+  OverrideCharacterTable = getenv("VDR_CHARSET_OVERRIDE");
+  const char *DeprecatedVdrCharsetOverride = OverrideCharacterTable;
+#endif
 
   bool UseKbd = true;
   const char *LircDevice = NULL;
@@ -223,11 +235,22 @@
   VdrUser = VDR_USER;
 #endif
 
+  cArgs *Args = NULL;
+  if (argc == 1) {
+     Args = new cArgs(argv[0]);
+     if (Args->ReadDirectory(DEFAULTARGSDIR)) {
+        argc = Args->GetArgc();
+        argv = Args->GetArgv();
+        }
+     }
+
+  cVideoDirectory::SetName(VideoDirectory);
   cPluginManager PluginManager(DEFAULTPLUGINDIR);
 
   static struct option long_options[] = {
       { "audio",    required_argument, NULL, 'a' },
       { "cachedir", required_argument, NULL, 'c' | 0x100 },
+      { "chartab",  required_argument, NULL, 'c' | 0x200 },
       { "config",   required_argument, NULL, 'c' },
       { "daemon",   no_argument,       NULL, 'd' },
       { "device",   required_argument, NULL, 'D' },
@@ -249,9 +272,11 @@
       { "port",     required_argument, NULL, 'p' },
       { "record",   required_argument, NULL, 'r' },
       { "resdir",   required_argument, NULL, 'r' | 0x100 },
+      { "showargs", optional_argument, NULL, 's' | 0x200 },
       { "shutdown", required_argument, NULL, 's' },
       { "split",    no_argument,       NULL, 's' | 0x100 },
       { "terminal", required_argument, NULL, 't' },
+      { "updindex", required_argument, NULL, 'u' | 0x200 },
       { "user",     required_argument, NULL, 'u' },
       { "userdump", no_argument,       NULL, 'u' | 0x100 },
       { "version",  no_argument,       NULL, 'V' },
@@ -269,6 +294,9 @@
           case 'c' | 0x100:
                     CacheDirectory = optarg;
                     break;
+          case 'c' | 0x200:
+                    OverrideCharacterTable = optarg;
+                    break;
           case 'c': ConfigDirectory = optarg;
                     break;
           case 'd': DaemonMode = true;
@@ -421,6 +449,19 @@
           case 's' | 0x100:
                     Setup.SplitEditedFiles = 1;
                     break;
+          case 's' | 0x200: {
+                    const char *ArgsDir = optarg ? optarg : DEFAULTARGSDIR;
+                    cArgs Args(argv[0]);
+                    if (!Args.ReadDirectory(ArgsDir)) {
+                       fprintf(stderr, "vdr: can't read arguments from directory: %s\n", ArgsDir);
+                       return 2;
+                       }
+                    int c = Args.GetArgc();
+                    char **v = Args.GetArgv();
+                    for (int i = 1; i < c; i++)
+                        printf("%s\n", v[i]);
+                    return 0;
+                    }
           case 't': Terminal = optarg;
                     if (access(Terminal, R_OK | W_OK) < 0) {
                        fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal);
@@ -433,6 +474,8 @@
           case 'u' | 0x100:
                     UserDump = true;
                     break;
+          case 'u' | 0x200:
+                    return GenerateIndex(optarg, true) ? 0 : 2;
           case 'V': DisplayVersion = true;
                     break;
           case 'v' | 0x100:
@@ -443,6 +486,7 @@
           case 'v': VideoDirectory = optarg;
                     while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
                           optarg[strlen(optarg) - 1] = 0;
+                    cVideoDirectory::SetName(VideoDirectory);
                     break;
           case 'w': if (isnumber(optarg)) {
                        int t = atoi(optarg);
@@ -483,6 +527,11 @@
         printf("Usage: vdr [OPTIONS]\n\n"          // for easier orientation, this is column 80|
                "  -a CMD,   --audio=CMD    send Dolby Digital audio to stdin of command CMD\n"
                "            --cachedir=DIR save cache files in DIR (default: %s)\n"
+               "            --chartab=CHARACTER_TABLE\n"
+               "                           set the character table to use for strings in the\n"
+               "                           DVB data stream that don't begin with a character\n"
+               "                           table indicator, but don't use the standard default\n"
+               "                           character table (for instance ISO-8859-9)\n"
                "  -c DIR,   --config=DIR   read config files from DIR (default: %s)\n"
                "  -d,       --daemon       run in daemon mode\n"
                "  -D NUM,   --device=NUM   use only the given DVB device (NUM = 0, 1, 2...)\n"
@@ -533,14 +582,17 @@
                "  -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"
+               "            --showargs[=DIR] print the arguments read from DIR and exit\n"
+               "                           (default: %s)\n"
                "  -t TTY,   --terminal=TTY controlling tty\n"
                "  -u USER,  --user=USER    run as user USER; only applicable if started as\n"
                "                           root\n"
+               "            --updindex=REC update index for recording REC and exit\n"
                "            --userdump     allow coredumps if -u is given (debugging)\n"
                "  -v DIR,   --video=DIR    use DIR as video directory (default: %s)\n"
                "  -V,       --version      print version information and exit\n"
                "            --vfat         for backwards compatibility (same as\n"
-               "                           --dirnames=250,40,1\n"
+               "                           --dirnames=250,40,1)\n"
                "  -w SEC,   --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
                "                           seconds (default: %d); '0' disables the watchdog\n"
                "\n",
@@ -555,6 +607,7 @@
                DEFAULTLOCDIR,
                DEFAULTSVDRPPORT,
                DEFAULTRESDIR,
+               DEFAULTARGSDIR,
                DEFAULTVIDEODIR,
                DEFAULTWATCHDOG
                );
@@ -636,6 +689,14 @@
      isyslog("codeset is '%s' - %s", CodeSet, known ? "known" : "unknown");
      cCharSetConv::SetSystemCharacterTable(CodeSet);
      }
+#ifdef DEPRECATED_VDR_CHARSET_OVERRIDE
+  if (DeprecatedVdrCharsetOverride)
+     isyslog("use of environment variable VDR_CHARSET_OVERRIDE (%s) is deprecated!", DeprecatedVdrCharsetOverride);
+#endif
+  if (OverrideCharacterTable) {
+     isyslog("override character table is '%s'", OverrideCharacterTable);
+     SI::SetOverrideCharacterTable(OverrideCharacterTable);
+     }
 
   // Initialize internationalization:
 
@@ -663,7 +724,6 @@
 
   // Directories:
 
-  SetVideoDirectory(VideoDirectory);
   if (!ConfigDirectory)
      ConfigDirectory = DEFAULTCONFDIR;
   cPlugin::SetConfigDirectory(ConfigDirectory);
@@ -800,10 +860,17 @@
   if (AudioCommand)
      new cExternalAudio(AudioCommand);
 
+  // Positioner:
+
+  if (!cPositioner::GetPositioner()) // no plugin has created a positioner
+     new cDiseqcPositioner;
+
   // Channel:
 
   if (!cDevice::WaitForAllDevicesReady(DEVICEREADYTIMEOUT))
      dsyslog("not all devices ready after %d seconds", DEVICEREADYTIMEOUT);
+  if (!CamSlots.WaitForAllCamSlotsReady(DEVICEREADYTIMEOUT))
+     dsyslog("not all CAM slots ready after %d seconds", DEVICEREADYTIMEOUT);
   if (*Setup.InitialChannel) {
      if (isnumber(Setup.InitialChannel)) { // for compatibility with old setup.conf files
         if (cChannel *Channel = Channels.GetByNumber(atoi(Setup.InitialChannel)))
@@ -836,6 +903,10 @@
      alarm(WatchdogTimeout); // Initial watchdog timer start
      }
 
+#ifdef SDNOTIFY
+  sd_notify(0, "READY=1\nSTATUS=Ready");
+#endif
+
   // Main program loop:
 
 #define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
@@ -908,14 +979,15 @@
               for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
                   if (Channel->Modification(CHANNELMOD_RETUNE)) {
                      cRecordControls::ChannelDataModified(Channel);
-                     if (Channel->Number() == cDevice::CurrentChannel()) {
+                     if (Channel->Number() == cDevice::CurrentChannel() && cDevice::PrimaryDevice()->HasDecoder()) {
                         if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) {
                            if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
-                              isyslog("retuning due to modification of channel %d", Channel->Number());
+                              isyslog("retuning due to modification of channel %d (%s)", Channel->Number(), Channel->Name());
                               Channels.SwitchTo(Channel->Number());
                               }
                            }
                         }
+                     cStatus::MsgChannelChange(Channel);
                      }
                   }
               Channels.Unlock();
@@ -987,7 +1059,7 @@
                         if (!Device->IsTunedToTransponder(Timer->Channel())) {
                            if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
                               cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
-                           dsyslog("switching device %d to channel %d", Device->DeviceNumber() + 1, Timer->Channel()->Number());
+                           dsyslog("switching device %d to channel %d (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), Timer->Channel()->Name());
                            if (Device->SwitchChannel(Timer->Channel(), false))
                               Device->SetOccupied(TIMERDEVICETIMEOUT);
                            }
@@ -1234,7 +1306,7 @@
              case osRecordings:
                             DELETE_MENU;
                             cControl::Shutdown();
-                            Menu = new cMenuMain(osRecordings);
+                            Menu = new cMenuMain(osRecordings, true);
                             break;
              case osReplay: DELETE_MENU;
                             cControl::Shutdown();
@@ -1315,8 +1387,9 @@
         if (!Menu) {
            if (!InhibitEpgScan)
               EITScanner.Process();
-           if (!cCutter::Active() && cCutter::Ended()) {
-              if (cCutter::Error())
+           bool Error = false;
+           if (RecordingsHandler.Finished(Error)) {
+              if (Error)
                  Skins.Message(mtError, tr("Editing process failed!"));
               else
                  Skins.Message(mtInfo, tr("Editing process finished"));
@@ -1336,7 +1409,10 @@
               ShutdownHandler.countdown.Cancel();
            }
 
-        if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !cCutter::Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) {
+        // Keep the recordings handler alive:
+        RecordingsHandler.Active();
+
+        if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) {
            // Handle housekeeping tasks
 
            // Shutdown:
@@ -1360,6 +1436,7 @@
 
            // Disk housekeeping:
            RemoveDeletedRecordings();
+           ClearVanishedRecordings();
            cSchedules::Cleanup();
            // Plugins housekeeping:
            PluginManager.Housekeeping();
@@ -1385,7 +1462,7 @@
 
   PluginManager.StopPlugins();
   cRecordControls::Shutdown();
-  cCutter::Stop();
+  RecordingsHandler.DelAll();
   delete Menu;
   cControl::Shutdown();
   delete Interface;
@@ -1400,6 +1477,8 @@
      Setup.Save();
      }
   cDevice::Shutdown();
+  cPositioner::DestroyPositioner();
+  cVideoDirectory::Destroy();
   EpgHandlers.Clear();
   PluginManager.Shutdown(true);
   cSchedules::Cleanup(true);
diff -ruN vdr-2.0.0/videodir.c vdr-2.2.0/videodir.c
--- vdr-2.0.0/videodir.c	2012-09-30 14:06:33.000000000 +0200
+++ vdr-2.2.0/videodir.c	2013-10-11 11:38:07.000000000 +0200
@@ -1,10 +1,10 @@
 /*
- * videodir.c: Functions to maintain a distributed video directory
+ * videodir.c: Functions to maintain the video directory
  *
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: videodir.c 2.4 2012/09/30 12:06:33 kls Exp $
+ * $Id: videodir.c 3.4 2013/10/11 09:38:07 kls Exp $
  */
 
 #include "videodir.h"
@@ -19,185 +19,129 @@
 #include "recording.h"
 #include "tools.h"
 
-const char *VideoDirectory = VIDEODIR;
+cString cVideoDirectory::name;
+cVideoDirectory *cVideoDirectory::current = NULL;
 
-void SetVideoDirectory(const char *Directory)
+cVideoDirectory::cVideoDirectory(void)
 {
-  VideoDirectory = strdup(Directory);
+  delete current;
+  current = this;
 }
 
-class cVideoDirectory {
-private:
-  char *name, *stored, *adjusted;
-  int length, number, digits;
-public:
-  cVideoDirectory(void);
-  ~cVideoDirectory();
-  int FreeMB(int *UsedMB = NULL);
-  const char *Name(void) { return name ? name : VideoDirectory; }
-  const char *Stored(void) { return stored; }
-  int Length(void) { return length; }
-  bool IsDistributed(void) { return name != NULL; }
-  bool Next(void);
-  void Store(void);
-  const char *Adjust(const char *FileName);
-  };
+cVideoDirectory::~cVideoDirectory()
+{
+  current = NULL;
+}
 
-cVideoDirectory::cVideoDirectory(void)
+cVideoDirectory *cVideoDirectory::Current(void)
 {
-  length = strlen(VideoDirectory);
-  name = (VideoDirectory[length - 1] == '0') ? strdup(VideoDirectory) : NULL;
-  stored = adjusted = NULL;
-  number = -1;
-  digits = 0;
+  if (!current)
+     current = new cVideoDirectory;
+  return current;
 }
 
-cVideoDirectory::~cVideoDirectory()
+void cVideoDirectory::Destroy(void)
 {
-  free(name);
-  free(stored);
-  free(adjusted);
+  delete current;
 }
 
 int cVideoDirectory::FreeMB(int *UsedMB)
 {
-  return FreeDiskSpaceMB(name ? name : VideoDirectory, UsedMB);
+  return FreeDiskSpaceMB(Name(), UsedMB);
 }
 
-bool cVideoDirectory::Next(void)
+const char *cVideoDirectory::Name(void)
 {
-  if (name) {
-     if (number < 0) {
-        int l = length;
-        while (l-- > 0 && isdigit(name[l]))
-              ;
-        l++;
-        digits = length - l;
-        int n = atoi(&name[l]);
-        if (n == 0)
-           number = n;
-        else
-           return false; // base video directory must end with zero
-        }
-     if (++number > 0) {
-        char buf[16];
-        if (sprintf(buf, "%0*d", digits, number) == digits) {
-           strcpy(&name[length - digits], buf);
-           return DirectoryOk(name);
-           }
-        }
-     }
-  return false;
+  return name;
 }
 
-void cVideoDirectory::Store(void)
+void cVideoDirectory::SetName(const char *Name)
 {
-  if (name) {
-     free(stored);
-     stored = strdup(name);
-     }
+  name = Name;
 }
 
-const char *cVideoDirectory::Adjust(const char *FileName)
+bool cVideoDirectory::Register(const char *FileName)
 {
-  if (stored) {
-     free(adjusted);
-     adjusted = strdup(FileName);
-     return strncpy(adjusted, stored, length);
+  // Incoming name must be in base video directory:
+  if (strstr(FileName, Name()) != FileName) {
+     esyslog("ERROR: %s not in %s", FileName, Name());
+     errno = ENOENT; // must set 'errno' - any ideas for a better value?
+     return false;
      }
-  return NULL;
+  return true;
 }
 
-cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags)
+bool cVideoDirectory::Rename(const char *OldName, const char *NewName)
 {
-  const char *ActualFileName = FileName;
-
-  // Incoming name must be in base video directory:
-  if (strstr(FileName, VideoDirectory) != FileName) {
-     esyslog("ERROR: %s not in %s", FileName, VideoDirectory);
-     errno = ENOENT; // must set 'errno' - any ideas for a better value?
-     return NULL;
+  dsyslog("renaming '%s' to '%s'", OldName, NewName);
+  if (rename(OldName, NewName) == -1) {
+     LOG_ERROR_STR(NewName);
+     return false;
      }
-  // Are we going to create a new file?
-  if ((Flags & O_CREAT) != 0) {
-     cVideoDirectory Dir;
-     if (Dir.IsDistributed()) {
-        // Find the directory with the most free space:
-        int MaxFree = Dir.FreeMB();
-        while (Dir.Next()) {
-              int Free = FreeDiskSpaceMB(Dir.Name());
-              if (Free > MaxFree) {
-                 Dir.Store();
-                 MaxFree = Free;
-                 }
-              }
-        if (Dir.Stored()) {
-           ActualFileName = Dir.Adjust(FileName);
-           if (!MakeDirs(ActualFileName, false))
-              return NULL; // errno has been set by MakeDirs()
-           if (symlink(ActualFileName, FileName) < 0) {
-              LOG_ERROR_STR(FileName);
-              return NULL;
-              }
-           ActualFileName = strdup(ActualFileName); // must survive Dir!
-           }
+  return true;
+}
+
+bool cVideoDirectory::Move(const char *FromName, const char *ToName)
+{
+  dsyslog("moving '%s' to '%s'", FromName, ToName);
+  if (EntriesOnSameFileSystem(FromName, ToName)) {
+     if (rename(FromName, ToName) == -1) {
+        LOG_ERROR_STR(ToName);
+        return false;
         }
      }
-  cUnbufferedFile *File = cUnbufferedFile::Create(ActualFileName, Flags, DEFFILEMODE);
-  if (ActualFileName != FileName)
-     free((char *)ActualFileName);
-  return File;
+  else
+     return RecordingsHandler.Add(ruMove, FromName, ToName);
+  return true;
 }
 
-int CloseVideoFile(cUnbufferedFile *File)
+bool cVideoDirectory::Remove(const char *Name)
 {
-  int Result = File->Close();
-  delete File;
-  return Result;
+  return RemoveFileOrDir(Name);
 }
 
-bool RenameVideoFile(const char *OldName, const char *NewName)
+void cVideoDirectory::Cleanup(const char *IgnoreFiles[])
 {
-  // Only the base video directory entry will be renamed, leaving the
-  // possible symlinks untouched. Going through all the symlinks and disks
-  // would be unnecessary work - maybe later...
-  if (rename(OldName, NewName) == -1) {
-     LOG_ERROR_STR(OldName);
-     return false;
-     }
-  return true;
+  RemoveEmptyDirectories(Name(), false, IgnoreFiles);
 }
 
-bool RemoveVideoFile(const char *FileName)
+bool cVideoDirectory::Contains(const char *Name)
 {
-  return RemoveFileOrDir(FileName, true);
+  return EntriesOnSameFileSystem(this->Name(), Name);
 }
 
-bool VideoFileSpaceAvailable(int SizeMB)
+cUnbufferedFile *cVideoDirectory::OpenVideoFile(const char *FileName, int Flags)
 {
-  cVideoDirectory Dir;
-  if (Dir.IsDistributed()) {
-     if (Dir.FreeMB() >= SizeMB * 2) // base directory needs additional space
-        return true;
-     while (Dir.Next()) {
-           if (Dir.FreeMB() >= SizeMB)
-              return true;
-           }
-     return false;
-     }
-  return Dir.FreeMB() >= SizeMB;
+  if (Current()->Register(FileName))
+     return cUnbufferedFile::Create(FileName, Flags, DEFFILEMODE);
+  return NULL;
+}
+
+bool cVideoDirectory::RenameVideoFile(const char *OldName, const char *NewName)
+{
+  return Current()->Rename(OldName, NewName);
+}
+
+bool cVideoDirectory::MoveVideoFile(const char *FromName, const char *ToName)
+{
+  return Current()->Move(FromName, ToName);
+}
+
+bool cVideoDirectory::RemoveVideoFile(const char *FileName)
+{
+  return Current()->Remove(FileName);
 }
 
-int VideoDiskSpace(int *FreeMB, int *UsedMB)
+bool cVideoDirectory::VideoFileSpaceAvailable(int SizeMB)
 {
-  int free = 0, used = 0;
+  return Current()->FreeMB() >= SizeMB;
+}
+
+int cVideoDirectory::VideoDiskSpace(int *FreeMB, int *UsedMB)
+{
+  int used = 0;
+  int free = Current()->FreeMB(&used);
   int deleted = DeletedRecordings.TotalFileSizeMB();
-  cVideoDirectory Dir;
-  do {
-     int u;
-     free += Dir.FreeMB(&u);
-     used += u;
-     } while (Dir.Next());
   if (deleted > used)
      deleted = used; // let's not get beyond 100%
   free += deleted;
@@ -209,7 +153,7 @@
   return (free + used) ? used * 100 / (free + used) : 0;
 }
 
-cString PrefixVideoFileName(const char *FileName, char Prefix)
+cString cVideoDirectory::PrefixVideoFileName(const char *FileName, char Prefix)
 {
   char PrefixedName[strlen(FileName) + 2];
 
@@ -229,22 +173,14 @@
   return NULL;
 }
 
-void RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
+void cVideoDirectory::RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
+{
+  Current()->Cleanup(IgnoreFiles);
+}
+
+bool cVideoDirectory::IsOnVideoDirectoryFileSystem(const char *FileName)
 {
-  cVideoDirectory Dir;
-  do {
-     RemoveEmptyDirectories(Dir.Name(), false, IgnoreFiles);
-     } while (Dir.Next());
-}
-
-bool IsOnVideoDirectoryFileSystem(const char *FileName)
-{
-  cVideoDirectory Dir;
-  do {
-     if (EntriesOnSameFileSystem(Dir.Name(), FileName))
-        return true;
-     } while (Dir.Next());
-  return false;
+  return Current()->Contains(FileName);
 }
 
 // --- cVideoDiskUsage -------------------------------------------------------
@@ -262,7 +198,7 @@
 {
   if (time(NULL) - lastChecked > DISKSPACECHEK) {
      int FreeMB;
-     int UsedPercent = VideoDiskSpace(&FreeMB);
+     int UsedPercent = cVideoDirectory::VideoDiskSpace(&FreeMB);
      if (FreeMB != freeMB) {
         usedPercent = UsedPercent;
         freeMB = FreeMB;
diff -ruN vdr-2.0.0/videodir.h vdr-2.2.0/videodir.h
--- vdr-2.0.0/videodir.h	2012-09-30 13:01:15.000000000 +0200
+++ vdr-2.2.0/videodir.h	2013-10-11 11:37:48.000000000 +0200
@@ -1,10 +1,10 @@
 /*
- * videodir.h: Functions to maintain a distributed video directory
+ * videodir.h: Functions to maintain the video directory
  *
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: videodir.h 2.3 2012/09/30 11:01:15 kls Exp $
+ * $Id: videodir.h 3.2 2013/10/11 09:37:48 kls Exp $
  */
 
 #ifndef __VIDEODIR_H
@@ -13,18 +13,75 @@
 #include <stdlib.h>
 #include "tools.h"
 
-extern const char *VideoDirectory;
-
-void SetVideoDirectory(const char *Directory);
-cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
-int CloseVideoFile(cUnbufferedFile *File);
-bool RenameVideoFile(const char *OldName, const char *NewName);
-bool RemoveVideoFile(const char *FileName);
-bool VideoFileSpaceAvailable(int SizeMB);
-int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
-cString PrefixVideoFileName(const char *FileName, char Prefix);
-void RemoveEmptyVideoDirectories(const char *IgnoreFiles[] = NULL);
-bool IsOnVideoDirectoryFileSystem(const char *FileName);
+class cVideoDirectory {
+private:
+  static cString name;
+  static cVideoDirectory *current;
+  static cVideoDirectory *Current(void);
+public:
+  cVideoDirectory(void);
+  virtual ~cVideoDirectory();
+  virtual int FreeMB(int *UsedMB = NULL);
+      ///< Returns the total amount (in MB) of free disk space for recording.
+      ///< If UsedMB is given, it returns the amount of disk space in use by
+      ///< existing recordings (or anything else) on that disk.
+  virtual bool Register(const char *FileName);
+      ///< By default VDR assumes that the video directory consists of one large
+      ///< volume, on which it can store its recordings. A derived cVideoDirectory
+      ///< may, for instance, use several separate disks to store recordings.
+      ///< The given FileName is the full path name (including the video directory) of
+      ///< a recording file ('*.ts') that is about to be opened for writing. If the actual
+      ///< file shall be put on an other disk, the derived cVideoDirectory should
+      ///< create a symbolic link from the given FileName to the other location.
+      ///< Returns true if the operation was successful.
+      ///< The default implementation just checks whether the incoming file name really
+      ///< is under the video directory.
+  virtual bool Rename(const char *OldName, const char *NewName);
+      ///< Renames the directory OldName to NewName.
+      ///< OldName and NewName are full path names that begin with the name of the
+      ///< video directory and end with '*.rec' or '*.del'. Only the base name (the
+      ///< rightmost component) of the two names may be different.
+      ///< Returns true if the operation was successful.
+      ///< The default implementation just calls the system's rename() function.
+  virtual bool Move(const char *FromName, const char *ToName);
+      ///< Moves the directory FromName to the location ToName. FromName is the full
+      ///< path name of a recording's '*.rec' directory. ToName has the same '*.rec'
+      ///< part as FromName, but a different directory path above it.
+      ///< Returns true if the operation was successful.
+      ///< The default implementation just calls the system's rename() function.
+  virtual bool Remove(const char *Name);
+      ///< Removes the directory with the given Name and everything it contains.
+      ///< Name is a full path name that begins with the name of the video directory.
+      ///< Returns true if the operation was successful.
+      ///< The default implementation calls RemoveFileOrDir().
+  virtual void Cleanup(const char *IgnoreFiles[] = NULL);
+      ///< Recursively removes all empty directories under the video directory.
+      ///< If IgnoreFiles is given, the file names in this (NULL terminated) array
+      ///< are ignored when checking whether a directory is empty. These are
+      ///< typically "dot files", like e.g. ".sort".
+      ///< The default implementation calls RemoveEmptyDirectories().
+  virtual bool Contains(const char *Name);
+      ///< Checks whether the directory Name is on the same file system as the
+      ///< video directory. Name is the full path name of a recording's '*.rec'
+      ///< directory. This function is usually called when an ongoing recording
+      ///< is about to run out of disk space, and an existing (old) recording needs
+      ///< to be deleted. It shall make sure that deleting this old recording will
+      ///< actually free up space in the video directory, and not on some other
+      ///< device that just happens to be mounted.
+      ///< The default implementation calls EntriesOnSameFileSystem().
+  static const char *Name(void);
+  static void SetName(const char *Name);
+  static void Destroy(void);
+  static cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
+  static bool RenameVideoFile(const char *OldName, const char *NewName);
+  static bool MoveVideoFile(const char *FromName, const char *ToName);
+  static bool RemoveVideoFile(const char *FileName);
+  static bool VideoFileSpaceAvailable(int SizeMB);
+  static int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
+  static cString PrefixVideoFileName(const char *FileName, char Prefix);
+  static void RemoveEmptyVideoDirectories(const char *IgnoreFiles[] = NULL);
+  static bool IsOnVideoDirectoryFileSystem(const char *FileName);
+  };
 
 class cVideoDiskUsage {
 private:
