[vdr-plugin-vnsiserver] 01/05: Imported Upstream version 1.3.0+git20150214

Tobias Grimm tiber-guest at moszumanska.debian.org
Sat Feb 14 12:15:29 UTC 2015


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

tiber-guest pushed a commit to branch master
in repository vdr-plugin-vnsiserver.

commit 01052e5a27822f0fb4562d48522e4d74618f7408
Author: etobi <git at e-tobi.net>
Date:   Sat Feb 14 13:04:44 2015 +0100

    Imported Upstream version 1.3.0+git20150214
---
 HISTORY                  |  50 +++-
 Makefile                 |   4 +-
 README                   |  36 ++-
 bitstream.c              |   5 +-
 bitstream.h              |   5 +-
 channelfilter.c          | 332 +++++++++++++++++++++
 channelfilter.h          |  55 ++++
 config.c                 |   7 +-
 config.h                 |   8 +-
 cxsocket.c               |   5 +-
 cxsocket.h               |   5 +-
 demuxer.c                |  57 +++-
 demuxer.h                |  12 +-
 hash.c                   |   5 +-
 hash.h                   |   5 +-
 parser.c                 |  71 ++++-
 parser.h                 |  19 +-
 parser_AAC.c             |  26 +-
 parser_AAC.h             |   3 +-
 parser_AC3.c             |   2 +-
 parser_AC3.h             |   2 +-
 parser_DTS.c             |   2 +-
 parser_DTS.h             |   2 +-
 parser_MPEGAudio.c       |  81 +++++-
 parser_MPEGAudio.h       |  11 +-
 parser_MPEGVideo.c       |  21 +-
 parser_MPEGVideo.h       |   3 +-
 parser_Subtitle.c        |   2 +-
 parser_Subtitle.h        |   2 +-
 parser_Teletext.c        |   2 +-
 parser_Teletext.h        |   2 +-
 parser_h264.c            |  22 +-
 parser_h264.h            |   2 +-
 po/de_DE.po              |  61 ++++
 po/lt_LT.po              |  61 ++++
 recordingscache.c        |  20 +-
 recordingscache.h        |  15 +-
 recplayer.c              |  28 +-
 recplayer.h              |   5 +-
 requestpacket.c          |   5 +-
 requestpacket.h          |   5 +-
 responsepacket.c         |   7 +-
 responsepacket.h         |   5 +-
 setup.c                  |  18 +-
 setup.h                  |   2 +
 status.c                 | 160 ++++++++++
 parser_DTS.c => status.h |  32 +-
 streamer.c               | 135 +++++++--
 streamer.h               |  12 +-
 videobuffer.c            |  58 +++-
 videobuffer.h            |   7 +-
 videoinput.c             |  36 ++-
 videoinput.h             |   8 +-
 vnsi.c                   |  74 ++++-
 vnsi.h                   |  27 +-
 vnsiclient.c             | 743 ++++++++++++++++++++++++++++++++++++++++++++---
 vnsiclient.h             |  33 ++-
 vnsicommand.h            |  31 +-
 vnsiserver.c             | 180 +++---------
 vnsiserver.h             |  11 +-
 60 files changed, 2222 insertions(+), 423 deletions(-)

diff --git a/HISTORY b/HISTORY
index 4be1019..3f40920 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,9 +1,51 @@
 VDR Plugin 'vnsiserver' Revision History
 ----------------------------------------
 
-2010-03-23: Version 0.0.1
+2015-02-07: Version 1.3.0
 
-- Initial revision.
+- add support for undelete recordings
+- add support for rds data if present on dvb radio and send to addon if supported there
+
+2015-01-25: Version 1.2.1
+
+- add cmd line switch for setting tcp port
+- fix potential segfault on stop streaming
+- stop triggering epg updates if client does not fetch epg
+
+2014-09-03: Version 1.2.0
+
+- add cmd line switch "-d". if set, vnsi creates a dummy dvb device which makes femon work 
+  on all channels
+- add setup parameter AvoidEPGScan. if set to 1, vnsi disables EPG scan while streaming.
+  EPG scan during streaming can lead to artifacs on some dual tuner cards.
+- fix div by zero on AAC audio, thanks to Jakob Sloth Jepsen
+
+2014-03-20: Version 1.1.0
+
+- add support for picons
+- add setup paramter to select if current recording should start when tuning to a channel
+- only activate AAC mux workaround if environment variable VNSI_AAC_MUXMODE is set
+- bump max timshift buffer RAM to 8 gig
+
+2014-03-20: Version 1.0.0
+
+- release 1.0
+- various fixes
+- remove suffix from plugin name vnsiserver5 -> vnsiserver
+
+2014-01-08: Version 0.9.4
+
+- update length of recorings in progress
+  while playing
+
+2013-12-04: Version 0.9.3
+
+- add support for EDL (marks)
+- add channel filter
+- send buffer times for timeshift
+- bump protocol to XBMC to 5
+- suffix plugin with version of protocol: vnsiserver5
+- this version is compatible with XBMC 13
 
 2013-02-03: Version 0.9.1
 
@@ -12,3 +54,7 @@ VDR Plugin 'vnsiserver' Revision History
 - proper handling of PMT changes
 - suffix plugin with version of protocol: vnsiserver3
 - this version is compatible with XBMC 12.0
+
+2010-03-23: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
index 8f37c92..d7e69bb 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
 # This name will be used in the '-P...' option of VDR to load the plugin.
 # By default the main source file also carries this name.
 
-PLUGIN = vnsiserver4
+PLUGIN = vnsiserver
 
 ### The version number of this plugin (taken from the main source file):
 
@@ -90,7 +90,7 @@ OBJS = vnsi.o bitstream.o vnsiclient.o config.o cxsocket.o parser.o parser_AAC.o
        parser_AC3.o parser_DTS.o parser_h264.o parser_MPEGAudio.o parser_MPEGVideo.o \
        parser_Subtitle.o parser_Teletext.o streamer.o recplayer.o requestpacket.o responsepacket.o \
        vnsiserver.o hash.o recordingscache.o setup.o vnsiosd.o demuxer.o videobuffer.o \
-       videoinput.o
+       videoinput.o channelfilter.o status.o
 
 ### The main target:
 
diff --git a/README b/README
index a1f5a45..f4f5adc 100644
--- a/README
+++ b/README
@@ -1,9 +1,13 @@
-This is a "plugin" for the Video Disk Recorder (VDR).
 
-Project's homepage:          xbmc.org
+-----------------------------------------------------------------------------
+1. Introduction
+-----------------------------------------------------------------------------
 
-Latest version available at: 
-https://github.com/opdenkamp/xbmc-pvr-addons/tree/master/addons/pvr.vdr.vnsi/vdr-plugin-vnsiserver
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+VDR plugin to handle XBMC clients.
+The vdr-plugin-vnsiserver is able to handle serveral XBMC clients connecting
+via the VNSI addon.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -11,15 +15,21 @@ the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 See the file COPYING for more information.
 
+Latest version available at: 
+https://github.com/FernetMenta/vdr-plugin-vnsiserver
+
+-----------------------------------------------------------------------------
+2. Compile in VDR's source tree
+-----------------------------------------------------------------------------
 
-Description:
-------------
+Get VDR sources.
 
-VDR plugin to handle XBMC clients.
-The vdr-plugin-vnsiserver is able to handle serveral XBMC clients connecting via the VNSI addon.
+   $ cd <path to vdr>/PLUGINS/src
+   $ git clone https://github.com/FernetMenta/vdr-plugin-vnsiserver
+   $ ln -s vdr-plugin-vnsiserver vnsiserver
+   
+   -----------------------------------------------------------------------------
+3. Help and Suppport
+-----------------------------------------------------------------------------
 
-VNSI supports also dynamic PID switching of the received DVB-TS stream. Further it detect and demuxing several
-not by VDR implemented Audio Streams, this are:
-- Enhanced AC3 (not tested)
-- Advanced Audio Coding (AAC) (not tested)
-- DTS (demuxer not finished now, and does not work)
+http://forum.xbmc.org/forumdisplay.php?fid=169
diff --git a/bitstream.c b/bitstream.c
index b5fa53a..3e44ff9 100644
--- a/bitstream.c
+++ b/bitstream.c
@@ -16,9 +16,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/bitstream.h b/bitstream.h
index 4607715..626142f 100644
--- a/bitstream.h
+++ b/bitstream.h
@@ -16,9 +16,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/channelfilter.c b/channelfilter.c
new file mode 100644
index 0000000..d8a61ca
--- /dev/null
+++ b/channelfilter.c
@@ -0,0 +1,332 @@
+/*
+ *      Copyright (C) 2005-2013 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "channelfilter.h"
+#include "config.h"
+#include "hash.h"
+#include <string>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <vdr/tools.h>
+
+cVNSIProvider::cVNSIProvider()
+  :m_name(""), m_caid(0)
+{
+
+}
+
+cVNSIProvider::cVNSIProvider(std::string name, int caid)
+  :m_name(name), m_caid(caid)
+{
+};
+
+bool cVNSIProvider::operator==(const cVNSIProvider &rhs)
+{
+  if (rhs.m_caid != m_caid)
+    return false;
+  if (rhs.m_name.compare(m_name) != 0)
+    return false;
+  return true;
+}
+
+
+bool cVNSIChannelFilter::IsRadio(const cChannel* channel)
+{
+  bool isRadio = false;
+
+  // assume channels without VPID & APID are video channels
+  if (channel->Vpid() == 0 && channel->Apid(0) == 0)
+    isRadio = false;
+  // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels)
+  else if (channel->Vpid() == 0 || channel->Vpid() == 1)
+    isRadio = true;
+
+  return isRadio;
+}
+
+void cVNSIChannelFilter::Load()
+{
+  cMutexLock lock(&m_Mutex);
+
+  cString filename;
+  std::string line;
+  std::ifstream rfile;
+  cVNSIProvider provider;
+  std::vector<cVNSIProvider>::iterator p_it;
+
+  filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+  m_providersVideo.clear();
+  rfile.open(filename);
+  if (rfile.is_open())
+  {
+    while(std::getline(rfile,line))
+    {
+      size_t pos = line.find("|");
+      if(pos == line.npos)
+      {
+        provider.m_name = line;
+        provider.m_caid = 0;
+      }
+      else
+      {
+        provider.m_name = line.substr(0, pos);
+        std::string tmp = line.substr(pos+1);
+        char *pend;
+        provider.m_caid = strtol(tmp.c_str(), &pend, 10);
+      }
+      p_it = std::find(m_providersVideo.begin(), m_providersVideo.end(), provider);
+      if(p_it == m_providersVideo.end())
+      {
+        m_providersVideo.push_back(provider);
+      }
+    }
+    rfile.close();
+  }
+
+  filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+  rfile.open(filename);
+  m_providersRadio.clear();
+  if (rfile.is_open())
+  {
+    while(std::getline(rfile,line))
+    {
+      unsigned int pos = line.find("|");
+      if(pos == line.npos)
+      {
+        provider.m_name = line;
+        provider.m_caid = 0;
+      }
+      else
+      {
+        provider.m_name = line.substr(0, pos);
+        std::string tmp = line.substr(pos+1);
+        char *pend;
+        provider.m_caid = strtol(tmp.c_str(), &pend, 10);
+      }
+      p_it = std::find(m_providersRadio.begin(), m_providersRadio.end(), provider);
+      if(p_it == m_providersRadio.end())
+      {
+        m_providersRadio.push_back(provider);
+      }
+    }
+    rfile.close();
+  }
+
+  filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+  rfile.open(filename);
+  m_channelsVideo.clear();
+  if (rfile.is_open())
+  {
+    while(getline(rfile,line))
+    {
+      char *pend;
+      int id = strtol(line.c_str(), &pend, 10);
+      m_channelsVideo.push_back(id);
+    }
+    rfile.close();
+  }
+
+  filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+  rfile.open(filename);
+  m_channelsRadio.clear();
+  if (rfile.is_open())
+  {
+    while(getline(rfile,line))
+    {
+      char *pend;
+      int id = strtol(line.c_str(), &pend, 10);
+      m_channelsRadio.push_back(id);
+    }
+    rfile.close();
+  }
+}
+
+void cVNSIChannelFilter::StoreWhitelist(bool radio)
+{
+  cMutexLock lock(&m_Mutex);
+
+  cString filename;
+  std::ofstream wfile;
+  cVNSIProvider provider;
+  std::vector<cVNSIProvider>::iterator p_it;
+  std::vector<cVNSIProvider> *whitelist;
+
+  if (radio)
+  {
+    filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+    whitelist = &m_providersRadio;
+  }
+  else
+  {
+    filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+    whitelist = &m_providersVideo;
+  }
+
+  wfile.open(filename);
+  if(wfile.is_open())
+  {
+    std::string tmp;
+    char buf[16];
+    for(p_it=whitelist->begin(); p_it!=whitelist->end(); ++p_it)
+    {
+      tmp = p_it->m_name;
+      tmp += "|";
+      sprintf(buf, "%d\n", p_it->m_caid);
+      tmp += buf;
+      wfile << tmp;
+    }
+    wfile.close();
+  }
+
+  SortChannels();
+}
+
+void cVNSIChannelFilter::StoreBlacklist(bool radio)
+{
+  cMutexLock lock(&m_Mutex);
+
+  cString filename;
+  std::ofstream wfile;
+  cVNSIProvider provider;
+  std::vector<int>::iterator it;
+  std::vector<int> *blacklist;
+
+  if (radio)
+  {
+    filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+    blacklist = &m_channelsRadio;
+  }
+  else
+  {
+    filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+    blacklist = &m_channelsVideo;
+  }
+
+  wfile.open(filename);
+  if(wfile.is_open())
+  {
+    std::string tmp;
+    char buf[16];
+    for(it=blacklist->begin(); it!=blacklist->end(); ++it)
+    {
+      sprintf(buf, "%d\n", *it);
+      tmp = buf;
+      wfile << tmp;
+    }
+    wfile.close();
+  }
+
+  SortChannels();
+}
+
+bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel)
+{
+  cVNSIProvider provider;
+  std::vector<cVNSIProvider>::iterator p_it;
+  std::vector<cVNSIProvider> *providers;
+  provider.m_name = channel.Provider();
+
+  if (IsRadio(&channel))
+    providers = &m_providersRadio;
+  else
+    providers = &m_providersVideo;
+
+  if(providers->empty())
+    return true;
+
+  if (channel.Ca(0) == 0)
+  {
+    provider.m_caid = 0;
+    p_it = std::find(providers->begin(), providers->end(), provider);
+    if(p_it!=providers->end())
+      return true;
+    else
+      return false;
+  }
+
+  int caid;
+  int idx = 0;
+  while((caid = channel.Ca(idx)) != 0)
+  {
+    provider.m_caid = caid;
+    p_it = std::find(providers->begin(), providers->end(), provider);
+    if(p_it!=providers->end())
+      return true;
+
+    idx++;
+  }
+  return false;
+}
+
+bool cVNSIChannelFilter::PassFilter(const cChannel &channel)
+{
+  cMutexLock lock(&m_Mutex);
+
+  if(channel.GroupSep())
+    return true;
+
+  if (!IsWhitelist(channel))
+    return false;
+
+  std::vector<int>::iterator it;
+  if (IsRadio(&channel))
+  {
+    it = std::find(m_channelsRadio.begin(), m_channelsRadio.end(), CreateChannelUID(&channel));
+    if(it!=m_channelsRadio.end())
+      return false;
+  }
+  else
+  {
+    it = std::find(m_channelsVideo.begin(), m_channelsVideo.end(), CreateChannelUID(&channel));
+    if(it!=m_channelsVideo.end())
+      return false;
+  }
+
+  return true;
+}
+
+void cVNSIChannelFilter::SortChannels()
+{
+  Channels.IncBeingEdited();
+  Channels.Lock(true);
+
+  for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+  {
+    if(!PassFilter(*channel))
+    {
+      for (cChannel *whitechan = Channels.Next(channel); whitechan; whitechan = Channels.Next(whitechan))
+      {
+        if(PassFilter(*whitechan))
+        {
+          Channels.Move(whitechan, channel);
+          channel = whitechan;
+          break;
+        }
+      }
+    }
+  }
+
+  Channels.SetModified(true);
+  Channels.Unlock();
+  Channels.DecBeingEdited();
+}
+
+cVNSIChannelFilter VNSIChannelFilter;
diff --git a/channelfilter.h b/channelfilter.h
new file mode 100644
index 0000000..dc49728
--- /dev/null
+++ b/channelfilter.h
@@ -0,0 +1,55 @@
+/*
+ *      Copyright (C) 2005-2013 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <vdr/thread.h>
+#include <vdr/channels.h>
+
+class cVNSIProvider
+{
+public:
+  cVNSIProvider();
+  cVNSIProvider(std::string name, int caid);
+  bool operator==(const cVNSIProvider &rhs);
+  std::string m_name;
+  int m_caid;
+};
+
+class cVNSIChannelFilter
+{
+public:
+  void Load();
+  void StoreWhitelist(bool radio);
+  void StoreBlacklist(bool radio);
+  bool IsWhitelist(const cChannel &channel);
+  bool PassFilter(const cChannel &channel);
+  void SortChannels();
+  static bool IsRadio(const cChannel* channel);
+  std::vector<cVNSIProvider> m_providersVideo;
+  std::vector<cVNSIProvider> m_providersRadio;
+  std::vector<int> m_channelsVideo;
+  std::vector<int> m_channelsRadio;
+  cMutex m_Mutex;
+};
+
+extern cVNSIChannelFilter VNSIChannelFilter;
diff --git a/config.c b/config.c
index 5ba1ba8..5ce3549 100644
--- a/config.c
+++ b/config.c
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -36,6 +35,8 @@ cVNSIServerConfig::cVNSIServerConfig()
   listen_port         = LISTEN_PORT;
   ConfigDirectory     = NULL;
   stream_timeout      = 10;
+  device              = false;
+  testStreamActive    = false;
 }
 
 /* Global instance */
diff --git a/config.h b/config.h
index 24657ad..50feb2e 100644
--- a/config.h
+++ b/config.h
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -79,6 +78,9 @@ public:
   cString ConfigDirectory;      // config directory path
   uint16_t listen_port;         // Port of remote server
   uint16_t stream_timeout;      // timeout in seconds for stream data
+  bool device;                  // true if vnsi should act as dummy device
+  cString testStreamFile;       // TS file to simulate channel
+  bool testStreamActive;        // true if test mode is enabled
 };
 
 // Global instance
diff --git a/cxsocket.c b/cxsocket.c
index 8f2fa5d..68fbaa9 100644
--- a/cxsocket.c
+++ b/cxsocket.c
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/cxsocket.h b/cxsocket.h
index 4088152..3d9e64f 100644
--- a/cxsocket.h
+++ b/cxsocket.h
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/demuxer.c b/demuxer.c
index 1f3c9e8..cb910c6 100644
--- a/demuxer.c
+++ b/demuxer.c
@@ -27,7 +27,8 @@
 #include <vdr/channels.h>
 #include <libsi/si.h>
 
-cVNSIDemuxer::cVNSIDemuxer()
+cVNSIDemuxer::cVNSIDemuxer(bool bAllowRDS)
+ : m_bAllowRDS(bAllowRDS)
 {
   m_OldPmtVersion = -1;
 }
@@ -50,13 +51,14 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
   else
     m_WaitIFrame = false;
 
-  m_FirstFrameDTS = 0;
+  m_FirstFramePTS = 0;
 
   m_PtsWrap.m_Wrap = false;
   m_PtsWrap.m_NoOfWraps = 0;
   m_PtsWrap.m_ConfirmCount = 0;
   m_MuxPacketSerial = 0;
   m_Error = ERROR_DEMUX_NODATA;
+  m_SetRefTime = true;
 }
 
 void cVNSIDemuxer::Close()
@@ -72,7 +74,7 @@ void cVNSIDemuxer::Close()
   m_StreamInfos.clear();
 }
 
-int cVNSIDemuxer::Read(sStreamPacket *packet)
+int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
 {
   uint8_t *buf;
   int len;
@@ -88,7 +90,7 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
   packet->pmtChange = false;
 
   // read TS Packet from buffer
-  len = m_VideoBuffer->Read(&buf, TS_SIZE);
+  len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime);
   // eof
   if (len == -2)
     return -2;
@@ -131,19 +133,26 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
   }
   else if (stream = FindStream(ts_pid))
   {
-    int error = stream->ProcessTSPacket(buf, packet, m_WaitIFrame);
+    int error = stream->ProcessTSPacket(buf, packet, packet_side_data, m_WaitIFrame);
     if (error == 0)
     {
       if (m_WaitIFrame)
       {
-        m_FirstFrameDTS = packet->dts;
+        if (packet->pts != DVD_NOPTS_VALUE)
+          m_FirstFramePTS = packet->pts;
         m_WaitIFrame = false;
       }
 
-      if (packet->dts < m_FirstFrameDTS)
+      if (packet->pts < m_FirstFramePTS)
         return 0;
 
       packet->serial = m_MuxPacketSerial;
+      if (m_SetRefTime)
+      {
+        m_refTime = m_VideoBuffer->GetRefTime();
+        packet->reftime = m_refTime;
+        m_SetRefTime = false;
+      }
       return 1;
     }
     else if (error < 0)
@@ -316,9 +325,27 @@ bool cVNSIDemuxer::SeekTime(int64_t time)
   return true;
 }
 
-void cVNSIDemuxer::BufferStatus(bool &timeshift, int &start, int &current, int &end)
+void cVNSIDemuxer::BufferStatus(bool &timeshift, uint32_t &start, uint32_t &end)
 {
   timeshift = m_VideoBuffer->HasBuffer();
+
+  if (timeshift)
+  {
+    if (!m_wrapTime)
+    {
+      start = m_refTime;
+    }
+    else
+    {
+      start = m_endTime - (m_wrapTime - m_refTime);
+    }
+    end = m_endTime;
+  }
+  else
+  {
+    start = 0;
+    end = 0;
+  }
 }
 
 cTSStream *cVNSIDemuxer::GetFirstStream()
@@ -408,7 +435,7 @@ bool cVNSIDemuxer::EnsureParsers()
     }
     else if (it->type == stMPEG2AUDIO)
     {
-      stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap);
+      stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap, it->handleRDS);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stAACADTS)
@@ -458,6 +485,8 @@ bool cVNSIDemuxer::EnsureParsers()
 void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
 {
   sStreamInfo newStream;
+  bool containsVideo = false;
+  int index = 0;
   if (channel->Vpid())
   {
     newStream.pID = channel->Vpid();
@@ -469,12 +498,13 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
       newStream.type = stMPEG2VIDEO;
 
     AddStreamInfo(newStream);
+    containsVideo = true;
   }
 
   const int *DPids = channel->Dpids();
+  index = 0;
   for ( ; *DPids; DPids++)
   {
-    int index = 0;
     if (!FindStream(*DPids))
     {
       newStream.pID = *DPids;
@@ -490,9 +520,9 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
   }
 
   const int *APids = channel->Apids();
+  index = 0;
   for ( ; *APids; APids++)
   {
-    int index = 0;
     if (!FindStream(*APids))
     {
       newStream.pID = *APids;
@@ -503,6 +533,7 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
       else if (channel->Atype(index) == 0x11)
         newStream.type = stAACLATM;
 #endif
+      newStream.handleRDS = m_bAllowRDS && newStream.type == stMPEG2AUDIO && !containsVideo ? true : false; // Relevant for RDS, if present only on mpeg 2 audio, use only if RDS is allowed
       newStream.SetLanguage(channel->Alang(index));
       AddStreamInfo(newStream);
     }
@@ -512,7 +543,7 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
   const int *SPids = channel->Spids();
   if (SPids)
   {
-    int index = 0;
+    index = 0;
     for ( ; *SPids; SPids++)
     {
       if (!FindStream(*SPids))
@@ -600,7 +631,7 @@ bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time)
 
   m_VideoBuffer->SetPos(*pos);
   ResetParsers();
-  while (len = m_VideoBuffer->Read(&buf, TS_SIZE) == TS_SIZE)
+  while (len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime) == TS_SIZE)
   {
     ts_pid = TsPid(buf);
     if (stream = FindStream(ts_pid))
diff --git a/demuxer.h b/demuxer.h
index 019595f..f1bb857 100644
--- a/demuxer.h
+++ b/demuxer.h
@@ -38,6 +38,7 @@ struct sStreamInfo
   int subtitlingType;
   int compositionPageId;
   int ancillaryPageId;
+  bool handleRDS;
   void SetLanguage(const char* lang)
   {
     language[0] = lang[0];
@@ -50,9 +51,9 @@ struct sStreamInfo
 class cVNSIDemuxer
 {
 public:
-  cVNSIDemuxer();
+  cVNSIDemuxer(bool bAllowRDS);
   virtual ~cVNSIDemuxer();
-  int Read(sStreamPacket *packet);
+  int Read(sStreamPacket *packet, sStreamPacket *packet_side_data);
   cTSStream *GetFirstStream();
   cTSStream *GetNextStream();
   void Open(const cChannel &channel, cVideoBuffer *videoBuffer);
@@ -60,7 +61,7 @@ public:
   bool SeekTime(int64_t time);
   uint32_t GetSerial() { return m_MuxPacketSerial; }
   void SetSerial(uint32_t serial) { m_MuxPacketSerial = serial; }
-  void BufferStatus(bool &timeshift, int &start, int &current, int &end);
+  void BufferStatus(bool &timeshift, uint32_t &start, uint32_t &end);
   uint16_t GetError();
 
 protected:
@@ -78,10 +79,13 @@ protected:
   cPatPmtParser m_PatPmtParser;
   int m_OldPmtVersion;
   bool m_WaitIFrame;
-  int64_t m_FirstFrameDTS;
+  int64_t m_FirstFramePTS;
   cVideoBuffer *m_VideoBuffer;
   cMutex m_Mutex;
   uint32_t m_MuxPacketSerial;
   sPtsWrap m_PtsWrap;
   uint16_t m_Error;
+  bool m_SetRefTime;
+  time_t m_refTime, m_endTime, m_wrapTime;
+  bool m_bAllowRDS;
 };
diff --git a/hash.c b/hash.c
index 888fd52..6afa505 100644
--- a/hash.c
+++ b/hash.c
@@ -15,9 +15,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/hash.h b/hash.h
index eba8a25..0aef425 100644
--- a/hash.h
+++ b/hash.h
@@ -15,9 +15,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/parser.c b/parser.c
index d325fae..d5f12ea 100644
--- a/parser.c
+++ b/parser.c
@@ -67,6 +67,7 @@ void cParser::Reset()
   m_PesParserPtr = 0;
   m_PesNextFramePtr = 0;
   m_FoundFrame = false;
+  m_FrameValid = false;
   m_PesPacketLength = 0;
   m_PesHeaderPtr = 0;
   m_Error = ERROR_PES_GENERAL;
@@ -433,7 +434,9 @@ inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
 
 // --- cTSStream ----------------------------------------------------
 
-cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
+uint32_t cTSStream::m_UniqueSideDataIDs = 0;
+
+cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData)
   : m_streamType(type)
   , m_pID(pid)
 {
@@ -464,7 +467,7 @@ cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
   }
   else if (m_streamType == stMPEG2AUDIO)
   {
-    m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true);
+    m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true, handleSideData);
     m_streamContent = scAUDIO;
   }
   else if (m_streamType == stAACADTS)
@@ -518,17 +521,19 @@ cTSStream::~cTSStream()
   }
 }
 
-int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
+int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe)
 {
+  int ret = 1;
+
   if (!data)
-    return false;
+    return ret;
 
   if (!m_pesParser)
-    return false;
+    return ret;
 
   int payloadSize = m_pesParser->ParsePacketHeader(data);
   if (payloadSize == 0)
-    return 1;
+    return ret;
   else if (payloadSize < 0)
   {
     return -m_pesParser->GetError();
@@ -539,26 +544,41 @@ int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
     return -m_pesParser->GetError();
   }
 
-  m_pesParser->Parse(pkt);
+  m_pesParser->Parse(pkt, pkt_side_data);
   if (iframe && !m_pesParser->IsVideo())
-    return 1;
+    return ret;
 
   if (pkt->data)
   {
     int64_t dts = pkt->dts;
     int64_t pts = pkt->pts;
 
-    if (dts == DVD_NOPTS_VALUE)
-      dts = pts;
-
     // Rescale for XBMC
-    pkt->dts      = Rescale(dts, DVD_TIME_BASE, 90000);
-    pkt->pts      = Rescale(pts, DVD_TIME_BASE, 90000);
+    if (pkt->dts != DVD_NOPTS_VALUE)
+      pkt->dts      = Rescale(dts, DVD_TIME_BASE, 90000);
+    if (pkt->pts != DVD_NOPTS_VALUE)
+      pkt->pts      = Rescale(pts, DVD_TIME_BASE, 90000);
     pkt->duration = Rescale(pkt->duration, DVD_TIME_BASE, 90000);
-    return 0;
+
+    ret = 0;
+  }
+
+  if (pkt_side_data && pkt_side_data->data)
+  {
+    int64_t dts = pkt_side_data->dts;
+    int64_t pts = pkt_side_data->pts;
+
+    // Rescale for XBMC
+    if (pkt_side_data->dts != DVD_NOPTS_VALUE)
+      pkt_side_data->dts      = Rescale(dts, DVD_TIME_BASE, 90000);
+    if (pkt_side_data->pts != DVD_NOPTS_VALUE)
+      pkt_side_data->pts      = Rescale(pts, DVD_TIME_BASE, 90000);
+    pkt_side_data->duration = Rescale(pkt_side_data->duration, DVD_TIME_BASE, 90000);
+
+    ret = 0;
   }
 
-  return 1;
+  return ret;
 }
 
 bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
@@ -578,6 +598,7 @@ bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
     data += TS_SIZE-payloadSize;
     if (payloadSize >= 6 && m_pesParser->IsValidStartCode(data, payloadSize))
     {
+      m_pesParser->m_curPTS = DVD_NOPTS_VALUE;
       m_pesParser->m_curDTS = DVD_NOPTS_VALUE;
       m_pesParser->ParsePESHeader(data, payloadSize);
       if (m_pesParser->m_curDTS != DVD_NOPTS_VALUE)
@@ -585,6 +606,11 @@ bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
         *dts = m_pesParser->m_curDTS;
         return true;
       }
+      else if (m_pesParser->m_curPTS != DVD_NOPTS_VALUE)
+      {
+        *dts = m_pesParser->m_curPTS;
+        return true;
+      }
     }
     m_pesParser->m_IsPusi = false;
   }
@@ -626,7 +652,7 @@ int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
     {
       a1+= a1 + ((a0>>i)&1);
       t1+=t1;
-      if (c <= a1)
+      if (c <= (int64_t)a1)
       {
         a1 -= c;
         t1++;
@@ -636,6 +662,19 @@ int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
   }
 }
 
+uint32_t cTSStream::AddSideDataType(eStreamContent content)
+{
+  m_UniqueSideDataIDs++;
+  if (m_UniqueSideDataIDs == 0)
+    m_UniqueSideDataIDs++;
+
+  uint32_t sidePid = m_UniqueSideDataIDs << 16;
+
+  m_SideDataTypes.push_back(std::make_pair(sidePid, content));
+
+  return sidePid;
+}
+
 void cTSStream::SetLanguage(const char *language)
 {
   m_language[0] = language[0];
diff --git a/parser.h b/parser.h
index f3e8db4..b4872c0 100644
--- a/parser.h
+++ b/parser.h
@@ -23,6 +23,7 @@
 
 #include <vdr/device.h>
 #include <queue>
+#include <vector>
 
 #define DVD_TIME_BASE 1000000
 #define DVD_NOPTS_VALUE    (-1LL<<52) // should be possible to represent in both double and __int64
@@ -84,7 +85,8 @@ enum eStreamContent
   scAUDIO,
   scSUBTITLE,
   scTELETEXT,
-  scPROGRAMM
+  scPROGRAMM,
+  scRDS
 };
 
 enum eStreamType
@@ -122,6 +124,7 @@ struct sStreamPacket
   bool      streamChange;
   bool      pmtChange;
   uint32_t  serial;
+  uint32_t  reftime;
 };
 
 struct sPtsWrap
@@ -143,7 +146,7 @@ public:
   virtual ~cParser();
 
   bool AddPESPacket(uint8_t *data, int size);
-  virtual void Parse(sStreamPacket *pkt) = 0;
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) = 0;
 //  void ClearFrame() {m_PesBufferPtr = 0;}
   int ParsePacketHeader(uint8_t *data);
   int ParsePESHeader(uint8_t *buf, size_t len);
@@ -166,6 +169,7 @@ protected:
   int         m_PesTimePos;
 
   bool        m_FoundFrame;
+  bool        m_FrameValid;
 
   int         m_pID;
   int64_t     m_curPTS;
@@ -208,15 +212,18 @@ private:
   int                   m_BitsPerSample;
   int                   m_BlockAlign;
 
+  std::vector< std::pair<uint32_t, eStreamContent> >  m_SideDataTypes;
+  static uint32_t       m_UniqueSideDataIDs;
+
   unsigned char         m_subtitlingType;
   uint16_t              m_compositionPageId;
   uint16_t              m_ancillaryPageId;
 
 public:
-  cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap);
+  cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData = false);
   virtual ~cTSStream();
 
-  int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+  int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe);
   bool ReadTime(uint8_t *data, int64_t *dts);
   void ResetParser();
 
@@ -224,8 +231,12 @@ public:
   const char *GetLanguage() { return m_language; }
   const eStreamContent Content() const { return m_streamContent; }
   const eStreamType Type() const { return m_streamType; }
+  void SetType(eStreamType type) { m_streamType = type; }
   const int GetPID() const { return m_pID; }
 
+  uint32_t AddSideDataType(eStreamContent content);
+  const std::vector< std::pair<uint32_t, eStreamContent> > *GetSideDataTypes() const { return &m_SideDataTypes; }
+
   /* Video Stream Information */
   bool SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect);
   void GetVideoInformation(uint32_t &FpsScale, uint32_t &FpsRate, uint32_t &Height, uint32_t &Width, double &Aspect);
diff --git a/parser_AAC.c b/parser_AAC.c
index 2ef3cb1..508a783 100644
--- a/parser_AAC.c
+++ b/parser_AAC.c
@@ -43,6 +43,14 @@ cParserAAC::cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool obser
   m_Channels                  = 0;
   m_BitRate                   = 0;
   m_PesBufferInitialSize      = 1920*2;
+  m_DetectMuxMode             = false;
+
+  char *detectEnv = getenv("VNSI_AAC_MUXMODE");
+  if (detectEnv)
+  {
+    m_DetectMuxMode = true;
+    INFOLOG("cParserAAC - detect muxing mode while parsing");
+  }
 }
 
 cParserAAC::~cParserAAC()
@@ -50,7 +58,7 @@ cParserAAC::~cParserAAC()
 
 }
 
-void cParserAAC::Parse(sStreamPacket *pkt)
+void cParserAAC::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   int p = m_PesParserPtr;
   int l;
@@ -68,6 +76,8 @@ void cParserAAC::Parse(sStreamPacket *pkt)
     pkt->id       = m_pID;
     pkt->data     = &m_PesBuffer[p];
     pkt->size     = m_FrameSize;
+    if(!m_SampleRate)
+      m_SampleRate = aac_sample_rates[4];
     pkt->duration = 1024 * 90000 / m_SampleRate;
     pkt->dts      = m_DTS;
     pkt->pts      = m_PTS;
@@ -103,9 +113,17 @@ int cParserAAC::FindHeaders(uint8_t *buf, int buf_size)
       m_FoundFrame = true;
       m_DTS = m_curPTS;
       m_PTS = m_curPTS;
+      if(!m_SampleRate)
+	m_SampleRate = aac_sample_rates[4];
       m_curPTS += 90000 * 1024 / m_SampleRate;
       return -1;
     }
+    else if (m_DetectMuxMode && buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xF0) == 0xF0)
+    {
+      m_Stream->SetType(stAACADTS);
+      INFOLOG("cParserAAC::FindHeaders - detected ADTS muxing mode");
+      return -1;
+    }
   }
   else if (m_Stream->Type() == stAACADTS)
   {
@@ -138,6 +156,12 @@ int cParserAAC::FindHeaders(uint8_t *buf, int buf_size)
       m_curPTS += 90000 * 1024 / m_SampleRate;
       return -1;
     }
+    else if (m_DetectMuxMode && buf_ptr[0] == 0x56 && (buf_ptr[1] & 0xE0) == 0xE0)
+    {
+      m_Stream->SetType(stAACLATM);
+      INFOLOG("cParserAAC::FindHeaders - detected LATM muxing mode");
+      return -1;
+    }
   }
   return 0;
 }
diff --git a/parser_AAC.h b/parser_AAC.h
index 15c2122..d95fcee 100644
--- a/parser_AAC.h
+++ b/parser_AAC.h
@@ -40,6 +40,7 @@ private:
   bool        m_Configured;
   int         m_AudioMuxVersion_A;
   int         m_FrameLengthType;
+  bool        m_DetectMuxMode;
 
   int FindHeaders(uint8_t *buf, int buf_size);
   bool ParseLATMAudioMuxElement(cBitstream *bs);
@@ -50,7 +51,7 @@ private:
 public:
   cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserAAC();
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
   virtual void Reset();
 };
 
diff --git a/parser_AC3.c b/parser_AC3.c
index a1645a2..b8ace56 100644
--- a/parser_AC3.c
+++ b/parser_AC3.c
@@ -121,7 +121,7 @@ cParserAC3::~cParserAC3()
 {
 }
 
-void cParserAC3::Parse(sStreamPacket *pkt)
+void cParserAC3::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   int p = m_PesParserPtr;
   int l;
diff --git a/parser_AC3.h b/parser_AC3.h
index b789423..2863b54 100644
--- a/parser_AC3.h
+++ b/parser_AC3.h
@@ -42,7 +42,7 @@ public:
   cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserAC3();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
   virtual void Reset();
 };
 
diff --git a/parser_DTS.c b/parser_DTS.c
index 2ad2385..2c82ba8 100644
--- a/parser_DTS.c
+++ b/parser_DTS.c
@@ -34,7 +34,7 @@ cParserDTS::~cParserDTS()
 {
 }
 
-void cParserDTS::Parse(sStreamPacket *pkt)
+void cParserDTS::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
 
 }
diff --git a/parser_DTS.h b/parser_DTS.h
index c28b4ba..741afe5 100644
--- a/parser_DTS.h
+++ b/parser_DTS.h
@@ -33,7 +33,7 @@ public:
   cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserDTS();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
 };
 
 
diff --git a/parser_MPEGAudio.c b/parser_MPEGAudio.c
index 2c62030..9dba379 100644
--- a/parser_MPEGAudio.c
+++ b/parser_MPEGAudio.c
@@ -25,6 +25,8 @@
 #include "parser_MPEGAudio.h"
 #include "bitstream.h"
 
+#define MAX_RDS_BUFFER_SIZE 100000
+
 const uint16_t FrequencyTable[3] = { 44100, 48000, 32000 };
 const uint16_t BitrateTable[2][3][15] =
 {
@@ -40,7 +42,7 @@ const uint16_t BitrateTable[2][3][15] =
   }
 };
 
-cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS)
  : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_PTS                       = 0;
@@ -50,13 +52,23 @@ cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWr
   m_Channels                  = 0;
   m_BitRate                   = 0;
   m_PesBufferInitialSize      = 2048;
+  m_RDSEnabled                = enableRDS;
+  m_RDSBufferInitialSize      = 384;
+  m_RDSBuffer                 = NULL;
+  m_RDSBufferSize             = 0;
+  m_RDSExtPID                 = 0;
 }
 
 cParserMPEG2Audio::~cParserMPEG2Audio()
 {
+  if (m_RDSBuffer)
+  {
+    delete m_RDSBuffer;
+    m_RDSBuffer = NULL;
+  }
 }
 
-void cParserMPEG2Audio::Parse(sStreamPacket *pkt)
+void cParserMPEG2Audio::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   int p = m_PesParserPtr;
   int l;
@@ -82,9 +94,74 @@ void cParserMPEG2Audio::Parse(sStreamPacket *pkt)
     m_PesNextFramePtr = p + m_FrameSize;
     m_PesParserPtr = 0;
     m_FoundFrame = false;
+
+    if (m_RDSEnabled)
+    {
+      /*
+       * Reading of RDS Universal Encoder Communication Protocol
+       * If present it starts on end of a mpeg audio stream and goes
+       * backwards.
+       * See ETSI TS 101 154 - C.4.2.18 for details.
+       */
+      int rdsl = m_PesBuffer[p+m_FrameSize-2];                  // RDS DataFieldLength
+      if (m_PesBuffer[p+m_FrameSize-1] == 0xfd && rdsl > 0)     // RDS DataSync = 0xfd @ end
+      {
+        if (m_RDSBuffer == NULL)
+        {
+          m_RDSBufferSize = m_RDSBufferInitialSize;
+          m_RDSBuffer = (uint8_t*)malloc(m_RDSBufferSize);
+
+          if (m_RDSBuffer == NULL)
+          {
+            ERRORLOG("PVR Parser MPEG2-Audio - %s - malloc failed for RDS data", __FUNCTION__);
+            m_RDSEnabled = false;
+            return;
+          }
+
+          m_RDSExtPID = m_Stream->AddSideDataType(scRDS);
+          if (!m_RDSExtPID)
+          {
+            ERRORLOG("PVR Parser MPEG2-Audio - %s - failed to add RDS data stream", __FUNCTION__);
+            m_RDSEnabled = false;
+            return;
+          }
+        }
+
+        if (rdsl >= m_RDSBufferSize)
+        {
+          if (rdsl >= MAX_RDS_BUFFER_SIZE)
+          {
+            ERRORLOG("PVR Parser MPEG2-Audio - %s - max buffer size (%i kB) for RDS data reached, pid: %d", __FUNCTION__, MAX_RDS_BUFFER_SIZE/1024, m_pID);
+            m_RDSEnabled = false;
+            return;
+          }
+          m_RDSBufferSize += m_RDSBufferInitialSize / 10;
+          m_RDSBuffer = (uint8_t*)realloc(m_RDSBuffer, m_RDSBufferSize);
+          if (m_RDSBuffer == NULL)
+          {
+            ERRORLOG("PVR Parser MPEG2-Audio - %s - realloc for RDS data failed", __FUNCTION__);
+            m_RDSEnabled = false;
+            return;
+          }
+        }
+
+        int pes_buffer_ptr = 0;
+        for (int i = m_FrameSize-3; i > m_FrameSize-3-rdsl; i--)    // <-- data reverse, from end to start
+          m_RDSBuffer[pes_buffer_ptr++] = m_PesBuffer[p+i];
+
+        pkt_side_data->id       = m_RDSExtPID;
+        pkt_side_data->data     = m_RDSBuffer;
+        pkt_side_data->size     = pes_buffer_ptr;
+        pkt_side_data->duration = 0;
+        pkt_side_data->dts      = m_curDTS;
+        pkt_side_data->pts      = m_curPTS;
+        pkt_side_data->streamChange = false;
+      }
+    }
   }
 }
 
+
 int cParserMPEG2Audio::FindHeaders(uint8_t *buf, int buf_size)
 {
   if (m_FoundFrame)
diff --git a/parser_MPEGAudio.h b/parser_MPEGAudio.h
index 32a3989..108ace9 100644
--- a/parser_MPEGAudio.h
+++ b/parser_MPEGAudio.h
@@ -36,13 +36,20 @@ private:
   int64_t     m_PTS;
   int64_t     m_DTS;
 
+  bool        m_RDSEnabled;
+  uint32_t    m_RDSExtPID;
+  uint8_t    *m_RDSBuffer;
+  int         m_RDSBufferSize;
+  int         m_RDSBufferPtr;
+  size_t      m_RDSBufferInitialSize;
+
   int FindHeaders(uint8_t *buf, int buf_size);
 
 public:
-  cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
+  cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS);
   virtual ~cParserMPEG2Audio();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
 };
 
 
diff --git a/parser_MPEGVideo.c b/parser_MPEGVideo.c
index 5aa911a..01dc9a4 100644
--- a/parser_MPEGVideo.c
+++ b/parser_MPEGVideo.c
@@ -57,6 +57,7 @@ cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWr
   m_Height            = 0;
   m_Width             = 0;
   m_Dar               = 0.0;
+  m_FpsScale          = 0;
   m_PesBufferInitialSize  = 80000;
   m_IsVideo = true;
   Reset();
@@ -66,7 +67,7 @@ cParserMPEG2Video::~cParserMPEG2Video()
 {
 }
 
-void cParserMPEG2Video::Parse(sStreamPacket *pkt)
+void cParserMPEG2Video::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   if (m_PesBufferPtr < 4)
     return;
@@ -91,10 +92,16 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
 
   if (frameComplete)
   {
-    if (!m_NeedSPS && !m_NeedIFrame)
+    if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid)
     {
-      int fpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
-      bool streamChange = m_Stream->SetVideoInformation(fpsScale,DVD_TIME_BASE, m_Height, m_Width, m_Dar);
+      if (m_FpsScale == 0)
+      {
+        if (m_FrameDuration != DVD_NOPTS_VALUE)
+          m_FpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
+        else
+          m_FpsScale = 40000;
+      }
+      bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, m_Dar);
 
       pkt->id       = m_pID;
       pkt->size     = m_PesNextFramePtr;
@@ -107,6 +114,7 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
     m_StartCode = 0xffffffff;
     m_PesParserPtr = 0;
     m_FoundFrame = false;
+    m_FrameValid = true;
   }
 }
 
@@ -148,13 +156,12 @@ int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &c
       m_AuPrevDTS = m_AuDTS;
       if (buf_ptr - 4 >= m_PesTimePos)
       {
-
-        m_AuDTS = m_curDTS;
+        m_AuDTS = m_curDTS != DVD_NOPTS_VALUE ? m_curDTS : m_curPTS;
         m_AuPTS = m_curPTS;
       }
       else
       {
-        m_AuDTS = m_prevDTS;
+        m_AuDTS = m_prevDTS != DVD_NOPTS_VALUE ? m_prevDTS : m_prevPTS;;
         m_AuPTS = m_prevPTS;
       }
     }
diff --git a/parser_MPEGVideo.h b/parser_MPEGVideo.h
index 18da858..5a03480 100644
--- a/parser_MPEGVideo.h
+++ b/parser_MPEGVideo.h
@@ -46,6 +46,7 @@ private:
   int             m_TemporalReference;
   int             m_TrLastTime;
   int             m_PicNumber;
+  int             m_FpsScale;
 
   int Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete);
   bool Parse_MPEG2Video_SeqStart(uint8_t *buf);
@@ -55,7 +56,7 @@ public:
   cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserMPEG2Video();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
   virtual void Reset();
 };
 
diff --git a/parser_Subtitle.c b/parser_Subtitle.c
index 6408fbd..cd30514 100644
--- a/parser_Subtitle.c
+++ b/parser_Subtitle.c
@@ -35,7 +35,7 @@ cParserSubtitle::~cParserSubtitle()
 
 }
 
-void cParserSubtitle::Parse(sStreamPacket *pkt)
+void cParserSubtitle::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   int l = m_PesBufferPtr;
 
diff --git a/parser_Subtitle.h b/parser_Subtitle.h
index 296ac7c..d6e208e 100644
--- a/parser_Subtitle.h
+++ b/parser_Subtitle.h
@@ -31,7 +31,7 @@ public:
   cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserSubtitle();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
 };
 
 
diff --git a/parser_Teletext.c b/parser_Teletext.c
index b2ecaee..028e852 100644
--- a/parser_Teletext.c
+++ b/parser_Teletext.c
@@ -33,7 +33,7 @@ cParserTeletext::~cParserTeletext()
 {
 }
 
-void cParserTeletext::Parse(sStreamPacket *pkt)
+void cParserTeletext::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   int l = m_PesBufferPtr;
   if (l < 1)
diff --git a/parser_Teletext.h b/parser_Teletext.h
index 831e7f5..2898176 100644
--- a/parser_Teletext.h
+++ b/parser_Teletext.h
@@ -35,7 +35,7 @@ public:
   cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserTeletext();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
 };
 
 #endif // VNSI_DEMUXER_TELETEXT_H
diff --git a/parser_h264.c b/parser_h264.c
index 6d45e30..d0b2620 100644
--- a/parser_h264.c
+++ b/parser_h264.c
@@ -68,7 +68,7 @@ cParserH264::~cParserH264()
 {
 }
 
-void cParserH264::Parse(sStreamPacket *pkt)
+void cParserH264::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
 {
   if (m_PesBufferPtr < 4)
     return;
@@ -93,30 +93,38 @@ void cParserH264::Parse(sStreamPacket *pkt)
 
   if (frameComplete)
   {
-    if (!m_NeedSPS && !m_NeedIFrame)
+    if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid)
     {
       double PAR = (double)m_PixelAspect.num/(double)m_PixelAspect.den;
       double DAR = (PAR * m_Width) / m_Height;
       DEBUGLOG("H.264 SPS: PAR %i:%i", m_PixelAspect.num, m_PixelAspect.den);
       DEBUGLOG("H.264 SPS: DAR %.2f", DAR);
-//      int fpsScale = DVD_TIME_BASE / m_FPS;
+
+      int duration;
+      if (m_curDTS != DVD_NOPTS_VALUE && m_prevDTS != DVD_NOPTS_VALUE)
+        duration = m_curDTS - m_prevDTS;
+      else
+        duration = m_Stream->Rescale(20000, 90000, DVD_TIME_BASE);
+
       if (m_FpsScale == 0)
       {
-        m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS, DVD_TIME_BASE, 90000);
+        m_FpsScale = m_Stream->Rescale(duration, DVD_TIME_BASE, 90000);
       }
-      bool streamChange = m_Stream->SetVideoInformation(m_FpsScale,DVD_TIME_BASE, m_Height, m_Width, DAR);
+
+      bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, DAR);
 
       pkt->id       = m_pID;
       pkt->size     = m_PesNextFramePtr;
       pkt->data     = m_PesBuffer;
       pkt->dts      = m_DTS;
       pkt->pts      = m_PTS;
-      pkt->duration = m_curDTS - m_prevDTS;
+      pkt->duration = duration;
       pkt->streamChange = streamChange;
     }
     m_StartCode = 0xffffffff;
     m_PesParserPtr = 0;
     m_FoundFrame = false;
+    m_FrameValid = true;
   }
 }
 
@@ -226,7 +234,7 @@ int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
   }
 
   case NAL_AUD:
-    if (m_FoundFrame && (m_prevDTS != DVD_NOPTS_VALUE))
+    if (m_FoundFrame && (m_prevPTS != DVD_NOPTS_VALUE))
     {
       complete = true;
       m_PesNextFramePtr = buf_ptr - 4;
diff --git a/parser_h264.h b/parser_h264.h
index 609ce0a..5ec7b31 100644
--- a/parser_h264.h
+++ b/parser_h264.h
@@ -108,7 +108,7 @@ public:
   cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserH264();
 
-  virtual void Parse(sStreamPacket *pkt);
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
   virtual void Reset();
 };
 
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..b85488b
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,61 @@
+# VDR VNSI plugin language source file.
+# Copyright (C) 2015 Alwin Esch
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VNSI-Server 1.0.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-01-23 22:25+0100\n"
+"PO-Revision-Date: 2015-01-23 21:46+0100\n"
+"Last-Translator: Alwin Esch\n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "PMT Timeout (0-10)"
+msgstr "PMT Auszeit (0-10)"
+
+msgid "Off"
+msgstr "Aus"
+
+msgid "RAM"
+msgstr "RAM"
+
+msgid "File"
+msgstr "Datei"
+
+msgid "Time Shift Mode"
+msgstr "Time Shift Modus"
+
+msgid "TS Buffersize (RAM) (1-80) x 100MB"
+msgstr "TS Puffergröße (RAM) (1-80) x 100MB"
+
+msgid "TS Buffersize (File) (1-10) x 1GB"
+msgstr "TS Puffergröße (Datei) (1-10) x 1GB"
+
+msgid "TS Buffer Directory"
+msgstr "TS-Puffer-Verzeichnis"
+
+msgid "Play Recording instead of live"
+msgstr "Wiedergeben als Aufzeichnung statt Live"
+
+msgid "Avoid EPG scan while streaming"
+msgstr "Keine EPG suche während der Wiedergabe durchführen"
+
+msgid "Recording with the same name exists"
+msgstr "Aufnahme mit der selben größe existiert"
+
+msgid "Error while read last filenumber"
+msgstr "Fehler beim lesen der letzen Dateinummer"
+
+msgid "Error while accessing vdrfile"
+msgstr "Fehler beim zugriff auf VDR-Datei"
+
+msgid "Error while accessing indexfile"
+msgstr "Fehler beim Zugriff der Indexdatei"
+
+msgid "Deleted recording vanished"
+msgstr "Gelöschte Aufnahme verschwunden"
diff --git a/po/lt_LT.po b/po/lt_LT.po
new file mode 100644
index 0000000..5b25d79
--- /dev/null
+++ b/po/lt_LT.po
@@ -0,0 +1,61 @@
+# VDR VNSI plugin language source file.
+# Copyright (C) 2015 Alwin Esch
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VNSI-Server 1.0.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-01-23 22:25+0100\n"
+"PO-Revision-Date: 2015-02-11 22:30+0200\n"
+"Last-Translator: Valdemaras Pipiras\n"
+"Language-Team: Lithuanian\n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "PMT Timeout (0-10)"
+msgstr "PMT  (0-10)"
+
+msgid "Off"
+msgstr "Išjungta"
+
+msgid "RAM"
+msgstr "RAM"
+
+msgid "File"
+msgstr "Failas"
+
+msgid "Time Shift Mode"
+msgstr "Atidėto žiūrėjimo (TS) būsena"
+
+msgid "TS Buffersize (RAM) (1-80) x 100MB"
+msgstr "TS buferio dydis (RAM) (1-80) x 100MB"
+
+msgid "TS Buffersize (File) (1-10) x 1GB"
+msgstr "TS buferio dydis (Failas) (1-10) x 1GB"
+
+msgid "TS Buffer Directory"
+msgstr "TS katalogas buferiavimui"
+
+msgid "Play Recording instead of live"
+msgstr "Groti įrašą vietoj gyvos transliacijos"
+
+msgid "Avoid EPG scan while streaming"
+msgstr "Vengti EPG skanavimo kol vyksta transliacija"
+
+msgid "Recording with the same name exists"
+msgstr "Jau yra įrašų tokiu pat pavadinimu"
+
+msgid "Error while read last filenumber"
+msgstr "Klaida nuskaitant paskutinių failų skaičių"
+
+msgid "Error while accessing vdrfile"
+msgstr "Klaida bandant atidaryti VDR failą"
+
+msgid "Error while accessing indexfile"
+msgstr "Klaida bandant atidaryti index failą"
+
+msgid "Deleted recording vanished"
+msgstr "Ištrintas įrašas galutinai išvalytas"
diff --git a/recordingscache.c b/recordingscache.c
index b435ce4..36f3593 100644
--- a/recordingscache.c
+++ b/recordingscache.c
@@ -14,14 +14,14 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
 #include "config.h"
 #include "recordingscache.h"
+#include "vnsiclient.h"
 #include "hash.h"
 
 cRecordingsCache::cRecordingsCache() {
@@ -35,7 +35,7 @@ cRecordingsCache& cRecordingsCache::GetInstance() {
   return singleton;
 }
 
-uint32_t cRecordingsCache::Register(cRecording* recording) {
+uint32_t cRecordingsCache::Register(cRecording* recording, bool deleted) {
   cString filename = recording->FileName();
   uint32_t uid = CreateStringHash(filename);
 
@@ -43,7 +43,8 @@ uint32_t cRecordingsCache::Register(cRecording* recording) {
   if(m_recordings.find(uid) == m_recordings.end())
   {
     DEBUGLOG("%s - uid: %08x '%s'", __FUNCTION__, uid, (const char*)filename);
-    m_recordings[uid] = filename;
+    m_recordings[uid].filename = filename;
+    m_recordings[uid].isDeleted = deleted;
   }
   m_mutex.Unlock();
 
@@ -59,10 +60,15 @@ cRecording* cRecordingsCache::Lookup(uint32_t uid) {
   }
 
   m_mutex.Lock();
-  cString filename = m_recordings[uid];
+  cString filename = m_recordings[uid].filename;
   DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
 
-  cRecording* r = Recordings.GetByName(filename);
+  cRecording* r;
+  if (!m_recordings[uid].isDeleted)
+    r = Recordings.GetByName(filename);
+  else
+    r = DeletedRecordings.GetByName(filename);
+
   DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found");
   m_mutex.Unlock();
 
diff --git a/recordingscache.h b/recordingscache.h
index 964e948..24933be 100644
--- a/recordingscache.h
+++ b/recordingscache.h
@@ -14,9 +14,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -41,13 +40,17 @@ public:
 
   static cRecordingsCache& GetInstance();
 
-  uint32_t Register(cRecording* recording);
+  uint32_t Register(cRecording* recording, bool deleted = false);
 
   cRecording* Lookup(uint32_t uid);
 
 private:
-
-  std::map<uint32_t, cString> m_recordings;
+  struct RecordingsInfo
+  {
+    cString filename;
+    bool isDeleted;
+  };
+  std::map<uint32_t, RecordingsInfo> m_recordings;
 
   cMutex m_mutex;
 };
diff --git a/recplayer.c b/recplayer.c
index f5af3e4..99d3997 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -200,7 +199,13 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
     amount = m_totalLength;
 
   if (position >= m_totalLength)
-    return 0;
+  {
+    reScan();
+    if (position >= m_totalLength)
+    {
+      return 0;
+    }
+  }
 
   if ((position + amount) > m_totalLength)
     amount = m_totalLength - position;
@@ -216,16 +221,19 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
   }
 
   // segment not found / invalid position
-  if (segmentNumber == -1) return 0;
+  if (segmentNumber == -1)
+    return 0;
 
   // open file (if not already open)
-  if (!openFile(segmentNumber)) return 0;
+  if (!openFile(segmentNumber))
+    return 0;
 
   // work out position in current file
   uint64_t filePosition = position - m_segments[segmentNumber]->start;
 
   // seek to position
-  if(lseek(m_file, filePosition, SEEK_SET) == -1) {
+  if(lseek(m_file, filePosition, SEEK_SET) == -1)
+  {
     ERRORLOG("unable to seek to position: %lu", filePosition);
     return 0;
   }
@@ -237,7 +245,8 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
   if ((bytes_read == 0) && (position < m_totalLength))
     bytes_read += getBlock(buffer, position+1 , amount);
 
-  if(bytes_read <= 0) {
+  if(bytes_read <= 0)
+  {
     return 0;
   }
 
@@ -254,7 +263,8 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
 
 uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
 {
-  if (!m_indexFile) return 0;
+  if (!m_indexFile)
+    return 0;
 #if VDRVERSNUM < 10703
   unsigned char retFileNumber;
   int retFileOffset;
diff --git a/recplayer.h b/recplayer.h
index 784489c..f7a4af5 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/requestpacket.c b/requestpacket.c
index 51245ca..77a6cf2 100644
--- a/requestpacket.c
+++ b/requestpacket.c
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/requestpacket.h b/requestpacket.h
index 44fab53..24328a0 100644
--- a/requestpacket.h
+++ b/requestpacket.h
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/responsepacket.c b/responsepacket.c
index 33ea5d0..439dfa7 100644
--- a/responsepacket.c
+++ b/responsepacket.c
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -214,7 +213,7 @@ bool cResponsePacket::copyin(const uint8_t* src, uint32_t len)
 }
 
 uint8_t* cResponsePacket::reserve(uint32_t len) {
-  if (!checkExtend(len)) return false;
+  if (!checkExtend(len)) return 0;
   uint8_t* result = buffer + bufUsed;
   bufUsed += len;
   return result;
diff --git a/responsepacket.h b/responsepacket.h
index 0e0c9a0..0f5c457 100644
--- a/responsepacket.h
+++ b/responsepacket.h
@@ -18,9 +18,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/setup.c b/setup.c
index 61df5be..0e6249f 100644
--- a/setup.c
+++ b/setup.c
@@ -26,6 +26,8 @@ int TimeshiftMode = 0;
 int TimeshiftBufferSize = 5;
 int TimeshiftBufferFileSize = 6;
 char TimeshiftBufferDir[PATH_MAX] = "\0";
+int PlayRecording = 0;
+int AvoidEPGScan = 0;
 
 cMenuSetupVNSI::cMenuSetupVNSI(void)
 {
@@ -39,13 +41,19 @@ cMenuSetupVNSI::cMenuSetupVNSI(void)
   Add(new cMenuEditStraItem( tr("Time Shift Mode"), &newTimeshiftMode, 3, timeshiftModesTexts));
 
   newTimeshiftBufferSize = TimeshiftBufferSize;
-  Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-20) x 100MB"), &newTimeshiftBufferSize));
+  Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-80) x 100MB"), &newTimeshiftBufferSize));
 
   newTimeshiftBufferFileSize = TimeshiftBufferFileSize;
   Add(new cMenuEditIntItem( tr("TS Buffersize (File) (1-10) x 1GB"), &newTimeshiftBufferFileSize));
 
   strn0cpy(newTimeshiftBufferDir, TimeshiftBufferDir, sizeof(newTimeshiftBufferDir));
   Add(new cMenuEditStrItem(tr("TS Buffer Directory"), newTimeshiftBufferDir, sizeof(newTimeshiftBufferDir)));
+
+  newPlayRecording = PlayRecording;
+  Add(new cMenuEditBoolItem( tr("Play Recording instead of live"), &newPlayRecording));
+
+  newAvoidEPGScan = AvoidEPGScan;
+  Add(new cMenuEditBoolItem( tr("Avoid EPG scan while streaming"), &newAvoidEPGScan));
 }
 
 void cMenuSetupVNSI::Store(void)
@@ -56,8 +64,8 @@ void cMenuSetupVNSI::Store(void)
 
   SetupStore(CONFNAME_TIMESHIFT, TimeshiftMode = newTimeshiftMode);
 
-  if (newTimeshiftBufferSize > 40)
-    newTimeshiftBufferSize = 40;
+  if (newTimeshiftBufferSize > 80)
+    newTimeshiftBufferSize = 80;
   else if (newTimeshiftBufferSize < 1)
     newTimeshiftBufferSize = 1;
   SetupStore(CONFNAME_TIMESHIFTBUFFERSIZE, TimeshiftBufferSize = newTimeshiftBufferSize);
@@ -69,4 +77,8 @@ void cMenuSetupVNSI::Store(void)
   SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize);
 
   SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir)));
+
+  SetupStore(CONFNAME_PLAYRECORDING, PlayRecording = newPlayRecording);
+
+  SetupStore(CONFNAME_AVOIDEPGSCAN, AvoidEPGScan = newAvoidEPGScan);
 }
diff --git a/setup.h b/setup.h
index acd584f..cf116cf 100644
--- a/setup.h
+++ b/setup.h
@@ -32,6 +32,8 @@ private:
   int newTimeshiftBufferSize;
   int newTimeshiftBufferFileSize;
   char newTimeshiftBufferDir[PATH_MAX];
+  int newPlayRecording;
+  int newAvoidEPGScan;
 protected:
   virtual void Store(void);
 public:
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..b94d877
--- /dev/null
+++ b/status.c
@@ -0,0 +1,160 @@
+/*
+ *      Copyright (C) 2005-2014 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "vnsi.h"
+#include "status.h"
+#include "vnsiclient.h"
+#include <vdr/tools.h>
+#include <vdr/recording.h>
+#include <vdr/videodir.h>
+#include <vdr/shutdown.h>
+
+cVNSIStatus::~cVNSIStatus()
+{
+  Shutdown();
+}
+
+void cVNSIStatus::Shutdown()
+{
+  Cancel(5);
+  cMutexLock lock(&m_mutex);
+  for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+  {
+    delete (*i);
+  }
+  m_clients.erase(m_clients.begin(), m_clients.end());
+}
+
+void cVNSIStatus::AddClient(cVNSIClient* client)
+{
+  cMutexLock lock(&m_mutex);
+  m_clients.push_back(client);
+}
+
+void cVNSIStatus::Action(void)
+{
+  cTimeMs chanTimer(0);
+
+  // get initial state of the recordings
+  int recState = -1;
+  Recordings.StateChanged(recState);
+
+  // get initial state of the timers
+  int timerState = -1;
+  Timers.Modified(timerState);
+
+  // last update of epg
+  time_t epgUpdate = cSchedules::Modified();
+
+  // delete old timeshift file
+  cString cmd;
+  struct stat sb;
+  if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
+  {
+    if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+      cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
+    else
+      cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
+  }
+  else
+  {
+#if VDRVERSNUM >= 20102
+    cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
+#else
+    cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
+#endif
+  }
+  int ret = system(cmd);
+
+  // set thread priority
+  SetPriority(1);
+
+  while (Running())
+  {
+    m_mutex.Lock();
+
+    // remove disconnected clients
+    for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
+    {
+      if (!(*i)->Active())
+      {
+        INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
+        delete (*i);
+        i = m_clients.erase(i);
+      }
+      else {
+        i++;
+      }
+    }
+
+    // trigger clients to reload the modified channel list
+    if(m_clients.size() > 0 && chanTimer.TimedOut())
+    {
+      int modified = Channels.Modified();
+      if (modified)
+      {
+        Channels.SetModified((modified == CHANNELSMOD_USER) ? true : false);
+        INFOLOG("Requesting clients to reload channel list");
+        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+          (*i)->ChannelsChange();
+      }
+      chanTimer.Set(5000);
+    }
+
+    // reset inactivity timeout as long as there are clients connected
+    if(m_clients.size() > 0) {
+      ShutdownHandler.SetUserInactiveTimeout();
+    }
+
+    // update recordings
+    if(Recordings.StateChanged(recState))
+    {
+      INFOLOG("Recordings state changed (%i)", recState);
+      INFOLOG("Requesting clients to reload recordings list");
+      for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+        (*i)->RecordingsChange();
+    }
+
+    // update timers
+    if(Timers.Modified(timerState))
+    {
+      INFOLOG("Timers state changed (%i)", timerState);
+      INFOLOG("Requesting clients to reload timers");
+      for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+      {
+        (*i)->TimerChange();
+      }
+    }
+
+    // update epg
+    if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300)
+    {
+      for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+      {
+        (*i)->EpgChange();
+      }
+      epgUpdate = time(NULL);
+    }
+
+    m_mutex.Unlock();
+
+    usleep(250*1000);
+  }
+}
diff --git a/parser_DTS.c b/status.h
similarity index 65%
copy from parser_DTS.c
copy to status.h
index 2ad2385..96e1726 100644
--- a/parser_DTS.c
+++ b/status.h
@@ -1,5 +1,5 @@
 /*
- *      Copyright (C) 2005-2012 Team XBMC
+ *      Copyright (C) 2005-2014 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -18,23 +18,25 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
+#pragma once
 
-#include "parser_DTS.h"
-#include "bitstream.h"
+#include <vdr/thread.h>
+#include <list>
 
-cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
- : cParser(pID, stream, ptsWrap, observePtsWraps)
-{
-}
+class cVNSIClient;
 
-cParserDTS::~cParserDTS()
-{
-}
+typedef std::list<cVNSIClient*> ClientList;
 
-void cParserDTS::Parse(sStreamPacket *pkt)
+class cVNSIStatus : public cThread
 {
+public:
+  virtual ~cVNSIStatus();
+  void Shutdown();
+  void AddClient(cVNSIClient* client);
+
+protected:
+  virtual void Action(void);
 
-}
+  ClientList m_clients;
+  cMutex m_mutex;
+};
diff --git a/streamer.c b/streamer.c
index b9b81a3..fb23f58 100644
--- a/streamer.c
+++ b/streamer.c
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -28,6 +27,7 @@
 #include <time.h>
 
 #include <vdr/channels.h>
+#include <vdr/eitscan.h>
 
 #include "config.h"
 #include "streamer.h"
@@ -40,10 +40,12 @@
 
 // --- cLiveStreamer -------------------------------------------------
 
-cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
+cLiveStreamer::cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout)
  : cThread("cLiveStreamer stream processor")
  , m_ClientID(clientID)
  , m_scanTimeout(timeout)
+ , m_Demuxer(bAllowRDS)
+ , m_VideoInput(m_Event, m_Mutex, m_IsRetune)
 {
   m_Channel         = NULL;
   m_Socket          = NULL;
@@ -55,6 +57,7 @@ cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
   m_IFrameSeen      = false;
   m_VideoBuffer     = NULL;
   m_Timeshift       = timeshift;
+  m_IsRetune        = false;
 
   memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
 
@@ -86,12 +89,12 @@ bool cLiveStreamer::Open(int serial)
     return false;
 
   bool recording = false;
-  if (0) // test harness
+  if (VNSIServerConfig.testStreamActive) // test harness
   {
     recording = true;
-    m_VideoBuffer = cVideoBuffer::Create("/home/xbmc/test.ts");
+    m_VideoBuffer = cVideoBuffer::Create(VNSIServerConfig.testStreamFile);
   }
-  else if (serial == -1)
+  else if (PlayRecording && serial == -1)
   {
     for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer))
     {
@@ -129,6 +132,7 @@ bool cLiveStreamer::Open(int serial)
     if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
       m_IsMPEGPS = true;
 
+    m_IsRetune = false;
     if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer))
     {
       ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
@@ -164,26 +168,53 @@ void cLiveStreamer::Close(void)
 void cLiveStreamer::Action(void)
 {
   int ret;
-  sStreamPacket pkt;
-  bool requestStreamChange = false;
+  sStreamPacket pkt_data;
+  sStreamPacket pkt_side_data; // Additional data
+  memset(&pkt_data, 0, sizeof(sStreamPacket));
+  memset(&pkt_side_data, 0, sizeof(sStreamPacket));
+  bool requestStreamChangeStdData = false;
+  bool requestStreamChangeExtData = false;
   cTimeMs last_info(1000);
   cTimeMs bufferStatsTimer(1000);
 
   while (Running())
   {
-    ret = m_Demuxer.Read(&pkt);
+    ret = m_Demuxer.Read(&pkt_data, &pkt_side_data);
     if (ret > 0)
     {
-      if (pkt.pmtChange)
+      if (pkt_data.pmtChange)
       {
-        requestStreamChange = true;
+        requestStreamChangeStdData = true;
+        requestStreamChangeExtData = true;
       }
-      if (pkt.data)
+
+      // Process normal data if present
+      if (pkt_data.data)
       {
-        if (pkt.streamChange || requestStreamChange)
+        if (pkt_data.streamChange || requestStreamChangeStdData)
           sendStreamChange();
-        requestStreamChange = false;
-        sendStreamPacket(&pkt);
+        requestStreamChangeStdData = false;
+        if (pkt_data.reftime)
+        {
+          sendRefTime(&pkt_data);
+          pkt_data.reftime = 0;
+        }
+        sendStreamPacket(&pkt_data);
+      }
+
+      // If some additional data is present inside the stream, it is written there (currently RDS inside MPEG2-Audio)
+      if (pkt_side_data.data)
+      {
+        if (pkt_side_data.streamChange || requestStreamChangeExtData)
+          sendStreamChange();
+        requestStreamChangeExtData = false;
+        if (pkt_side_data.reftime)
+        {
+          sendRefTime(&pkt_side_data);
+          pkt_side_data.reftime = 0;
+        }
+        sendStreamPacket(&pkt_side_data);
+        pkt_side_data.data = NULL;
       }
 
       // send signal info every 10 sec.
@@ -191,6 +222,13 @@ void cLiveStreamer::Action(void)
       {
         last_info.Set(0);
         sendSignalInfo();
+
+        // prevent EPG scan (activity timeout is 60s)
+        // EPG scan can cause artifacts on dual tuner cards
+        if (AvoidEPGScan)
+        {
+          EITScanner.Activity();
+        }
       }
 
       // send buffer stats
@@ -203,7 +241,17 @@ void cLiveStreamer::Action(void)
     else if (ret == -1)
     {
       // no data
-      usleep(10000);
+      {
+        cMutexLock lock(&m_Mutex);
+        if (m_IsRetune)
+        {
+          m_VideoInput.Close();
+          m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer);
+          m_IsRetune = false;
+        }
+        else
+          m_Event.TimedWait(m_Mutex, 10);
+      }
       if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
       {
         sendStreamStatus();
@@ -314,6 +362,17 @@ void cLiveStreamer::sendStreamChange()
       resp->add_U32(BlockAlign);
       resp->add_U32(BitRate);
       resp->add_U32(BitsPerSample);
+
+      for (unsigned int i = 0; i < stream->GetSideDataTypes()->size(); i++)
+      {
+        resp->add_U32(stream->GetSideDataTypes()->at(i).first);
+        if (stream->GetSideDataTypes()->at(i).second == scRDS)
+        {
+          resp->add_String("RDS");
+          resp->add_String(stream->GetLanguage());
+          resp->add_U32(stream->GetPID());
+        }
+      }
     }
     else if (stream->Type() == stMPEG2VIDEO)
     {
@@ -374,7 +433,7 @@ void cLiveStreamer::sendStreamChange()
     else if (stream->Type() == stAACLATM)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("AACLATM");
+      resp->add_String("AAC_LATM");
       resp->add_String(stream->GetLanguage());
       resp->add_U32(Channels);
       resp->add_U32(SampleRate);
@@ -601,13 +660,32 @@ void cLiveStreamer::sendBufferStatus()
     delete resp;
     return;
   }
-  int32_t start, current, end;
+  uint32_t start, end;
   bool timeshift;
-  m_Demuxer.BufferStatus(timeshift, start, current, end);
+  m_Demuxer.BufferStatus(timeshift, start, end);
   resp->add_U8(timeshift);
-  resp->add_S32(start);
-  resp->add_S32(current);
-  resp->add_S32(end);
+  resp->add_U32(start);
+  resp->add_U32(end);
+  resp->finaliseStream();
+  m_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cLiveStreamer::sendRefTime(sStreamPacket *pkt)
+{
+  if(pkt == NULL)
+    return;
+
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initStream(VNSI_STREAM_REFTIME, 0, 0, 0, 0, 0))
+  {
+    ERRORLOG("stream response packet init fail");
+    delete resp;
+    return;
+  }
+
+  resp->add_U32(pkt->reftime);
+  resp->add_U64(pkt->pts);
   resp->finaliseStream();
   m_Socket->write(resp->getPtr(), resp->getLen());
   delete resp;
@@ -619,3 +697,14 @@ bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial)
   serial = m_Demuxer.GetSerial();
   return ret;
 }
+
+void cLiveStreamer::RetuneChannel(const cChannel *channel)
+{
+  if (m_Channel != channel || !m_VideoInput.IsOpen())
+    return;
+
+  INFOLOG("re-tune to channel %s", m_Channel->Name());
+  cMutexLock lock(&m_Mutex);
+  m_IsRetune = true;
+  m_Event.Broadcast();
+}
diff --git a/streamer.h b/streamer.h
index 3c308e5..602248c 100644
--- a/streamer.h
+++ b/streamer.h
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -58,6 +57,7 @@ private:
   void sendSignalInfo();
   void sendStreamStatus();
   void sendBufferStatus();
+  void sendRefTime(sStreamPacket *pkt);
 
   int               m_ClientID;
   const cChannel   *m_Channel;                      /*!> Channel to stream */
@@ -80,6 +80,9 @@ private:
   cVideoInput       m_VideoInput;
   int               m_Priority;
   uint8_t           m_Timeshift;
+  cCondVar          m_Event;
+  cMutex            m_Mutex;
+  bool              m_IsRetune;
 
 protected:
   virtual void Action(void);
@@ -87,7 +90,7 @@ protected:
   void Close();
 
 public:
-  cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout = 0);
+  cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout = 0);
   virtual ~cLiveStreamer();
 
   void Activate(bool On);
@@ -97,6 +100,7 @@ public:
   bool IsAudioOnly() { return m_IsAudioOnly; }
   bool IsMPEGPS() { return m_IsMPEGPS; }
   bool SeekTime(int64_t time, uint32_t &serial);
+  void RetuneChannel(const cChannel *channel);
 };
 
 #endif  // VNSI_RECEIVER_H
diff --git a/videobuffer.c b/videobuffer.c
index 92c38f0..94292bb 100644
--- a/videobuffer.c
+++ b/videobuffer.c
@@ -37,7 +37,7 @@ class cVideoBufferSimple : public cVideoBuffer
 friend class cVideoBuffer;
 public:
   virtual void Put(uint8_t *buf, unsigned int size);
-  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
 
 protected:
   cVideoBufferSimple();
@@ -64,7 +64,7 @@ void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size)
   m_Buffer->Put(buf, size);
 }
 
-int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
 {
   int  readBytes;
   if (m_BytesConsumed)
@@ -96,6 +96,8 @@ int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
   }
 
   m_BytesConsumed += TS_SIZE;
+  endTime = 0;
+  wrapTime = 0;
   return TS_SIZE;
 }
 
@@ -193,7 +195,7 @@ class cVideoBufferRAM : public cVideoBufferTimeshift
 friend class cVideoBuffer;
 public:
   virtual void Put(uint8_t *buf, unsigned int size);
-  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
   virtual void SetPos(off_t pos);
 
 protected:
@@ -241,7 +243,6 @@ void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
 {
   if (Available() + MARGIN >= m_BufferSize)
   {
-    ERRORLOG("------------- skipping data");
     return;
   }
 
@@ -263,11 +264,16 @@ void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
   if (!m_BufferFull)
   {
     if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+    {
       m_BufferFull = true;
+      time(&m_bufferWrapTime);
+    }
   }
+
+  time(&m_bufferEndTime);
 }
 
-int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
 {
   // move read pointer
   if (m_BytesConsumed)
@@ -276,6 +282,9 @@ int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
     m_ReadPtr += m_BytesConsumed;
     if (m_ReadPtr >= m_BufferSize)
       m_ReadPtr -= m_BufferSize;
+
+    endTime = m_bufferEndTime;
+    wrapTime = m_bufferWrapTime;
   }
   m_BytesConsumed = 0;
 
@@ -323,7 +332,7 @@ friend class cVideoBuffer;
 public:
   virtual off_t GetPosMax();
   virtual void Put(uint8_t *buf, unsigned int size);
-  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
   virtual void SetPos(off_t pos);
 
 protected:
@@ -442,7 +451,6 @@ void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
 {
   if (Available() + MARGIN >= m_BufferSize)
   {
-    ERRORLOG("------------- skipping data");
     return;
   }
 
@@ -491,8 +499,13 @@ void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
   if (!m_BufferFull)
   {
     if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+    {
       m_BufferFull = true;
+      time(&m_bufferWrapTime);
+    }
   }
+
+  time(&m_bufferEndTime);
 }
 
 int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
@@ -509,7 +522,7 @@ int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
   }
 }
 
-int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
 {
   // move read pointer
   if (m_BytesConsumed)
@@ -519,6 +532,9 @@ int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
     if (m_ReadPtr >= m_BufferSize)
       m_ReadPtr -= m_BufferSize;
     m_ReadCachePtr += m_BytesConsumed;
+
+    endTime = m_bufferEndTime;
+    wrapTime = m_bufferWrapTime;
   }
   m_BytesConsumed = 0;
 
@@ -607,7 +623,8 @@ friend class cVideoBuffer;
 public:
   virtual off_t GetPosMax();
   virtual void Put(uint8_t *buf, unsigned int size);
-  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
+  virtual time_t GetRefTime();
 
 protected:
   cVideoBufferRecording(cRecording *rec);
@@ -663,9 +680,15 @@ bool cVideoBufferRecording::Init()
   m_ReadCacheSize = 0;
   m_InputAttached = false;
   m_ScanTimer.Set(0);
+
   return true;
 }
 
+time_t cVideoBufferRecording::GetRefTime()
+{
+  return m_Recording->Start();
+}
+
 off_t cVideoBufferRecording::Available()
 {
   if (m_ScanTimer.TimedOut())
@@ -677,7 +700,7 @@ off_t cVideoBufferRecording::Available()
   return cVideoBufferTimeshift::Available();
 }
 
-int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
 {
   // move read pointer
   if (m_BytesConsumed)
@@ -739,6 +762,8 @@ int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
   }
 
   m_BytesConsumed += TS_SIZE;
+  time(&endTime);
+  wrapTime = 0;
   return TS_SIZE;
 }
 
@@ -829,6 +854,8 @@ cVideoBuffer::cVideoBuffer()
 {
   m_CheckEof = false;
   m_InputAttached = true;
+  m_bufferEndTime = 0;
+  m_bufferWrapTime = 0;
 }
 
 cVideoBuffer::~cVideoBuffer()
@@ -897,9 +924,9 @@ cVideoBuffer* cVideoBuffer::Create(cRecording *rec)
     return buffer;
 }
 
-int cVideoBuffer::Read(uint8_t **buf, unsigned int size)
+int cVideoBuffer::Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
 {
-  int count = ReadBlock(buf, size);
+  int count = ReadBlock(buf, size, endTime, wrapTime);
 
   // check for end of file
   if (!m_InputAttached && count != TS_SIZE)
@@ -925,3 +952,10 @@ void cVideoBuffer::AttachInput(bool attach)
 {
   m_InputAttached = attach;
 }
+
+time_t cVideoBuffer::GetRefTime()
+{
+  time_t t;
+  time(&t);
+  return t;
+}
diff --git a/videobuffer.h b/videobuffer.h
index 8ea74fc..c61bd3c 100644
--- a/videobuffer.h
+++ b/videobuffer.h
@@ -34,7 +34,7 @@ public:
   static cVideoBuffer* Create(cString filename);
   static cVideoBuffer* Create(cRecording *rec);
   virtual void Put(uint8_t *buf, unsigned int size) = 0;
-  virtual int ReadBlock(uint8_t **buf, unsigned int size) = 0;
+  virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) = 0;
   virtual off_t GetPosMin() { return 0; };
   virtual off_t GetPosMax() { return 0; };
   virtual off_t GetPosCur() { return 0; };
@@ -42,11 +42,14 @@ public:
   virtual void SetPos(off_t pos) {};
   virtual void SetCache(bool on) {};
   virtual bool HasBuffer() { return false; };
-  int Read(uint8_t **buf, unsigned int size);
+  virtual time_t GetRefTime();
+  int Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
   void AttachInput(bool attach);
 protected:
   cVideoBuffer();
   cTimeMs m_Timer;
   bool m_CheckEof;
   bool m_InputAttached;
+  time_t m_bufferEndTime;
+  time_t m_bufferWrapTime;
 };
diff --git a/videoinput.c b/videoinput.c
index 45769ae..d7e45eb 100644
--- a/videoinput.c
+++ b/videoinput.c
@@ -67,8 +67,11 @@ void cLiveReceiver::Receive(uchar *Data, int Length)
 
 inline void cLiveReceiver::Activate(bool On)
 {
-  m_VideoInput->Attach(On);
   DEBUGLOG("activate live receiver: %d", On);
+  if (!On && !m_VideoInput->m_PmtChange)
+  {
+    m_VideoInput->Retune();
+  }
 }
 
 // --- cLivePatFilter ----------------------------------------------------
@@ -387,7 +390,8 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
 
 // ----------------------------------------------------------------------------
 
-cVideoInput::cVideoInput()
+cVideoInput::cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune) :
+    m_Event(condVar), m_Mutex(mutex), m_IsRetune(retune)
 {
   m_Device = NULL;;
   m_PatFilter = NULL;
@@ -408,7 +412,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
   m_VideoBuffer = videoBuffer;
   m_Channel = channel;
   m_Priority = priority;
-  m_Device = cDevice::GetDevice(m_Channel, m_Priority, true);
+  m_Device = cDevice::GetDevice(m_Channel, m_Priority, false);
 
   if (m_Device != NULL)
   {
@@ -423,6 +427,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
       m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority);
       m_Device->AttachReceiver(m_Receiver0);
       m_Device->AttachFilter(m_PatFilter);
+      m_VideoBuffer->AttachInput(true);
       Start();
       return true;
     }
@@ -432,6 +437,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
 
 void cVideoInput::Close()
 {
+  INFOLOG("close video input ...");
   Cancel(5);
 
   if (m_Device)
@@ -484,6 +490,21 @@ void cVideoInput::Close()
       DELETENULL(m_PatFilter);
     }
   }
+  m_Channel = NULL;
+  m_Device = NULL;
+  if (m_VideoBuffer)
+  {
+    m_VideoBuffer->AttachInput(false);
+    m_VideoBuffer = NULL;
+  }
+}
+
+bool cVideoInput::IsOpen()
+{
+  if (m_Channel)
+    return true;
+  else
+    return false;
 }
 
 cChannel *cVideoInput::PmtChannel()
@@ -496,11 +517,11 @@ void cVideoInput::PmtChange(int pidChange)
   if (pidChange)
   {
     INFOLOG("Video Input - new pmt, attaching receiver");
+    m_PmtChange = true;
     m_Device->Detach(m_Receiver);
     m_Receiver->SetPids(NULL);
     m_Receiver->SetPids(&m_Receiver->m_PmtChannel);
     m_Receiver->AddPid(m_Receiver->m_PmtChannel.Tpid());
-    m_PmtChange = true;
     m_Device->AttachReceiver(m_Receiver);
     m_SeenPmt = true;
   }
@@ -521,9 +542,12 @@ inline void cVideoInput::Receive(uchar *data, int length)
   m_VideoBuffer->Put(data, length);
 }
 
-inline void cVideoInput::Attach(bool on)
+void cVideoInput::Retune()
 {
-  m_VideoBuffer->AttachInput(on);
+  INFOLOG("call retune ...");
+  cMutexLock lock(&m_Mutex);
+  m_IsRetune = true;
+  m_Event.Broadcast();
 }
 
 void cVideoInput::Action()
diff --git a/videoinput.h b/videoinput.h
index 19a1d7d..f8e175d 100644
--- a/videoinput.h
+++ b/videoinput.h
@@ -33,17 +33,18 @@ class cVideoInput : public cThread
 friend class cLivePatFilter;
 friend class cLiveReceiver;
 public:
-  cVideoInput();
+  cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune);
   virtual ~cVideoInput();
   bool Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer);
   void Close();
+  bool IsOpen();
 
 protected:
   virtual void Action(void);
   void PmtChange(int pidChange);
   cChannel *PmtChannel();
   void Receive(uchar *data, int length);
-  void Attach(bool on);
+  void Retune();
   cDevice          *m_Device;
   cLivePatFilter   *m_PatFilter;
   cLiveReceiver    *m_Receiver;
@@ -53,4 +54,7 @@ protected:
   int               m_Priority;
   bool              m_PmtChange;
   bool              m_SeenPmt;
+  cCondVar          &m_Event;
+  cMutex            &m_Mutex;
+  bool              &m_IsRetune;
 };
diff --git a/vnsi.c b/vnsi.c
index 6ae4cf5..18c8616 100644
--- a/vnsi.c
+++ b/vnsi.c
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -35,32 +34,56 @@ cPluginVNSIServer::cPluginVNSIServer(void)
 {
   Server = NULL;
   VNSIServer = NULL;
+  probe = new cDvbVsniDeviceProbe();
 }
 
 cPluginVNSIServer::~cPluginVNSIServer()
 {
   // Clean up after yourself!
+  delete probe;
 }
 
 const char *cPluginVNSIServer::CommandLineHelp(void)
 {
-    return "  -t n, --timeout=n      stream data timeout in seconds (default: 10)\n";
+    return "  -t n, --timeout=n      stream data timeout in seconds (default: 10)\n"
+           "  -d  , --device         act as the primary device\n"
+           "  -s n, --test=n         TS stream test file to simulate as channel\n"
+           "  -p n, --port=n         tcp port to listen on\n";
 }
 
 bool cPluginVNSIServer::ProcessArgs(int argc, char *argv[])
 {
   // Implement command line argument processing here if applicable.
   static struct option long_options[] = {
+       { "port",     required_argument, NULL, 'p' },
        { "timeout",  required_argument, NULL, 't' },
+       { "device",   no_argument,       NULL, 'd' },
+       { "test",     required_argument, NULL, 'T' },
        { NULL,       no_argument,       NULL,  0  }
      };
 
   int c;
 
-  while ((c = getopt_long(argc, argv, "t:", long_options, NULL)) != -1) {
+  while ((c = getopt_long(argc, argv, "t:dT:p:", long_options, NULL)) != -1) {
         switch (c) {
+          case 'p': if(optarg != NULL) VNSIServerConfig.listen_port = atoi(optarg);
+                    break;
           case 't': if(optarg != NULL) VNSIServerConfig.stream_timeout = atoi(optarg);
                     break;
+          case 'd': VNSIServerConfig.device = true;
+                    break;
+          case 'T': if(optarg != NULL) {
+                    VNSIServerConfig.testStreamFile = optarg;
+
+                    struct stat file_stat;
+                    if (stat(VNSIServerConfig.testStreamFile, &file_stat) == 0) {
+                      VNSIServerConfig.testStreamActive = true;
+                      printf("vnsiserver: requested test stream file '%s' played now on all channels\n", *VNSIServerConfig.testStreamFile);
+                      }
+                    else
+                      printf("vnsiserver: requested test stream file '%s' not present, started without\n", *VNSIServerConfig.testStreamFile);
+                      }
+                    break;
           default:  return false;
           }
         }
@@ -78,6 +101,7 @@ bool cPluginVNSIServer::Initialize(void)
 
 bool cPluginVNSIServer::Start(void)
 {
+  INFOLOG("Starting vnsi server at port=%d\n", VNSIServerConfig.listen_port);
   Server = new cVNSIServer(VNSIServerConfig.listen_port);
 
   return true;
@@ -131,6 +155,10 @@ bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
     TimeshiftBufferFileSize = atoi(Value);
   else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR))
     strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir));
+  else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING))
+    PlayRecording = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_AVOIDEPGSCAN))
+    AvoidEPGScan = atoi(Value);
   else
     return false;
   return true;
@@ -166,4 +194,40 @@ void cPluginVNSIServer::StoreSetup(const char *Name, int Value)
   }
 }
 
+bool cDvbVsniDeviceProbe::Probe(int Adapter, int Frontend)
+{
+  if (VNSIServerConfig.device)
+  {
+    new cDvbVnsiDevice(Adapter, Frontend);
+    return true;
+  }
+  else
+    return false;
+}
+
+cDvbVnsiDevice::cDvbVnsiDevice(int Adapter, int Frontend) :cDvbDevice(Adapter, Frontend)
+{
+
+}
+
+cDvbVnsiDevice::~cDvbVnsiDevice()
+{
+
+}
+
+bool cDvbVnsiDevice::HasDecoder(void) const
+{
+  return true;
+}
+
+int cDvbVnsiDevice::PlayVideo(const uchar *Data, int Length)
+{
+  return Length;
+}
+
+int cDvbVnsiDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
+{
+  return Length;
+}
+
 VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
diff --git a/vnsi.h b/vnsi.h
index eb7ed79..49a3740 100644
--- a/vnsi.h
+++ b/vnsi.h
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -27,7 +26,7 @@
 #include <vdr/plugin.h>
 #include "vnsiserver.h"
 
-static const char *VERSION        = "0.9.2";
+static const char *VERSION        = "1.3.0";
 static const char *DESCRIPTION    = "VDR-Network-Streaming-Interface (VNSI) Server";
 
 extern int PmtTimeout;
@@ -35,11 +34,16 @@ extern int TimeshiftMode;
 extern int TimeshiftBufferSize;
 extern int TimeshiftBufferFileSize;
 extern char TimeshiftBufferDir[PATH_MAX];
+extern int PlayRecording;
+extern int AvoidEPGScan;
+
+class cDvbVsniDeviceProbe;
 
 class cPluginVNSIServer : public cPlugin {
 private:
   cVNSIServer *Server;
   static cPluginVNSIServer *VNSIServer;
+  cDvbVsniDeviceProbe *probe;
 
 public:
   cPluginVNSIServer(void);
@@ -66,3 +70,18 @@ public:
   static void StoreSetup(const char *Name, int Value);
 };
 
+class cDvbVsniDeviceProbe : public cDvbDeviceProbe
+{
+public:
+  virtual bool Probe(int Adapter, int Frontend);
+};
+
+class cDvbVnsiDevice : public cDvbDevice
+{
+public:
+  cDvbVnsiDevice(int Adapter, int Frontend);
+  virtual ~cDvbVnsiDevice();
+  virtual bool HasDecoder(void) const;
+  int PlayVideo(const uchar *Data, int Length);
+  int PlayAudio(const uchar *Data, int Length, uchar Id);
+};
diff --git a/vnsiclient.c b/vnsiclient.c
index f88d00b..82b7926 100644
--- a/vnsiclient.c
+++ b/vnsiclient.c
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -47,24 +46,11 @@
 #include "requestpacket.h"
 #include "responsepacket.h"
 #include "hash.h"
+#include "channelfilter.h"
 #include "wirbelscanservice.h" /// copied from modified wirbelscan plugin
                                /// must be hold up to date with wirbelscan
 
 
-static bool IsRadio(const cChannel* channel)
-{
-  bool isRadio = false;
-
-  // assume channels without VPID & APID are video channels
-  if (channel->Vpid() == 0 && channel->Apid(0) == 0)
-    isRadio = false;
-  // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels)
-  else if (channel->Vpid() == 0 || channel->Vpid() == 1)
-    isRadio = true;
-
-  return isRadio;
-}
-
 cMutex cVNSIClient::m_timerLock;
 
 cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
@@ -80,6 +66,7 @@ cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
   m_processSCAN_Response    = NULL;
   m_processSCAN_Socket      = NULL;
   m_Osd                     = NULL;
+  m_bSupportRDS             = false;
 
   m_socket.SetHandle(fd);
 
@@ -179,7 +166,7 @@ void cVNSIClient::Action(void)
 
 bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
 {
-  m_Streamer    = new cLiveStreamer(m_Id, timeshift, timeout);
+  m_Streamer    = new cLiveStreamer(m_Id, m_bSupportRDS, timeshift, timeout);
   m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp);
   return m_isStreaming;
 }
@@ -218,7 +205,7 @@ void cVNSIClient::TimerChange()
   }
 }
 
-void cVNSIClient::ChannelChange()
+void cVNSIClient::ChannelsChange()
 {
   cMutexLock lock(&m_msgLock);
 
@@ -268,22 +255,37 @@ void cVNSIClient::EpgChange()
   if (!schedules)
     return;
 
-  std::map<int, time_t>::iterator it;
+  std::map<int, sEpgUpdate>::iterator it;
   for (const cSchedule *schedule = schedules->First(); schedule; schedule = schedules->Next(schedule))
   {
     cEvent *lastEvent =  schedule->Events()->Last();
     if (!lastEvent)
       continue;
 
+    Channels.Lock(false);
+    const cChannel *channel = Channels.GetByChannelID(schedule->ChannelID());
+    Channels.Unlock();
+
+    if (!channel)
+      continue;
+
+    if (!VNSIChannelFilter.PassFilter(*channel))
+      continue;
+
     uint32_t channelId = CreateStringHash(schedule->ChannelID().ToString());
     it = m_epgUpdate.find(channelId);
-    if (it == m_epgUpdate.end())
+    if (it != m_epgUpdate.end() && it->second.lastEvent >= lastEvent->StartTime())
     {
       continue;
     }
 
-    if (it->second >= lastEvent->StartTime())
+    if (it->second.attempts > 3)
+    {
       continue;
+    }
+    it->second.attempts++;
+
+    INFOLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId);
 
     cResponsePacket *resp = new cResponsePacket();
     if (!resp->initStatus(VNSI_STATUS_EPGCHANGE))
@@ -295,10 +297,6 @@ void cVNSIClient::EpgChange()
     resp->finalise();
     m_socket.write(resp->getPtr(), resp->getLen());
     delete resp;
-
-    const cChannel *channel = FindChannelByUID(channelId);
-    if (channel)
-      INFOLOG("Trigger EPG update for channel %s", channel->Name());
   }
 }
 
@@ -376,6 +374,15 @@ void cVNSIClient::OsdStatusMessage(const char *Message)
   }
 }
 
+void cVNSIClient::ChannelChange(const cChannel *Channel)
+{
+  cMutexLock lock(&m_msgLock);
+  if (m_isStreaming && m_Streamer)
+  {
+    m_Streamer->RetuneChannel(Channel);
+  }
+}
+
 bool cVNSIClient::processRequest(cRequestPacket* req)
 {
   cMutexLock lock(&m_msgLock);
@@ -458,6 +465,10 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       result = processRecStream_GetIFrame();
       break;
 
+    case VNSI_RECSTREAM_GETLENGTH:
+      result = processRecStream_GetLength();
+      break;
+
 
     /** OPCODE 60 - 79: VNSI network functions for channel access */
     case VNSI_CHANNELS_GETCOUNT:
@@ -480,6 +491,26 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       result = processCHANNELS_GetGroupMembers();
       break;
 
+    case VNSI_CHANNELS_GETCAIDS:
+      result = processCHANNELS_GetCaids();
+      break;
+
+    case VNSI_CHANNELS_GETWHITELIST:
+      result = processCHANNELS_GetWhitelist();
+      break;
+
+    case VNSI_CHANNELS_GETBLACKLIST:
+      result = processCHANNELS_GetBlacklist();
+      break;
+
+    case VNSI_CHANNELS_SETWHITELIST:
+      result = processCHANNELS_SetWhitelist();
+      break;
+
+    case VNSI_CHANNELS_SETBLACKLIST:
+      result = processCHANNELS_SetBlacklist();
+      break;
+
     /** OPCODE 80 - 99: VNSI network functions for timer access */
     case VNSI_TIMER_GETCOUNT:
       result = processTIMER_GetCount();
@@ -527,6 +558,10 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       result = processRECORDINGS_Delete();
       break;
 
+    case VNSI_RECORDINGS_GETEDL:
+      result = processRECORDINGS_GetEdl();
+      break;
+
 
     /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
     case VNSI_EPG_GETFORCHANNEL:
@@ -567,6 +602,32 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
     case VNSI_OSD_HITKEY:
       result = processOSD_Hitkey();
       break;
+
+
+    /** OPCODE 180 - 189: VNSI network functions for deleted recording access */
+    case VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED:
+      result = processRECORDINGS_DELETED_Supported();
+      break;
+
+    case VNSI_RECORDINGS_DELETED_GETCOUNT:
+      result = processRECORDINGS_DELETED_GetCount();
+      break;
+
+    case VNSI_RECORDINGS_DELETED_GETLIST:
+      result = processRECORDINGS_DELETED_GetList();
+      break;
+
+    case VNSI_RECORDINGS_DELETED_DELETE:
+      result = processRECORDINGS_DELETED_Delete();
+      break;
+
+    case VNSI_RECORDINGS_DELETED_UNDELETE:
+      result = processRECORDINGS_DELETED_Undelete();
+      break;
+
+    case VNSI_RECORDINGS_DELETED_DELETE_ALL:
+      result = processRECORDINGS_DELETED_DeleteAll();
+      break;
   }
 
   delete m_resp;
@@ -603,11 +664,21 @@ bool cVNSIClient::process_Login() /* OPCODE 1 */
   m_resp->add_String(VNSI_SERVER_VERSION);
   m_resp->finalise();
 
-  if (m_protocolVersion != VNSI_PROTOCOLVERSION)
+  if (m_protocolVersion < VNSI_MIN_PROTOCOLVERSION)
     ERRORLOG("Client '%s' have a not allowed protocol version '%u', terminating client", clientName, m_protocolVersion);
   else
     SetLoggedIn(true);
 
+  if (m_protocolVersion < VNSI_RDS_PROTOCOLVERSION)
+  {
+    INFOLOG("RDS not supported on client '%s' and stream type disabled", clientName);
+    m_bSupportRDS = false;
+  }
+  else
+  {
+    m_bSupportRDS = true;
+  }
+
   m_socket.write(m_resp->getPtr(), m_resp->getLen());
 
   delete[] clientName;
@@ -633,6 +704,7 @@ bool cVNSIClient::process_EnableStatusInterface()
   bool enabled = m_req->extract_U8();
 
   SetStatusInterface(enabled);
+  SetPriority(1);
 
   m_resp->add_U32(VNSI_RET_OK);
   m_resp->finalise();
@@ -689,6 +761,11 @@ bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */
     int value = m_req->extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value);
   }
+  else if (!strcasecmp(name, CONFNAME_PLAYRECORDING))
+  {
+    int value = m_req->extract_U32();
+    cPluginVNSIServer::StoreSetup(CONFNAME_PLAYRECORDING, value);
+  }
 
   m_resp->add_U32(VNSI_RET_OK);
   m_resp->finalise();
@@ -919,6 +996,23 @@ bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */
   return true;
 }
 
+bool cVNSIClient::processRecStream_GetLength() /* OPCODE 46 */
+{
+  uint64_t length = 0;
+
+  if (m_RecPlayer)
+  {
+    m_RecPlayer->reScan();
+    length = m_RecPlayer->getLengthBytes();
+  }
+
+  m_resp->add_U64(length);
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  return true;
+}
 
 /** OPCODE 60 - 79: VNSI network functions for channel access */
 
@@ -937,31 +1031,56 @@ bool cVNSIClient::processCHANNELS_ChannelsCount() /* OPCODE 61 */
 
 bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
 {
-  if (m_req->getDataLength() != 4) return false;
+  if (m_req->getDataLength() != 5) return false;
 
   bool radio = m_req->extract_U32();
+  bool filter = m_req->extract_U8();
 
   Channels.Lock(false);
 
+  cString caids;
+  int caid;
+  int caid_idx;
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
   {
-    if (radio != IsRadio(channel))
+    if (radio != cVNSIChannelFilter::IsRadio(channel))
       continue;
 
     // skip invalid channels
     if (channel->Sid() == 0)
       continue;
 
+    // check filter
+    if (filter && !VNSIChannelFilter.PassFilter(*channel))
+      continue;
+
+    uint32_t uuid = CreateChannelUID(channel);
     m_resp->add_U32(channel->Number());
     m_resp->add_String(m_toUTF8.Convert(channel->Name()));
-    m_resp->add_U32(CreateChannelUID(channel));
-    m_resp->add_U32(0); // groupindex unused
-    m_resp->add_U32(channel->Ca());
-#if APIVERSNUM >= 10701
-    m_resp->add_U32(channel->Vtype());
-#else
-    m_resp->add_U32(2);
-#endif
+    m_resp->add_String(m_toUTF8.Convert(channel->Provider()));
+    m_resp->add_U32(uuid);
+    m_resp->add_U32(channel->Ca(0));
+    caid_idx = 0;
+    caids = "caids:";
+    while((caid = channel->Ca(caid_idx)) != 0)
+    {
+      caids = cString::sprintf("%s%d;", (const char*)caids, caid);
+      caid_idx++;
+    }
+    m_resp->add_String((const char*)caids);
+    if (m_protocolVersion >= 6)
+    {
+      m_resp->add_String(CreatePiconRef(channel));
+    }
+
+    // create entry in EPG map on first query
+    std::map<int, sEpgUpdate>::iterator it;
+    it = m_epgUpdate.find(uuid);
+    if (it == m_epgUpdate.end())
+    {
+      m_epgUpdate[uuid].lastEvent = 0;
+      m_epgUpdate[uuid].attempts = 0;
+    }
   }
 
   Channels.Unlock();
@@ -1024,6 +1143,7 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
 {
   char* groupname = m_req->extract_String();
   uint32_t radio = m_req->extract_U8();
+  bool filter = m_req->extract_U8();
   int index = 0;
 
   // unknown group
@@ -1057,7 +1177,11 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
     if(name.empty())
       continue;
 
-    if(IsRadio(channel) != radio)
+    if(cVNSIChannelFilter::IsRadio(channel) != radio)
+      continue;
+
+    // check filter
+    if (filter && !VNSIChannelFilter.PassFilter(*channel))
       continue;
 
     if(name == groupname)
@@ -1075,13 +1199,142 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
   return true;
 }
 
+bool cVNSIClient::processCHANNELS_GetCaids()
+{
+  uint32_t uid = m_req->extract_U32();
+
+  Channels.Lock(false);
+  const cChannel *channel = NULL;
+  channel = FindChannelByUID(uid);
+  Channels.Unlock();
+
+  if (channel != NULL)
+  {
+    int caid;
+    int idx = 0;
+    while((caid = channel->Ca(idx)) != 0)
+    {
+      m_resp->add_U32(caid);
+      idx++;
+    }
+  }
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  return true;
+}
+
+bool cVNSIClient::processCHANNELS_GetWhitelist()
+{
+  bool radio = m_req->extract_U8();
+  std::vector<cVNSIProvider> *providers;
+
+  if(radio)
+    providers = &VNSIChannelFilter.m_providersRadio;
+  else
+    providers = &VNSIChannelFilter.m_providersVideo;
+
+  VNSIChannelFilter.m_Mutex.Lock();
+  for(unsigned int i=0; i<providers->size(); i++)
+  {
+    m_resp->add_String((*providers)[i].m_name.c_str());
+    m_resp->add_U32((*providers)[i].m_caid);
+  }
+  VNSIChannelFilter.m_Mutex.Unlock();
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processCHANNELS_GetBlacklist()
+{
+  bool radio = m_req->extract_U8();
+  std::vector<int> *channels;
+
+  if(radio)
+    channels = &VNSIChannelFilter.m_channelsRadio;
+  else
+    channels = &VNSIChannelFilter.m_channelsVideo;
+
+  VNSIChannelFilter.m_Mutex.Lock();
+  for(unsigned int i=0; i<channels->size(); i++)
+  {
+    m_resp->add_U32((*channels)[i]);
+  }
+  VNSIChannelFilter.m_Mutex.Unlock();
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processCHANNELS_SetWhitelist()
+{
+  bool radio = m_req->extract_U8();
+  cVNSIProvider provider;
+  std::vector<cVNSIProvider> *providers;
+
+  if(radio)
+    providers = &VNSIChannelFilter.m_providersRadio;
+  else
+    providers = &VNSIChannelFilter.m_providersVideo;
+
+  VNSIChannelFilter.m_Mutex.Lock();
+  providers->clear();
+
+  while(!m_req->end())
+  {
+    char *str = m_req->extract_String();
+    provider.m_name = str;
+    provider.m_caid = m_req->extract_U32();
+    delete [] str;
+    providers->push_back(provider);
+  }
+  VNSIChannelFilter.StoreWhitelist(radio);
+  VNSIChannelFilter.m_Mutex.Unlock();
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processCHANNELS_SetBlacklist()
+{
+  bool radio = m_req->extract_U8();
+  cVNSIProvider provider;
+  std::vector<int> *channels;
+
+  if(radio)
+    channels = &VNSIChannelFilter.m_channelsRadio;
+  else
+    channels = &VNSIChannelFilter.m_channelsVideo;
+
+  VNSIChannelFilter.m_Mutex.Lock();
+  channels->clear();
+
+  int id;
+  while(!m_req->end())
+  {
+    id = m_req->extract_U32();
+    channels->push_back(id);
+  }
+  VNSIChannelFilter.StoreBlacklist(radio);
+  VNSIChannelFilter.m_Mutex.Unlock();
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
 void cVNSIClient::CreateChannelGroups(bool automatic)
 {
   std::string groupname;
 
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
   {
-    bool isRadio = IsRadio(channel);
+    bool isRadio = cVNSIChannelFilter::IsRadio(channel);
 
     if(automatic && !channel->GroupSep())
       groupname = channel->Provider();
@@ -1533,7 +1786,7 @@ bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
     m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
 
     // filename / uid of recording
-    uint32_t uid = cRecordingsCache::GetInstance().Register(recording);
+    uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false);
     m_resp->add_U32(uid);
 
     free(fullname);
@@ -1631,6 +1884,37 @@ bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */
   return true;
 }
 
+bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */
+{
+  cString recName;
+  cRecording* recording = NULL;
+
+  uint32_t uid = m_req->extract_U32();
+  recording = cRecordingsCache::GetInstance().Lookup(uid);
+
+  if (recording)
+  {
+    cMarks marks;
+    if(marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()))
+    {
+#if VDRVERSNUM >= 10732
+      cMark* mark = NULL;
+      double fps = recording->FramesPerSecond();
+      while((mark = marks.GetNextBegin(mark)) != NULL)
+      {
+        m_resp->add_U64(mark->Position() *1000 / fps);
+        m_resp->add_U64(mark->Position() *1000 / fps);
+        m_resp->add_S32(2);
+      }
+#endif
+    }
+  }
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  return true;
+}
+
 
 /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
 
@@ -1666,7 +1950,6 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
 
   cSchedulesLock MutexLock;
   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
-  m_epgUpdate[channelUID] = 0;
   if (!Schedules)
   {
     m_resp->add_U32(0);
@@ -1761,7 +2044,8 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
   cEvent *lastEvent =  Schedule->Events()->Last();
   if (lastEvent)
   {
-    m_epgUpdate[channelUID] = lastEvent->StartTime();
+    m_epgUpdate[channelUID].lastEvent = lastEvent->StartTime();
+    m_epgUpdate[channelUID].attempts = 0;
   }
   DEBUGLOG("written schedules packet");
 
@@ -2056,3 +2340,378 @@ bool cVNSIClient::processOSD_Hitkey() /* OPCODE 162 */
   }
   return true;
 }
+
+/** OPCODE 180 - 189: VNSI network functions for deleted recording access */
+
+bool cVNSIClient::processRECORDINGS_DELETED_Supported() /* OPCODE 180 */
+{
+  m_resp->add_U32(VNSI_RET_OK);
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_GetCount() /* OPCODE 181 */
+{
+  DeletedRecordings.Load();
+  m_resp->add_U32(DeletedRecordings.Count());
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
+{
+  cMutexLock lock(&m_timerLock);
+  cThreadLock RecordingsLock(&Recordings);
+
+  for (cRecording *recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+  {
+#if APIVERSNUM >= 10705
+    const cEvent *event = recording->Info()->GetEvent();
+#else
+    const cEvent *event = NULL;
+#endif
+
+    time_t recordingStart    = 0;
+    int    recordingDuration = 0;
+    if (event)
+    {
+      recordingStart    = event->StartTime();
+      recordingDuration = event->Duration();
+    }
+    else
+    {
+      cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+      if (rc)
+      {
+        recordingStart    = rc->Timer()->StartTime();
+        recordingDuration = rc->Timer()->StopTime() - recordingStart;
+      }
+      else
+      {
+#if APIVERSNUM >= 10727
+        recordingStart = recording->Start();
+#else
+        recordingStart = recording->start;
+#endif
+      }
+    }
+    DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration);
+
+    // recording_time
+    m_resp->add_U32(recordingStart);
+
+    // duration
+    m_resp->add_U32(recordingDuration);
+
+    // priority
+#if APIVERSNUM >= 10727
+    m_resp->add_U32(recording->Priority());
+#else
+    m_resp->add_U32(recording->priority);
+#endif
+
+    // lifetime
+#if APIVERSNUM >= 10727
+    m_resp->add_U32(recording->Lifetime());
+#else
+    m_resp->add_U32(recording->lifetime);
+#endif
+
+    // channel_name
+    m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+
+    char* fullname = strdup(recording->Name());
+    char* recname = strrchr(fullname, FOLDERDELIMCHAR);
+    char* directory = NULL;
+
+    if(recname == NULL) {
+      recname = fullname;
+    }
+    else {
+      *recname = 0;
+      recname++;
+      directory = fullname;
+    }
+
+    // title
+    m_resp->add_String(m_toUTF8.Convert(recname));
+
+    // subtitle
+    if (!isempty(recording->Info()->ShortText()))
+      m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+    else
+      m_resp->add_String("");
+
+    // description
+    if (!isempty(recording->Info()->Description()))
+      m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+    else
+      m_resp->add_String("");
+
+    // directory
+    if(directory != NULL) {
+      char* p = directory;
+      while(*p != 0) {
+        if(*p == FOLDERDELIMCHAR) *p = '/';
+        if(*p == '_') *p = ' ';
+        p++;
+      }
+      while(*directory == '/') directory++;
+    }
+
+    m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
+
+    // filename / uid of recording
+    uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false);
+    m_resp->add_U32(uid);
+
+    free(fullname);
+  }
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */
+{
+  cString recName;
+  cRecording* recording = NULL;
+
+#if VDRVERSNUM >= 20102
+  cLockFile LockFile(cVideoDirectory::Name());
+#else
+  cLockFile LockFile(VideoDirectory);
+#endif
+  if (LockFile.Lock())
+  {
+    uint32_t uid = m_req->extract_U32();
+
+    cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+    for (recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+    {
+      if (uid == CreateStringHash(recording->FileName()))
+      {
+#if VDRVERSNUM >= 20102
+        if (!cVideoDirectory::RemoveVideoFile(recording->FileName()))
+#else
+        if (!RemoveVideoFile(recording->FileName()))
+#endif
+        {
+          ERRORLOG("Error while remove deleted recording (%s)", recording->FileName());
+          m_resp->add_U32(VNSI_RET_ERROR);
+        }
+        else
+        {
+          DeletedRecordings.Del(recording);
+          DeletedRecordings.Update();
+          INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
+          m_resp->add_U32(VNSI_RET_OK);
+        }
+        break;
+      }
+    }
+  }
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  return true;
+}
+
+bool cVNSIClient::Undelete(cRecording* recording)
+{
+  DEBUGLOG("undelete recording: %s", recording->Name());
+
+  char *NewName = strdup(recording->FileName());
+  char *ext = strrchr(NewName, '.');
+  if (ext && strcmp(ext, ".del") == 0)
+  {
+    strncpy(ext, ".rec", strlen(ext));
+    if (!access(NewName, F_OK))
+    {
+      ERRORLOG("Recording with the same name exists (%s)", NewName);
+      OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Recording with the same name exists"), NewName));
+    }
+    else
+    {
+      if (access(recording->FileName(), F_OK) == 0)
+      {
+#if VDRVERSNUM >= 20102
+        if (!cVideoDirectory::RenameVideoFile(recording->FileName(), NewName))
+#else
+        if (!RenameVideoFile(recording->FileName(), NewName))
+#endif
+        {
+          ERRORLOG("Error while rename deleted recording (%s) to (%s)", recording->FileName(), NewName);
+        }
+
+        cIndexFile *index = new cIndexFile(NewName, false, recording->IsPesRecording());
+        int LastFrame = index->Last() - 1;
+        if (LastFrame > 0)
+        {
+          uint16_t FileNumber = 0;
+          off_t FileOffset = 0;
+          index->Get(LastFrame, &FileNumber, &FileOffset);
+          delete index;
+          if (FileNumber == 0)
+          {
+            ERRORLOG("while read last filenumber (%s)", NewName);
+            OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while read last filenumber"), NewName));
+          }
+          else
+          {
+            for (int i = 1; i <= FileNumber; i++)
+            {
+              cString temp = cString::sprintf(recording->IsPesRecording() ? "%s/%03d.vdr" : "%s/%05d.ts", (const char *)NewName, i);
+              if (access(*temp, R_OK) != 0)
+              {
+                i = FileNumber;
+                OsdStatusMessage(*cString::sprintf("%s %03d (%s)", tr("Error while accessing vdrfile"), i, NewName));
+              }
+            }
+          }
+        }
+        else
+        {
+          delete index;
+          ERRORLOG("accessing indexfile (%s)", NewName);
+          OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while accessing indexfile"), NewName));
+        }
+
+        DeletedRecordings.Del(recording);
+        Recordings.Update();
+        DeletedRecordings.Update();
+      }
+      else
+      {
+        ERRORLOG("deleted recording '%s' vanished", recording->FileName());
+        OsdStatusMessage(*cString::sprintf("%s \"%s\"", tr("Deleted recording vanished"), recording->FileName()));
+      }
+    }
+  }
+  free(NewName);
+  return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */
+{
+  int ret = VNSI_RET_DATAUNKNOWN;
+
+#if VDRVERSNUM >= 20102
+  cLockFile LockFile(cVideoDirectory::Name());
+#else
+  cLockFile LockFile(VideoDirectory);
+#endif
+  if (LockFile.Lock())
+  {
+    uint32_t uid = m_req->extract_U32();
+
+    cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+    for (cRecording* recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+    {
+      if (uid == CreateStringHash(recording->FileName()))
+      {
+        if (Undelete(recording))
+        {
+          INFOLOG("Recording \"%s\" undeleted", recording->FileName());
+          ret = VNSI_RET_OK;
+        }
+        else
+          ret = VNSI_RET_ERROR;
+        break;
+      }
+    }
+  }
+
+  m_resp->add_U32(ret);
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */
+{
+  int ret = VNSI_RET_OK;
+
+#if VDRVERSNUM >= 20102
+  cLockFile LockFile(cVideoDirectory::Name());
+#else
+  cLockFile LockFile(VideoDirectory);
+#endif
+
+  if (LockFile.Lock())
+  {
+    cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+    for (cRecording *recording = DeletedRecordings.First(); recording; )
+    {
+      cRecording *next = DeletedRecordings.Next(recording);
+#if VDRVERSNUM >= 20102
+      if (!cVideoDirectory::RemoveVideoFile(recording->FileName()))
+#else
+      if (!RemoveVideoFile(recording->FileName()))
+#endif
+      {
+        ERRORLOG("Error while remove deleted recording (%s)", recording->FileName());
+        ret = VNSI_RET_ERROR;
+        break;
+      }
+      else
+        INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
+      recording = next;
+    }
+    DeletedRecordings.Clear();
+    DeletedRecordings.Update();
+  }
+
+  m_resp->add_U32(ret);
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  return true;
+}
+
+// this method is taken from XVDR
+cString cVNSIClient::CreatePiconRef(cChannel* channel)
+{
+  int hash = 0;
+
+  if(cSource::IsSat(channel->Source()))
+  {
+    hash = channel->Source() & cSource::st_Pos;
+
+#if VDRVERSNUM >= 20101
+    hash = -hash;
+#endif
+
+    if(hash > 0x00007FFF)
+      hash |= 0xFFFF0000;
+
+    if(hash < 0)
+      hash = -hash;
+    else
+      hash = 1800 + hash;
+
+    hash = hash << 16;
+  }
+  else if(cSource::IsCable(channel->Source()))
+    hash = 0xFFFF0000;
+  else if(cSource::IsTerr(channel->Source()))
+    hash = 0xEEEE0000;
+  else if(cSource::IsAtsc(channel->Source()))
+    hash = 0xDDDD0000;
+
+  cString serviceref = cString::sprintf("1_0_%i_%X_%X_%X_%X_0_0_0",
+                                cVNSIChannelFilter::IsRadio(channel) ? 2 : (channel->Vtype() == 27) ? 19 : 1,
+                                channel->Sid(),
+                                channel->Tid(),
+                                channel->Nid(),
+                                hash);
+
+  return serviceref;
+}
diff --git a/vnsiclient.h b/vnsiclient.h
index 948940a..efbd7de 100644
--- a/vnsiclient.h
+++ b/vnsiclient.h
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -57,6 +56,7 @@ private:
   bool             m_StatusInterfaceEnabled;
   cLiveStreamer   *m_Streamer;
   bool             m_isStreaming;
+  bool             m_bSupportRDS;
   cString          m_ClientAddress;
   cRecPlayer      *m_RecPlayer;
   cRequestPacket  *m_req;
@@ -66,7 +66,12 @@ private:
   cMutex           m_msgLock;
   static cMutex    m_timerLock;
   cVnsiOsdProvider *m_Osd;
-  std::map<int, time_t> m_epgUpdate;
+  typedef struct
+  {
+    int attempts;
+    time_t lastEvent;
+  } sEpgUpdate;
+  std::map<int, sEpgUpdate> m_epgUpdate;
 
 protected:
 
@@ -77,13 +82,14 @@ protected:
   virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
   virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
   virtual void OsdStatusMessage(const char *Message);
+  virtual void ChannelChange(const cChannel *Channel);
 
 public:
 
   cVNSIClient(int fd, unsigned int id, const char *ClientAdr);
   virtual ~cVNSIClient();
 
-  void ChannelChange();
+  void ChannelsChange();
   void RecordingsChange();
   void TimerChange();
   void EpgChange();
@@ -124,12 +130,18 @@ private:
   bool processRecStream_PositionFromFrameNumber();
   bool processRecStream_FrameNumberFromPosition();
   bool processRecStream_GetIFrame();
+  bool processRecStream_GetLength();
 
   bool processCHANNELS_GroupsCount();
   bool processCHANNELS_ChannelsCount();
   bool processCHANNELS_GroupList();
   bool processCHANNELS_GetChannels();
   bool processCHANNELS_GetGroupMembers();
+  bool processCHANNELS_GetCaids();
+  bool processCHANNELS_GetWhitelist();
+  bool processCHANNELS_GetBlacklist();
+  bool processCHANNELS_SetWhitelist();
+  bool processCHANNELS_SetBlacklist();
 
   void CreateChannelGroups(bool automatic);
 
@@ -147,6 +159,13 @@ private:
   bool processRECORDINGS_Rename();
   bool processRECORDINGS_Delete();
   bool processRECORDINGS_Move();
+  bool processRECORDINGS_GetEdl();
+  bool processRECORDINGS_DELETED_Supported();
+  bool processRECORDINGS_DELETED_GetCount();
+  bool processRECORDINGS_DELETED_GetList();
+  bool processRECORDINGS_DELETED_Delete();
+  bool processRECORDINGS_DELETED_Undelete();
+  bool processRECORDINGS_DELETED_DeleteAll();
 
   bool processEPG_GetForChannel();
 
@@ -156,6 +175,8 @@ private:
   bool processSCAN_Start();
   bool processSCAN_Stop();
 
+  bool Undelete(cRecording* recording);
+
   /** Static callback functions to interact with wirbelscan plugin over
       the plugin service interface */
   static void processSCAN_AddCountry(int index, const char *isoName, const char *longName);
@@ -173,6 +194,8 @@ private:
   bool processOSD_Connect();
   bool processOSD_Disconnect();
   bool processOSD_Hitkey();
+
+  cString CreatePiconRef(cChannel* channel);
 };
 
 #endif // VNSI_CLIENT_H
diff --git a/vnsicommand.h b/vnsicommand.h
index 17d3d08..9889d82 100644
--- a/vnsicommand.h
+++ b/vnsicommand.h
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -27,7 +26,13 @@
 #define VNSI_COMMAND_H
 
 /** Current VNSI Protocol Version number */
-#define VNSI_PROTOCOLVERSION 4
+#define VNSI_PROTOCOLVERSION 8
+
+/** Start of RDS support protocol Version */
+#define VNSI_RDS_PROTOCOLVERSION 8
+
+/** Minimum VNSI Protocol Version number */
+#define VNSI_MIN_PROTOCOLVERSION 5
 
 /** Packet types */
 #define VNSI_CHANNEL_REQUEST_RESPONSE 1
@@ -45,6 +50,8 @@
 #define CONFNAME_TIMESHIFTBUFFERSIZE "TimeshiftBufferSize"
 #define CONFNAME_TIMESHIFTBUFFERFILESIZE "TimeshiftBufferFileSize"
 #define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir"
+#define CONFNAME_PLAYRECORDING "PlayRecording"
+#define CONFNAME_AVOIDEPGSCAN "AvoidEPGScan"
 
 /* OPCODE 1 - 19: VNSI network functions for general purpose */
 #define VNSI_LOGIN                 1
@@ -66,6 +73,7 @@
 #define VNSI_RECSTREAM_POSTOFRAME  43
 #define VNSI_RECSTREAM_FRAMETOPOS  44
 #define VNSI_RECSTREAM_GETIFRAME   45
+#define VNSI_RECSTREAM_GETLENGTH   46
 
 /* OPCODE 60 - 79: VNSI network functions for channel access */
 #define VNSI_CHANNELS_GETCOUNT     61
@@ -73,6 +81,11 @@
 #define VNSI_CHANNELGROUP_GETCOUNT 65
 #define VNSI_CHANNELGROUP_LIST     66
 #define VNSI_CHANNELGROUP_MEMBERS  67
+#define VNSI_CHANNELS_GETCAIDS     68
+#define VNSI_CHANNELS_GETWHITELIST 69
+#define VNSI_CHANNELS_GETBLACKLIST 70
+#define VNSI_CHANNELS_SETWHITELIST 71
+#define VNSI_CHANNELS_SETBLACKLIST 72
 
 /* OPCODE 80 - 99: VNSI network functions for timer access */
 #define VNSI_TIMER_GETCOUNT        80
@@ -88,6 +101,7 @@
 #define VNSI_RECORDINGS_GETLIST    102
 #define VNSI_RECORDINGS_RENAME     103
 #define VNSI_RECORDINGS_DELETE     104
+#define VNSI_RECORDINGS_GETEDL     105
 
 /* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
 #define VNSI_EPG_GETFORCHANNEL     120
@@ -104,6 +118,14 @@
 #define VNSI_OSD_DISCONNECT        161
 #define VNSI_OSD_HITKEY            162
 
+/* OPCODE 180 - 189: VNSI network functions for deleted recording access */
+#define VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED 180
+#define VNSI_RECORDINGS_DELETED_GETCOUNT         181
+#define VNSI_RECORDINGS_DELETED_GETLIST          182
+#define VNSI_RECORDINGS_DELETED_DELETE           183
+#define VNSI_RECORDINGS_DELETED_UNDELETE         184
+#define VNSI_RECORDINGS_DELETED_DELETE_ALL       185
+
 /** Stream packet types (server -> client) */
 #define VNSI_STREAM_CHANGE       1
 #define VNSI_STREAM_STATUS       2
@@ -112,6 +134,7 @@
 #define VNSI_STREAM_SIGNALINFO   5
 #define VNSI_STREAM_CONTENTINFO  6
 #define VNSI_STREAM_BUFFERSTATS  7
+#define VNSI_STREAM_REFTIME      8
 
 /** Scan packet types (server -> client) */
 #define VNSI_SCANNER_PERCENTAGE  1
diff --git a/vnsiserver.c b/vnsiserver.c
index db2e033..2919308 100644
--- a/vnsiserver.c
+++ b/vnsiserver.c
@@ -17,9 +17,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -39,12 +38,11 @@
 #include <sys/stat.h>
 
 #include <vdr/plugin.h>
-#include <vdr/shutdown.h>
-#include <vdr/videodir.h>
 
 #include "vnsi.h"
 #include "vnsiserver.h"
 #include "vnsiclient.h"
+#include "channelfilter.h"
 
 unsigned int cVNSIServer::m_IdCnt = 0;
 
@@ -74,41 +72,6 @@ cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
 {
   m_ServerPort  = listenPort;
 
-  if(*VNSIServerConfig.ConfigDirectory)
-  {
-    m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
-  }
-  else
-  {
-    ERRORLOG("cVNSIServer: missing ConfigDirectory!");
-    m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
-  }
-
-  m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
-  if(m_ServerFD == -1)
-    return;
-
-  fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
-
-  int one = 1;
-  setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
-
-  struct sockaddr_in s;
-  memset(&s, 0, sizeof(s));
-  s.sin_family = AF_INET;
-  s.sin_port = htons(m_ServerPort);
-
-  int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
-  if (x < 0)
-  {
-    close(m_ServerFD);
-    INFOLOG("Unable to start VNSI Server, port already in use ?");
-    m_ServerFD = -1;
-    return;
-  }
-
-  listen(m_ServerFD, 10);
-
   Start();
 
   INFOLOG("VNSI Server started");
@@ -118,12 +81,7 @@ cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
 
 cVNSIServer::~cVNSIServer()
 {
-  Cancel(-1);
-  for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-  {
-    delete (*i);
-  }
-  m_clients.erase(m_clients.begin(), m_clients.end());
+  m_Status.Shutdown();
   Cancel();
   INFOLOG("VNSI Server stopped");
 }
@@ -175,7 +133,7 @@ void cVNSIServer::NewClientConnected(int fd)
 
   INFOLOG("Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
   cVNSIClient *connection = new cVNSIClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
-  m_clients.push_back(connection);
+  m_Status.AddClient(connection);
   m_IdCnt++;
 }
 
@@ -184,42 +142,44 @@ void cVNSIServer::Action(void)
   fd_set fds;
   struct timeval tv;
 
-  // initial time for channels change
-  struct timespec channelsUpdate;
-  channelsUpdate.tv_sec = 0;
-  channelsUpdate.tv_nsec = 0;
-  cTimeMs chanTimer(0);
+  if(*VNSIServerConfig.ConfigDirectory)
+  {
+    m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
+  }
+  else
+  {
+    ERRORLOG("cVNSIServer: missing ConfigDirectory!");
+    m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
+  }
 
-  // get initial state of the recordings
-  int recState = -1;
-  Recordings.StateChanged(recState);
+  VNSIChannelFilter.Load();
+  VNSIChannelFilter.SortChannels();
+  m_Status.Start();
 
-  // get initial state of the timers
-  int timerState = -1;
-  Timers.Modified(timerState);
+  m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
+  if(m_ServerFD == -1)
+    return;
 
-  // last update of epg
-  time_t epgUpdate = cSchedules::Modified();
+  fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
 
-  // delete old timeshift file
-  cString cmd;
-  struct stat sb;
-  if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
-  {
-    if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
-      cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
-    else
-      cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
-  }
-  else
+  int one = 1;
+  setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+
+  struct sockaddr_in s;
+  memset(&s, 0, sizeof(s));
+  s.sin_family = AF_INET;
+  s.sin_port = htons(m_ServerPort);
+
+  int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
+  if (x < 0)
   {
-#if VDRVERSNUM >= 20102
-    cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
-#else
-    cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
-#endif
+    close(m_ServerFD);
+    INFOLOG("Unable to start VNSI Server, port already in use ?");
+    m_ServerFD = -1;
+    return;
   }
-  int ret = system(cmd);
+
+  listen(m_ServerFD, 10);
 
   while (Running())
   {
@@ -237,72 +197,6 @@ void cVNSIServer::Action(void)
     }
     if (r == 0)
     {
-      // remove disconnected clients
-      for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
-      {
-        if (!(*i)->Active())
-        {
-          INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
-          delete (*i);
-          i = m_clients.erase(i);
-        }
-        else {
-          i++;
-        }
-      }
-
-      // trigger clients to reload the modified channel list
-      if(m_clients.size() > 0 && chanTimer.TimedOut())
-      {
-        struct stat s;
-        if(stat(Channels.FileName(), &s) != -1)
-        {
-          if ((s.st_mtim.tv_sec != channelsUpdate.tv_sec) &&
-              (s.st_mtim.tv_nsec != channelsUpdate.tv_nsec))
-          {
-            INFOLOG("Requesting clients to reload channel list");
-            for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-              (*i)->ChannelChange();
-            channelsUpdate = s.st_mtim;
-          }
-        }
-        chanTimer.Set(5000);
-      }
-
-      // reset inactivity timeout as long as there are clients connected
-      if(m_clients.size() > 0) {
-        ShutdownHandler.SetUserInactiveTimeout();
-      }
-
-      // update recordings
-      if(Recordings.StateChanged(recState))
-      {
-        INFOLOG("Recordings state changed (%i)", recState);
-        INFOLOG("Requesting clients to reload recordings list");
-        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-          (*i)->RecordingsChange();
-      }
-
-      // update timers
-      if(Timers.Modified(timerState))
-      {
-        INFOLOG("Timers state changed (%i)", timerState);
-        INFOLOG("Requesting clients to reload timers");
-        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-        {
-         (*i)->TimerChange();
-        }
-      }
-
-      // update epg
-      if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300)
-      {
-        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-        {
-         (*i)->EpgChange();
-        }
-        epgUpdate = cSchedules::Modified();
-      }
       continue;
     }
 
diff --git a/vnsiserver.h b/vnsiserver.h
index bf52b41..472d0bb 100644
--- a/vnsiserver.h
+++ b/vnsiserver.h
@@ -17,19 +17,18 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
 #ifndef VNSI_SERVER_H
 #define VNSI_SERVER_H
 
-#include <list>
 #include <vdr/thread.h>
 
 #include "config.h"
+#include "status.h"
 
 class cVNSIClient;
 
@@ -37,15 +36,13 @@ class cVNSIServer : public cThread
 {
 protected:
 
-  typedef std::list<cVNSIClient*> ClientList;
-
   virtual void Action(void);
   void NewClientConnected(int fd);
 
   int           m_ServerPort;
   int           m_ServerFD;
   cString       m_AllowedHostsFile;
-  ClientList    m_clients;
+  cVNSIStatus   m_Status;
 
   static unsigned int m_IdCnt;
 

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



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