[vdr] 01/11: Imported Upstream version 2.1.8

Tobias Grimm tiber-guest at moszumanska.debian.org
Sun Feb 8 17:32:29 UTC 2015


This is an automated email from the git hooks/post-receive script.

tiber-guest pushed a commit to branch e-tobi
in repository vdr.

commit a479fc677a293226e06ddc023cf6309fc37b6af3
Author: etobi <git at e-tobi.net>
Date:   Sat Feb 7 11:11:52 2015 +0100

    Imported Upstream version 2.1.8
---
 CONTRIBUTORS                            |  132 ++-
 Doxyfile                                |    2 +-
 Doxyfile.filter                         |    2 +-
 HISTORY                                 |  545 ++++++++++-
 INSTALL                                 |   64 +-
 MANUAL                                  |  139 ++-
 Make.config.template                    |    4 +-
 Make.global                             |    2 +-
 Makefile                                |   19 +-
 PLUGINS.html                            |  139 ++-
 PLUGINS/src/dvbhddevice/HISTORY         |   20 +-
 PLUGINS/src/dvbhddevice/Makefile        |    2 +-
 PLUGINS/src/dvbhddevice/dvbhddevice.c   |    2 +-
 PLUGINS/src/dvbhddevice/dvbhdffdevice.c |   82 +-
 PLUGINS/src/dvbhddevice/dvbhdffdevice.h |   10 +-
 PLUGINS/src/dvbhddevice/hdffosd.c       |   82 +-
 PLUGINS/src/dvbhddevice/menu.c          |    7 +-
 PLUGINS/src/dvbhddevice/po/de_DE.po     |    5 +-
 PLUGINS/src/dvbhddevice/po/fi_FI.po     |    5 +-
 PLUGINS/src/dvbhddevice/po/it_IT.po     |    9 +-
 PLUGINS/src/dvbhddevice/setup.c         |   29 +-
 PLUGINS/src/dvbhddevice/setup.h         |    2 +
 PLUGINS/src/dvbsddevice/HISTORY         |    9 +
 PLUGINS/src/dvbsddevice/Makefile        |    4 +-
 PLUGINS/src/dvbsddevice/dvbsddevice.c   |    4 +-
 PLUGINS/src/dvbsddevice/dvbsdffdevice.c |   19 +-
 PLUGINS/src/dvbsddevice/dvbsdffdevice.h |    5 +-
 PLUGINS/src/dvbsddevice/dvbsdffosd.c    |    2 +-
 PLUGINS/src/dvbsddevice/dvbsdffosd.h    |    2 +-
 PLUGINS/src/epgtableid0/HISTORY         |    4 +
 PLUGINS/src/epgtableid0/Makefile        |    4 +-
 PLUGINS/src/epgtableid0/epgtableid0.c   |    4 +-
 PLUGINS/src/hello/HISTORY               |    4 +
 PLUGINS/src/hello/Makefile              |    4 +-
 PLUGINS/src/hello/hello.c               |    4 +-
 PLUGINS/src/osddemo/HISTORY             |    6 +-
 PLUGINS/src/osddemo/Makefile            |    4 +-
 PLUGINS/src/osddemo/osddemo.c           |    4 +-
 PLUGINS/src/pictures/HISTORY            |    8 +
 PLUGINS/src/pictures/Makefile           |    4 +-
 PLUGINS/src/pictures/entry.c            |    2 +-
 PLUGINS/src/pictures/entry.h            |    2 +-
 PLUGINS/src/pictures/menu.c             |    2 +-
 PLUGINS/src/pictures/menu.h             |    2 +-
 PLUGINS/src/pictures/pic2mpg            |   10 +-
 PLUGINS/src/pictures/pictures.c         |    4 +-
 PLUGINS/src/pictures/player.c           |    4 +-
 PLUGINS/src/pictures/player.h           |    2 +-
 PLUGINS/src/rcu/HISTORY                 |    4 +
 PLUGINS/src/rcu/Makefile                |    4 +-
 PLUGINS/src/rcu/rcu.c                   |    4 +-
 PLUGINS/src/servicedemo/HISTORY         |    4 +
 PLUGINS/src/servicedemo/Makefile        |    4 +-
 PLUGINS/src/servicedemo/svccli.c        |    4 +-
 PLUGINS/src/servicedemo/svcsvr.c        |    4 +-
 PLUGINS/src/skincurses/HISTORY          |    4 +
 PLUGINS/src/skincurses/Makefile         |    4 +-
 PLUGINS/src/skincurses/skincurses.c     |    4 +-
 PLUGINS/src/status/HISTORY              |    4 +
 PLUGINS/src/status/Makefile             |    4 +-
 PLUGINS/src/status/status.c             |    4 +-
 PLUGINS/src/svdrpdemo/HISTORY           |    4 +
 PLUGINS/src/svdrpdemo/Makefile          |    4 +-
 PLUGINS/src/svdrpdemo/svdrpdemo.c       |    4 +-
 args.c                                  |  129 +++
 args.h                                  |   34 +
 audio.c                                 |    2 +-
 audio.h                                 |    2 +-
 channels.c                              |   59 +-
 channels.h                              |   10 +-
 ci.c                                    |  321 ++++++-
 ci.h                                    |  129 ++-
 config.c                                |   41 +-
 config.h                                |   23 +-
 cutter.c                                |  102 +--
 cutter.h                                |   40 +-
 device.c                                |   65 +-
 device.h                                |   54 +-
 diseqc.c                                |  260 +++++-
 diseqc.conf                             |   38 +-
 diseqc.h                                |   43 +-
 dvbci.c                                 |    4 +-
 dvbci.h                                 |    2 +-
 dvbdevice.c                             |  179 +++-
 dvbdevice.h                             |   15 +-
 dvbplayer.c                             |   92 +-
 dvbplayer.h                             |    4 +-
 dvbspu.c                                |    2 +-
 dvbspu.h                                |    3 +-
 dvbsubtitle.c                           | 1498 ++++++++++++++++++++-----------
 dvbsubtitle.h                           |    5 +-
 eit.c                                   |   10 +-
 eit.h                                   |    2 +-
 eitscan.c                               |    2 +-
 eitscan.h                               |    2 +-
 epg.c                                   |   20 +-
 epg.h                                   |   10 +-
 epg2html                                |    2 +-
 filter.c                                |    2 +-
 filter.h                                |    2 +-
 font.c                                  |    2 +-
 font.h                                  |    2 +-
 i18n.c                                  |    2 +-
 i18n.h                                  |    2 +-
 interface.c                             |    8 +-
 interface.h                             |    2 +-
 keys.c                                  |    2 +-
 keys.h                                  |    4 +-
 libsi/Makefile                          |    2 +-
 libsi/descriptor.c                      |   99 +-
 libsi/descriptor.h                      |   61 +-
 libsi/gendescr                          |    2 +-
 libsi/headers.h                         |   71 +-
 libsi/section.c                         |    2 +-
 libsi/section.h                         |    2 +-
 libsi/si.c                              |   11 +-
 libsi/si.h                              |   21 +-
 libsi/util.c                            |    2 +-
 libsi/util.h                            |    2 +-
 lirc.c                                  |    2 +-
 lirc.h                                  |    2 +-
 menu.c                                  |  688 ++++++++++++--
 menu.h                                  |   37 +-
 menuitems.c                             |   56 +-
 menuitems.h                             |   15 +-
 newplugin                               |    4 +-
 nit.c                                   |   43 +-
 nit.h                                   |    2 +-
 osd.c                                   |   36 +-
 osd.h                                   |   24 +-
 osdbase.c                               |   19 +-
 osdbase.h                               |    6 +-
 pat.c                                   |   73 +-
 pat.h                                   |   19 +-
 player.c                                |    2 +-
 player.h                                |    4 +-
 plugin.c                                |    2 +-
 plugin.h                                |    2 +-
 po/ar.po                                |  159 +++-
 po/ca_ES.po                             |  159 +++-
 po/cs_CZ.po                             |  159 +++-
 po/da_DK.po                             |  159 +++-
 po/de_DE.po                             |  175 +++-
 po/el_GR.po                             |  159 +++-
 po/es_ES.po                             |  159 +++-
 po/et_EE.po                             |  181 +++-
 po/fi_FI.po                             |  159 +++-
 po/fr_FR.po                             |  159 +++-
 po/hr_HR.po                             |  159 +++-
 po/hu_HU.po                             |  159 +++-
 po/it_IT.po                             |  161 +++-
 po/lt_LT.po                             |  159 +++-
 po/mk_MK.po                             |  159 +++-
 po/nl_NL.po                             |  159 +++-
 po/nn_NO.po                             |  159 +++-
 po/pl_PL.po                             |  159 +++-
 po/pt_PT.po                             |  159 +++-
 po/ro_RO.po                             |  193 +++-
 po/ru_RU.po                             |  159 +++-
 po/sk_SK.po                             |  159 +++-
 po/sl_SI.po                             |  159 +++-
 po/sr_RS.po                             |  159 +++-
 po/sv_SE.po                             |  159 +++-
 po/tr_TR.po                             |  159 +++-
 po/uk_UA.po                             |  163 +++-
 po/zh_CN.po                             |  159 +++-
 positioner.c                            |  140 +++
 positioner.h                            |  171 ++++
 receiver.c                              |   35 +-
 receiver.h                              |   11 +-
 recorder.c                              |    3 +-
 recorder.h                              |   10 +-
 recording.c                             |  718 +++++++++++++--
 recording.h                             |  144 ++-
 remote.c                                |   23 +-
 remote.h                                |    3 +-
 remux.c                                 |   46 +-
 remux.h                                 |    4 +-
 ringbuffer.c                            |    2 +-
 ringbuffer.h                            |    2 +-
 runvdr.template                         |    2 +-
 scr.conf                                |   22 +-
 sdt.c                                   |    8 +-
 sdt.h                                   |    2 +-
 sections.c                              |    5 +-
 sections.h                              |    2 +-
 shutdown.c                              |    8 +-
 shutdown.h                              |    2 +-
 skinclassic.c                           |    2 +-
 skinclassic.h                           |    2 +-
 skinlcars.c                             |   79 +-
 skinlcars.h                             |    2 +-
 skins.c                                 |   18 +-
 skins.h                                 |   30 +-
 skinsttng.c                             |    4 +-
 skinsttng.h                             |    2 +-
 sourceparams.c                          |    2 +-
 sourceparams.h                          |    2 +-
 sources.c                               |   36 +-
 sources.conf                            |    2 +
 sources.h                               |   17 +-
 spu.c                                   |    2 +-
 spu.h                                   |    2 +-
 status.c                                |    8 +-
 status.h                                |    6 +-
 summary2info                            |    2 +-
 svdrp.c                                 |   96 +-
 svdrp.h                                 |    3 +-
 themes.c                                |    2 +-
 themes.h                                |    2 +-
 thread.c                                |    3 +-
 thread.h                                |   11 +-
 timers.c                                |    6 +-
 timers.h                                |    2 +-
 tools.c                                 |   77 +-
 tools.h                                 |   54 +-
 transfer.c                              |    2 +-
 transfer.h                              |    2 +-
 vdr.1                                   |   24 +-
 vdr.5                                   |   39 +-
 vdr.c                                   |   72 +-
 videodir.c                              |  236 ++---
 videodir.h                              |   85 +-
 223 files changed, 11043 insertions(+), 1817 deletions(-)

diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ee4e81c..64edccc 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -232,6 +232,7 @@ Stefan Huelswitt <s.huelswitt at gmx.de>
  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 at efr-net.de>
  for pointing out that there are channels that have a symbol rate higher than 27500
@@ -537,7 +538,7 @@ Andreas B
 Onno Kreuzinger <ok at solutas.net>
  for reporting leftover references to the file FORMATS in MANUAL and svdrp.c
 
-Rudi Hofer (Rudi.Hofer at gmx.de)
+Rudi Hofer <Rudi.Hofer at 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
 
@@ -622,6 +623,8 @@ Helmut Auer <vdr at helmutauer.de>
  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 at UU.NET>
  for fixing an incomplete initialization of the filter parameters in eit.c
@@ -706,8 +709,11 @@ Oliver Endriss <o.endriss at gmx.de>
  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 at freenet.de>
  for adding some satellites to 'sources.conf'
@@ -1175,9 +1181,20 @@ Rolf Ahrenberg <Rolf.Ahrenberg at sci.fi>
  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)
 
 Ralf Klueber <ralf.klueber at vodafone.com>
  for reporting a bug in cutting a recording if there is only a single editing mark
@@ -1500,6 +1517,8 @@ Thomas Bergwinkl <Thomas.Bergwinkl at vr-web.de>
  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 at free.fr>
  for fixing a typo in libsi/si.h
@@ -1708,6 +1727,7 @@ Arthur Konovalov <artlov at gmail.com>
  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
 
 Milos Kapoun <m.kapoun at cra.cz>
  for suggesting to skip code table info in SI data
@@ -1801,6 +1821,7 @@ Udo Richter <udo_richter at gmx.de>
  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 at kammer.uni-hannover.de>
  for his help in keeping 'channels.conf.terr' up to date
@@ -2030,6 +2051,11 @@ Ville Skytt
  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 at reactor.de>
  for fixing setting the colored button help after deleting a recording in case the next
@@ -2116,6 +2142,7 @@ Thomas G
  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 at infradead.org>
  for his help in replacing the get/put_unaligned() macros from asm/unaligned.h with
@@ -2144,6 +2171,7 @@ Marko M
  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
 
@@ -2379,6 +2407,11 @@ Christoph Haubrich <christoph1.haubrich at arcor.de>
  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
 
 Pekka Mauno <pekka.mauno at iki.fi>
  for fixing cSchedule::GetFollowingEvent() in case there is currently no present
@@ -2495,6 +2528,7 @@ Antti Hartikainen <ami+vdr at 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 at bernd-melcher.de>
  for reporting a problem with the 'servicedemo' plugin having no PLUGIN macro
@@ -2520,6 +2554,7 @@ Frank Schmirler <vdr at schmirler.de>
  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 at zyn.de>
  for reporting that a recording may unnecessarily block a device with a CAM, while
@@ -2564,6 +2599,7 @@ J
  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 at unterbrecher.de>
  for reporting that 'uint32_t' requires including stdint.h in font.h on some systems
@@ -2588,6 +2624,7 @@ Jose Alberto Reguero <jareguero at telefonica.net>
  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 at engsystem.net>
  for adding full weekday names to i18n.c for plugins to use
@@ -2644,6 +2681,7 @@ Torsten Kunkel <vdrml at tkunkel.de>
  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 at club-internet.fr>
  for translating OSD texts to the French language
@@ -2714,6 +2752,7 @@ Ales Jurik <ajurik at quick.cz>
  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 at bahnhof.se>
  for translating OSD texts to the Swedish language
@@ -2771,7 +2810,7 @@ Johann Friedrichs <johann.friedrichs at web.de>
 Timo Helkio <timolavi at mbnet.fi>
  for reporting a hangup when replaying a TS recording with subtitles activated
 
-Derek Kelly (user.vdr at gmail.com)
+Derek Kelly <user.vdr at 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
@@ -2790,6 +2829,7 @@ Derek Kelly (user.vdr at gmail.com)
  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
 
 Marcel Unbehaun <frostworks at gmx.de>
  for adding cRecordingInfo::GetEvent()
@@ -2863,7 +2903,12 @@ Lars Hanisch <dvb at flensrocker.de>
  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
 
 Alex Lasnier <alex at fepg.org>
  for adding tuning support for ATSC devices
@@ -2883,6 +2928,7 @@ Paul Menzel <paulepanter at users.sourceforge.net>
  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 at gmail.com>
  for translating OSD texts to the Czech language
@@ -2901,7 +2947,9 @@ Christopher Reimer <vdr at creimer.net>
  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
 
 Stefan Huskamp <coca_cola1 at gmx.de>
  for suggesting to make entering characters via the number keys
@@ -2922,10 +2970,12 @@ Henning Heinold <heinold at inf.fu-berlin.de>
 
 Dominik Strasser <dominik at 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 at 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 at opendreambox.org>
  for suggesting to retrieve the include path to the freetype2 header files
@@ -2938,6 +2988,7 @@ Frank Neumann <fnu at yavdr.org>
  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 at dachsweb.de>
  for reporting a problem with checking for minimum line length of 21 characters in
@@ -3002,6 +3053,7 @@ Frank Niederwipper <f.niederwipper at gmail.com>
 
 Chris Mayo <aklhfex at 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 at gmail.com>
  for making the SVDRP command LSTC accepts channel IDs
@@ -3067,8 +3119,10 @@ Christian Richter <cr at 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
 
-Christian Kaiser <christian.kaiser at teleservice.com>
+Christian Kaiser <chr-kaiser at 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 at gmx.de>
  for adding SetComponents() to the EPG handler interface
@@ -3111,6 +3165,8 @@ S
  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
 
 Peter M�nster <pmlists at free.fr>
  for fixing 'make install' to not overwrite existing configuration files
@@ -3129,6 +3185,7 @@ Stefan Blochberger <Stefan.Blochberger at gmx.de>
  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 at telfort.nl>
  for adding maximum SNR value for PCTV Systems PCTV 73ESE
@@ -3179,6 +3236,9 @@ Stefan Braun <louis.braun at 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 at dolze.de>
  for changing cThread::SetIOPriority() from "best effort class" to "idle class" in order
@@ -3187,30 +3247,96 @@ Jochen Dolze <vdr at dolze.de>
 Dominique Dumont <domi.dumont at free.fr>
  for reporting a crash in the LCARS skin's main menu in case there is no current channel
 
+Seppo Ingalsuo <seppo.ingalsuo at iki.fi>
+ for a patch that was used as a base for implementing support for positioners
+
 Manfred V�lkel <mvoelkel at 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 at 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 at martins-kabuff.de>
+ for writing the "extrecmenu" plugin, which inspired the implementation of editing
+ recording properties
+
 Eike Edener <eike at edener.de>
  for reporting a bug in writing group separators to channels.conf that contain a comma
 
+Harald Koenig <koenig at 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 at tiscali.it>
+ for adding maximum signal strength value for TechniSat SkyStar 2 DVB-S rev 2.3P
+
+Thomas Reufer <thomas at 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()
+
 Eike Sauer <EikeSauer at 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 at xeatre.tv>
  for reporting a problem with frame detection in MPEG-2 streams that have "bottom fields"
  or varying GOP structures
 
 Mariusz Bialonczyk <manio at 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 at 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 at 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 at 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 at nafets.de>
+ for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement()
+ to the cVector class
+
+Claus Muus <email at clausmuus.de>
+ for adding the new parameters "Setup/Miscellaneous/Volume steps" and
+ ".../Volume linearize"
+
+Dieter Ferdinand <dieter.ferdinand at 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 at 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 at gmail.com>
+ for suggesting to provide a way for skin plugins to get informed about the currently
+ used sort mode of a menu
diff --git a/Doxyfile b/Doxyfile
index aa4cdb4..98891d7 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1658,7 +1658,7 @@ UML_LOOK               = NO
 # 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 --git a/Doxyfile.filter b/Doxyfile.filter
index 7fde955..59ba783 100755
--- a/Doxyfile.filter
+++ b/Doxyfile.filter
@@ -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 --git a/HISTORY b/HISTORY
index fa6a362..2e8be67 100644
--- a/HISTORY
+++ b/HISTORY
@@ -7144,7 +7144,7 @@ Video Disk Recorder Revision History
   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).
 
@@ -7782,7 +7782,7 @@ Video Disk Recorder Revision History
 - Bumped all version numbers to 2.0.0.
 - Official release.
 
-2013-04-13: Version 2.0.1
+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
@@ -7805,7 +7805,7 @@ Video Disk Recorder Revision History
 - 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.
+  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
@@ -7813,6 +7813,94 @@ Video Disk Recorder Revision History
 - 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).
+  + 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.
@@ -7832,6 +7920,96 @@ Video Disk Recorder Revision History
 - 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.
+- 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,
@@ -7854,6 +8032,90 @@ Video Disk Recorder Revision History
   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
@@ -7870,7 +8132,136 @@ Video Disk Recorder Revision History
   and also to use the correct directory with --edit (the latter reported by Marko
   M�kel�).
 
-2014-03-16: Version 2.0.6
+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.
@@ -7915,3 +8306,149 @@ Video Disk Recorder Revision History
   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 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 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).
diff --git a/INSTALL b/INSTALL
index dd0c999..07581b5 100644
--- a/INSTALL
+++ b/INSTALL
@@ -254,6 +254,7 @@ The program will be called with two or three (in case of "edited") string
 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 @@ case "$1" in
      before)
             echo "Before recording $2"
             ;;
+     started)
+            echo "Started recording $2"
+            ;;
      after)
             echo "After recording $2"
             ;;
@@ -325,38 +329,9 @@ Note that the file system need not be 64-bit proof, since the 'vdr'
 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 @@ access your DiSEqC equipment (see man vdr(5) for details).
 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 @@ for a detailed description).
 
 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 --git a/MANUAL b/MANUAL
index cce2b0a..4f783c4 100644
--- a/MANUAL
+++ b/MANUAL
@@ -363,15 +363,29 @@ Version 2.0
   - 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 "binary" mode. Pressing
+            either of these keys for the first time moves the mark 120 seconds
+            in the given direction (configurable via "Setup/Replay/Binary skip
+            initial value"). 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 "binary") 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/Binary
+            skip timeout") 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 +514,34 @@ Version 2.0
   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). Once you are
+  finished with editing the recording properties, press Ok to confirm the changes.
 
 * Parameters in the "Setup" menu
 
@@ -706,13 +745,13 @@ Version 2.0
                          1 = ANSI/SCTE
 
   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 +815,28 @@ Version 2.0
                          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 +845,19 @@ Version 2.0
                          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 +969,43 @@ Version 2.0
                          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.
+                         In order to work, this option must be enabled before starting
+                         replay.
+
+  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.
+                         In order to work, this option must be enabled before starting
+                         replay.
+
+  Binary skip initial value (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 "Binary skip timeout".
+                         The valid range is 10...600.
+
+  Binary skip timeout (s) = 3
+                         Defines the number of seconds after which pressing the
+                         '1' or '3' key falls back to the "Binary skip initial value".
+                         The valid range is 0...10. Setting the timeout to 0 disables
+                         the binary mode and makes '1' and '3' always skip the number
+                         of seconds configured as the initial value.
+
   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 +1062,17 @@ Version 2.0
                          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 --git a/Make.config.template b/Make.config.template
index 2bc0a8f..88b6286 100644
--- a/Make.config.template
+++ b/Make.config.template
@@ -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.2 2015/01/30 15:18:47 kls Exp $
 
 ### The C compiler and options:
 
@@ -38,6 +38,7 @@ endif
 
 #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 @@ ifdef ONEDIR
 VIDEODIR     = /video
 CACHEDIR     = $(VIDEODIR)
 CONFDIR      = $(VIDEODIR)
+ARGSDIR      = $(VIDEODIR)/conf.d
 RESDIR       = $(VIDEODIR)
 endif
 
diff --git a/Make.global b/Make.global
index e259402..665cc28 100644
--- a/Make.global
+++ b/Make.global
@@ -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 --git a/Makefile b/Makefile
index d24d08f..40a610a 100644
--- a/Makefile
+++ b/Makefile
@@ -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.4 2015/01/01 13:52:07 kls Exp $
 
 .DELETE_ON_ERROR:
 
@@ -31,6 +31,7 @@ PLUGINDIR ?= $(CWD)/PLUGINS
 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 @@ endif
 
 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 @@ INCLUDES += $(shell pkg-config --cflags fribidi)
 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)\"
@@ -141,8 +148,9 @@ $(SILIB):
 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 +228,7 @@ plugins: include-dir vdr.pc
 	    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;\
@@ -260,6 +268,7 @@ install-bin: vdr
 install-dirs:
 	@mkdir -p $(DESTDIR)$(VIDEODIR)
 	@mkdir -p $(DESTDIR)$(CONFDIR)
+	@mkdir -p $(DESTDIR)$(ARGSDIR)
 	@mkdir -p $(DESTDIR)$(CACHEDIR)
 	@mkdir -p $(DESTDIR)$(RESDIR)
 
diff --git a/PLUGINS.html b/PLUGINS.html
index d864fe2..b912301 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -31,14 +31,14 @@ modified {
 <div class="center">
 <h1>The VDR Plugin System</h1>
 
-<b>Version 2.0</b>
+<b>Version 2.1</b>
 <p>
 Copyright © 2013 Klaus Schmidinger<br>
 <a href="mailto:vdr at tvdr.de">vdr at 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 @@ structures and allows it to hook itself into specific areas to perform special a
 <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 @@ structures and allows it to hook itself into specific areas to perform special a
 <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 <tt>src</tt> directory contains one subdirectory for each plugin, which carr
 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 @@ You can first assign the temporary values to the global variables and then do th
 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 @@ plugin's name. Again, VDR will make sure that the requested directory will exist
 (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 @@ If a plugin wants to get informed on various events in VDR, it can derive a clas
 
 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->CardIndex());
@@ -1525,13 +1502,11 @@ public:
   cMyReceiver(int Pid);
   };
 
-<modified>
 cMyReceiver::cMyReceiver(int Pid)
 :cReceiver(NULL, -1)
 {
   AddPid(Pid);
 }
-</modified>
 
 cMyReceiver::~cMyReceiver()
 {
@@ -1557,7 +1532,7 @@ The above example sets up a receiver that wants to receive data from only one
 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 @@ the receiver is attached to the device that actually receives the current live
 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 @@ If the new device can receive, it most likely needs to provide a way of
 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 HasDecoder(void) const;
 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 @@ the functions
 
 <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 @@ user - whether this goes through OSD facilities of the physical device (like
 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 @@ virtual void GetOsdSize(int &Width, int &Height, double &Aspect);
 </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 @@ Nothing needs to be done to shut down the devices. VDR will automatically
 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 @@ new cMyDeviceHook;
 </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 <vdr/positioner.h>
+
+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 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
 
 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 @@ virtual bool Assign(cDevice *Device, bool Query = false);
 
 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 @@ where <tt>DescriptionFromDatabase()</tt> would derive the description of the
 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 <vdr/videodir.h>
+
+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 --git a/PLUGINS/src/dvbhddevice/HISTORY b/PLUGINS/src/dvbhddevice/HISTORY
index 2d95978..b26b0c4 100644
--- a/PLUGINS/src/dvbhddevice/HISTORY
+++ b/PLUGINS/src/dvbhddevice/HISTORY
@@ -76,7 +76,25 @@ VDR Plugin 'dvbhddevice' Revision History
 
 - Fixed aspect ratio and position of scaled video.
 
-2013-08-26: Version 2.0.2
+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.
diff --git a/PLUGINS/src/dvbhddevice/Makefile b/PLUGINS/src/dvbhddevice/Makefile
index 0bfd136..a994e2e 100644
--- a/PLUGINS/src/dvbhddevice/Makefile
+++ b/PLUGINS/src/dvbhddevice/Makefile
@@ -15,7 +15,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/dvbhddevice/dvbhddevice.c b/PLUGINS/src/dvbhddevice/dvbhddevice.c
index e718a28..045b0a9 100644
--- a/PLUGINS/src/dvbhddevice/dvbhddevice.c
+++ b/PLUGINS/src/dvbhddevice/dvbhddevice.c
@@ -10,7 +10,7 @@
 #include "menu.h"
 #include "setup.h"
 
-static const char *VERSION        = "2.0.2";
+static const char *VERSION        = "2.1.6";
 static const char *DESCRIPTION    = trNOOP("HD Full Featured DVB device");
 static const char *MAINMENUENTRY  = "dvbhddevice";
 
diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
index e8dabd9..8ee8075 100644
--- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
+++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
@@ -51,6 +51,12 @@ cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend)
      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 @@ cDvbHdFfDevice::~cDvbHdFfDevice()
 
 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 @@ uchar *cDvbHdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, i
 
 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)
@@ -437,11 +435,6 @@ void cDvbHdFfDevice::SetVolumeDevice(int Volume)
   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 @@ bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode)
         }
         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;
@@ -606,7 +601,11 @@ void cDvbHdFfDevice::ScaleVideo(const cRect &Rect)
     }
 }
 
+#if (APIVERSNUM >= 20103)
+void cDvbHdFfDevice::TrickSpeed(int Speed, bool Forward)
+#else
 void cDvbHdFfDevice::TrickSpeed(int Speed)
+#endif
 {
   freezed = false;
   mHdffCmdIf->CmdAvEnableSync(0, false);
@@ -811,6 +810,11 @@ int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length)
         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;
@@ -896,6 +900,14 @@ int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length)
             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 --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
index 330f9d3..580bf9e 100644
--- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
+++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
@@ -52,8 +52,6 @@ public:
 
 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 @@ protected:
   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 @@ public:
   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 --git a/PLUGINS/src/dvbhddevice/hdffosd.c b/PLUGINS/src/dvbhddevice/hdffosd.c
index 4a6eaec..47a5ab0 100644
--- a/PLUGINS/src/dvbhddevice/hdffosd.c
+++ b/PLUGINS/src/dvbhddevice/hdffosd.c
@@ -42,7 +42,6 @@ private:
     tFont mFonts[MAX_NUM_FONTS];
     uint32_t mBitmapPalette;
     uint32_t mBitmapColors[256];
-    uint32_t mBitmapNumColors;
 
     bool mSupportsUtf8Text;
 
@@ -555,7 +554,6 @@ private:
     uint32_t mDisplay;
     uint32_t mBitmapPalette;
     uint32_t mBitmapColors[256];
-    uint32_t mBitmapNumColors;
 
 protected:
     virtual void SetActive(bool On);
@@ -665,14 +663,23 @@ void cHdffOsdRaw::Flush(void)
 {
     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())
         {
@@ -684,17 +691,56 @@ void cHdffOsdRaw::Flush(void)
                 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;
             render = true;
         }
+        if (buffer)
+            delete[] buffer;
     }
     else
     {
@@ -757,10 +803,12 @@ void cHdffOsdRaw::Flush(void)
     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 --git a/PLUGINS/src/dvbhddevice/menu.c b/PLUGINS/src/dvbhddevice/menu.c
index b7d35cf..fa953fb 100644
--- a/PLUGINS/src/dvbhddevice/menu.c
+++ b/PLUGINS/src/dvbhddevice/menu.c
@@ -56,12 +56,7 @@ eOSState cHdffMenu::ProcessKey(eKeys key)
 
 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 --git a/PLUGINS/src/dvbhddevice/po/de_DE.po b/PLUGINS/src/dvbhddevice/po/de_DE.po
index 34b19a7..cca422b 100644
--- a/PLUGINS/src/dvbhddevice/po/de_DE.po
+++ b/PLUGINS/src/dvbhddevice/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2013-08-23 12:10+0200\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"
@@ -121,5 +121,8 @@ msgstr "High Level OSD"
 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 --git a/PLUGINS/src/dvbhddevice/po/fi_FI.po b/PLUGINS/src/dvbhddevice/po/fi_FI.po
index 8cb0fde..797aa70 100644
--- a/PLUGINS/src/dvbhddevice/po/fi_FI.po
+++ b/PLUGINS/src/dvbhddevice/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2013-08-23 12:10+0200\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 at linuxtv.org>\n"
@@ -121,5 +121,8 @@ msgstr "Käytä korkean tason kuvaruutunäyttöä"
 msgid "Allow True Color OSD"
 msgstr "Salli tosivärit kuvaruutunäytölle"
 
+msgid "True Color format"
+msgstr ""
+
 msgid "Hide mainmenu entry"
 msgstr "Piilota valinta päävalikosta"
diff --git a/PLUGINS/src/dvbhddevice/po/it_IT.po b/PLUGINS/src/dvbhddevice/po/it_IT.po
index 2d895c6..cfb4b1b 100644
--- a/PLUGINS/src/dvbhddevice/po/it_IT.po
+++ b/PLUGINS/src/dvbhddevice/po/it_IT.po
@@ -8,8 +8,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: vdr-dvbhddevice 0.0.4\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2013-08-23 12:10+0200\n"
-"PO-Revision-Date: 2013-02-18 23:42+0100\n"
+"POT-Creation-Date: 2014-09-21 14:01+0200\n"
+"PO-Revision-Date: 2013-09-19 00:00+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian at tiscali.it>\n"
 "Language-Team:  <see README>\n"
 "Language: it\n"
@@ -30,7 +30,7 @@ msgid "TV on"
 msgstr "TV accesa"
 
 msgid "TV off"
-msgstr ""
+msgstr "TV spenta"
 
 msgid "Automatic"
 msgstr "Automatica"
@@ -125,5 +125,8 @@ msgstr "OSD alto livello"
 msgid "Allow True Color OSD"
 msgstr "Permetti OSD True Color"
 
+msgid "True Color format"
+msgstr ""
+
 msgid "Hide mainmenu entry"
 msgstr "Nascondi voce menu principale"
diff --git a/PLUGINS/src/dvbhddevice/setup.c b/PLUGINS/src/dvbhddevice/setup.c
index b919688..fd16a48 100644
--- a/PLUGINS/src/dvbhddevice/setup.c
+++ b/PLUGINS/src/dvbhddevice/setup.c
@@ -33,6 +33,7 @@ cHdffSetup::cHdffSetup(void)
     RemoteAddress = -1;
     HighLevelOsd = 1;
     TrueColorOsd = 1;
+    TrueColorFormat = 0;
     HideMainMenu = 0;
 }
 
@@ -54,6 +55,7 @@ bool cHdffSetup::SetupParse(const char *Name, const char *Value)
     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 @@ const char * cHdffSetup::GetVideoConversionString(void)
     }
 }
 
+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 @@ cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf)
     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 @@ cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf)
         "RC6",
     };
 
+    static const char * TrueColorFormatItems[kTrueColorFormats] =
+    {
+        "ARGB8888",
+        "ARGB8565",
+        "ARGB4444",
+    };
+
     mHdffCmdIf = pHdffCmdIf;
     mNewHdffSetup = gHdffSetup;
 
@@ -265,6 +285,7 @@ cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf)
     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 @@ void cHdffSetupPage::Store(void)
     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 @@ void cHdffSetupPage::Store(void)
         {
             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 --git a/PLUGINS/src/dvbhddevice/setup.h b/PLUGINS/src/dvbhddevice/setup.h
index a413614..582ee76 100644
--- a/PLUGINS/src/dvbhddevice/setup.h
+++ b/PLUGINS/src/dvbhddevice/setup.h
@@ -18,6 +18,7 @@ struct cHdffSetup
     HdffVideoMode_t GetVideoMode(void);
     void SetNextVideoConversion(void);
     const char * GetVideoConversionString(void);
+    void SetVideoFormat(HDFF::cHdffCmdIf * HdffCmdIf);
 
     int Resolution;
     int VideoModeAdaption;
@@ -36,6 +37,7 @@ struct cHdffSetup
 
     int HighLevelOsd;
     int TrueColorOsd;
+    int TrueColorFormat;
 
     int HideMainMenu;
 };
diff --git a/PLUGINS/src/dvbsddevice/HISTORY b/PLUGINS/src/dvbsddevice/HISTORY
index cb6753a..94e31dd 100644
--- a/PLUGINS/src/dvbsddevice/HISTORY
+++ b/PLUGINS/src/dvbsddevice/HISTORY
@@ -50,3 +50,12 @@ VDR Plugin 'dvbsddevice' Revision History
 
 - 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.
diff --git a/PLUGINS/src/dvbsddevice/Makefile b/PLUGINS/src/dvbsddevice/Makefile
index f7e73b2..a864774 100644
--- a/PLUGINS/src/dvbsddevice/Makefile
+++ b/PLUGINS/src/dvbsddevice/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/dvbsddevice/dvbsddevice.c b/PLUGINS/src/dvbsddevice/dvbsddevice.c
index 6b1621c..0d06f17 100644
--- a/PLUGINS/src/dvbsddevice/dvbsddevice.c
+++ b/PLUGINS/src/dvbsddevice/dvbsddevice.c
@@ -3,14 +3,14 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsddevice.c 1.10.1.1 2013/08/22 08:30:52 kls Exp $
+ * $Id: dvbsddevice.c 3.3 2014/03/15 12:28:14 kls Exp $
  */
 
 #include <getopt.h>
 #include <vdr/plugin.h>
 #include "dvbsdffdevice.h"
 
-static const char *VERSION        = "2.0.1";
+static const char *VERSION        = "2.1.2";
 static const char *DESCRIPTION    = "SD Full Featured DVB device";
 
 class cPluginDvbsddevice : public cPlugin {
diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
index 8dd69c2..5711e04 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
+++ b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
@@ -3,7 +3,7 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: dvbsdffdevice.c 2.35.1.1 2014/02/27 17:10:51 kls Exp $
+ * $Id: dvbsdffdevice.c 3.3 2014/03/15 12:35:21 kls Exp $
  */
 
 #include "dvbsdffdevice.h"
@@ -241,21 +241,6 @@ void cDvbSdFfDevice::SetVideoFormat(bool VideoFormat16_9)
   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) {
@@ -593,7 +578,7 @@ int64_t cDvbSdFfDevice::GetSTC(void)
   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 --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h b/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
index bbf2ec2..f7348d0 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
+++ b/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
@@ -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:
 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 @@ protected:
   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 --git a/PLUGINS/src/dvbsddevice/dvbsdffosd.c b/PLUGINS/src/dvbsddevice/dvbsdffosd.c
index 46c65e0..1a8caf8 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffosd.c
+++ b/PLUGINS/src/dvbsddevice/dvbsdffosd.c
@@ -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 --git a/PLUGINS/src/dvbsddevice/dvbsdffosd.h b/PLUGINS/src/dvbsddevice/dvbsdffosd.h
index bf12343..b832208 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffosd.h
+++ b/PLUGINS/src/dvbsddevice/dvbsdffosd.h
@@ -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 --git a/PLUGINS/src/epgtableid0/HISTORY b/PLUGINS/src/epgtableid0/HISTORY
index da97932..9f1eb7b 100644
--- a/PLUGINS/src/epgtableid0/HISTORY
+++ b/PLUGINS/src/epgtableid0/HISTORY
@@ -16,3 +16,7 @@ VDR Plugin 'epgtableid0' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/epgtableid0/Makefile b/PLUGINS/src/epgtableid0/Makefile
index 6190e62..e3c0c7b 100644
--- a/PLUGINS/src/epgtableid0/Makefile
+++ b/PLUGINS/src/epgtableid0/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/epgtableid0/epgtableid0.c b/PLUGINS/src/epgtableid0/epgtableid0.c
index 1aef2a7..db668d1 100644
--- a/PLUGINS/src/epgtableid0/epgtableid0.c
+++ b/PLUGINS/src/epgtableid0/epgtableid0.c
@@ -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.1 2014/01/05 10:56:10 kls Exp $
  */
 
 #include <vdr/epg.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.1.1";
 static const char *DESCRIPTION    = "EPG handler for events with table id 0x00";
 
 // --- cTable0Handler --------------------------------------------------------
diff --git a/PLUGINS/src/hello/HISTORY b/PLUGINS/src/hello/HISTORY
index 250f36a..f9d05a8 100644
--- a/PLUGINS/src/hello/HISTORY
+++ b/PLUGINS/src/hello/HISTORY
@@ -86,3 +86,7 @@ VDR Plugin 'hello' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/hello/Makefile b/PLUGINS/src/hello/Makefile
index 4bdd5c9..455721f 100644
--- a/PLUGINS/src/hello/Makefile
+++ b/PLUGINS/src/hello/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/hello/hello.c b/PLUGINS/src/hello/hello.c
index 21f9383..ecc9170 100644
--- a/PLUGINS/src/hello/hello.c
+++ b/PLUGINS/src/hello/hello.c
@@ -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.1 2014/01/05 10:56:14 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.1.1";
 static const char *DESCRIPTION    = trNOOP("A friendly greeting");
 static const char *MAINMENUENTRY  = trNOOP("Hello");
 
diff --git a/PLUGINS/src/osddemo/HISTORY b/PLUGINS/src/osddemo/HISTORY
index 99674af..4c3bfe6 100644
--- a/PLUGINS/src/osddemo/HISTORY
+++ b/PLUGINS/src/osddemo/HISTORY
@@ -60,7 +60,11 @@ VDR Plugin 'osddemo' Revision History
 
 - Official release.
 
-2014-02-06: Version 2.0.1
+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).
diff --git a/PLUGINS/src/osddemo/Makefile b/PLUGINS/src/osddemo/Makefile
index 9b7e2c1..30e3de4 100644
--- a/PLUGINS/src/osddemo/Makefile
+++ b/PLUGINS/src/osddemo/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/osddemo/osddemo.c b/PLUGINS/src/osddemo/osddemo.c
index 296fc78..0a826b7 100644
--- a/PLUGINS/src/osddemo/osddemo.c
+++ b/PLUGINS/src/osddemo/osddemo.c
@@ -3,13 +3,13 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: osddemo.c 2.12.1.2 2014/02/06 11:59:40 kls Exp $
+ * $Id: osddemo.c 3.3 2014/02/06 11:51:53 kls Exp $
  */
 
 #include <vdr/osd.h>
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.1";
+static const char *VERSION        = "2.1.2";
 static const char *DESCRIPTION    = "Demo of arbitrary OSD setup";
 static const char *MAINMENUENTRY  = "Osd Demo";
 
diff --git a/PLUGINS/src/pictures/HISTORY b/PLUGINS/src/pictures/HISTORY
index a9995d7..20c081c 100644
--- a/PLUGINS/src/pictures/HISTORY
+++ b/PLUGINS/src/pictures/HISTORY
@@ -87,3 +87,11 @@ VDR Plugin 'pictures' Revision History
 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.
diff --git a/PLUGINS/src/pictures/Makefile b/PLUGINS/src/pictures/Makefile
index 3081e27..912e3d9 100644
--- a/PLUGINS/src/pictures/Makefile
+++ b/PLUGINS/src/pictures/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/pictures/entry.c b/PLUGINS/src/pictures/entry.c
index 8c124b5..ce2a3e2 100644
--- a/PLUGINS/src/pictures/entry.c
+++ b/PLUGINS/src/pictures/entry.c
@@ -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 --git a/PLUGINS/src/pictures/entry.h b/PLUGINS/src/pictures/entry.h
index 60b7773..9de4dd3 100644
--- a/PLUGINS/src/pictures/entry.h
+++ b/PLUGINS/src/pictures/entry.h
@@ -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 --git a/PLUGINS/src/pictures/menu.c b/PLUGINS/src/pictures/menu.c
index 8c51d52..567f9e7 100644
--- a/PLUGINS/src/pictures/menu.c
+++ b/PLUGINS/src/pictures/menu.c
@@ -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 --git a/PLUGINS/src/pictures/menu.h b/PLUGINS/src/pictures/menu.h
index 9090fd0..585b0d5 100644
--- a/PLUGINS/src/pictures/menu.h
+++ b/PLUGINS/src/pictures/menu.h
@@ -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 --git a/PLUGINS/src/pictures/pic2mpg b/PLUGINS/src/pictures/pic2mpg
index 2ba8de7..8bb6c2e 100755
--- a/PLUGINS/src/pictures/pic2mpg
+++ b/PLUGINS/src/pictures/pic2mpg
@@ -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 @@ Options: -f             Force conversion
          -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 @@ $Force     = $opt_f;
 $Overscan  = $opt_o || 0;
 $Size      = $opt_s || "1920x1080";
 $Verbose   = $opt_v;
+ at Exclude   = split(',', $opt_x || "");
 
 $ListFiles = $Verbose >= 1;
 $Detailed  = $Verbose >= 2;
@@ -85,7 +87,11 @@ chdir($PICDIR) || die "$PICDIR: $!\n";
 @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 --git a/PLUGINS/src/pictures/pictures.c b/PLUGINS/src/pictures/pictures.c
index 7a46a5f..de50f1e 100644
--- a/PLUGINS/src/pictures/pictures.c
+++ b/PLUGINS/src/pictures/pictures.c
@@ -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.1 2014/01/05 10:56:20 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.1.1";
 static const char *DESCRIPTION   = trNOOP("A simple picture viewer");
 static const char *MAINMENUENTRY = trNOOP("Pictures");
 
diff --git a/PLUGINS/src/pictures/player.c b/PLUGINS/src/pictures/player.c
index 6048d4c..d2dbd7b 100644
--- a/PLUGINS/src/pictures/player.c
+++ b/PLUGINS/src/pictures/player.c
@@ -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 @@ cString cPictureControl::GetHeader(void)
 
 eOSState cPictureControl::ProcessKey(eKeys Key)
 {
-  switch (Key) {
+  switch (int(Key)) {
     case kUp:
     case kPlay:   slideShowDelay.Set();
                   slideShow = true;
diff --git a/PLUGINS/src/pictures/player.h b/PLUGINS/src/pictures/player.h
index 7a0b401..cf18d35 100644
--- a/PLUGINS/src/pictures/player.h
+++ b/PLUGINS/src/pictures/player.h
@@ -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 --git a/PLUGINS/src/rcu/HISTORY b/PLUGINS/src/rcu/HISTORY
index b9043bb..1e330eb 100644
--- a/PLUGINS/src/rcu/HISTORY
+++ b/PLUGINS/src/rcu/HISTORY
@@ -20,3 +20,7 @@ VDR Plugin 'rcu' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/rcu/Makefile b/PLUGINS/src/rcu/Makefile
index 3dbecdd..25bde4a 100644
--- a/PLUGINS/src/rcu/Makefile
+++ b/PLUGINS/src/rcu/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/rcu/rcu.c b/PLUGINS/src/rcu/rcu.c
index 17b538a..5fb210c 100644
--- a/PLUGINS/src/rcu/rcu.c
+++ b/PLUGINS/src/rcu/rcu.c
@@ -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.1 2014/01/05 10:56:22 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.1.1";
 static const char *DESCRIPTION    = "Remote Control Unit";
 
 #define REPEATLIMIT      150 // ms
diff --git a/PLUGINS/src/servicedemo/HISTORY b/PLUGINS/src/servicedemo/HISTORY
index b1c7480..9565b7d 100644
--- a/PLUGINS/src/servicedemo/HISTORY
+++ b/PLUGINS/src/servicedemo/HISTORY
@@ -21,3 +21,7 @@ VDR Plugin 'servicedemo' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/servicedemo/Makefile b/PLUGINS/src/servicedemo/Makefile
index e5ba5f5..128b9c7 100644
--- a/PLUGINS/src/servicedemo/Makefile
+++ b/PLUGINS/src/servicedemo/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN1).c | awk '{ pr
 ### 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 --git a/PLUGINS/src/servicedemo/svccli.c b/PLUGINS/src/servicedemo/svccli.c
index eb671ac..4758161 100644
--- a/PLUGINS/src/servicedemo/svccli.c
+++ b/PLUGINS/src/servicedemo/svccli.c
@@ -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.1 2014/01/05 10:56:24 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.1.1";
 static const char *DESCRIPTION    = "Service demo client";
 static const char *MAINMENUENTRY  = "Service demo";
 
diff --git a/PLUGINS/src/servicedemo/svcsvr.c b/PLUGINS/src/servicedemo/svcsvr.c
index 252a4a2..c55f6bd 100644
--- a/PLUGINS/src/servicedemo/svcsvr.c
+++ b/PLUGINS/src/servicedemo/svcsvr.c
@@ -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.1 2014/01/05 10:56:26 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.1.1";
 static const char *DESCRIPTION    = "Service demo server";
 
 class cPluginSvcSvr : public cPlugin {
diff --git a/PLUGINS/src/skincurses/HISTORY b/PLUGINS/src/skincurses/HISTORY
index 6359507..a2cdb60 100644
--- a/PLUGINS/src/skincurses/HISTORY
+++ b/PLUGINS/src/skincurses/HISTORY
@@ -118,3 +118,7 @@ VDR Plugin 'skincurses' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/skincurses/Makefile b/PLUGINS/src/skincurses/Makefile
index fa0ce41..812fe97 100644
--- a/PLUGINS/src/skincurses/Makefile
+++ b/PLUGINS/src/skincurses/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c
index 8a4da96..db49ea4 100644
--- a/PLUGINS/src/skincurses/skincurses.c
+++ b/PLUGINS/src/skincurses/skincurses.c
@@ -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.1 2014/01/05 10:56:27 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.1.1";
 static const char *DESCRIPTION    = trNOOP("A text only skin");
 static const char *MAINMENUENTRY  = NULL;
 
diff --git a/PLUGINS/src/status/HISTORY b/PLUGINS/src/status/HISTORY
index bbf2c69..7a80842 100644
--- a/PLUGINS/src/status/HISTORY
+++ b/PLUGINS/src/status/HISTORY
@@ -60,3 +60,7 @@ VDR Plugin 'status' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/status/Makefile b/PLUGINS/src/status/Makefile
index 8f6e26f..40a0351 100644
--- a/PLUGINS/src/status/Makefile
+++ b/PLUGINS/src/status/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/status/status.c b/PLUGINS/src/status/status.c
index e9e3ae7..cbf0c6e 100644
--- a/PLUGINS/src/status/status.c
+++ b/PLUGINS/src/status/status.c
@@ -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.1 2014/01/05 10:56:29 kls Exp $
  */
 
 #include <vdr/plugin.h>
 #include <vdr/status.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.1.1";
 static const char *DESCRIPTION    = "Status monitor test";
 static const char *MAINMENUENTRY  = NULL;
 
diff --git a/PLUGINS/src/svdrpdemo/HISTORY b/PLUGINS/src/svdrpdemo/HISTORY
index c6f0395..6207479 100644
--- a/PLUGINS/src/svdrpdemo/HISTORY
+++ b/PLUGINS/src/svdrpdemo/HISTORY
@@ -25,3 +25,7 @@ VDR Plugin 'svdrpdemo' Revision History
 2013-03-31: Version 2.0.0
 
 - Official release.
+
+2014-01-01: Version 2.1.1
+
+- Avoiding unnecessary pkg-config warnings in plugin Makefiles.
diff --git a/PLUGINS/src/svdrpdemo/Makefile b/PLUGINS/src/svdrpdemo/Makefile
index 2e39140..9a8235a 100644
--- a/PLUGINS/src/svdrpdemo/Makefile
+++ b/PLUGINS/src/svdrpdemo/Makefile
@@ -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 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### 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 --git a/PLUGINS/src/svdrpdemo/svdrpdemo.c b/PLUGINS/src/svdrpdemo/svdrpdemo.c
index a1a2766..34f513e 100644
--- a/PLUGINS/src/svdrpdemo/svdrpdemo.c
+++ b/PLUGINS/src/svdrpdemo/svdrpdemo.c
@@ -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.1 2014/01/05 10:56:31 kls Exp $
  */
 
 #include <vdr/plugin.h>
 
-static const char *VERSION        = "2.0.0";
+static const char *VERSION        = "2.1.1";
 static const char *DESCRIPTION    = "How to add SVDRP support to a plugin";
 
 class cPluginSvdrpdemo : public cPlugin {
diff --git a/args.c b/args.c
new file mode 100644
index 0000000..6a31605
--- /dev/null
+++ b/args.c
@@ -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 at 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 --git a/args.h b/args.h
new file mode 100644
index 0000000..2e91afb
--- /dev/null
+++ b/args.h
@@ -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 at 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 --git a/audio.c b/audio.c
index abffa3a..ebb98ed 100644
--- a/audio.c
+++ b/audio.c
@@ -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 --git a/audio.h b/audio.h
index f99a84a..37d0b51 100644
--- a/audio.h
+++ b/audio.h
@@ -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 --git a/channels.c b/channels.c
index 67245ac..564088f 100644
--- a/channels.c
+++ b/channels.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.c 2.24.1.1 2013/10/11 11:40:02 kls Exp $
+ * $Id: channels.c 3.8 2015/02/01 13:47:05 kls Exp $
  */
 
 #include "channels.h"
@@ -29,7 +29,7 @@ tChannelID tChannelID::FromString(const char *s)
   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 @@ cChannel::cChannel(void)
   memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
   parameters = "";
   modification = CHANNELMOD_NONE;
+  seen         = 0;
   schedule     = NULL;
   linkChannels = NULL;
   refChannel   = NULL;
@@ -220,7 +221,7 @@ bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const ch
      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 @@ void cChannel::SetId(int Nid, int Tid, int Sid, int Rid)
 {
   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::SetId(int Nid, int Tid, int Sid, int Rid)
      }
 }
 
+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 @@ void cChannel::SetPortalName(const char *PortalName)
 {
   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 @@ static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[]
 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 @@ void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, c
      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 @@ void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, c
      spids[MAXSPIDS] = 0;
      tpid = Tpid;
      modification |= mod;
-     Channels.SetModified();
+     if (Number())
+        Channels.SetModified();
      }
 }
 
@@ -408,6 +421,11 @@ void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *Compos
      }
 }
 
+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 @@ void cChannel::SetCaIds(const int *CaIds)
      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 @@ void cChannel::SetCaDescriptors(int Level)
      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 @@ void cChannel::SetLinkChannels(cLinkChannels *LinkChannels)
      }
   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);
@@ -586,7 +604,7 @@ bool cChannel::Parse(const char *s)
      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
@@ -1016,6 +1034,7 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, c
      NewChannel->CopyTransponderData(Transponder);
      NewChannel->SetId(Nid, Tid, Sid, Rid);
      NewChannel->SetName(Name, ShortName, Provider);
+     NewChannel->SetSeen();
      Add(NewChannel);
      ReNumber();
      return NewChannel;
@@ -1023,6 +1042,22 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, c
   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 --git a/channels.h b/channels.h
index 815cb47..3323882 100644
--- a/channels.h
+++ b/channels.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.h 2.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 @@ private:
   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 @@ private:
   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 @@ public:
   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 @@ public:
   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 @@ public:
   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 @@ public:
       ///< 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 --git a/ci.c b/ci.c
index fe38bc6..b1958bd 100644
--- a/ci.c
+++ b/ci.c
@@ -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.1.1 2014/01/22 09:43:48 kls Exp $
+ * $Id: ci.c 3.18 2015/01/30 12:24:53 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 @@ static char *GetString(int &Length, const uint8_t **Data)
   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 @@ void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
   *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 @@ bool cCiApplicationInformation::EnterMenu(void)
 #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 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
      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 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
                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: {
@@ -1493,7 +1679,6 @@ public:
 cCiAdapter::cCiAdapter(void)
 :cThread("CI adapter")
 {
-  assignedDevice = NULL;
   for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
       camSlots[i] = NULL;
 }
@@ -1519,13 +1704,15 @@ void cCiAdapter::AddCamSlot(cCamSlot *CamSlot)
      }
 }
 
-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)
@@ -1549,14 +1736,15 @@ void cCiAdapter::Action(void)
 
 // --- 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;
@@ -1573,6 +1761,10 @@ cCamSlot::cCamSlot(cCiAdapter *CiAdapter)
 
 cCamSlot::~cCamSlot()
 {
+  if (assignedDevice)
+     assignedDevice->SetCamSlot(NULL);
+  delete caPidReceiver;
+  delete caActivationReceiver;
   CamSlots.Del(this, false);
   DeleteAllConnections();
 }
@@ -1582,19 +1774,21 @@ bool cCamSlot::Assign(cDevice *Device, bool Query)
   cMutexLock MutexLock(&mutex);
   if (ciAdapter) {
      if (ciAdapter->Assign(Device, true)) {
-        if (!Device && ciAdapter->assignedDevice)
-           ciAdapter->assignedDevice->SetCamSlot(NULL);
+        if (!Device && assignedDevice)
+           assignedDevice->SetCamSlot(NULL);
         if (!Query) {
            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;
@@ -1605,17 +1799,6 @@ bool cCamSlot::Assign(cDevice *Device, bool Query)
   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);
@@ -1641,6 +1824,8 @@ void cCamSlot::DeleteAllConnections(void)
 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) {
@@ -1664,6 +1849,7 @@ void cCamSlot::Process(cTPDU *TPDU)
                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);
@@ -1725,6 +1911,37 @@ bool cCamSlot::Reset(void)
   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);
@@ -1803,6 +2020,10 @@ void cCamSlot::SendCaPmt(uint8_t CmdId)
      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) {
@@ -1815,6 +2036,15 @@ void cCamSlot::SendCaPmt(uint8_t CmdId)
                              }
                           }
                       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())
@@ -1824,11 +2054,20 @@ void cCamSlot::SendCaPmt(uint8_t CmdId)
                       }
                    }
                }
+           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();
+              }
            }
         }
      }
@@ -1985,6 +2224,32 @@ bool cCamSlot::IsDecrypting(void)
   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 --git a/ci.h b/ci.h
index 74e0270..5a4dc98 100644
--- a/ci.h
+++ b/ci.h
@@ -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 @@ enum eModuleStatus { msNone, msReset, msPresent, msReady };
 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 @@ public:
   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 @@ private:
   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 @@ private:
   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 @@ public:
        ///< 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 @@ public:
        ///< 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 --git a/config.c b/config.c
index 7a153cb..9e6f301 100644
--- a/config.c
+++ b/config.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.c 2.38 2013/03/18 08:57:50 kls Exp $
+ * $Id: config.c 3.6 2015/01/29 09:01:30 kls Exp $
  */
 
 #include "config.h"
@@ -389,6 +389,12 @@ cSetup::cSetup(void)
   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,16 @@ cSetup::cSetup(void)
   ShowRemainingTime = 0;
   ProgressDisplayTime = 0;
   PauseOnMarkSet = 0;
+  PauseOnMarkJump = 1;
+  SkipEdited = 0;
+  PauseAtLastMark = 0;
+  BinarySkipInitial = 120;
+  BinarySkipTimeout = 3;
   ResumeID = 0;
   CurrentChannel = -1;
   CurrentVolume = MAXVOLUME;
+  VolumeSteps = 51;
+  VolumeLinearize = 0;
   CurrentDolby = 0;
   InitialChannel = "";
   DeviceBondings = "";
@@ -594,6 +607,12 @@ bool cSetup::Parse(const char *Name, const char *Value)
   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 +688,18 @@ bool cSetup::Parse(const char *Name, const char *Value)
   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, "BinarySkipInitial"))   BinarySkipInitial  = atoi(Value);
+  else if (!strcasecmp(Name, "BinarySkipTimeout"))   BinarySkipTimeout  = 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 +729,12 @@ bool cSetup::Save(void)
   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 +810,18 @@ bool cSetup::Save(void)
   Store("ShowRemainingTime",  ShowRemainingTime);
   Store("ProgressDisplayTime",ProgressDisplayTime);
   Store("PauseOnMarkSet",     PauseOnMarkSet);
+  Store("PauseOnMarkJump",    PauseOnMarkJump);
+  Store("SkipEdited",         SkipEdited);
+  Store("PauseAtLastMark",    PauseAtLastMark);
+  Store("BinarySkipInitial",  BinarySkipInitial);
+  Store("BinarySkipTimeout",  BinarySkipTimeout);
   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 --git a/config.h b/config.h
index b4d3756..6eeda2c 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.h 2.76.1.7 2014/03/22 11:00:00 kls Exp $
+ * $Id: config.h 3.13 2015/01/27 10:50:11 kls Exp $
  */
 
 #ifndef __CONFIG_H
@@ -22,13 +22,13 @@
 
 // VDR's own version number:
 
-#define VDRVERSION  "2.0.6"
-#define VDRVERSNUM   20006  // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION  "2.1.8"
+#define VDRVERSNUM   20108  // Version * 10000 + Major * 100 + Minor
 
 // The plugin API's version number:
 
-#define APIVERSION  "2.0.6"
-#define APIVERSNUM   20006  // Version * 10000 + Major * 100 + Minor
+#define APIVERSION  "2.1.8"
+#define APIVERSNUM   20108  // 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
@@ -266,6 +266,12 @@ public:
   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 +333,16 @@ public:
   int ShowRemainingTime;
   int ProgressDisplayTime;
   int PauseOnMarkSet;
+  int PauseOnMarkJump;
+  int SkipEdited;
+  int PauseAtLastMark;
+  int BinarySkipInitial;
+  int BinarySkipTimeout;
   int ResumeID;
   int CurrentChannel;
   int CurrentVolume;
+  int VolumeSteps;
+  int VolumeLinearize;
   int CurrentDolby;
   int InitialVolume;
   int ChannelsWrap;
diff --git a/cutter.c b/cutter.c
index 32c4e31..9aee66b 100644
--- a/cutter.c
+++ b/cutter.c
@@ -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.1.2 2013/08/21 13:43:46 kls Exp $
+ * $Id: cutter.c 3.4 2013/10/02 13:18:02 kls Exp $
  */
 
 #include "cutter.h"
@@ -642,46 +642,47 @@ void cCuttingThread::Action(void)
 
 // --- 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 @@ bool cCutter::Start(const char *FileName)
 
 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 @@ void cCutter::Stop(void)
         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 @@ bool CutRecording(const char *FileName)
         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 --git a/cutter.h b/cutter.h
index f48ae3f..c67429a 100644
--- a/cutter.h
+++ b/cutter.h
@@ -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 cCuttingThread;
 
 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 --git a/device.c b/device.c
index 6154b6d..14ab07d 100644
--- a/device.c
+++ b/device.c
@@ -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.1.4 2014/03/11 09:29:52 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 @@ cDevice::cDevice(void)
   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;
@@ -358,6 +359,7 @@ bool cDevice::HasCi(void)
 
 void cDevice::SetCamSlot(cCamSlot *CamSlot)
 {
+  LOCK_THREAD;
   camSlot = CamSlot;
 }
 
@@ -429,11 +431,6 @@ void cDevice::SetVideoFormat(bool VideoFormat16_9)
 {
 }
 
-eVideoSystem cDevice::GetVideoSystem(void)
-{
-  return vsPAL;
-}
-
 void cDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
 {
   Width = 0;
@@ -666,6 +663,11 @@ int cDevice::NumProvidedSystems(void) const
   return 0;
 }
 
+const cPositioner *cDevice::Positioner(void) const
+{
+  return NULL;
+}
+
 int cDevice::SignalStrength(void) const
 {
   return -1;
@@ -694,7 +696,7 @@ bool cDevice::MaySwitchTransponder(const cChannel *Channel) const
 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--;) {
@@ -754,7 +756,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
 
   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;
 
@@ -762,7 +767,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
   // 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
@@ -914,8 +919,10 @@ void cDevice::SetAudioChannel(int AudioChannel)
 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) {
@@ -1117,7 +1124,7 @@ int64_t cDevice::GetSTC(void)
   return -1;
 }
 
-void cDevice::TrickSpeed(int Speed)
+void cDevice::TrickSpeed(int Speed, bool Forward)
 {
 }
 
@@ -1578,13 +1585,13 @@ void cDevice::Action(void)
                  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;
                           }
@@ -1598,7 +1605,8 @@ void cDevice::Action(void)
                  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]);
                            }
@@ -1662,7 +1670,7 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
          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);
             }
@@ -1693,8 +1701,13 @@ void cDevice::Detach(cReceiver *Receiver)
       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);
 }
@@ -1722,7 +1735,7 @@ void cDevice::DetachAllReceivers(void)
 
 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;
@@ -1760,7 +1773,7 @@ void cTSBuffer::Action(void)
      }
 }
 
-uchar *cTSBuffer::Get(void)
+uchar *cTSBuffer::Get(int *Available)
 {
   int Count = 0;
   if (delivered) {
@@ -1781,7 +1794,15 @@ uchar *cTSBuffer::Get(void)
         return NULL;
         }
      delivered = true;
+     if (Available)
+        *Available = Count;
      return p;
      }
   return NULL;
 }
+
+void cTSBuffer::Skip(int Count)
+{
+  ringBuffer->Del(Count);
+  delivered = false;
+}
diff --git a/device.h b/device.h
index 6ba5c38..b06d977 100644
--- a/device.h
+++ b/device.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: device.h 2.47.1.1 2013/08/22 12:01:48 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 @@ enum ePlayMode { pmNone,           // audio/video from decoder
                  // KNOWN TO YOUR PLAYER.
                };
 
+#define DEPRECATED_VIDEOSYSTEM
+#ifdef DEPRECATED_VIDEOSYSTEM
 enum eVideoSystem { vsPAL,
                     vsNTSC
                   };
+#endif
 
 enum eVideoDisplayFormat { vdfPanAndScan,
                            vdfLetterBox,
@@ -200,7 +205,7 @@ public:
   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 @@ public:
          ///< 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 @@ public:
          ///< 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 @@ protected:
   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 @@ public:
   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 @@ public:
        ///< 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 @@ private:
   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 --git a/diseqc.c b/diseqc.c
index 97da936..de64257 100644
--- a/diseqc.c
+++ b/diseqc.c
@@ -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.1.1 2013/05/02 09:33:12 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 @@ static bool ParseDeviceNumbers(const char *s, int &Devices)
   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 @@ cScr::cScr(void)
 
 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 @@ bool cScr::Parse(const char *s)
 
 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 @@ cDiseqc::cDiseqc(void)
   slof = 0;
   polarization = 0;
   lof = 0;
+  position = -1;
   scrBank = -1;
   commands = NULL;
   parsing = false;
@@ -108,15 +223,14 @@ cDiseqc::~cDiseqc()
 
 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 @@ bool cDiseqc::Parse(const char *s)
 
 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;
+  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 {
-     Codes[2] = 0x5A;
-     return 5;
+  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 @@ const char *cDiseqc::Wait(const char *s) const
   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;
@@ -250,8 +412,12 @@ cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Code
           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 @@ cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Code
                        return daCodes;
                        }
                     break;
-          default: return daNone;
+          default:  esyslog("ERROR: unknown diseqc code '%c'", *(*CurrentAction - 1));
+                    return daNone;
           }
         }
   return daNone;
@@ -271,17 +438,18 @@ cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Code
 
 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 --git a/diseqc.conf b/diseqc.conf
index 2895c43..77324b8 100644
--- a/diseqc.conf
+++ b/diseqc.conf
@@ -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 @@ S13.0E  99999 H 10600  t V W15 [E0 10 38 F7] W15 B W15 T
 # 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 H 10600  t V W15 [E0 10 38 F7] W15 B W15 T
 # 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 --git a/diseqc.h b/diseqc.h
index 6e9058c..eef36a6 100644
--- a/diseqc.h
+++ b/diseqc.h
@@ -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 @@ class cScrs : public cConfig<cScr> {
 private:
   cMutex mutex;
 public:
+  bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
   cScr *GetUnused(int Device);
   };
 
@@ -50,8 +69,11 @@ public:
     daVoltage18,
     daMiniA,
     daMiniB,
+    daPositionN,
+    daPositionA,
     daScr,
     daCodes,
+    daWait,
     };
   enum { MaxDiseqcCodes = 6 };
 private:
@@ -60,12 +82,14 @@ private:
   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 @@ public:
       ///< 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 --git a/dvbci.c b/dvbci.c
index 5289bbd..fa65f0f 100644
--- a/dvbci.c
+++ b/dvbci.c
@@ -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 --git a/dvbci.h b/dvbci.h
index adbe40d..f74703c 100644
--- a/dvbci.h
+++ b/dvbci.h
@@ -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 --git a/dvbdevice.c b/dvbdevice.c
index 0ccd162..9321f16 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbdevice.c 2.88.1.4 2013/10/21 09:01:21 kls Exp $
+ * $Id: dvbdevice.c 3.14 2015/01/14 12:09:19 kls Exp $
  */
 
 #include "dvbdevice.h"
@@ -36,6 +36,13 @@ static int DvbApiVersion = 0x0000; // the version of the DVB driver actually in
 
 // --- 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") },
@@ -206,6 +213,9 @@ cDvbTransponderParameters::cDvbTransponderParameters(const char *Parameters)
   hierarchy    = HIERARCHY_AUTO;
   rollOff      = ROLLOFF_AUTO;
   streamId     = 0;
+  t2systemId   = 0;
+  sisoMiso     = 0;
+  pilot        = PILOT_AUTO;
   Parse(Parameters);
 }
 
@@ -227,10 +237,13 @@ cString cDvbTransponderParameters::ToString(char Type) const
   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;
 }
@@ -263,12 +276,15 @@ bool cDvbTransponderParameters::Parse(const char *s)
           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;
@@ -284,7 +300,7 @@ bool cDvbTransponderParameters::Parse(const char *s)
 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;
@@ -295,6 +311,9 @@ private:
   time_t lastTimeoutReport;
   cChannel channel;
   const cDiseqc *lastDiseqc;
+  int diseqcOffset;
+  int lastSource;
+  cPositioner *positioner;
   const cScr *scr;
   bool lnbPowerTurnedOn;
   eTunerStatus tunerStatus;
@@ -309,6 +328,7 @@ private:
   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);
@@ -325,6 +345,7 @@ public:
   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;
   };
@@ -343,12 +364,15 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int
   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();
 }
 
@@ -482,6 +506,7 @@ void cDvbTuner::SetChannel(const cChannel *Channel)
      cMutexLock MutexLock(&mutex);
      if (!IsTunedTo(Channel))
         tunerStatus = tsSet;
+     diseqcOffset = 0;
      channel = *Channel;
      lastTimeoutReport = 0;
      newSet.Broadcast();
@@ -662,6 +687,15 @@ static unsigned int FrequencyToHz(unsigned int f)
   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) {
@@ -673,23 +707,47 @@ void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency)
      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();
@@ -713,7 +771,7 @@ static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTranspon
   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;
 }
 
@@ -752,7 +810,7 @@ bool cDvbTuner::SetFrontend(void)
      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)
@@ -761,10 +819,11 @@ bool cDvbTuner::SetFrontend(void)
               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;
            }
         }
@@ -796,7 +855,7 @@ bool cDvbTuner::SetFrontend(void)
      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());
         if (DvbApiVersion >= 0x0508)
            SETCMD(DTV_STREAM_ID, dtp.StreamId());
@@ -877,33 +936,50 @@ void cDvbTuner::Action(void)
         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;
@@ -912,7 +988,7 @@ void cDvbTuner::Action(void)
                   }
                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;
@@ -978,6 +1054,9 @@ cOsdItem *cDvbSourceParam::GetOsdItem(void)
     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;
@@ -1144,8 +1223,8 @@ bool cDvbDevice::Initialize(void)
               }
            }
      }
-  int Checked = 0;
   int Found = 0;
+  int Used = 0;
   if (Nodes.Size() > 0) {
      Nodes.Sort();
      for (int i = 0; i < Nodes.Size(); i++) {
@@ -1153,10 +1232,11 @@ bool cDvbDevice::Initialize(void)
          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
@@ -1165,9 +1245,11 @@ bool cDvbDevice::Initialize(void)
             }
          }
      }
-  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;
@@ -1257,13 +1339,6 @@ bool cDvbDevice::QueryDeliverySystems(int fd_frontend)
   return false;
 }
 
-bool cDvbDevice::Ready(void)
-{
-  if (ciAdapter)
-     return ciAdapter->Ready();
-  return true;
-}
-
 bool cDvbDevice::BondDevices(const char *Bondings)
 {
   UnBondDevices();
@@ -1357,7 +1432,7 @@ void cDvbDevice::UnBond(void)
 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;
 }
@@ -1539,6 +1614,11 @@ int cDvbDevice::NumProvidedSystems(void) const
   return numDeliverySystems + numModulations;
 }
 
+const cPositioner *cDvbDevice::Positioner(void) const
+{
+  return dvbTuner ? dvbTuner->Positioner() : NULL;
+}
+
 int cDvbDevice::SignalStrength(void) const
 {
   return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
@@ -1603,6 +1683,17 @@ void cDvbDevice::CloseDvr(void)
 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;
      }
@@ -1660,12 +1751,28 @@ uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend)
                                 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 --git a/dvbdevice.h b/dvbdevice.h
index 76b8665..0a148ce 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -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.1.1 2013/04/09 13:43:33 kls Exp $
+ * $Id: dvbdevice.h 3.5 2014/03/16 10:38:31 kls Exp $
  */
 
 #ifndef __DVBDEVICE_H
@@ -67,7 +67,6 @@ enum {
 
 // --- End of definitions for older DVB API versions -------------------------
 
-#define MAXDVBDEVICES  8
 #define MAXDELIVERYSYSTEMS 8
 
 #define DEV_VIDEO         "/dev/video"
@@ -93,6 +92,7 @@ int MapToDriver(int Value, const tDvbParameterMap *Map);
 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[];
@@ -119,6 +119,9 @@ private:
   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:
@@ -135,6 +138,9 @@ public:
   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; }
@@ -147,6 +153,9 @@ public:
   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);
   };
@@ -186,7 +195,6 @@ public:
   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);
@@ -233,6 +241,7 @@ public:
   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 --git a/dvbplayer.c b/dvbplayer.c
index dc206a3..0d7b390 100644
--- a/dvbplayer.c
+++ b/dvbplayer.c
@@ -4,7 +4,7 @@
  * 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.3 2015/02/01 10:45:41 kls Exp $
  */
 
 #include "dvbplayer.h"
@@ -211,6 +211,7 @@ private:
   cNonBlockingFileReader *nonBlockingFileReader;
   cRingBufferFrame *ringBuffer;
   cPtsIndex ptsIndex;
+  cMarks marks;
   cFileName *fileName;
   cIndexFile *index;
   cUnbufferedFile *replayFile;
@@ -296,6 +297,8 @@ cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
      }
   else if (PauseLive)
      framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
+  if (Setup.SkipEdited || Setup.PauseAtLastMark)
+     marks.Load(FileName, framesPerSecond, isPesRecording);
 }
 
 cDvbPlayer::~cDvbPlayer()
@@ -324,7 +327,7 @@ void cDvbPlayer::TrickSpeed(int Increment)
      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);
      }
 }
 
@@ -402,8 +405,19 @@ void cDvbPlayer::Action(void)
   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) {
+     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;
+           }
+        }
+     }
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
@@ -413,6 +427,8 @@ void cDvbPlayer::Action(void)
   uint32_t LastStc = 0;
   int LastReadIFrame = -1;
   int SwitchToPlayFrame = 0;
+  bool CutIn = false;
+  bool AtLastMark = false;
 
   if (pauseLive)
      Goto(0, true);
@@ -431,7 +447,7 @@ void cDvbPlayer::Action(void)
 
           // Read the next frame from the file:
 
-          if (playMode != pmStill && playMode != pmPause) {
+          if (playMode != pmStill && playMode != pmPause && !AtLastMark) {
              if (!readFrame && (replayFile || readIndex >= 0)) {
                 if (!nonBlockingFileReader->Reading()) {
                    if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
@@ -468,8 +484,30 @@ void cDvbPlayer::Action(void)
                    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.Update();
+                            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;
+                                  }
+                               }
+                            }
+                         }
                       else
                          eof = true;
                       }
@@ -512,6 +550,11 @@ void cDvbPlayer::Action(void)
              // 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 +621,17 @@ void cDvbPlayer::Action(void)
                 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 +852,26 @@ void cDvbPlayer::Goto(int Index, bool Still)
      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;
      }
 }
 
diff --git a/dvbplayer.h b/dvbplayer.h
index e2f2082..678c969 100644
--- a/dvbplayer.h
+++ b/dvbplayer.h
@@ -4,7 +4,7 @@
  * 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.1 2015/02/01 11:20:54 kls Exp $
  */
 
 #ifndef __DVBPLAYER_H
@@ -56,7 +56,7 @@ public:
        // 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 --git a/dvbspu.c b/dvbspu.c
index 6b3c216..da18d0a 100644
--- a/dvbspu.c
+++ b/dvbspu.c
@@ -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 --git a/dvbspu.h b/dvbspu.h
index 5c7cc0a..dadf734 100644
--- a/dvbspu.h
+++ b/dvbspu.h
@@ -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 @@ class cDvbSpuDecoder:public cSpuDecoder {
     uint8_t *spu;
     uint32_t spupts;
     bool clean;
-    bool ready;
     bool restricted_osd;
 
     enum spFlag { spNONE, spHIDE, spSHOW, spMENU };
diff --git a/dvbsubtitle.c b/dvbsubtitle.c
index 9facc86..d5ae60b 100644
--- a/dvbsubtitle.c
+++ b/dvbsubtitle.c
@@ -5,9 +5,9 @@
  * how to reach the author.
  *
  * Original author: Marco Schluessler <marco at lordzodiac.de>
- * With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen at sci.fi>
+ * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen at 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 @@ cSubtitleClut::cSubtitleClut(int ClutId)
 {
   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 @@ cSubtitleClut::cSubtitleClut(int ClutId)
       }
 }
 
+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 @@ const cPalette *cSubtitleClut::GetPalette(int Bpp)
 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 @@ void cSubtitleObject::DecodeSubBlock(const uchar *Data, int Length, bool Even)
   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 @@ bool cSubtitleObject::Decode2BppCodeString(cBitStream *bs, int &x, int y, const
      }
   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 @@ bool cSubtitleObject::Decode4BppCodeString(cBitStream *bs, int &x, int y, const
      }
   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 @@ bool cSubtitleObject::Decode8BppCodeString(cBitStream *bs, int &x, int y)
      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)
+void cSubtitleRegion::Parse(cBitStream &bs)
 {
-  dbgregions("FillRegion %d\n", Index);
-  for (int y = 0; y < Height(); y++) {
-      for (int x = 0; x < Width(); x++)
-          SetIndex(x, y, Index);
-      }
+  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));
 }
 
-cSubtitleObject *cSubtitleRegion::GetObjectById(int ObjectId, bool New)
+void cSubtitleRegion::ParsePgs(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;
+  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::UpdateTextData(cSubtitleClut *Clut)
+void cSubtitleRegion::Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
 {
-  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;
+  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)
+// --- cSubtitleRegionRef ----------------------------------------------------
+
+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 (Level > 0 && Level < 4)
-     level = 1 << Level;
+  regionId = id;
+  regionHorizontalAddress = x;
+  regionVerticalAddress = y;
+  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
 }
-
-void cSubtitleRegion::SetDepth(int Depth)
+cSubtitleRegionRef::cSubtitleRegionRef(cBitStream &bs)
 {
-  if (Depth > 0 && Depth < 4)
-     SetBpp(1 << Depth);
+  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 @@ void cSubtitleRegion::SetDepth(int Depth)
 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;
 }
 
-cDvbSubtitlePage::~cDvbSubtitlePage()
+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;
 }
 
-tArea *cDvbSubtitlePage::GetAreas(double FactorX, double FactorY)
+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(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 @@ void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
 
 class cDvbSubtitleBitmaps : public cListObject {
 private:
+  int state;
   int64_t pts;
   int timeout;
   tArea *areas;
@@ -722,16 +1224,20 @@ private:
   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 @@ void cDvbSubtitleBitmaps::Draw(cOsd *Osd)
          }
      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 @@ cDvbSubtitleConverter::cDvbSubtitleConverter(void)
   windowVerticalOffset = 0;
   pages = new cList<cDvbSubtitlePage>;
   bitmaps = new cList<cDvbSubtitleBitmaps>;
+  SD.Reset();
   Start();
 }
 
@@ -818,7 +1352,7 @@ void cDvbSubtitleConverter::SetupChanged(void)
 
 void cDvbSubtitleConverter::Reset(void)
 {
-  dbgconverter("Converter reset -----------------------\n");
+  dbgconverter("converter reset -----------------------<br>\n");
   dvbSubtitleAssembler->Reset();
   Lock();
   pages->Clear();
@@ -848,9 +1382,9 @@ int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length)
         }
 
      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 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
   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 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
 }
 
 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
-#define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms
 
 void cDvbSubtitleConverter::Action(void)
 {
@@ -927,55 +1462,47 @@ void cDvbSubtitleConverter::Action(void)
            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 @@ bool cDvbSubtitleConverter::AssertOsd(void)
   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 cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t
      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 @@ int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t
             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<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\n", segmentType);
+            dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
+            return -1;
        }
      return bs.Length() / 8;
      }
@@ -1262,11 +1735,12 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
 {
   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 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
         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 --git a/dvbsubtitle.h b/dvbsubtitle.h
index 8de9f63..f178b61 100644
--- a/dvbsubtitle.h
+++ b/dvbsubtitle.h
@@ -6,7 +6,7 @@
  *
  * Original author: Marco Schluessler <marco at 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 @@ private:
   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 --git a/eit.c b/eit.c
index 5ebbfc4..30c017b 100644
--- a/eit.c
+++ b/eit.c
@@ -8,7 +8,7 @@
  * Robert Schneider <Robert.Schneider at web.de> and Rolf Hakenes <hakenes at hippomi.de>.
  * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg at gmx.de>.
  *
- * $Id: eit.c 2.23.1.1 2013/10/12 11:24:51 kls Exp $
+ * $Id: eit.c 3.5 2014/02/08 14:20:27 kls Exp $
  */
 
 #include "eit.h"
@@ -46,6 +46,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo
      return;
      }
 
+  EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus);
   bool handledExternally = EpgHandlers.HandledExternally(channel);
   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
 
@@ -53,8 +54,8 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo
   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); ) {
@@ -219,7 +220,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo
             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 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo
      Schedules->SetModified(pSchedule);
      }
   Channels.Unlock();
+  EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus);
 }
 
 // --- cTDT ------------------------------------------------------------------
diff --git a/eit.h b/eit.h
index b552372..ec30253 100644
--- a/eit.h
+++ b/eit.h
@@ -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 --git a/eitscan.c b/eitscan.c
index 027bf44..77f15c6 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: eitscan.c 2.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 --git a/eitscan.h b/eitscan.h
index 2dd24d7..25658b6 100644
--- a/eitscan.h
+++ b/eitscan.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: eitscan.h 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 --git a/epg.c b/epg.c
index ce7e7f8..096b68c 100644
--- a/epg.c
+++ b/epg.c
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider at web.de> and Rolf Hakenes <hakenes at hippomi.de>.
  *
- * $Id: epg.c 2.23.1.1 2013/09/01 09:16:53 kls Exp $
+ * $Id: epg.c 3.3 2013/12/28 11:33:08 kls Exp $
  */
 
 #include "epg.h"
@@ -32,7 +32,7 @@ cString tComponent::ToString(void)
 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;
@@ -1540,3 +1540,19 @@ void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t
       }
   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 --git a/epg.h b/epg.h
index 3a7df77..65fbcbf 100644
--- a/epg.h
+++ b/epg.h
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider at web.de> and Rolf Hakenes <hakenes at 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 @@ public:
   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 @@ public:
   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 --git a/epg2html b/epg2html
index 1b1a251..34f9521 100755
--- a/epg2html
+++ b/epg2html
@@ -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 --git a/filter.c b/filter.c
index 4de6785..72590f7 100644
--- a/filter.c
+++ b/filter.c
@@ -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 --git a/filter.h b/filter.h
index b85ab07..a00ece5 100644
--- a/filter.h
+++ b/filter.h
@@ -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 --git a/font.c b/font.c
index 9129938..cd2c494 100644
--- a/font.c
+++ b/font.c
@@ -6,7 +6,7 @@
  *
  * BiDi support by Osama Alrawab <alrawab at hotmail.com> @2008 Tripoli-Libya.
  *
- * $Id: font.c 2.13.1.2 2014/01/25 14:25:29 kls Exp $
+ * $Id: font.c 3.2 2014/01/07 12:19:45 kls Exp $
  */
 
 #include "font.h"
diff --git a/font.h b/font.h
index 0200874..cd62b4a 100644
--- a/font.h
+++ b/font.h
@@ -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.1.1 2014/01/25 14:24:51 kls Exp $
+ * $Id: font.h 3.1 2014/01/07 12:11:55 kls Exp $
  */
 
 #ifndef __FONT_H
diff --git a/i18n.c b/i18n.c
index f010b6b..f26c96b 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: i18n.c 2.5 2012/09/01 10:53:43 kls Exp $
+ * $Id: i18n.c 3.0 2012/09/01 10:53:43 kls Exp $
  */
 
 /*
diff --git a/i18n.h b/i18n.h
index 03deb6f..c3f1fc7 100644
--- a/i18n.h
+++ b/i18n.h
@@ -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 --git a/interface.c b/interface.c
index 82091d9..6bf7ffc 100644
--- a/interface.c
+++ b/interface.c
@@ -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 @@ void cInterface::LearnKeys(void)
       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 --git a/interface.h b/interface.h
index 9e10f40..b9082bd 100644
--- a/interface.h
+++ b/interface.h
@@ -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 --git a/keys.c b/keys.c
index 2b4198d..284858c 100644
--- a/keys.c
+++ b/keys.c
@@ -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 --git a/keys.h b/keys.h
index 9a79dd6..5e861a5 100644
--- a/keys.h
+++ b/keys.h
@@ -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 @@ enum eKeys { // "Up" and "Down" must be the first two keys!
 
 // 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 --git a/libsi/Makefile b/libsi/Makefile
index dff5ca9..2219af6 100644
--- a/libsi/Makefile
+++ b/libsi/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for a libsi
 #
-# $Id: Makefile 2.4 2012/12/18 13:35:54 kls Exp $
+# $Id: Makefile 3.0 2012/12/18 13:35:54 kls Exp $
 
 ### The archiver options:
 
diff --git a/libsi/descriptor.c b/libsi/descriptor.c
index 07429b5..f763d62 100644
--- a/libsi/descriptor.c
+++ b/libsi/descriptor.c
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: descriptor.c 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 @@ int T2DeliverySystemDescriptor::getTfsFlag() const {
    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);
 }
@@ -1145,4 +1187,59 @@ void RegistrationDescriptor::Parse() {
       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 --git a/libsi/descriptor.h b/libsi/descriptor.h
index 3025876..30c9f6b 100644
--- a/libsi/descriptor.h
+++ b/libsi/descriptor.h
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: descriptor.h 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 @@ public:
    StructureLoop<Language> languageLoop;
 protected:
    virtual void Parse();
-private:
-   const descr_iso_639_language *s;
 };
 
 class PDCDescriptor : public Descriptor {
@@ -557,6 +555,42 @@ private:
    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 @@ private:
    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 --git a/libsi/gendescr b/libsi/gendescr
index 8af5c69..b0628fb 100755
--- a/libsi/gendescr
+++ b/libsi/gendescr
@@ -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 --git a/libsi/headers.h b/libsi/headers.h
index 85c83b2..ff5bb5d 100644
--- a/libsi/headers.h
+++ b/libsi/headers.h
@@ -10,7 +10,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: headers.h 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 @@ struct descr_t2_delivery_system {
    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 @@ struct descr_registration {
    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 --git a/libsi/section.c b/libsi/section.c
index 8c86f46..d7a4292 100644
--- a/libsi/section.c
+++ b/libsi/section.c
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: section.c 2.0 2006/04/14 10:53:44 kls Exp $
+ *   $Id: section.c 3.0 2006/04/14 10:53:44 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff --git a/libsi/section.h b/libsi/section.h
index 5b70a22..133fba3 100644
--- a/libsi/section.h
+++ b/libsi/section.h
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: section.h 2.1 2012/02/26 13:58:26 kls Exp $
+ *   $Id: section.h 3.0 2012/02/26 13:58:26 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
diff --git a/libsi/si.c b/libsi/si.c
index 80d34d1..0ef3939 100644
--- a/libsi/si.c
+++ b/libsi/si.c
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: si.c 2.8 2012/09/29 14:44:20 kls Exp $
+ *   $Id: si.c 3.1 2013/10/30 10:16:18 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -508,6 +508,9 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain,
          case CarouselIdentifierDescriptorTag:
             d=new CarouselIdentifierDescriptor();
             break;
+         case AVCDescriptorTag:
+            d=new AVCDescriptor();
+            break;
          case NetworkNameDescriptorTag:
             d=new NetworkNameDescriptor();
             break;
@@ -614,6 +617,12 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain,
          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 --git a/libsi/si.h b/libsi/si.h
index 8e4255e..1e65111 100644
--- a/libsi/si.h
+++ b/libsi/si.h
@@ -6,7 +6,7 @@
  *   the Free Software Foundation; either version 2 of the License, or     *
  *   (at your option) any later version.                                   *
  *                                                                         *
- *   $Id: si.h 2.6 2012/10/15 11:56:06 kls Exp $
+ *   $Id: si.h 3.2 2014/02/08 14:11:32 kls Exp $
  *                                                                         *
  ***************************************************************************/
 
@@ -70,6 +70,10 @@ enum DescriptorTag {
   // 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 @@ enum DescriptorTag {
                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 @@ enum DescriptorTag {
                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 @@ enum LinkageType { LinkageTypeInformationService = 0x01,
                    LinkageTypeRCSMap = 0x07,
                    LinkageTypeMobileHandover = 0x08,
                    LinkageTypeSystemSoftwareUpdateService = 0x09,
-                   LinkageTypeTSContainingSsuBatOrNit = 0x0A
+                   LinkageTypeTSContainingSsuBatOrNit = 0x0A,
+                   LinkageTypePremiere = 0xB0
                  };
 
 enum AudioType { AudioTypeUndefined = 0x00,
diff --git a/libsi/util.c b/libsi/util.c
index 36a80d0..4da1917 100644
--- a/libsi/util.c
+++ b/libsi/util.c
@@ -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 --git a/libsi/util.h b/libsi/util.h
index a7f5d60..019c4a8 100644
--- a/libsi/util.h
+++ b/libsi/util.h
@@ -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 --git a/lirc.c b/lirc.c
index f7fc452..2b0c07b 100644
--- a/lirc.c
+++ b/lirc.c
@@ -6,7 +6,7 @@
  *
  * LIRC support added by Carsten Koch <Carsten.Koch at icem.de>  2000-06-16.
  *
- * $Id: lirc.c 2.5.1.2 2013/10/29 16:06:20 kls Exp $
+ * $Id: lirc.c 3.2 2013/10/29 12:32:12 kls Exp $
  */
 
 #include "lirc.h"
diff --git a/lirc.h b/lirc.h
index fb6745f..070aa3d 100644
--- a/lirc.h
+++ b/lirc.h
@@ -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 --git a/menu.c b/menu.c
index c158f02..16f98c4 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.c 2.82.1.8 2014/02/26 11:42:28 kls Exp $
+ * $Id: menu.c 3.35 2015/02/01 10:42:11 kls Exp $
  */
 
 #include "menu.h"
@@ -214,9 +214,9 @@ void cMenuEditChannel::Setup(void)
   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:
@@ -387,6 +387,9 @@ void cMenuChannels::Setup(void)
             currentItem = item;
          }
       }
+  SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName :
+                  cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider :
+                  msmNumber);
   if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber)
      Sort();
   SetCurrent(currentItem);
@@ -723,9 +726,11 @@ cMenuFolder::cMenuFolder(const char *Title, cNestedItemList *NestedItemList, con
   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)
@@ -737,14 +742,28 @@ cMenuFolder::cMenuFolder(const char *Title, cList<cNestedItem> *List, cNestedIte
   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);
+     }
 }
 
 void cMenuFolder::Set(const char *CurrentFolder)
@@ -773,7 +792,7 @@ void cMenuFolder::DescendPath(const char *Path)
         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;
                }
@@ -782,12 +801,12 @@ void cMenuFolder::DescendPath(const char *Path)
     }
 }
 
-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;
@@ -832,8 +851,7 @@ eOSState cMenuFolder::Edit(void)
 
 eOSState cMenuFolder::SetFolder(void)
 {
-  cMenuEditFolder *mef = (cMenuEditFolder *)SubMenu();
-  if (mef) {
+  if (cMenuEditFolder *mef = dynamic_cast<cMenuEditFolder *>(SubMenu())) {
      Set(mef->GetFolder());
      SetHelpKeys();
      Display();
@@ -847,8 +865,7 @@ cString cMenuFolder::GetFolder(void)
   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();
         }
@@ -864,8 +881,8 @@ eOSState cMenuFolder::ProcessKey(eKeys Key)
 
   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();
@@ -874,6 +891,7 @@ eOSState cMenuFolder::ProcessKey(eKeys Key)
      }
   else if (state == osEnd && HasSubMenu() && editing)
      state = SetFolder();
+  SetHelpKeys();
   return state;
 }
 
@@ -934,8 +952,7 @@ void cMenuEditTimer::SetFirstDayItem(void)
 
 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)
@@ -969,7 +986,7 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
                        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);
@@ -2109,30 +2126,352 @@ bool CamMenuActive(void)
   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!
+  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;
+  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 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;
+  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 = 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::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!
+  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 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())
@@ -2141,6 +2480,10 @@ void cMenuRecording::Display(void)
 
 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:
@@ -2168,6 +2511,9 @@ eOSState cMenuRecording::ProcessKey(eKeys Key)
                      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;
        }
      }
@@ -2187,6 +2533,7 @@ public:
   ~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; }
@@ -2225,27 +2572,38 @@ void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, b
 
 // --- 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);
 }
 
@@ -2256,18 +2614,14 @@ void cMenuRecordings::SetHelpKeys(void)
   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;
@@ -2276,7 +2630,7 @@ void cMenuRecordings::SetHelpKeys(void)
 
 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) {
@@ -2287,7 +2641,7 @@ void cMenuRecordings::Set(bool Refresh)
   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()) {
@@ -2308,20 +2662,35 @@ void cMenuRecordings::Set(bool Refresh)
          else
             delete Item;
          if (LastItem || LastDir) {
-            if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0)
+            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)
             LastDir->IncrementCounter(recording->IsNew());
          }
       }
+  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);
@@ -2333,14 +2702,14 @@ cString cMenuRecordings::DirectoryName(void)
 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;
@@ -2400,10 +2769,10 @@ eOSState cMenuRecordings::Delete(void)
            }
         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.
               }
@@ -2434,9 +2803,12 @@ eOSState cMenuRecordings::Info(void)
 {
   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;
 }
 
@@ -2487,6 +2859,17 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
        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;
@@ -2985,6 +3368,14 @@ void cMenuSetupLNB::Setup(void)
          }
      }
 
+  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();
 }
@@ -2992,6 +3383,7 @@ void cMenuSetupLNB::Setup(void)
 eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
 {
   int oldDiSEqC = data.DiSEqC;
+  int oldUsePositioner = data.UsePositioner;
   bool DeviceBondingsChanged = false;
   if (Key == kOk) {
      cString NewDeviceBondings = satCableNumbers.ToString();
@@ -3000,7 +3392,7 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
      }
   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);
@@ -3027,7 +3419,7 @@ cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot)
 
 bool cMenuSetupCAMItem::Changed(void)
 {
-  char buffer[32];
+  const char *Activating = "";
   const char *CamName = camSlot->GetCamName();
   if (!CamName) {
      switch (camSlot->ModuleStatus()) {
@@ -3037,7 +3429,10 @@ bool cMenuSetupCAMItem::Changed(void)
        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;
@@ -3047,8 +3442,11 @@ bool cMenuSetupCAMItem::Changed(void)
 
 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);
@@ -3056,13 +3454,33 @@ public:
 
 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)
@@ -3092,6 +3510,43 @@ eOSState cMenuSetupCAM::Menu(void)
   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());
@@ -3113,12 +3568,14 @@ eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
        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;
 }
@@ -3179,6 +3636,11 @@ cMenuSetupReplay::cMenuSetupReplay(void)
   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$Binary skip initial value (s)"), &data.BinarySkipInitial, 10, 600));
+  Add(new cMenuEditIntItem( tr("Setup.Replay$Binary skip timeout (s)"), &data.BinarySkipTimeout, 0, 10));
   Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
 }
 
@@ -3209,6 +3671,8 @@ cMenuSetupMisc::cMenuSetupMisc(void)
   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));
@@ -3467,13 +3931,13 @@ bool cMenuMain::Update(bool Force)
      }
 
   // 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;
@@ -3523,7 +3987,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
                           }
                        break;
     case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
-                          cCutter::Stop();
+                          RecordingsHandler.DelAll();
                           return osEnd;
                           }
                        break;
@@ -3639,6 +4103,8 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched)
   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) {
@@ -3660,6 +4126,7 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)
   lastTime.Set();
   withInfo = Setup.ShowInfoOnChSwitch;
   displayChannel = Skins.Current()->DisplayChannel(withInfo);
+  positioner = NULL;
   channel = Channels.GetByNumber(cDevice::CurrentChannel());
   ProcessKey(FirstKey);
 }
@@ -3722,6 +4189,10 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
 
 eOSState cDisplayChannel::ProcessKey(eKeys Key)
 {
+  if (cOsdProvider::OsdSizeChanged(osdState)) {
+     delete displayChannel;
+     displayChannel = Skins.Current()->DisplayChannel(withInfo);
+     }
   cChannel *NewChannel = NULL;
   if (Key != kNone)
      lastTime.Set();
@@ -3867,7 +4338,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
             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());
@@ -3875,13 +4346,24 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
         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;
@@ -4334,7 +4816,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
      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() : "");
@@ -4353,7 +4835,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
      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;
@@ -4368,7 +4850,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
            }
         }
      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!"));
         }
      }
@@ -4463,7 +4945,7 @@ void cRecordControls::ChannelDataModified(cChannel *Channel)
       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.
@@ -4498,6 +4980,39 @@ bool cRecordControls::StateChanged(int &State)
   return Result;
 }
 
+// --- cBinarySkipper --------------------------------------------------------
+
+cBinarySkipper::cBinarySkipper(void)
+{
+  initialValue = NULL;
+  currentValue = 0;
+  framesPerSecond = 0;
+  lastKey = kNone;
+}
+
+void cBinarySkipper::Initialize(int *InitialValue, double FramesPerSecond)
+{
+  initialValue = InitialValue;
+  framesPerSecond = FramesPerSecond;
+  currentValue = 0;
+}
+
+int cBinarySkipper::GetValue(eKeys Key)
+{
+  if (!initialValue)
+     return 0;
+  if (timeout.TimedOut()) {
+     currentValue = int(round(*initialValue * framesPerSecond));
+     lastKey = Key;
+     }
+  else if (Key != lastKey) {
+     currentValue /= 2;
+     lastKey = kNone; // once the direction has changed, every further call halves the value
+     }
+  timeout.Set(Setup.BinarySkipTimeout * 1000);
+  return max(currentValue, 1);
+}
+
 // --- cReplayControl --------------------------------------------------------
 
 cReplayControl *cReplayControl::currentReplayControl = NULL;
@@ -4519,6 +5034,7 @@ cReplayControl::cReplayControl(bool PauseLive)
   cRecording Recording(fileName);
   cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
   marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
+  binarySkipper.Initialize(&Setup.BinarySkipInitial, Recording.FramesPerSecond());
   SetTrackDescriptions(false);
   if (Setup.ProgressDisplayTime)
      ShowTimed(Setup.ProgressDisplayTime);
@@ -4564,6 +5080,7 @@ void cReplayControl::Stop(void)
         }
      }
   cDvbPlayerControl::Stop();
+  cMenuRecordings::SetRecording(NULL); // make sure opening the Recordings menu navigates to the last replayed recording
 }
 
 void cReplayControl::SetRecording(const char *FileName)
@@ -4743,7 +5260,8 @@ void cReplayControl::TimeSearchProcess(eKeys Key)
     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;
@@ -4807,6 +5325,14 @@ void cReplayControl::MarkJump(bool Forward)
   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;
@@ -4818,26 +5344,40 @@ void cReplayControl::MarkJump(bool Forward)
      }
 }
 
-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);
      }
 }
 
@@ -4845,12 +5385,14 @@ void cReplayControl::EditCut(void)
 {
   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"));
@@ -4869,12 +5411,10 @@ void cReplayControl::EditTest(void)
      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();
-           }
         }
      }
 }
@@ -4966,9 +5506,13 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
         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(-binarySkipper.GetValue(RAWKEY(Key)), false); break;
+        case kMarkSkipForward|k_Repeat:
+        case kMarkSkipForward: MarkMove(+binarySkipper.GetValue(RAWKEY(Key)), false); break;
         case kEditCut:         EditCut(); break;
         case kEditTest:        EditTest(); break;
         default: {
diff --git a/menu.h b/menu.h
index 1a76b41..f602b96 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.h 2.13.1.1 2013/10/16 09:46:15 kls Exp $
+ * $Id: menu.h 3.7 2015/01/27 11:38:20 kls Exp $
  */
 
 #ifndef __MENU_H
@@ -38,11 +38,12 @@ private:
   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);
@@ -120,6 +121,8 @@ private:
   cTimeMs lastTime;
   int number;
   bool timeout;
+  int osdState;
+  const cPositioner *positioner;
   cChannel *channel;
   const cEvent *lastPresent;
   const cEvent *lastFollowing;
@@ -189,6 +192,13 @@ public:
 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 @@ private:
   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 @@ private:
 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 @@ public:
   static bool StateChanged(int &State);
   };
 
+class cBinarySkipper {
+private:
+  int *initialValue;
+  int currentValue;
+  double framesPerSecond;
+  eKeys lastKey;
+  cTimeMs timeout;
+public:
+  cBinarySkipper(void);
+  void Initialize(int *InitialValue, double FramesPerSecond);
+  int GetValue(eKeys Key);
+  };
+
 class cReplayControl : public cDvbPlayerControl {
 private:
   cSkinDisplayReplay *displayReplay;
+  cBinarySkipper binarySkipper;
   cMarks marks;
   bool marksModified;
   bool visible, modeOnly, shown, displayFrames;
@@ -277,7 +306,7 @@ private:
   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 --git a/menuitems.c b/menuitems.c
index f7ec776..a7f8283 100644
--- a/menuitems.c
+++ b/menuitems.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menuitems.c 2.16 2013/02/15 14:20:29 kls Exp $
+ * $Id: menuitems.c 3.2 2013/11/03 14:48:21 kls Exp $
  */
 
 #include "menuitems.h"
@@ -51,14 +51,14 @@ void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yell
   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 @@ eOSState cMenuEditNumItem::ProcessKey(eKeys Key)
   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 @@ void cMenuEditStrItem::Set(void)
   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;
diff --git a/menuitems.h b/menuitems.h
index ffe9957..2a30b46 100644
--- a/menuitems.h
+++ b/menuitems.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menuitems.h 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 @@ public:
   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 @@ public:
   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 --git a/newplugin b/newplugin
index 1e9e910..8f7087e 100755
--- a/newplugin
+++ b/newplugin
@@ -12,7 +12,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: newplugin 2.17.1.1 2013/05/02 10:04:06 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 @@ VERSION = \$(shell grep 'static const char \\*VERSION *=' \$(PLUGIN).c | awk '{
 ### 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 --git a/nit.c b/nit.c
index 8265f50..d9ec1f8 100644
--- a/nit.c
+++ b/nit.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: nit.c 2.10.1.1 2014/03/11 09:29:59 kls Exp $
+ * $Id: nit.c 3.4 2015/02/01 13:46:00 kls Exp $
  */
 
 #include "nit.h"
@@ -245,7 +245,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
                            }
                         }
                     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')))
@@ -317,7 +317,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
                            else
                               delete Channel;
                            }
-                        }
+                       }
                     }
                  sdtFilter->Trigger(Source);
                  }
@@ -333,11 +333,12 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
                                   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 };
@@ -356,6 +357,40 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
                    }
                  }
                  break;
+            case SI::LogicalChannelDescriptorTag: {
+                 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: {
+                 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 --git a/nit.h b/nit.h
index 2411e0f..4673f96 100644
--- a/nit.h
+++ b/nit.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: nit.h 2.0.1.1 2014/03/11 09:30:05 kls Exp $
+ * $Id: nit.h 3.1 2014/03/10 14:12:05 kls Exp $
  */
 
 #ifndef __NIT_H
diff --git a/osd.c b/osd.c
index 45d4419..d31ee8a 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.c 2.38.1.1 2013/05/18 12:41:48 kls Exp $
+ * $Id: osd.c 3.4 2015/01/15 11:20:56 kls Exp $
  */
 
 #include "osd.h"
@@ -512,6 +512,17 @@ void cBitmap::SetIndex(int x, int y, tIndex Index)
      }
 }
 
+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;
@@ -824,7 +835,7 @@ void cBitmap::ShrinkBpp(int NewBpp)
      }
 }
 
-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 at mindtremors.com
@@ -1906,6 +1917,16 @@ void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColo
      }
 }
 
+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 +1978,7 @@ int cOsdProvider::oldWidth = 0;
 int cOsdProvider::oldHeight = 0;
 double cOsdProvider::oldAspect = 1.0;
 cImage *cOsdProvider::images[MAXOSDIMAGES] = { NULL };
+int cOsdProvider::osdState = 0;
 
 cOsdProvider::cOsdProvider(void)
 {
@@ -1994,6 +2016,7 @@ void cOsdProvider::UpdateOsdSize(bool Force)
   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 +2034,18 @@ void cOsdProvider::UpdateOsdSize(bool Force)
      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 --git a/osd.h b/osd.h
index 777a652..e5864c4 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.h 2.20 2013/02/12 13:39:08 kls Exp $
+ * $Id: osd.h 3.5 2015/01/15 11:23:52 kls Exp $
  */
 
 #ifndef __OSD_H
@@ -192,6 +192,8 @@ public:
        ///< 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 @@ public:
   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 @@ public:
        ///< 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 @@ public:
        ///< 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
@@ -813,6 +817,8 @@ public:
        ///< 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 +887,11 @@ public:
        ///< 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
@@ -938,6 +949,7 @@ private:
   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 +986,12 @@ public:
       ///< 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 --git a/osdbase.c b/osdbase.c
index fafa9c7..e03f2d0 100644
--- a/osdbase.c
+++ b/osdbase.c
@@ -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 @@ cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
   displayMenuItems = 0;
   title = NULL;
   menuCategory = mcUnknown;
+  menuSortMode = msmUnknown;
   SetTitle(Title);
   SetCols(c0, c1, c2, c3, c4);
   first = 0;
@@ -114,6 +115,11 @@ void cOsdMenu::SetMenuCategory(eMenuCategory MenuCategory)
   menuCategory = MenuCategory;
 }
 
+void cOsdMenu::SetMenuSortMode(eMenuSortMode MenuSortMode)
+{
+  menuSortMode = MenuSortMode;
+}
+
 void cOsdMenu::SetDisplayMenu(void)
 {
   if (displayMenu) {
@@ -224,6 +230,7 @@ void cOsdMenu::Display(void)
   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 @@ void cOsdMenu::DisplayCurrent(bool Current)
      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 @@ eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
   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 --git a/osdbase.h b/osdbase.h
index 4635184..5b8ae1c 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -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 @@ private:
   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 @@ protected:
   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 @@ public:
   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 --git a/pat.c b/pat.c
index 7b3f91f..98d306e 100644
--- a/pat.c
+++ b/pat.c
@@ -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.1.2 2014/02/19 09:31:29 kls Exp $
+ * $Id: pat.c 3.5 2015/01/04 13:14:01 kls Exp $
  */
 
 #include "pat.h"
@@ -20,6 +20,7 @@
 class cCaDescriptor : public cListObject {
 private:
   int caSystem;
+  int caPid;
   int esPid;
   int length;
   uchar *data;
@@ -28,6 +29,7 @@ public:
   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; }
@@ -36,6 +38,7 @@ public:
 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);
@@ -66,26 +69,30 @@ private:
   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;
 }
@@ -162,7 +169,7 @@ int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar
          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();
@@ -178,6 +185,30 @@ int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar
   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> {
@@ -189,6 +220,8 @@ public:
       // 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)
@@ -219,6 +252,26 @@ int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int Serv
   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)
@@ -226,6 +279,16 @@ int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSy
   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
@@ -340,7 +403,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
      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);
diff --git a/pat.h b/pat.h
index dd8f08c..19e60dc 100644
--- a/pat.h
+++ b/pat.h
@@ -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.1.1 2014/02/18 14:12:24 kls Exp $
+ * $Id: pat.h 3.4 2015/01/04 13:17:22 kls Exp $
  */
 
 #ifndef __PAT_H
@@ -42,10 +42,21 @@ int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSy
          ///< 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 --git a/player.c b/player.c
index 5c95f4e..5fc4dbf 100644
--- a/player.c
+++ b/player.c
@@ -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 --git a/player.h b/player.h
index 01bc958..9ec47fa 100644
--- a/player.h
+++ b/player.h
@@ -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 @@ protected:
   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 --git a/plugin.c b/plugin.c
index db16d08..650f923 100644
--- a/plugin.c
+++ b/plugin.c
@@ -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 --git a/plugin.h b/plugin.h
index 3502741..d04274a 100644
--- a/plugin.h
+++ b/plugin.h
@@ -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 --git a/po/ar.po b/po/ar.po
index 54c3f3c..e26a430 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-10-16 11:16-0400\n"
 "Last-Translator: Osama Alrawab <alrawab at hotmail.com>\n"
 "Language-Team: Arabic <ar at li.org>\n"
@@ -23,6 +23,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** قناة خاطئة ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "القناة غير متاحة"
 
@@ -80,6 +83,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "EPG ا لبدء بالبحث على دليل القنوات الالكترونى "
 
@@ -567,6 +579,12 @@ msgstr "الكامة"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "اعدادات القناة غبر موحد"
 
@@ -607,8 +625,8 @@ msgstr "المجلد موجود بالفعل"
 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 "حذف المجلد وكل المجلدات الفرعية"
@@ -727,6 +745,73 @@ msgstr "الرجاء ادخال %d رقم!"
 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 "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 +824,6 @@ msgstr "اعادة"
 msgid "Recordings"
 msgstr "التسجيلات"
 
-msgid "Button$Open"
-msgstr "افتح"
-
 msgid "Commands"
 msgstr "الاوامر"
 
@@ -997,6 +1079,33 @@ msgstr "Device %d connected to sat cable"
 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,9 +1115,19 @@ msgstr "الكامة موجودة"
 msgid "CAM ready"
 msgstr "الكامة جاهزة"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "الكامة "
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "القائمة"
 
@@ -1021,6 +1140,9 @@ msgstr "فتح قائمة الكامة"
 msgid "Can't open CAM menu!"
 msgstr "تعذر فتح قائمة الكامة"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "الكامة مستخدمة الان هل تريد اعادة تشغيلها"
 
@@ -1114,6 +1236,21 @@ msgstr "Progress display time (s)"
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "رقم المواصلة"
 
@@ -1150,6 +1287,12 @@ msgstr "كسابق"
 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"
 
@@ -1334,6 +1477,10 @@ msgstr "بث حى"
 msgid "PLAY"
 msgstr "عرض"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG شاشة "
 
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 89d6556..4d91025 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-03-02 19:02+0100\n"
 "Last-Translator: Luca Olivetti <luca at ventoso.org>\n"
 "Language-Team: Catalan <vdr at linuxtv.org>\n"
@@ -22,6 +22,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal incorrecte ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Canal no disponible!"
 
@@ -79,6 +82,15 @@ msgstr "Rolloff"
 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 @@ msgstr "Acc
 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 @@ msgstr "La carpeta ja existeix!"
 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,73 @@ msgstr "Si us plau introdueixi %d digitos"
 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 "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 +823,6 @@ msgstr "Enrera"
 msgid "Recordings"
 msgstr "Veure programes gravats"
 
-msgid "Button$Open"
-msgstr "Obrir"
-
 msgid "Commands"
 msgstr "Ordres"
 
@@ -996,6 +1078,33 @@ msgstr "Dispositiu %d connectat a cable sat
 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 +1114,19 @@ msgstr "CAM present"
 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 +1139,9 @@ msgstr "Obrint menu CAM..."
 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 +1235,21 @@ msgstr "Visualitzaci
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de Continuar"
 
@@ -1149,6 +1286,12 @@ msgstr "anterior"
 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"
 
@@ -1333,6 +1476,10 @@ msgstr "EN DIRECTE"
 msgid "PLAY"
 msgstr "REPRODUIR"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Quadres ST:TNG"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5409f08..78fa408 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2010-05-06 11:00+0200\n"
 "Last-Translator: Aleš Juřík <ajurik at quick.cz>\n"
 "Language-Team: Czech <vdr at linuxtv.org>\n"
@@ -22,6 +22,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Neplatný kanál ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanál není dostupný!"
 
@@ -79,6 +82,15 @@ msgstr "RollOff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Začíná prohledávání EPG"
 
@@ -566,6 +578,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Nastavení kanálu není jedinečné!"
 
@@ -606,8 +624,8 @@ msgstr "Složka již existuje!"
 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,73 @@ msgstr "Prosím vložte %d znaků!"
 msgid "CAM not responding!"
 msgstr "CAM neodpovídá!"
 
+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 "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 "Detail nahrávky"
 
@@ -738,9 +823,6 @@ msgstr "Na začátek"
 msgid "Recordings"
 msgstr "Nahrávky"
 
-msgid "Button$Open"
-msgstr "Otevřít"
-
 msgid "Commands"
 msgstr "Příkazy"
 
@@ -996,6 +1078,33 @@ msgstr "Zařízení %d připojeno k sat. kabelu"
 msgid "Setup.LNB$own"
 msgstr "vlastní"
 
+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 CAMu"
 
@@ -1005,9 +1114,19 @@ msgstr "CAM přítomen"
 msgid "CAM ready"
 msgstr "CAM připraven"
 
+#. 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"
 
@@ -1020,6 +1139,9 @@ msgstr "Otevírá se menu CAM..."
 msgid "Can't open CAM menu!"
 msgstr "Menu CAM není dostupné"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM se používá - opravdu restartovat?"
 
@@ -1113,6 +1235,21 @@ msgstr "Čas zobrazení ukazatele průběhu (s)"
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID obnovení"
 
@@ -1149,6 +1286,12 @@ msgstr "jako naposledy"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hlasitost po spuštění"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Přecházet z konce na začátek seznamu kanálů"
 
@@ -1333,6 +1476,10 @@ msgstr "ŽIVĚ"
 msgid "PLAY"
 msgstr "PŘEHRÁVÁNÍ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG konzola"
 
diff --git a/po/da_DK.po b/po/da_DK.po
index 4069a7b..c2773f8 100644
--- a/po/da_DK.po
+++ b/po/da_DK.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Mogens Elneff <mogens at elneff.dk>\n"
 "Language-Team: Danish <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Ugyldig kanal! ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal er ikke tilg�ngelig!"
 
@@ -76,6 +79,15 @@ msgstr ""
 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 @@ msgstr "CA"
 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 @@ msgstr ""
 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,73 @@ msgstr "Indtast venligst %d cifre!"
 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 "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 +820,6 @@ msgstr "Forfra"
 msgid "Recordings"
 msgstr "Optagelser"
 
-msgid "Button$Open"
-msgstr "�bn"
-
 msgid "Commands"
 msgstr "Kommandoer"
 
@@ -993,6 +1075,33 @@ msgstr ""
 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 +1111,19 @@ msgstr "CAM til stede"
 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 +1136,9 @@ msgstr "
 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 +1232,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Genoptagelses ID"
 
@@ -1146,6 +1283,12 @@ msgstr "som f
 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 +1473,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG konsol"
 
diff --git a/po/de_DE.po b/po/de_DE.po
index 0e24ab2..2d0ff56 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2010-01-16 16:46+0100\n"
 "Last-Translator: Klaus Schmidinger <vdr at tvdr.de>\n"
 "Language-Team: German <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Ung�ltiger Kanal ***"
 
+msgid "CAM activated!"
+msgstr "CAM aktiviert!"
+
 msgid "Channel not available!"
 msgstr "Kanal nicht verf�gbar!"
 
@@ -76,6 +79,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr "Pilot"
+
+msgid "T2SystemId"
+msgstr "T2-Systemkennung"
+
+msgid "SISO/MISO"
+msgstr "SISO/MISO"
+
 msgid "Starting EPG scan"
 msgstr "Aktualisiere EPG-Daten"
 
@@ -563,6 +575,12 @@ msgstr "CA"
 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 +621,8 @@ msgstr "Verzeichnisname existiert bereits!"
 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 +741,73 @@ msgstr "Bitte geben Sie %d Ziffern ein!"
 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 "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 +820,6 @@ msgstr "Anfang"
 msgid "Recordings"
 msgstr "Aufzeichnungen"
 
-msgid "Button$Open"
-msgstr "�ffnen"
-
 msgid "Commands"
 msgstr "Befehle"
 
@@ -745,7 +827,7 @@ msgid "Delete recording?"
 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!"
@@ -993,6 +1075,33 @@ msgstr "Device %d angeschlossen an Sat-Kabel"
 msgid "Setup.LNB$own"
 msgstr "eigenes"
 
+msgid "Setup.LNB$Use dish positioner"
+msgstr "Antennen-Positionierer benutzen"
+
+msgid "Setup.LNB$Site latitude (degrees)"
+msgstr "Standort L�ngengrad"
+
+msgid "South"
+msgstr "S�d"
+
+msgid "North"
+msgstr "Nord"
+
+msgid "Setup.LNB$Site longitude (degrees)"
+msgstr "Standort Breitengrad"
+
+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 +1111,19 @@ msgstr "CAM vorhanden"
 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 +1136,9 @@ msgstr "CAM-Men
 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 +1232,21 @@ msgstr "Anzeigedauer f
 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$Binary skip initial value (s)"
+msgstr "Anfangswert f�r bin�res Springen (s)"
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr "Zeitlimit f�r bin�res Springen (ms)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Wiedergabe-ID"
 
@@ -1146,6 +1283,12 @@ msgstr "wie vorher"
 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 +1335,13 @@ msgstr "Weiter"
 
 #. 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 +1423,7 @@ msgid "Can't shutdown - option '-s' not given!"
 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 +1440,7 @@ msgid "Plugin %s wakes up in %ld min, continue?"
 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 +1473,10 @@ msgstr "LIVE"
 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 +1486,7 @@ msgstr "MDMDFSS"
 
 #. 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"
@@ -1381,10 +1528,10 @@ msgid "Switching primary DVB..."
 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 --git a/po/el_GR.po b/po/el_GR.po
index b61174e..ab60f68 100644
--- a/po/el_GR.po
+++ b/po/el_GR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Dimitrios Dimitrakos <mail at dimitrios.de>\n"
 "Language-Team: Greek <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** ����� ������ ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "�� ������ ��� ����� ���������!"
 
@@ -76,6 +79,15 @@ msgstr ""
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "���� ������ EPG"
 
@@ -563,6 +575,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "�� ��������� ��� �������� ����������������!"
 
@@ -603,8 +621,8 @@ msgstr ""
 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,73 @@ msgstr ""
 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 "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 +820,6 @@ msgstr "
 msgid "Recordings"
 msgstr "��������"
 
-msgid "Button$Open"
-msgstr "�������"
-
 msgid "Commands"
 msgstr "�������"
 
@@ -993,6 +1075,33 @@ msgstr ""
 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 +1111,19 @@ msgstr ""
 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 +1136,9 @@ msgstr ""
 msgid "Can't open CAM menu!"
 msgstr "������� � �������� ��� CAM �����!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr ""
 
@@ -1110,6 +1232,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID ������������"
 
@@ -1146,6 +1283,12 @@ msgstr ""
 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 +1473,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "������ ST:TNG"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index fba8c21..40d7208 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-03-02 19:02+0100\n"
 "Last-Translator: Luca Olivetti <luca at ventoso.org>\n"
 "Language-Team: Spanish <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal no v�lido ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "�Canal no disponible!"
 
@@ -77,6 +80,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Iniciando la exploraci�n de EPG"
 
@@ -564,6 +576,12 @@ msgstr "Acceso condicional (CA)"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "!Propiedades de canal duplicadas!"
 
@@ -604,8 +622,8 @@ msgstr "
 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?"
@@ -724,6 +742,73 @@ msgstr "
 msgid "CAM not responding!"
 msgstr "�CAM no 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 "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�n de grabaci�n"
 
@@ -736,9 +821,6 @@ msgstr "Rebobinar"
 msgid "Recordings"
 msgstr "Grabaciones"
 
-msgid "Button$Open"
-msgstr "Abrir"
-
 msgid "Commands"
 msgstr "�rdenes"
 
@@ -994,6 +1076,33 @@ msgstr "Dispositivo %d conectado a cable sat
 msgid "Setup.LNB$own"
 msgstr "propio"
 
+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"
 
@@ -1003,9 +1112,19 @@ msgstr "CAM presente"
 msgid "CAM ready"
 msgstr "CAM preparado"
 
+#. 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�"
 
@@ -1018,6 +1137,9 @@ msgstr "Abriendo el men
 msgid "Can't open CAM menu!"
 msgstr "�No se puede abrir el men� CAM!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM en uso - �reiniciar?"
 
@@ -1111,6 +1233,21 @@ msgstr "Visualizaci
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de continuaci�n"
 
@@ -1147,6 +1284,12 @@ msgstr "anterior"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volumen inicial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Primer canal despu�s del �ltimo"
 
@@ -1331,6 +1474,10 @@ msgstr "EN DIRECTO"
 msgid "PLAY"
 msgstr "REPRODUCIR"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Paneles ST:TNG"
 
diff --git a/po/et_EE.po b/po/et_EE.po
index 333f0b0..607f734 100644
--- a/po/et_EE.po
+++ b/po/et_EE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Arthur Konovalov <artlov at gmail.com>\n"
 "Language-Team: Estonian <vdr at linuxtv.org>\n"
@@ -17,7 +17,10 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 
 msgid "*** Invalid Channel ***"
-msgstr "*** Vigane kanal ***"
+msgstr "*** Kehtetu kanal ***"
+
+msgid "CAM activated!"
+msgstr ""
 
 msgid "Channel not available!"
 msgstr "Kanal ei ole kättesaadav!"
@@ -74,7 +77,16 @@ msgid "Rolloff"
 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"
@@ -563,6 +575,12 @@ msgstr "CA"
 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 @@ msgid "Button$Mark"
 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 @@ msgstr "Sellenimeline kaust juba olemas!"
 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?"
@@ -697,14 +715,14 @@ msgid "Button$Now"
 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,73 @@ msgstr "Palun sisestada %d numbrit!"
 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 lõikamiseks lisamine ebaõnnestus!"
+
+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 +820,6 @@ msgstr "Algusesse"
 msgid "Recordings"
 msgstr "Salvestused"
 
-msgid "Button$Open"
-msgstr "Avada"
-
 msgid "Commands"
 msgstr "Käsud"
 
@@ -993,6 +1075,33 @@ msgstr "DVB seade %d ühendatud SAT-kaabliga"
 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 max pöördeulatus (°)"
+
+msgid "Setup.LNB$Positioner speed (degrees/s)"
+msgstr "Positsioneerija kiirus (°/s)"
+
 msgid "CAM reset"
 msgstr "CAM taaskäivitamine"
 
@@ -1002,9 +1111,19 @@ msgstr "CAM esitletud"
 msgid "CAM ready"
 msgstr "CAM töövalmis"
 
+#. 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 +1136,9 @@ msgstr "CAM-menüü avamine..."
 msgid "Can't open CAM menu!"
 msgstr "Ei saa avada CAM menüüd!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM on kasutuses - taaskäivitada?"
 
@@ -1110,6 +1232,21 @@ msgstr "Edenemiseriba kuvamise aeg (s)"
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Taasesituse peatamine markeri seadmisel"
 
+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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Jätkamise ID"
 
@@ -1146,6 +1283,12 @@ msgstr "endine"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Helitugevus käivitamisel"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr "Helitugevuse samm"
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr "Helitugevuse lineariseerimine"
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kanalite ringkerimine"
 
@@ -1192,13 +1335,13 @@ msgstr "Jätkata"
 
 #. 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!"
@@ -1280,7 +1423,7 @@ msgid "Can't shutdown - option '-s' not given!"
 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 +1440,7 @@ msgid "Plugin %s wakes up in %ld min, continue?"
 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 +1473,10 @@ msgstr "LIVE"
 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 +1528,10 @@ msgid "Switching primary DVB..."
 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 --git a/po/fi_FI.po b/po/fi_FI.po
index f44dd72..cd59767 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2007-08-15 15:52+0200\n"
 "Last-Translator: Matti Lehtimäki <matti.lehtimaki at gmail.com>\n"
 "Language-Team: Finnish <vdr at linuxtv.org>\n"
@@ -23,6 +23,9 @@ msgstr ""
 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 @@ msgstr "Rolloff"
 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 @@ msgstr "Salaus (CA)"
 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 @@ msgstr "Kansio on jo olemassa!"
 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,73 @@ msgstr "Syötä %d numeroa!"
 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 "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 +824,6 @@ msgstr "Alkuun"
 msgid "Recordings"
 msgstr "Tallenteet"
 
-msgid "Button$Open"
-msgstr "Avaa"
-
 msgid "Commands"
 msgstr "Komennot"
 
@@ -997,6 +1079,33 @@ msgstr "DVB-sovitin %d kytketty SAT-kaapeliin"
 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 +1115,19 @@ msgstr "CAM havaittu"
 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 +1140,9 @@ msgstr "Avataan CA-moduulin valikkoa..."
 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 +1236,21 @@ msgstr "Näytä toiston kontrollit (s)"
 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$Binary skip initial value (s)"
+msgstr "Binäärihypyn oletuspituus (s)"
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr "Binäärihypyn odotusaika (ms)"
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Tallenteen paluutunniste"
 
@@ -1150,6 +1287,12 @@ msgstr "edellinen"
 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 +1477,10 @@ msgstr "LIVE"
 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 --git a/po/fr_FR.po b/po/fr_FR.po
index 6228d7f..65ed4fb 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -17,7 +17,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-02-24 12:56+0100\n"
 "Last-Translator: Dominique Plu <dplu at free.fr>\n"
 "Language-Team: French <vdr at linuxtv.org>\n"
@@ -29,6 +29,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Chaîne invalide ! ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Chaîne non disponible !"
 
@@ -86,6 +89,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Mise à jour du guide des programmes"
 
@@ -573,6 +585,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Cette chaîne n'est pas unique !"
 
@@ -613,8 +631,8 @@ msgstr "Ce nom de dossier existe déjà !"
 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 ?"
@@ -733,6 +751,73 @@ msgstr "Veuillez entrer %d chiffres !"
 msgid "CAM not responding!"
 msgstr "Pas de réponse du 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 "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 "Infos sur l'enregistrement"
 
@@ -745,9 +830,6 @@ msgstr "Retour"
 msgid "Recordings"
 msgstr "Enregistrements"
 
-msgid "Button$Open"
-msgstr "Ouvrir"
-
 msgid "Commands"
 msgstr "Commandes"
 
@@ -1003,6 +1085,33 @@ msgstr "Le périphérique %d est connecté au câble satellite"
 msgid "Setup.LNB$own"
 msgstr "Propriétaire"
 
+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 réinitialisé"
 
@@ -1012,9 +1121,19 @@ msgstr "CAM présent"
 msgid "CAM ready"
 msgstr "CAM prêt"
 
+#. 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"
 
@@ -1027,6 +1146,9 @@ msgstr "Ouverture du menu CAM..."
 msgid "Can't open CAM menu!"
 msgstr "Impossible d'ouvrir le menu CAM !"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM en cours d'utilisation - confirmer réinitialisation ?"
 
@@ -1120,6 +1242,21 @@ msgstr "Afficher la barre de progression (s)"
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Arrêt lecture si ajout marqueur"
 
+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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de reprise"
 
@@ -1156,6 +1293,12 @@ msgstr "comme avant"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Volume initial"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Affichage circulaire des chaînes"
 
@@ -1340,6 +1483,10 @@ msgstr "DIRECT"
 msgid "PLAY"
 msgstr "LECTURE"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Consoles ST:TNG"
 
diff --git a/po/hr_HR.po b/po/hr_HR.po
index 3925422..b9bd700 100644
--- a/po/hr_HR.po
+++ b/po/hr_HR.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-03-17 19:00+0100\n"
 "Last-Translator: Adrian Caval <anrxc at sysphere.org>\n"
 "Language-Team: Croatian <vdr at linuxtv.org>\n"
@@ -21,6 +21,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Neispravan Program ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Program nije dostupan!"
 
@@ -78,6 +81,15 @@ msgstr ""
 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 @@ msgstr "Kodiranje (CA)"
 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 @@ msgstr ""
 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,73 @@ msgstr "Molim unesite %d znamenki!"
 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 "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 +822,6 @@ msgstr "Na po
 msgid "Recordings"
 msgstr "Snimke"
 
-msgid "Button$Open"
-msgstr "Otvori"
-
 msgid "Commands"
 msgstr "Naredbe"
 
@@ -995,6 +1077,33 @@ msgstr ""
 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 +1113,19 @@ msgstr "CAM prisutan"
 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 +1138,9 @@ msgstr "Otvaram CAM izbornik..."
 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 +1234,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID nastavka"
 
@@ -1148,6 +1285,12 @@ msgstr "kao prethodno"
 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 ""
 
@@ -1332,6 +1475,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Panele"
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index e4f8594..00dcc24 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-01 19:22+0200\n"
 "Last-Translator: István Füley <ifuley at tigercomp.ro>\n"
 "Language-Team: Hungarian <vdr at linuxtv.org>\n"
@@ -23,6 +23,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Érvénytelen csatorna ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Az adó nem elérhető"
 
@@ -80,6 +83,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "Stream azonosító"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "EPG adatok aktualizálása"
 
@@ -567,6 +579,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Az adóbeállítások nem egyértelműek"
 
@@ -607,8 +625,8 @@ msgstr "Ez a könyvtárnév már létezik!"
 msgid "Folder name must not contain '%c'!"
 msgstr "A könyvtár neve nem tartalmazhatja: '%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?"
@@ -727,6 +745,73 @@ msgstr "Üssön be %d számot!"
 msgid "CAM not responding!"
 msgstr "A CAM nem válaszol!"
 
+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 "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 "Felvétel infó"
 
@@ -739,9 +824,6 @@ msgstr "Vissza az elejére"
 msgid "Recordings"
 msgstr "Felvételek"
 
-msgid "Button$Open"
-msgstr "Kinyitni"
-
 msgid "Commands"
 msgstr "Parancsok"
 
@@ -997,6 +1079,33 @@ msgstr "Fejkábel a %d. tunerhez"
 msgid "Setup.LNB$own"
 msgstr "saját"
 
+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 újraindítás"
 
@@ -1006,9 +1115,19 @@ msgstr "CAM jelen"
 msgid "CAM ready"
 msgstr "CAM működik"
 
+#. 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ü"
 
@@ -1021,6 +1140,9 @@ msgstr "A CAM menü nyitása..."
 msgid "Can't open CAM menu!"
 msgstr "A CAM menü nem nyitható"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM használatban - valóban újraindítjuk?"
 
@@ -1114,6 +1236,21 @@ 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"
 
+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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Lejátszás ID"
 
@@ -1150,6 +1287,12 @@ msgstr "ahogy az előbb"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hangerő indulásnál"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Csatornalista görgetése"
 
@@ -1334,6 +1477,10 @@ msgstr "ÉLŐ"
 msgid "PLAY"
 msgstr "LEJÁTSZÁS"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Konzol"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 281434c..7845643 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -11,8 +11,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at 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-01-30 13:14+0100\n"
+"PO-Revision-Date: 2015-01-19 20:19+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian at tiscali.it>\n"
 "Language-Team: Italian <vdr at linuxtv.org>\n"
 "Language: it\n"
@@ -26,6 +26,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Canale NON valido ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Canale non disponibile!"
 
@@ -83,6 +86,15 @@ msgstr "Rolloff"
 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 +582,12 @@ msgstr "CA"
 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 +628,8 @@ msgstr "Nome cartella già esistente!"
 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 +748,73 @@ msgstr "Inserisci %d cifre!"
 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 "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 +827,6 @@ msgstr "Riavvolgi"
 msgid "Recordings"
 msgstr "Registrazioni"
 
-msgid "Button$Open"
-msgstr "Apri"
-
 msgid "Commands"
 msgstr "Comandi"
 
@@ -1000,6 +1082,33 @@ msgstr "Periferica %d connessa al cavo sat"
 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 +1118,19 @@ msgstr "La CAM è presente"
 msgid "CAM ready"
 msgstr "La CAM è pronta"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "Accesso condizionato CAM"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1024,6 +1143,9 @@ msgstr "Apertura menu CAM..."
 msgid "Can't open CAM menu!"
 msgstr "Impossibile aprire il menu CAM!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "La CAM è in uso - vuoi reimpostarla?"
 
@@ -1117,6 +1239,21 @@ msgstr "Mostra tempo elaborazione (s)"
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pausa riproduzione durante impostazione segni"
 
+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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID di ripristino"
 
@@ -1153,6 +1290,12 @@ msgstr "come prima"
 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"
 
@@ -1337,6 +1480,10 @@ msgstr "DAL VIVO"
 msgid "PLAY"
 msgstr "RIPRODUCI"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr "Muovi antenna verso %.1f..."
+
 msgid "ST:TNG Panels"
 msgstr "Consolle ST:TNG"
 
diff --git a/po/lt_LT.po b/po/lt_LT.po
index dba4224..e76ef36 100644
--- a/po/lt_LT.po
+++ b/po/lt_LT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2010-10-30 11:55+0200\n"
 "Last-Translator: Valdemaras Pipiras <varas at ambernet.lt>\n"
 "Language-Team: Lithuanian <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Blogi kanalo nustatymai ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanalas neegzistuoja!"
 
@@ -76,6 +79,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Pradedamas EPG skanavimas"
 
@@ -563,6 +575,12 @@ msgstr "CA (dekodavimo sistema)"
 msgid "Sid"
 msgstr "Serviso id"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Kanalų nustatymai neunikalūs!"
 
@@ -603,8 +621,8 @@ msgstr "Toks katalogo vardas jau egzistuoja!"
 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,73 @@ msgstr "Įveskite %d skaičius!"
 msgid "CAM not responding!"
 msgstr "Dekodavimo modulis (CAM) neveikia!"
 
+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 "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 "Informacija apie įrašus"
 
@@ -735,9 +820,6 @@ msgstr "Atsukti"
 msgid "Recordings"
 msgstr "Įrašai"
 
-msgid "Button$Open"
-msgstr "Atidaryti"
-
 msgid "Commands"
 msgstr "Komandos"
 
@@ -993,6 +1075,33 @@ msgstr "Įrenginys %d prijungtas prie sat kabelio"
 msgid "Setup.LNB$own"
 msgstr "savas"
 
+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 "Dekodavimo modulis (CAM) perkrautas"
 
@@ -1002,9 +1111,19 @@ msgstr "Dekodavimo modulis (CAM) įjungtas"
 msgid "CAM ready"
 msgstr "Dekodavimo modulis (CAM) paruoštas darbui"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "Dekodavimo modulis (CAM)"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Meniu"
 
@@ -1017,6 +1136,9 @@ msgstr "Atidaromas dekodavimo modulio meniu..."
 msgid "Can't open CAM menu!"
 msgstr "Negali atidaryti dekodavimo modulio meniu!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "Dekodavimo modulis šiuo metu naudojamas - tikrai perkrauti?"
 
@@ -1110,6 +1232,21 @@ msgstr "Progreso rodymo laikas (s)"
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Kūrinio ID"
 
@@ -1146,6 +1283,12 @@ msgstr "kaip anksčiau"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Garsas įjungimo metu"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kanalų pridengimas"
 
@@ -1330,6 +1473,10 @@ msgstr "GYVAI"
 msgid "PLAY"
 msgstr "GROTI"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Skydeliai"
 
diff --git a/po/mk_MK.po b/po/mk_MK.po
index b9f56a2..418381c 100644
--- a/po/mk_MK.po
+++ b/po/mk_MK.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2012-11-19 15:18+0100\n"
 "Last-Translator: Dimitar Petrovski <dimeptr at gmail.com>\n"
 "Language-Team: Macedonian <en at li.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Невалиден Канал ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Каналот е недостапен!"
 
@@ -77,6 +80,15 @@ msgstr "Рол-оф"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Започнувам скенирање на EPG"
 
@@ -564,6 +576,12 @@ msgstr "Кодирање (CA)"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Уредбите на каналот не се уникатни!"
 
@@ -604,8 +622,8 @@ msgstr "Името веќе постои"
 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,73 @@ msgstr "Внеси %d цифри!"
 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 "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 +821,6 @@ msgstr "Премотај"
 msgid "Recordings"
 msgstr "Снимки"
 
-msgid "Button$Open"
-msgstr "Отвори"
-
 msgid "Commands"
 msgstr "Наредби"
 
@@ -994,6 +1076,33 @@ msgstr "Уред %d поврзан со сателитски кабел"
 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 +1112,19 @@ msgstr "CAM присутен"
 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 +1137,9 @@ msgstr "Отварам CAM мени..."
 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 +1233,21 @@ msgstr "Време на прикажување на прогрес (сек)"
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID на продолжеток"
 
@@ -1147,6 +1284,12 @@ msgstr "како претходно"
 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 +1474,10 @@ msgstr "ЖИВО"
 msgid "PLAY"
 msgstr "ПУШТИ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Панели"
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 8766f34..7f9af10 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -12,7 +12,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-02-26 17:20+0100\n"
 "Last-Translator: Cedric Dewijs <cedric.dewijs at telfort.nl>\n"
 "Language-Team: Dutch <vdr at linuxtv.org>\n"
@@ -24,6 +24,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Ongeldig kanaal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanaal niet beschikbaar"
 
@@ -81,6 +84,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Bezig met starten EPG scan"
 
@@ -568,6 +580,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Kanaalinstellingen zijn niet uniek!"
 
@@ -608,8 +626,8 @@ msgstr "Map bestaat al"
 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 +746,73 @@ msgstr "Vul %d cijfers in!"
 msgid "CAM not responding!"
 msgstr "CAM reageert niet!"
 
+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 "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 "Opname info"
 
@@ -740,9 +825,6 @@ msgstr "Naar begin"
 msgid "Recordings"
 msgstr "Opnames"
 
-msgid "Button$Open"
-msgstr "Openen"
-
 msgid "Commands"
 msgstr "Commando's"
 
@@ -998,6 +1080,33 @@ msgstr "Apparaat %d verbionden met sat. kabel"
 msgid "Setup.LNB$own"
 msgstr "eigen"
 
+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 herstarten"
 
@@ -1007,9 +1116,19 @@ msgstr "CAM aanwezig"
 msgid "CAM ready"
 msgstr "CAM gereed"
 
+#. 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"
 
@@ -1022,6 +1141,9 @@ msgstr "CAM-menu wordt geopend..."
 msgid "Can't open CAM menu!"
 msgstr "Kan CAM-menu niet openen!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM wordt gebruikt - werkelijk herstarten?"
 
@@ -1115,6 +1237,21 @@ msgstr "Verlopen tijd tijd (s)"
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Hervattings ID"
 
@@ -1151,6 +1288,12 @@ msgstr "zoals eerder"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Opstartvolume"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Doorscrollen kanalenlijst"
 
@@ -1335,6 +1478,10 @@ msgstr "LIVE"
 msgid "PLAY"
 msgstr "AFSPELEN"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Consoles"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 4f6668c..6eb63fa 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2007-08-12 14:17+0200\n"
 "Last-Translator: Truls Slevigen <truls at slevigen.no>\n"
 "Language-Team: Norwegian Nynorsk <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Ugyldig Kanal! ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr ""
 
@@ -77,6 +80,15 @@ msgstr ""
 msgid "StreamId"
 msgstr ""
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr ""
 
@@ -564,6 +576,12 @@ msgstr "Kortleser"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr ""
 
@@ -604,8 +622,8 @@ msgstr ""
 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,73 @@ msgstr ""
 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 "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 +821,6 @@ msgstr "Spol tilbake"
 msgid "Recordings"
 msgstr "Opptak"
 
-msgid "Button$Open"
-msgstr "�pne"
-
 msgid "Commands"
 msgstr "Kommandoer"
 
@@ -994,6 +1076,33 @@ msgstr ""
 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 +1112,19 @@ msgstr ""
 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 +1137,9 @@ msgstr ""
 msgid "Can't open CAM menu!"
 msgstr ""
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr ""
 
@@ -1111,6 +1233,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "Resume ID"
 
@@ -1147,6 +1284,12 @@ msgstr ""
 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 +1474,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr ""
 
diff --git a/po/pl_PL.po b/po/pl_PL.po
index c11c656..18c8662 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-03-09 12:59+0100\n"
 "Last-Translator: Marek Nazarko <mnazarko at gmail.com>\n"
 "Language-Team: Polish <vdr at linuxtv.org>\n"
@@ -21,6 +21,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Niepoprawny kana� ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kana� nie jest dost�pny!"
 
@@ -78,6 +81,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Rozpoczynam skanowanie EPG"
 
@@ -565,6 +577,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Ustawienia kana�u nie s� unikalne!"
 
@@ -605,8 +623,8 @@ msgstr "Taka nazwa katalogu juz istnieje"
 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 +743,73 @@ msgstr "Prosz
 msgid "CAM not responding!"
 msgstr "CAM nie 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 "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 "Informacje o nagraniu"
 
@@ -737,9 +822,6 @@ msgstr "Pocz
 msgid "Recordings"
 msgstr "Nagrania"
 
-msgid "Button$Open"
-msgstr "Otw�rz"
-
 msgid "Commands"
 msgstr "Polecenia"
 
@@ -995,6 +1077,33 @@ msgstr "Urz
 msgid "Setup.LNB$own"
 msgstr "W�asny"
 
+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 zresetowany"
 
@@ -1004,9 +1113,19 @@ msgstr "CAM obecny"
 msgid "CAM ready"
 msgstr "CAM gotowy"
 
+#. 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"
 
@@ -1019,6 +1138,9 @@ msgstr "Otwieram menu CAM..."
 msgid "Can't open CAM menu!"
 msgstr "Nie mo�na otworzy� menu CAM!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM jest w u�yciu - naprawd� zresetowa�?"
 
@@ -1112,6 +1234,21 @@ msgstr "Wy
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID wznowienia"
 
@@ -1148,6 +1285,12 @@ msgstr "jak ostatnio"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Pocz�tkowa g�o�no��"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Zawijanie kana��w"
 
@@ -1332,6 +1475,10 @@ msgstr "NA 
 msgid "PLAY"
 msgstr "ODTWARZA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Panel ST:TNG"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index ffd3fec..b3a891c 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2010-03-28 22:49+0100\n"
 "Last-Translator: Cris Silva <hudokkow at gmail.com>\n"
 "Language-Team: Portuguese <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Canal inv�lido ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Canal indispon�vel!"
 
@@ -77,6 +80,15 @@ msgstr "Rolloff"
 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 @@ msgstr "CA"
 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 @@ msgstr "Nome da pasta j
 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,73 @@ msgstr "Por favor introduza %d d
 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 "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 +821,6 @@ msgstr "Retroceder"
 msgid "Recordings"
 msgstr "Grava��es"
 
-msgid "Button$Open"
-msgstr "Abrir"
-
 msgid "Commands"
 msgstr "Comandos"
 
@@ -994,6 +1076,33 @@ msgstr ""
 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 +1112,19 @@ msgstr "CAM presente"
 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 +1137,9 @@ msgstr "A abrir menu da CAM..."
 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 +1233,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID de resumo"
 
@@ -1147,6 +1284,12 @@ msgstr "como estava"
 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 +1474,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "Consola ST:TNG"
 
diff --git a/po/ro_RO.po b/po/ro_RO.po
index 3214a2e..25766cf 100644
--- a/po/ro_RO.po
+++ b/po/ro_RO.po
@@ -8,19 +8,22 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at 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 at users.sorceforge.net>\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
+"PO-Revision-Date: 2015-01-21 22:34+0100\n"
+"Last-Translator: Lucian Muresan <lucianm at users.sourceforge.net>\n"
 "Language-Team: Romanian <vdr at 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 ""
+
 msgid "Channel not available!"
 msgstr "Canal indisponibil"
 
@@ -78,6 +81,15 @@ msgstr "Rolloff"
 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 @@ msgstr "CA (Acces Condiţional)"
 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 @@ msgstr "Un director cu acelaşi nume există!"
 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,73 @@ msgstr "Vă rog introduceţi %d cifre!"
 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 "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 +822,6 @@ msgstr "De la capăt"
 msgid "Recordings"
 msgstr "Înregistrări"
 
-msgid "Button$Open"
-msgstr "Deschide"
-
 msgid "Commands"
 msgstr "Comenzi"
 
@@ -747,7 +829,7 @@ msgid "Delete recording?"
 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 +1077,33 @@ msgstr "Receptorul %d conectat la cablul de satelit"
 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 +1113,19 @@ msgstr "CAM prezent"
 msgid "CAM ready"
 msgstr "CAM pregătit"
 
+#. 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 "Meniu"
 
@@ -1019,6 +1138,9 @@ msgstr "Deschid meniul CAM..."
 msgid "Can't open CAM menu!"
 msgstr "Nu pot deschide meniul CAM"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM-ul este in folosinţă - totuşi resetez?"
 
@@ -1089,7 +1211,7 @@ msgid "Setup.Recording$Max. video file size (MB)"
 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ă"
@@ -1110,7 +1232,22 @@ msgid "Setup.Replay$Progress display time (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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
 
 msgid "Setup.Replay$Resume ID"
 msgstr "Identificator continuare"
@@ -1148,6 +1285,12 @@ msgstr "ca mai înainte"
 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 +1337,13 @@ msgstr "Continuă redarea"
 
 #. 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 +1371,19 @@ msgid "Jump: "
 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 +1425,7 @@ msgid "Can't shutdown - option '-s' not given!"
 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 +1442,7 @@ msgid "Plugin %s wakes up in %ld min, continue?"
 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 +1475,10 @@ msgstr "LIVE"
 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 +1530,10 @@ msgid "Switching primary DVB..."
 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 --git a/po/ru_RU.po b/po/ru_RU.po
index eecc89e..e0a291a 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-10 17:13+0100\n"
 "Last-Translator: Oleg Roitburd <oroitburd at gmail.com>\n"
 "Language-Team: Russian <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** ������������ ����� ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "����� ����������!"
 
@@ -77,6 +80,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "������� EPG-������������"
 
@@ -564,6 +576,12 @@ msgstr "CA (
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "��������� ������ �� ���������!"
 
@@ -604,8 +622,8 @@ msgstr "
 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,73 @@ msgstr "
 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 "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 +821,6 @@ msgstr "
 msgid "Recordings"
 msgstr "������"
 
-msgid "Button$Open"
-msgstr "�������"
-
 msgid "Commands"
 msgstr "�������"
 
@@ -994,6 +1076,33 @@ msgstr "
 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 +1112,19 @@ msgstr "CAM 
 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 +1137,9 @@ msgstr "
 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 +1233,21 @@ msgstr "
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID ���������������"
 
@@ -1147,6 +1284,12 @@ msgstr "
 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 +1474,10 @@ msgstr "LIVE"
 msgid "PLAY"
 msgstr "���������������"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG ������"
 
diff --git a/po/sk_SK.po b/po/sk_SK.po
index 46dc3ae..c8c4225 100644
--- a/po/sk_SK.po
+++ b/po/sk_SK.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-04 21:24+0100\n"
 "Last-Translator: Milan Hrala <hrala.milan at gmail.com>\n"
 "Language-Team: Slovak <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Neplatn� kan�l ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kan�l nie je dostupn�!"
 
@@ -76,6 +79,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Sp���a sa sn�manie EPG"
 
@@ -563,6 +575,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Nastavenia kan�lu nie s� v�nimo�n�!"
 
@@ -603,8 +621,8 @@ msgstr "N
 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 +741,73 @@ msgstr "Pros
 msgid "CAM not responding!"
 msgstr "CAM neodpoved�!"
 
+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 "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 "Podrobnosti nahr�vky"
 
@@ -735,9 +820,6 @@ msgstr "Od za
 msgid "Recordings"
 msgstr "Nahr�vky"
 
-msgid "Button$Open"
-msgstr "Otvori�"
-
 msgid "Commands"
 msgstr "Pr�kazy"
 
@@ -993,6 +1075,33 @@ msgstr "Zariadenie %d je pripojen
 msgid "Setup.LNB$own"
 msgstr "vlastn�"
 
+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 "Resetnutie CAMu"
 
@@ -1002,9 +1111,19 @@ msgstr "CAM pr
 msgid "CAM ready"
 msgstr "CAM pripraven�"
 
+#. TRANSLATORS: note the leading blank!
+msgid " (activating)"
+msgstr ""
+
 msgid "CAM"
 msgstr "CAM (modul podmienen�ho pr�stupu)"
 
+msgid "Button$Cancel activation"
+msgstr ""
+
+msgid "Button$Activate"
+msgstr ""
+
 msgid "Button$Menu"
 msgstr "Menu"
 
@@ -1017,6 +1136,9 @@ msgstr "Otv
 msgid "Can't open CAM menu!"
 msgstr "Menu CAM nie je dostupn�"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM sa pou��va - naozaj re�tartova�?"
 
@@ -1110,6 +1232,21 @@ msgstr "Doba zobrazenia ukazovate
 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 ""
+
+msgid "Setup.Replay$Skip edited parts"
+msgstr ""
+
+msgid "Setup.Replay$Pause replay at last mark"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ident. ��slo obnovenia prehr�vania"
 
@@ -1146,6 +1283,12 @@ msgstr "ako naposledy"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Hlasitos� po spusten�"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Kan�ly cyklova� pri prepnut�"
 
@@ -1330,6 +1473,10 @@ msgstr "NA
 msgid "PLAY"
 msgstr "PREHR�VA SA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG panely"
 
diff --git a/po/sl_SI.po b/po/sl_SI.po
index 842157e..9d58dd5 100644
--- a/po/sl_SI.po
+++ b/po/sl_SI.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-04 12:46+0100\n"
 "Last-Translator: Matjaz Thaler <matjaz.thaler at guest.arnes.si>\n"
 "Language-Team: Slovenian <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Neznan kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal ni razpolo�ljiv!"
 
@@ -77,6 +80,15 @@ msgstr "Odpadanje"
 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 @@ msgstr "CA"
 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 @@ msgstr "Ime direktorija 
 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,73 @@ msgstr "Prosim vnesite %d 
 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 "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 +821,6 @@ msgstr "Na za
 msgid "Recordings"
 msgstr "Posnetki"
 
-msgid "Button$Open"
-msgstr "Odpri"
-
 msgid "Commands"
 msgstr "Ukazi"
 
@@ -994,6 +1076,33 @@ msgstr "Naprava %d priklju
 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 +1112,19 @@ msgstr "CAM prisoten"
 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 +1137,9 @@ msgstr "Odpiram CAM meni..."
 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 +1233,21 @@ msgstr "Progresivni 
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID za predvajanje"
 
@@ -1147,6 +1284,12 @@ msgstr "kot prej"
 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 +1474,10 @@ msgstr "V 
 msgid "PLAY"
 msgstr "PREDVAJAJ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG Konsola"
 
diff --git a/po/sr_RS.po b/po/sr_RS.po
index 2968b91..dbe75fd 100644
--- a/po/sr_RS.po
+++ b/po/sr_RS.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-16 15:05+0100\n"
 "Last-Translator: Zoran Turalija <zoran.turalija at gmail.com>\n"
 "Language-Team: Serbian <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Neispravan Kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal nije dostupan!"
 
@@ -77,6 +80,15 @@ msgstr "Rolloff"
 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 @@ msgstr "Kodiranje (CA)"
 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 @@ msgstr "Naziv direktorijuma ve
 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,73 @@ msgstr "Molimo unesite %d brojeve!"
 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 "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 +821,6 @@ msgstr "Na po
 msgid "Recordings"
 msgstr "Snimci"
 
-msgid "Button$Open"
-msgstr "Otvori"
-
 msgid "Commands"
 msgstr "Komande"
 
@@ -994,6 +1076,33 @@ msgstr "Ure
 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 +1112,19 @@ msgstr "CAM prisutan"
 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 +1137,9 @@ msgstr "Otvaram CAM meni..."
 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 +1233,21 @@ msgstr "Trajanje prikazivanja progresa (s)"
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID reprodukcije"
 
@@ -1147,6 +1284,12 @@ msgstr "kao prethodno"
 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"
 
@@ -1331,6 +1474,10 @@ msgstr "U
 msgid "PLAY"
 msgstr "REPRODUKCIJA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG paneli"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 15a49ee..33add95 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-02-18 17:04+0100\n"
 "Last-Translator: Richard Lithvall <r-vdr at boomer.se>\n"
 "Language-Team: Swedish <vdr at linuxtv.org>\n"
@@ -23,6 +23,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Felaktig kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanalen �r inte tillg�nglig!"
 
@@ -80,6 +83,15 @@ msgstr "Rolloff"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "P�b�rjar EPG-avs�kning"
 
@@ -567,6 +579,12 @@ msgstr "Kortl
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Kanalinst�llningarna �r ej unika!"
 
@@ -607,8 +625,8 @@ msgstr "Mappnamnet finns redan!"
 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 +745,73 @@ msgstr "Mata in %d siffror!"
 msgid "CAM not responding!"
 msgstr "CAM svarar inte!"
 
+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 "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 "Inspelningsinformation"
 
@@ -739,9 +824,6 @@ msgstr "
 msgid "Recordings"
 msgstr "Inspelningar"
 
-msgid "Button$Open"
-msgstr "�ppna"
-
 msgid "Commands"
 msgstr "Kommandon"
 
@@ -997,6 +1079,33 @@ msgstr "Enhet %d ansluten till satellitkabel"
 msgid "Setup.LNB$own"
 msgstr "�gare"
 
+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 omstart"
 
@@ -1006,9 +1115,19 @@ msgstr "CAM n
 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 "Meny"
 
@@ -1021,6 +1140,9 @@ msgstr "
 msgid "Can't open CAM menu!"
 msgstr "Det g�r inte att �ppna CAM menyn!"
 
+msgid "Can't activate CAM!"
+msgstr ""
+
 msgid "CAM is in use - really reset?"
 msgstr "CAM upptagen, vill du verkligen �terst�lla?"
 
@@ -1114,6 +1236,21 @@ msgstr "Tid f
 msgid "Setup.Replay$Pause replay when setting mark"
 msgstr "Pausa uppspelningen vid s�ttande av redigeringsm�rke"
 
+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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "�terupptagnings-ID"
 
@@ -1150,6 +1287,12 @@ msgstr "samma som vid avslut"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "Ljudstyrka vid uppstart"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "Cirkul�r kanallista"
 
@@ -1334,6 +1477,10 @@ msgstr "LIVE"
 msgid "PLAY"
 msgstr "SPELA"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG"
 
diff --git a/po/tr_TR.po b/po/tr_TR.po
index 54956de..e670842 100644
--- a/po/tr_TR.po
+++ b/po/tr_TR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2008-02-28 00:33+0100\n"
 "Last-Translator: Oktay Yolge�en <oktay_73 at yahoo.de>\n"
 "Language-Team: Turkish <vdr at linuxtv.org>\n"
@@ -19,6 +19,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Ge�ersiz kanal ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Kanal kullan�lam�yor!"
 
@@ -76,6 +79,15 @@ msgstr ""
 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 @@ msgstr "CA"
 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 @@ msgstr ""
 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,73 @@ msgstr "L
 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 "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 +820,6 @@ msgstr "Baslang
 msgid "Recordings"
 msgstr "Kay�tlar"
 
-msgid "Button$Open"
-msgstr "A�"
-
 msgid "Commands"
 msgstr "Komutlar"
 
@@ -993,6 +1075,33 @@ msgstr ""
 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 +1111,19 @@ msgstr "CAM mevcut"
 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 +1136,9 @@ msgstr "CAM men
 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 +1232,21 @@ msgstr ""
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "G�steri� ID'si"
 
@@ -1146,6 +1283,12 @@ msgstr "
 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 +1473,10 @@ msgstr ""
 msgid "PLAY"
 msgstr ""
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG paneli"
 
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 7626d8d..351d94d 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-03-04 14:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-02-09 16:00+0100\n"
 "Last-Translator: Yarema aka Knedlyk <yupadmin at gmail.com>\n"
 "Language-Team: Ukrainian <vdr at linuxtv.org>\n"
@@ -20,6 +20,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "*** Неправильний канал ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "Канал недоступний!"
 
@@ -77,6 +80,15 @@ msgstr "Крен"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "Починаю EPG-сканування"
 
@@ -564,6 +576,12 @@ msgstr "CA (декодер)"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "Настройки каналу не єдині!"
 
@@ -604,8 +622,8 @@ msgstr "Назва теки вже існує!"
 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,73 @@ msgstr "Введіть %d цифри!"
 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 "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 +821,6 @@ msgstr "Назад"
 msgid "Recordings"
 msgstr "Записи"
 
-msgid "Button$Open"
-msgstr "Відкрити"
-
 msgid "Commands"
 msgstr "Команди"
 
@@ -994,6 +1076,33 @@ msgstr "Пристрій %d під’єднано до сателітарног
 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 +1112,19 @@ msgstr "CAM присутній"
 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 +1137,9 @@ msgstr "Відкривання меню CAM-модуля..."
 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 +1233,21 @@ msgstr "Час показу індикатора (с)"
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "ID продовження"
 
@@ -1147,6 +1284,12 @@ msgstr "як раніше"
 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 +1385,10 @@ msgid "Editing process already active!"
 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-.#~,/_ at 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 +1474,10 @@ msgstr "ЕФІР"
 msgid "PLAY"
 msgstr "ПРОГРАВАННЯ"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG панелі"
 
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 98e3f98..bf25381 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 2.0.0\n"
 "Report-Msgid-Bugs-To: <vdr-bugs at tvdr.de>\n"
-"POT-Creation-Date: 2013-02-03 16:46+0100\n"
+"POT-Creation-Date: 2015-01-30 13:14+0100\n"
 "PO-Revision-Date: 2013-03-04 14:52+0800\n"
 "Last-Translator: NFVDR <nfvdr at live.com>\n"
 "Language-Team: Chinese (simplified) <nfvdr at live.com>\n"
@@ -21,6 +21,9 @@ msgstr ""
 msgid "*** Invalid Channel ***"
 msgstr "***无效的频道 ***"
 
+msgid "CAM activated!"
+msgstr ""
+
 msgid "Channel not available!"
 msgstr "频道不可用!"
 
@@ -78,6 +81,15 @@ msgstr "越零率"
 msgid "StreamId"
 msgstr "StreamId"
 
+msgid "Pilot"
+msgstr ""
+
+msgid "T2SystemId"
+msgstr ""
+
+msgid "SISO/MISO"
+msgstr ""
+
 msgid "Starting EPG scan"
 msgstr "开始节目单扫描"
 
@@ -565,6 +577,12 @@ msgstr "CA"
 msgid "Sid"
 msgstr "Sid"
 
+msgid "Nid"
+msgstr ""
+
+msgid "Tid"
+msgstr ""
+
 msgid "Channel settings are not unique!"
 msgstr "频道设置不是唯一的!"
 
@@ -605,8 +623,8 @@ msgstr "文件夹名称已经存在!"
 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 "删除文件夹和所有子文件夹吗?"
@@ -725,6 +743,73 @@ msgstr "请输入 %d 数字!"
 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 "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 +822,6 @@ msgstr "重放"
 msgid "Recordings"
 msgstr "录像回放列表"
 
-msgid "Button$Open"
-msgstr "打开"
-
 msgid "Commands"
 msgstr "常用操作命令"
 
@@ -995,6 +1077,33 @@ msgstr "设备%d卫星连接线"
 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 +1113,19 @@ msgstr "存在的CAM"
 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 +1138,9 @@ msgstr "打开CAM菜单..."
 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 +1234,21 @@ msgstr "处理显示时间(s)"
 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$Binary skip initial value (s)"
+msgstr ""
+
+msgid "Setup.Replay$Binary skip timeout (s)"
+msgstr ""
+
 msgid "Setup.Replay$Resume ID"
 msgstr "恢复 ID"
 
@@ -1148,6 +1285,12 @@ msgstr "之前"
 msgid "Setup.Miscellaneous$Initial volume"
 msgstr "初始化声音"
 
+msgid "Setup.Miscellaneous$Volume steps"
+msgstr ""
+
+msgid "Setup.Miscellaneous$Volume linearize"
+msgstr ""
+
 msgid "Setup.Miscellaneous$Channels wrap"
 msgstr "频道排序"
 
@@ -1332,6 +1475,10 @@ msgstr "现场"
 msgid "PLAY"
 msgstr "播放"
 
+#, c-format
+msgid "Moving dish to %.1f..."
+msgstr ""
+
 msgid "ST:TNG Panels"
 msgstr "ST:TNG 面板"
 
diff --git a/positioner.c b/positioner.c
new file mode 100644
index 0000000..c6e0c73
--- /dev/null
+++ b/positioner.c
@@ -0,0 +1,140 @@
+/*
+ * 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
+ *
+ * $Id: positioner.c 3.4 2013/10/30 09:56:34 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 --git a/positioner.h b/positioner.h
new file mode 100644
index 0000000..4fdc3d0
--- /dev/null
+++ b/positioner.h
@@ -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 --git a/receiver.c b/receiver.c
index bde60e4..d8f51e6 100644
--- a/receiver.c
+++ b/receiver.c
@@ -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(const cChannel *Channel, int Priority)
 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 @@ bool cReceiver::SetPids(const cChannel *Channel)
   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 --git a/receiver.h b/receiver.h
index 775dabd..b354825 100644
--- a/receiver.h
+++ b/receiver.h
@@ -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 @@ private:
   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 @@ public:
                ///< 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 @@ public:
                ///< 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 --git a/recorder.c b/recorder.c
index 7eee925..3d6b806 100644
--- a/recorder.c
+++ b/recorder.c
@@ -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.1.2 2014/02/21 09:21:30 kls Exp $
+ * $Id: recorder.c 3.3 2014/02/21 09:19:52 kls Exp $
  */
 
 #include "recorder.h"
@@ -139,6 +139,7 @@ void cRecorder::Action(void)
                           }
                        }
                     InfoWritten = true;
+                    cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName);
                     }
                  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
                     FirstIframeSeen = true; // start recording with the first I-frame
diff --git a/recorder.h b/recorder.h
index 05cc42b..4e7848a 100644
--- a/recorder.h
+++ b/recorder.h
@@ -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 @@ private:
   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 --git a/recording.c b/recording.c
index d62dd9c..079e0de 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.c 2.91.1.7 2014/03/16 11:03:18 kls Exp $
+ * $Id: recording.c 3.24 2015/01/25 15:39:24 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,9 +65,12 @@
 #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;
@@ -91,13 +96,18 @@ cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void)
 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();
@@ -110,7 +120,7 @@ void cRemoveDeletedRecordingsThread::Action(void)
          }
      if (deleted) {
         const char *IgnoreFiles[] = { SORTMODEFILE, NULL };
-        RemoveEmptyVideoDirectories(IgnoreFiles);
+        cVideoDirectory::RemoveEmptyVideoDirectories(IgnoreFiles);
         }
      }
 }
@@ -146,9 +156,9 @@ void AssertFreeDiskSpace(int Priority, bool Force)
   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":
@@ -431,6 +441,13 @@ void cRecordingInfo::SetFramesPerSecond(double FramesPerSecond)
   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) {
@@ -778,7 +795,7 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event)
   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();
@@ -809,8 +826,8 @@ cRecording::cRecording(const char *FileName)
   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;
@@ -965,7 +982,7 @@ char *cRecording::SortName(void) const
         *sb = strdup(buf);
         }
      else {
-        char *s = strdup(FileName() + strlen(VideoDirectory));
+        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
@@ -980,8 +997,9 @@ char *cRecording::SortName(void) const
 
 void cRecording::ClearSortName(void)
 {
-  DELETENULL(sortBufferName);
-  DELETENULL(sortBufferTime);
+  free(sortBufferName);
+  free(sortBufferTime);
+  sortBufferName = sortBufferTime = NULL;
 }
 
 int cRecording::GetResume(void) const
@@ -999,6 +1017,28 @@ int cRecording::Compare(const cListObject &ListObject) const
   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) {
@@ -1007,11 +1047,11 @@ const char *cRecording::FileName(void) const
      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;
@@ -1080,7 +1120,7 @@ const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) cons
 
 const char *cRecording::PrefixFileName(char Prefix)
 {
-  cString p = PrefixVideoFileName(FileName(), Prefix);
+  cString p = cVideoDirectory::PrefixVideoFileName(FileName(), Prefix);
   if (*p) {
      free(fileName);
      fileName = strdup(p);
@@ -1110,10 +1150,26 @@ bool cRecording::IsEdited(void) const
 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();
@@ -1122,13 +1178,13 @@ void cRecording::ReadInfo(void)
   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);
@@ -1142,6 +1198,59 @@ void cRecording::SetStartTime(time_t Start)
   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;
@@ -1152,11 +1261,11 @@ bool cRecording::Delete(void)
      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 {
@@ -1176,7 +1285,7 @@ bool cRecording::Remove(void)
      return false;
      }
   isyslog("removing recording %s", FileName());
-  return RemoveVideoFile(FileName());
+  return cVideoDirectory::RemoveVideoFile(FileName());
 }
 
 bool cRecording::Undelete(void)
@@ -1194,7 +1303,7 @@ bool cRecording::Undelete(void)
      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;
@@ -1205,6 +1314,17 @@ bool cRecording::Undelete(void)
   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;
@@ -1268,22 +1388,26 @@ void cRecordings::Action(void)
 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, int DirLevel)
+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;
@@ -1313,7 +1437,10 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev
                           r->deleted = time(NULL);
                        Lock();
                        Add(r);
-                       ChangeState();
+                       if (initial)
+                          ChangeState();
+                       else
+                          DoChangeState = true;
                        Unlock();
                        }
                     else
@@ -1321,7 +1448,7 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev
                     }
                  }
               else
-                 ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
+                 DoChangeState |= ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
               }
            }
         }
@@ -1334,11 +1461,14 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev
             Lock();
             Del(r, false);
             VanishedRecordings.Add(r);
-            ChangeState();
+            DoChangeState = true;
             Unlock();
             }
          }
      }
+  if (DoChangeState && DirLevel == 0)
+     ChangeState();
+  return DoChangeState;
 }
 
 bool cRecordings::StateChanged(int &State)
@@ -1405,22 +1535,24 @@ void cRecordings::DelByName(const char *FileName)
 {
   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)
@@ -1454,8 +1586,10 @@ double cRecordings::MBperMinute(void)
          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;
+                  }
                }
             }
          }
@@ -1463,6 +1597,46 @@ double cRecordings::MBperMinute(void)
   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;
@@ -1480,6 +1654,362 @@ void cRecordings::ClearSortNames(void)
       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 = false;
+  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);
+                 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);
+  error = true;
+}
+
+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;
@@ -1522,6 +2052,11 @@ bool cMark::Save(FILE *f)
 
 // --- 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)
 {
   recordingFileName = RecordingFileName;
@@ -1537,7 +2072,7 @@ bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool Is
 bool cMarks::Update(void)
 {
   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;
@@ -1706,17 +2241,19 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi
 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();
 }
 
@@ -1735,15 +2272,34 @@ void cIndexFileGenerator::Action(void)
   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;
            }
@@ -1758,7 +2314,8 @@ void cIndexFileGenerator::Action(void)
               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;
                     }
@@ -1803,10 +2360,25 @@ void cIndexFileGenerator::Action(void)
         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:
@@ -1868,7 +2440,7 @@ struct tIndexTs {
 #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;
@@ -1904,10 +2476,10 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
            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) {
@@ -2169,7 +2741,7 @@ int cIndexFile::Get(uint16_t FileNumber, off_t FileOffset)
   return -1;
 }
 
-bool cIndexFile::IsStillRecording()
+bool cIndexFile::IsStillRecording(void)
 {
   return f >= 0;
 }
@@ -2195,15 +2767,16 @@ int cIndexFile::GetLength(const char *FileName, bool IsPesRecording)
   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)
@@ -2311,7 +2884,7 @@ cUnbufferedFile *cFileName::Open(void)
      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);
         }
@@ -2332,8 +2905,9 @@ cUnbufferedFile *cFileName::Open(void)
 void cFileName::Close(void)
 {
   if (file) {
-     if (CloseVideoFile(file) < 0)
+     if (file->Close() < 0)
         LOG_ERROR_STR(fileName);
+     delete file;
      file = NULL;
      }
 }
@@ -2395,7 +2969,7 @@ cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
      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;
@@ -2405,12 +2979,12 @@ cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
 
 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;
 }
 
diff --git a/recording.h b/recording.h
index 66239ea..1079ae1 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.h 2.46.1.1 2013/12/25 10:54:05 kls Exp $
+ * $Id: recording.h 3.6 2015/01/31 13:34:44 kls Exp $
  */
 
 #ifndef __RECORDING_H
@@ -25,6 +25,21 @@ extern int DirectoryNameMax;
 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);
@@ -74,6 +89,7 @@ public:
   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;
@@ -101,7 +117,6 @@ private:
   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;
@@ -115,8 +130,21 @@ public:
   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);
@@ -131,12 +159,24 @@ public:
   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
@@ -145,6 +185,17 @@ public:
        ///< 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
@@ -155,6 +206,14 @@ public:
        ///< 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 {
@@ -166,7 +225,7 @@ private:
   int state;
   const char *UpdateFileName(void);
   void Refresh(bool Foreground = false);
-  void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0, int DirLevel = 0);
+  bool ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0, int DirLevel = 0);
 protected:
   void Action(void);
 public:
@@ -199,6 +258,27 @@ public:
   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
@@ -206,6 +286,50 @@ public:
 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 {
@@ -236,6 +360,9 @@ private:
   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);
@@ -261,6 +388,7 @@ public:
   };
 
 #define RUC_BEFORERECORDING "before"
+#define RUC_STARTRECORDING  "started"
 #define RUC_AFTERRECORDING  "after"
 #define RUC_EDITEDRECORDING "edited"
 #define RUC_DELETERECORDING "deleted"
@@ -303,7 +431,7 @@ private:
   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);
@@ -362,7 +490,11 @@ char *ExchangeChars(char *s, bool ToFileSystem);
       // 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 --git a/remote.c b/remote.c
index a6e9a65..ac10dce 100644
--- a/remote.c
+++ b/remote.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remote.c 2.8.1.1 2014/02/15 12:44:48 kls Exp $
+ * $Id: remote.c 3.3 2015/01/20 14:53:57 kls Exp $
  */
 
 #include "remote.h"
@@ -124,7 +124,7 @@ bool cRemote::PutMacro(eKeys Key)
 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 @@ cKbdRemote::cKbdRemote(void)
      tcsetattr(STDIN_FILENO, TCSANOW, &tm);
      }
   kbdAvailable = true;
+  systemIsUtf8 = !cCharSetConv::SystemCharacterTable() || strcmp(cCharSetConv::SystemCharacterTable(), "UTF-8") == 0;
   Start();
 }
 
@@ -324,7 +325,23 @@ uint64_t cKbdRemote::ReadKeySequence(void)
 
   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 --git a/remote.h b/remote.h
index d453b2f..415f2e7 100644
--- a/remote.h
+++ b/remote.h
@@ -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 @@ class cKbdRemote : public cRemote, private cThread {
 private:
   static bool kbdAvailable;
   static bool rawMode;
+  bool systemIsUtf8;
   struct termios savedTm;
   virtual void Action(void);
   int ReadKey(void);
diff --git a/remux.c b/remux.c
index c42bf93..23e8387 100644
--- a/remux.c
+++ b/remux.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remux.c 2.75.1.5 2014/03/08 15:10:24 kls Exp $
+ * $Id: remux.c 3.9 2015/01/14 09:57:09 kls Exp $
  */
 
 #include "remux.h"
@@ -822,9 +822,12 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
                          }
                       }
                       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)); ) {
@@ -850,6 +853,36 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
                          }
                       }
                       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");
@@ -1511,7 +1544,12 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
                        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 +  parser->IFrameTemporalReferenceOffset());
+                       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)
diff --git a/remux.h b/remux.h
index 12ee970..6bc91ad 100644
--- a/remux.h
+++ b/remux.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remux.h 2.37.1.2 2014/01/28 12:36:26 kls Exp $
+ * $Id: remux.h 3.4 2014/03/22 14:58:24 kls Exp $
  */
 
 #ifndef __REMUX_H
@@ -50,6 +50,7 @@ public:
 #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
@@ -483,7 +484,6 @@ private:
   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 --git a/ringbuffer.c b/ringbuffer.c
index abe7899..e2921be 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -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 --git a/ringbuffer.h b/ringbuffer.h
index 1fff611..a975742 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -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 --git a/runvdr.template b/runvdr.template
index 2f6fd87..63f2830 100755
--- a/runvdr.template
+++ b/runvdr.template
@@ -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.0 2011/04/17 12:34:30 kls Exp $
 
 VDRPRG="./vdr"
 
diff --git a/scr.conf b/scr.conf
index 61e09f1..84cdcec 100644
--- a/scr.conf
+++ b/scr.conf
@@ -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 --git a/sdt.c b/sdt.c
index 18efcbc..4e6748d 100644
--- a/sdt.c
+++ b/sdt.c
@@ -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.1.2 2014/03/11 09:30:59 kls Exp $
+ * $Id: sdt.c 3.4 2015/01/04 14:33:35 kls Exp $
  */
 
 #include "sdt.h"
@@ -54,6 +54,8 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
       cChannel *channel = Channels.GetByChannelID(tChannelID(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()));
       if (!channel)
          channel = Channels.GetByChannelID(tChannelID(source, 0, Transponder(), SiSdtService.getServiceId()));
+      if (channel)
+         channel->SetSeen();
 
       cLinkChannels *LinkChannels = NULL;
       SI::Descriptor *d;
@@ -150,5 +152,9 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
             delete LinkChannels;
          }
       }
+  if (sdt.getSectionNumber() == sdt.getLastSectionNumber()) {
+     if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3)
+        Channels.MarkObsoleteChannels(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId());
+     }
   Channels.Unlock();
 }
diff --git a/sdt.h b/sdt.h
index 3b6d003..01653f4 100644
--- a/sdt.h
+++ b/sdt.h
@@ -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.1.1 2014/03/11 09:31:08 kls Exp $
+ * $Id: sdt.h 3.1 2014/03/10 14:40:54 kls Exp $
  */
 
 #ifndef __SDT_H
diff --git a/sections.c b/sections.c
index 991499b..32d80de 100644
--- a/sections.c
+++ b/sections.c
@@ -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 @@ public:
 // --- 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 --git a/sections.h b/sections.h
index 3b00c8b..7cb833c 100644
--- a/sections.h
+++ b/sections.h
@@ -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 --git a/shutdown.c b/shutdown.c
index 445ed6f..97d056e 100644
--- a/shutdown.c
+++ b/shutdown.c
@@ -6,7 +6,7 @@
  *
  * Original version written by Udo Richter <udo_richter at 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 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive)
         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::ConfirmShutdown(bool Interactive)
 
 bool cShutdownHandler::ConfirmRestart(bool Interactive)
 {
-  if (cCutter::Active()) {
+  if (RecordingsHandler.Active()) {
      if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?")))
         return false;
      }
diff --git a/shutdown.h b/shutdown.h
index bf2a2a4..5e4af29 100644
--- a/shutdown.h
+++ b/shutdown.h
@@ -6,7 +6,7 @@
  *
  * Original version written by Udo Richter <udo_richter at 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 --git a/skinclassic.c b/skinclassic.c
index b9919d8..fa38431 100644
--- a/skinclassic.c
+++ b/skinclassic.c
@@ -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 --git a/skinclassic.h b/skinclassic.h
index 60878e4..005901b 100644
--- a/skinclassic.h
+++ b/skinclassic.h
@@ -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 --git a/skinlcars.c b/skinlcars.c
index 8c949b3..a722b52 100644
--- a/skinlcars.c
+++ b/skinlcars.c
@@ -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.1.4 2014/03/10 12:12:19 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 DrawDeviceSignal(cOsd *Osd, const cDevice *Device, int x0, int y0, i
      }
 }
 
+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 @@ private:
   bool initial;
   cString lastDate;
   int lastSeen;
+  int lastCurrentPosition;
   int lastDeviceNumber;
   cString lastDeviceType;
   cCamSlot *lastCamSlot;
@@ -336,6 +376,7 @@ public:
   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 @@ cSkinLCARSDisplayChannel::cSkinLCARSDisplayChannel(bool WithInfo)
   initial = true;
   present = NULL;
   lastSeen = -1;
+  lastCurrentPosition = -1;
   lastDeviceNumber = -1;
   lastCamSlot = NULL;
   lastSignalStrength = -1;
@@ -440,7 +482,7 @@ cSkinLCARSDisplayChannel::~cSkinLCARSDisplayChannel()
 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 @@ void cSkinLCARSDisplayChannel::DrawTrack(void)
 {
   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::DrawTrack(void)
 
 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 @@ void cSkinLCARSDisplayChannel::SetChannel(const cChannel *Channel, int Number)
   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::SetMessage(eMessageType Type, const char *Text)
      }
 }
 
+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) {
@@ -863,7 +926,7 @@ cSkinLCARSDisplayMenu::cSkinLCARSDisplayMenu(void)
   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;
@@ -1007,7 +1070,7 @@ void cSkinLCARSDisplayMenu::DrawMenuFrame(void)
 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);
@@ -1354,6 +1417,8 @@ void cSkinLCARSDisplayMenu::DrawLive(const cChannel *Channel)
   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);
      }
@@ -1793,7 +1858,7 @@ void cSkinLCARSDisplayReplay::DrawTrack(void)
 {
   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 --git a/skinlcars.h b/skinlcars.h
index 71117f6..abab018 100644
--- a/skinlcars.h
+++ b/skinlcars.h
@@ -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 --git a/skins.c b/skins.c
index 5fb85a7..8d00e8d 100644
--- a/skins.c
+++ b/skins.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: skins.c 2.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 @@ cSkinDisplay::~cSkinDisplay()
   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 --git a/skins.h b/skins.h
index c08d757..e1d12c0 100644
--- a/skins.h
+++ b/skins.h
@@ -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.1.1 2014/02/18 14:06:50 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 @@ class cSkinDisplayChannel : public cSkinDisplay {
        ///< 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 @@ public:
        ///< 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 @@ enum eMenuCategory {
   mcTimerEdit,
   mcRecording,
   mcRecordingInfo,
+  mcRecordingEdit,
   mcPlugin,
   mcPluginSetup,
   mcSetup,
@@ -105,6 +121,14 @@ enum eMenuCategory {
   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 @@ public:
   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
diff --git a/skinsttng.c b/skinsttng.c
index 39a831a..7e450fd 100644
--- a/skinsttng.c
+++ b/skinsttng.c
@@ -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 @@ void cSkinSTTNGDisplayChannel::Flush(void)
            }
         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 --git a/skinsttng.h b/skinsttng.h
index cef3b91..f29262d 100644
--- a/skinsttng.h
+++ b/skinsttng.h
@@ -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 --git a/sourceparams.c b/sourceparams.c
index 6d85f86..3eec406 100644
--- a/sourceparams.c
+++ b/sourceparams.c
@@ -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.1.1 2014/03/09 12:13:18 kls Exp $
+ * $Id: sourceparams.c 3.1 2014/03/09 12:03:09 kls Exp $
  */
 
 #include "sourceparams.h"
diff --git a/sourceparams.h b/sourceparams.h
index be04b56..9f98bd0 100644
--- a/sourceparams.h
+++ b/sourceparams.h
@@ -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 --git a/sources.c b/sources.c
index 20c4304..f0a3432 100644
--- a/sources.c
+++ b/sources.c
@@ -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.1.1 2014/03/09 12:13:25 kls Exp $
+ * $Id: sources.c 3.6 2014/03/09 12:05:42 kls Exp $
  */
 
 #include "sources.h"
@@ -31,23 +31,35 @@ cSource::~cSource()
 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 @@ int cSource::FromString(const char *s)
                                      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 @@ int cSource::FromString(const char *s)
         return code;
         }
      else
-       esyslog("ERROR: unknown source key '%c'", *s);
+        esyslog("ERROR: unknown source key '%c'", *s);
      }
   return stNone;
 }
@@ -93,9 +105,9 @@ int cSource::FromData(eSourceType SourceType, int Position, bool East)
 {
   int code = SourceType;
   if (SourceType == stSat) {
-     if (East)
+     if (!East)
         Position = -Position;
-     code |= (Position & st_Pos);;
+     code |= (Position & st_Pos);
      }
   return code;
 }
diff --git a/sources.conf b/sources.conf
index 134db32..1a59c2f 100644
--- a/sources.conf
+++ b/sources.conf
@@ -182,6 +182,8 @@ S135W   AMC 10
 S137W   AMC 7
 S139W   AMC 8
 
+S360E   Any satellite
+
 # Cable
 
 C       DVB-C
diff --git a/sources.h b/sources.h
index c83ec59..1cd05b8 100644
--- a/sources.h
+++ b/sources.h
@@ -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.1.1 2014/03/09 12:13:34 kls Exp $
+ * $Id: sources.h 3.3 2014/03/09 11:59:49 kls Exp $
  */
 
 #ifndef __SOURCES_H
@@ -22,6 +22,7 @@ public:
     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 @@ public:
   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);
diff --git a/spu.c b/spu.c
index 9e38eec..8394abf 100644
--- a/spu.c
+++ b/spu.c
@@ -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 --git a/spu.h b/spu.h
index 3a8ab3e..8945b44 100644
--- a/spu.h
+++ b/spu.h
@@ -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 --git a/status.c b/status.c
index b0a6aba..10b8ce8 100644
--- a/status.c
+++ b/status.c
@@ -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 @@ cStatus::~cStatus()
   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 --git a/status.h b/status.h
index 6319165..222280a 100644
--- a/status.h
+++ b/status.h
@@ -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 @@ private:
   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 @@ public:
   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 --git a/summary2info b/summary2info
index fc633f9..6baf4e7 100755
--- a/summary2info
+++ b/summary2info
@@ -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 --git a/svdrp.c b/svdrp.c
index 8a50dae..2b5edb2 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
  * and interact with the Video Disk Recorder - or write a full featured
  * graphical interface that sits on top of an SVDRP connection.
  *
- * $Id: svdrp.c 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 @@ const char *HelpPages[] = {
   "    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 @@ void cSVDRP::CmdDELC(const char *Option)
      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 @@ void cSVDRP::CmdEDIT(const char *Option)
         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 @@ void cSVDRP::CmdMOVC(const char *Option)
      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 @@ void cSVDRP::CmdSTAT(const char *Option)
   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 @@ void cSVDRP::Execute(char *Cmd)
   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 --git a/svdrp.h b/svdrp.h
index 5ec9bc7..8ac419a 100644
--- a/svdrp.h
+++ b/svdrp.h
@@ -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 @@ private:
   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 --git a/themes.c b/themes.c
index 88c7005..ccc2164 100644
--- a/themes.c
+++ b/themes.c
@@ -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 --git a/themes.h b/themes.h
index e51464f..1bb9cf1 100644
--- a/themes.h
+++ b/themes.h
@@ -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 --git a/thread.c b/thread.c
index 80fe588..e5e19c9 100644
--- a/thread.c
+++ b/thread.c
@@ -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.1.1 2013/04/11 08:59:26 kls Exp $
+ * $Id: thread.c 3.2 2013/12/29 15:26:33 kls Exp $
  */
 
 #include "thread.h"
@@ -494,7 +494,6 @@ bool cPipe::Open(const char *Command, const char *Mode)
   else { // child process
      int iofd = STDOUT_FILENO;
      if (strcmp(Mode, "w") == 0) {
-        mode = "r";
         iopipe = 1;
         iofd = STDIN_FILENO;
         }
diff --git a/thread.h b/thread.h
index d79e15a..d2d8ee2 100644
--- a/thread.h
+++ b/thread.h
@@ -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 @@ public:
   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 @@ public:
        ///< 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 --git a/timers.c b/timers.c
index 9a880e1..3eb8068 100644
--- a/timers.c
+++ b/timers.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.c 2.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 @@ bool cTimer::Parse(const char *s)
   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 @@ bool cTimer::Parse(const char *s)
      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 --git a/timers.h b/timers.h
index 8910c10..4ce1cff 100644
--- a/timers.h
+++ b/timers.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.h 2.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 --git a/tools.c b/tools.c
index ab46d02..332fce4 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: tools.c 2.29 2012/12/08 11:16:30 kls Exp $
+ * $Id: tools.c 3.3 2014/03/22 14:22:55 kls Exp $
  */
 
 #include "tools.h"
@@ -173,6 +173,31 @@ char *strreplace(char *s, const char *s1, const char *s2)
   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 @@ char *compactspace(char *s)
   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;
@@ -560,7 +609,7 @@ int DirSizeMB(const char *DirName)
            }
      return size;
      }
-  else
+  else if (errno != ENOENT)
      LOG_ERROR_STR(DirName);
   return -1;
 }
@@ -691,12 +740,12 @@ void cTimeMs::Set(int Ms)
   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 @@ cString::cString(const char *S, bool TakePointer)
   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 @@ cString &cString::Truncate(int Index)
   return *this;
 }
 
+cString &cString::CompactChars(char c)
+{
+  compactchars(s, c);
+  return *this;
+}
+
 cString cString::sprintf(const char *fmt, ...)
 {
   va_list ap;
diff --git a/tools.h b/tools.h
index d6a778e..920a1d1 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: tools.h 2.24 2013/02/17 13:18:06 kls Exp $
+ * $Id: tools.h 3.6 2015/01/14 09:09:06 kls Exp $
  */
 
 #ifndef __TOOLS_H
@@ -31,9 +31,9 @@ typedef unsigned char uchar;
 
 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 @@ void syslog_with_tid(int priority, const char *format, ...) __attribute__ ((form
 #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 @@ private:
   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 @@ public:
   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 *strcpyrealloc(char *dest, const char *src);
 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 @@ inline char *skipspace(const char *s)
 }
 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);
@@ -330,8 +337,8 @@ public:
       ///< 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 +537,14 @@ public:
   {
     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 +557,45 @@ public:
     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 --git a/transfer.c b/transfer.c
index 4119366..eb07e82 100644
--- a/transfer.c
+++ b/transfer.c
@@ -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.1.1 2013/08/22 12:37:02 kls Exp $
+ * $Id: transfer.c 3.1 2013/08/22 12:33:02 kls Exp $
  */
 
 #include "transfer.h"
diff --git a/transfer.h b/transfer.h
index 47a3398..ce74898 100644
--- a/transfer.h
+++ b/transfer.h
@@ -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 --git a/vdr.1 b/vdr.1
index c9da711..7605bf2 100644
--- a/vdr.1
+++ b/vdr.1
@@ -8,7 +8,7 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.1 2.17.1.1 2013/12/25 11:05:27 kls Exp $
+.\" $Id: vdr.1 3.4 2015/01/20 15:34:29 kls Exp $
 .\"
 .TH vdr 1 "31 Mar 2013" "2.0" "Video Disk Recorder"
 .SH NAME
@@ -178,6 +178,10 @@ more information.
 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,6 +201,18 @@ be set from the transponder data, but for security reasons
 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
@@ -213,6 +229,10 @@ Print version information and exit.
 .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 +299,7 @@ also be read at program startup to have the full EPG data available immediately.
 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 --git a/vdr.5 b/vdr.5
index b54b294..44e56c4 100644
--- a/vdr.5
+++ b/vdr.5
@@ -8,7 +8,7 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.5 2.36 2013/03/29 10:25:56 kls Exp $
+.\" $Id: vdr.5 3.3 2014/04/14 13:11:49 kls Exp $
 .\"
 .TH vdr 5 "31 Mar 2013" "2.0" "Video Disk Recorder Files"
 .SH NAME
@@ -99,12 +99,15 @@ l l.
 \fBI\fR at Inversion (0, 1)
 \fBL\fR at Left circular polarization
 \fBM\fR at Modulation (2, 5, 6, 7, 10, 11, 12, 16, 32, 64, 128, 256, 999)
+\fBN\fR at pilot mode (0, 1, 999)
 \fBO\fR at rollOff (0, 20, 25, 35)
 \fBP\fR at stream id (0-255)
+\fBQ\fR at t2 system id (0-65535)
 \fBR\fR at Right circular polarization
 \fBS\fR at delivery System (0, 1)
 \fBT\fR at Transmission mode (1, 2, 4, 8, 16, 32)
 \fBV\fR at Vertical polarization
+\fBX\fR at siso/miso mode (0, 1)
 \fBY\fR at hierarchY (0, 1, 2, 4)
 .TE
 
@@ -139,6 +142,8 @@ l l.
 \fB256\fR at 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 @@ l l.
 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 @@ l l.
 \fBV\fR at voltage high (18V)
 \fBA\fR at mini A
 \fBB\fR at mini B
+\fBPn\fR at use positioner to move dish to satellite position n (or to the satellite's orbital position, if no position number is given)
 \fBSn\fR at Satellite channel routing code sequence for bank n follows
 \fBWnn\fR at wait nn milliseconds (nn may be any positive integer number)
 \fB[xx ...]\fR at hex code sequence (max. 6)
@@ -889,6 +899,33 @@ Note that the \fBevent id\fR that comes from the DVB data stream is actually
 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 --git a/vdr.c b/vdr.c
index ffd0e01..336130f 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
  *
  * The project's page is at http://www.tvdr.de
  *
- * $Id: vdr.c 2.57.1.5 2014/01/26 12:45:00 kls Exp $
+ * $Id: vdr.c 3.15 2015/01/17 14:48:09 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 @@ int main(int argc, char *argv[])
 #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
@@ -223,7 +229,16 @@ int main(int argc, char *argv[])
   VdrUser = VDR_USER;
 #endif
 
-  SetVideoDirectory(VideoDirectory);
+  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[] = {
@@ -250,9 +265,11 @@ int main(int argc, char *argv[])
       { "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' },
@@ -422,6 +439,19 @@ int main(int argc, char *argv[])
           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);
@@ -434,6 +464,8 @@ int main(int argc, char *argv[])
           case 'u' | 0x100:
                     UserDump = true;
                     break;
+          case 'u' | 0x200:
+                    return GenerateIndex(optarg, true) ? 0 : 2;
           case 'V': DisplayVersion = true;
                     break;
           case 'v' | 0x100:
@@ -444,7 +476,7 @@ int main(int argc, char *argv[])
           case 'v': VideoDirectory = optarg;
                     while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
                           optarg[strlen(optarg) - 1] = 0;
-                    SetVideoDirectory(VideoDirectory);
+                    cVideoDirectory::SetName(VideoDirectory);
                     break;
           case 'w': if (isnumber(optarg)) {
                        int t = atoi(optarg);
@@ -535,9 +567,12 @@ int main(int argc, char *argv[])
                "  -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"
@@ -557,6 +592,7 @@ int main(int argc, char *argv[])
                DEFAULTLOCDIR,
                DEFAULTSVDRPPORT,
                DEFAULTRESDIR,
+               DEFAULTARGSDIR,
                DEFAULTVIDEODIR,
                DEFAULTWATCHDOG
                );
@@ -801,10 +837,17 @@ int main(int argc, char *argv[])
   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)))
@@ -837,6 +880,10 @@ int main(int argc, char *argv[])
      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)
@@ -912,11 +959,12 @@ int main(int argc, char *argv[])
                      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();
@@ -988,7 +1036,7 @@ int main(int argc, char *argv[])
                         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);
                            }
@@ -1316,8 +1364,9 @@ int main(int argc, char *argv[])
         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"));
@@ -1337,7 +1386,10 @@ int main(int argc, char *argv[])
               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:
@@ -1387,7 +1439,7 @@ Exit:
 
   PluginManager.StopPlugins();
   cRecordControls::Shutdown();
-  cCutter::Stop();
+  RecordingsHandler.DelAll();
   delete Menu;
   cControl::Shutdown();
   delete Interface;
@@ -1402,6 +1454,8 @@ Exit:
      Setup.Save();
      }
   cDevice::Shutdown();
+  cPositioner::DestroyPositioner();
+  cVideoDirectory::Destroy();
   EpgHandlers.Clear();
   PluginManager.Shutdown(true);
   cSchedules::Cleanup(true);
diff --git a/videodir.c b/videodir.c
index d39ab05..932f8ce 100644
--- a/videodir.c
+++ b/videodir.c
@@ -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;
 }
 
-int VideoDiskSpace(int *FreeMB, int *UsedMB)
+bool cVideoDirectory::RenameVideoFile(const char *OldName, const char *NewName)
 {
-  int free = 0, used = 0;
+  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);
+}
+
+bool cVideoDirectory::VideoFileSpaceAvailable(int SizeMB)
+{
+  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 @@ int VideoDiskSpace(int *FreeMB, int *UsedMB)
   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 @@ cString PrefixVideoFileName(const char *FileName, char Prefix)
   return NULL;
 }
 
-void RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
+void cVideoDirectory::RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
 {
-  cVideoDirectory Dir;
-  do {
-     RemoveEmptyDirectories(Dir.Name(), false, IgnoreFiles);
-     } while (Dir.Next());
+  Current()->Cleanup(IgnoreFiles);
 }
 
-bool IsOnVideoDirectoryFileSystem(const char *FileName)
+bool cVideoDirectory::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 @@ bool cVideoDiskUsage::HasChanged(int &State)
 {
   if (time(NULL) - lastChecked > DISKSPACECHEK) {
      int FreeMB;
-     int UsedPercent = VideoDiskSpace(&FreeMB);
+     int UsedPercent = cVideoDirectory::VideoDiskSpace(&FreeMB);
      if (FreeMB != freeMB) {
         usedPercent = UsedPercent;
         freeMB = FreeMB;
diff --git a/videodir.h b/videodir.h
index a25ac31..f520d77 100644
--- a/videodir.h
+++ b/videodir.h
@@ -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:

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr.git



More information about the pkg-vdr-dvb-changes mailing list