[SCM] vdr-plugin-vnsiserver/master: New upstream version 1.5.1

tiber-guest at users.alioth.debian.org tiber-guest at users.alioth.debian.org
Fri Nov 4 23:55:56 UTC 2016


The following commit has been merged in the master branch:
commit e52a261265a5b511f284320b885d14501a10855f
Author: Tobias Grimm <etobi at debian.org>
Date:   Fri Nov 4 20:48:19 2016 +0100

    New upstream version 1.5.1

diff --git a/HISTORY b/HISTORY
index 1df333c..ac60789 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,5 +1,19 @@
 VDR Plugin 'vnsiserver' Revision History
 ----------------------------------------
+2016-09-25: Version 1.5.1
+
+- see github history for fixes
+- added new setup parameter DisableCamBlacklist:
+  avoids a VDR hard-coded 15 seconds timeout if tuning failed due to hard-coded scramble timeout
+
+2016-08-01: Version 1.5.0
+
+- support for vdr 2.3.1
+- add epg search timers
+- support for h265 (hevc)
+- fix PID change
+- fixes and improvements for handling of CAMs
+- see github history for other fixes
 
 2015-09-18: Version 1.3.0
 
diff --git a/Makefile b/Makefile
index 8f1cb05..660df05 100644
--- a/Makefile
+++ b/Makefile
@@ -36,32 +36,9 @@ APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print
 NOCONFIG := 1
 endif
 
-# backwards compatibility version < 1.7.34
-API1733 := $(shell if [ "$(APIVERSION)" \< "1.7.34" ]; then echo true; fi; )
-
-ifdef API1733
-
-VDRSRC = $(VDRDIR)
-VDRSRC ?= ../../..
-ifeq ($(strip $(LIBDIR)),)
-LIBDIR = $(VDRSRC)/PLUGINS/lib
-endif
-
-ifndef NOCONFIG
-CXXFLAGS = $(call PKGCFG,cflags)
-CXXFLAGS += -fPIC
-else
--include $(VDRSRC)/Make.global
--include $(VDRSRC)/Make.config
-endif
-
-export CXXFLAGS
-else
-
 ### Allow user defined options to overwrite defaults:
 
 -include $(PLGCFG)
-endif
 
 ### The name of the distribution archive:
 
@@ -79,6 +56,8 @@ INCLUDES += -I$(VDRSRC)/include
 endif
 
 DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DVNSI_SERVER_VERSION='"$(VERSION)"'
+CXXFLAGS += -std=c++11
+export CXXFLAGS
 
 ifeq ($(DEBUG),1)
 DEFINES += -DDEBUG
@@ -87,10 +66,10 @@ endif
 ### The object files (add further files here):
 
 OBJS = vnsi.o bitstream.o vnsiclient.o channelscancontrol.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_AC3.o parser_DTS.o parser_h264.o parser_hevc.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 channelfilter.o status.o
+       videoinput.o channelfilter.o status.o vnsitimer.o
 
 ### The main target:
 
diff --git a/bitstream.c b/bitstream.c
index d0c06f3..60ee244 100644
--- a/bitstream.c
+++ b/bitstream.c
@@ -22,37 +22,62 @@
  *
  */
 
-#include <stdio.h>
-#include <inttypes.h>
 #include "bitstream.h"
 
-cBitstream::cBitstream(uint8_t *data, int bits)
+void cBitstream::skipBits(unsigned int num)
 {
-  m_data   = data;
-  m_offset = 0;
-  m_len    = bits;
-  m_error  = false;
-}
+  if (m_doEP3)
+  {
+    register unsigned int tmp;
 
-void cBitstream::setBitstream(uint8_t *data, int bits)
-{
-  m_data   = data;
-  m_offset = 0;
-  m_len    = bits;
-  m_error  = false;
-}
+    while (num)
+    {
+      tmp = m_offset >> 3;
+      if (!(m_offset & 7) && (m_data[tmp--] == 3) && (m_data[tmp--] == 0) && (m_data[tmp] == 0))
+        m_offset += 8;   // skip EP3 byte
+
+      if (!(m_offset & 7) && (num >= 8)) // byte boundary, speed up things a little bit
+      {
+        m_offset += 8;
+        num -= 8;
+      }
+      else if ((tmp = 8-(m_offset & 7)) <= num) // jump to byte boundary
+      {
+        m_offset += tmp;
+        num -= tmp;
+      }
+      else
+      {
+        m_offset += num;
+         num = 0;
+      }
+
+      if (m_offset >= m_len)
+      {
+        m_error = true;
+        break;
+      }
+    }
+
+    return;
+  }
 
-void cBitstream::skipBits(int num)
-{
   m_offset += num;
 }
 
 unsigned int cBitstream::readBits(int num)
 {
-  int r = 0;
+  unsigned int r = 0;
 
   while(num > 0)
   {
+    if (m_doEP3)
+    {
+      size_t tmp = m_offset >> 3;
+      if (!(m_offset & 7) && (m_data[tmp--] == 3) && (m_data[tmp--] == 0) && (m_data[tmp] == 0))
+        m_offset += 8;   // skip EP3 byte
+    }
+
     if(m_offset >= m_len)
     {
       m_error = true;
@@ -71,8 +96,8 @@ unsigned int cBitstream::readBits(int num)
 
 unsigned int cBitstream::showBits(int num)
 {
-  int r = 0;
-  int offs = m_offset;
+  unsigned int r = 0;
+  size_t offs = m_offset;
 
   while(num > 0)
   {
@@ -118,30 +143,3 @@ signed int cBitstream::readGolombSE()
   v = (v + 1) >> 1;
   return pos ? v : -v;
 }
-
-
-unsigned int cBitstream::remainingBits()
-{
-  return m_len - m_offset;
-}
-
-
-void cBitstream::putBits(int val, int num)
-{
-  while(num > 0) {
-    if(m_offset >= m_len)
-    {
-      m_error = true;
-      return;
-    }
-
-    num--;
-
-    if(val & (1 << num))
-      m_data[m_offset / 8] |= 1 << (7 - (m_offset & 7));
-    else
-      m_data[m_offset / 8] &= ~(1 << (7 - (m_offset & 7)));
-
-    m_offset++;
-  }
-}
diff --git a/bitstream.h b/bitstream.h
index 4d236b5..c589654 100644
--- a/bitstream.h
+++ b/bitstream.h
@@ -25,28 +25,43 @@
 #ifndef VNSI_BITSTREAM_H
 #define VNSI_BITSTREAM_H
 
+#include <stdint.h>
+#include <stddef.h>
+
 class cBitstream
 {
 private:
-  uint8_t *m_data;
-  int      m_offset;
-  int      m_len;
-  bool     m_error;
+  uint8_t *const m_data;
+  size_t   m_offset = 0;
+  const size_t m_len;
+  bool     m_error = false;
+  const bool m_doEP3 = false;
 
 public:
-  cBitstream(uint8_t *data, int bits);
+  constexpr cBitstream(uint8_t *data, size_t bits)
+    :m_data(data), m_len(bits)
+  {
+  }
+
+  // this is a bitstream that has embedded emulation_prevention_three_byte
+  // sequences that need to be removed as used in HECV.
+  // Data must start at byte 2
+  constexpr cBitstream(uint8_t *data, size_t bits, bool doEP3)
+    :m_data(data),
+     m_offset(16), // skip header and use as sentinel for EP3 detection
+     m_len(bits),
+     m_doEP3(true)
+  {
+  }
 
-  void         setBitstream(uint8_t *data, int bits);
-  void         skipBits(int num);
+  void skipBits(unsigned int num);
   unsigned int readBits(int num);
   unsigned int showBits(int num);
   unsigned int readBits1() { return readBits(1); }
   unsigned int readGolombUE(int maxbits = 32);
-  signed int   readGolombSE();
-  unsigned int remainingBits();
-  void         putBits(int val, int num);
-  int          length() { return m_len; }
-  bool         isError() { return m_error; }
+  signed int readGolombSE();
+  constexpr size_t length() const { return m_len; }
+  constexpr bool isError() const { return m_error; }
 };
 
 #endif // VNSI_BITSTREAM_H
diff --git a/channelfilter.c b/channelfilter.c
index a497a40..847bdeb 100644
--- a/channelfilter.c
+++ b/channelfilter.c
@@ -32,7 +32,7 @@
 #include <vdr/tools.h>
 
 cVNSIProvider::cVNSIProvider()
-  :m_name(""), m_caid(0)
+  :m_caid(0)
 {
 
 }
@@ -42,10 +42,12 @@ cVNSIProvider::cVNSIProvider(std::string name, int caid)
 {
 };
 
-bool cVNSIProvider::operator==(const cVNSIProvider &rhs)
+bool cVNSIProvider::operator==(const cVNSIProvider &rhs) const
 {
   if (rhs.m_caid != m_caid)
     return false;
+  if (m_name.empty())
+    return false;
   if (rhs.m_name.compare(m_name) != 0)
     return false;
   return true;
@@ -73,8 +75,6 @@ void cVNSIChannelFilter::Load()
   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();
@@ -83,23 +83,21 @@ void cVNSIChannelFilter::Load()
   {
     while(std::getline(rfile,line))
     {
+      cVNSIProvider provider;
       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);
+        provider.m_name.assign(line, 0, pos);
+        provider.m_caid = strtol(line.c_str() + pos + 1, nullptr, 10);
       }
-      p_it = std::find(m_providersVideo.begin(), m_providersVideo.end(), provider);
+      auto p_it = std::find(m_providersVideo.begin(), m_providersVideo.end(), provider);
       if(p_it == m_providersVideo.end())
       {
-        m_providersVideo.push_back(provider);
+        m_providersVideo.emplace_back(std::move(provider));
       }
     }
     rfile.close();
@@ -112,23 +110,21 @@ void cVNSIChannelFilter::Load()
   {
     while(std::getline(rfile,line))
     {
-      unsigned int pos = line.find("|");
+      cVNSIProvider provider;
+      auto 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);
+        provider.m_name.assign(line, 0, pos);
+        provider.m_caid = strtol(line.c_str() + pos + 1, nullptr, 10);
       }
-      p_it = std::find(m_providersRadio.begin(), m_providersRadio.end(), provider);
+      auto p_it = std::find(m_providersRadio.begin(), m_providersRadio.end(), provider);
       if(p_it == m_providersRadio.end())
       {
-        m_providersRadio.push_back(provider);
+        m_providersRadio.emplace_back(std::move(provider));
       }
     }
     rfile.close();
@@ -141,9 +137,8 @@ void cVNSIChannelFilter::Load()
   {
     while(getline(rfile,line))
     {
-      char *pend;
-      int id = strtol(line.c_str(), &pend, 10);
-      m_channelsVideo.push_back(id);
+      int id = strtol(line.c_str(), nullptr, 10);
+      m_channelsVideo.insert(id);
     }
     rfile.close();
   }
@@ -155,9 +150,8 @@ void cVNSIChannelFilter::Load()
   {
     while(getline(rfile,line))
     {
-      char *pend;
-      int id = strtol(line.c_str(), &pend, 10);
-      m_channelsRadio.push_back(id);
+      int id = strtol(line.c_str(), nullptr, 10);
+      m_channelsRadio.insert(id);
     }
     rfile.close();
   }
@@ -169,8 +163,6 @@ void cVNSIChannelFilter::StoreWhitelist(bool radio)
 
   cString filename;
   std::ofstream wfile;
-  cVNSIProvider provider;
-  std::vector<cVNSIProvider>::iterator p_it;
   std::vector<cVNSIProvider> *whitelist;
 
   if (radio)
@@ -187,15 +179,9 @@ void cVNSIChannelFilter::StoreWhitelist(bool radio)
   wfile.open(filename);
   if(wfile.is_open())
   {
-    std::string tmp;
-    char buf[16];
-    for(p_it=whitelist->begin(); p_it!=whitelist->end(); ++p_it)
+    for (const auto i : *whitelist)
     {
-      tmp = p_it->m_name;
-      tmp += "|";
-      sprintf(buf, "%d\n", p_it->m_caid);
-      tmp += buf;
-      wfile << tmp;
+      wfile << i.m_name << '|' << i.m_caid << '\n';
     }
     wfile.close();
   }
@@ -209,9 +195,7 @@ void cVNSIChannelFilter::StoreBlacklist(bool radio)
 
   cString filename;
   std::ofstream wfile;
-  cVNSIProvider provider;
-  std::vector<int>::iterator it;
-  std::vector<int> *blacklist;
+  std::set<int> *blacklist;
 
   if (radio)
   {
@@ -227,13 +211,9 @@ void cVNSIChannelFilter::StoreBlacklist(bool radio)
   wfile.open(filename);
   if(wfile.is_open())
   {
-    std::string tmp;
-    char buf[16];
-    for(it=blacklist->begin(); it!=blacklist->end(); ++it)
+    for (const auto i : *blacklist)
     {
-      sprintf(buf, "%d\n", *it);
-      tmp = buf;
-      wfile << tmp;
+      wfile << i << '\n';
     }
     wfile.close();
   }
@@ -244,7 +224,6 @@ void cVNSIChannelFilter::StoreBlacklist(bool radio)
 bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel)
 {
   cVNSIProvider provider;
-  std::vector<cVNSIProvider>::iterator p_it;
   std::vector<cVNSIProvider> *providers;
   provider.m_name = channel.Provider();
 
@@ -259,7 +238,7 @@ bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel)
   if (channel.Ca(0) == 0)
   {
     provider.m_caid = 0;
-    p_it = std::find(providers->begin(), providers->end(), provider);
+    auto p_it = std::find(providers->begin(), providers->end(), provider);
     if(p_it!=providers->end())
       return true;
     else
@@ -271,7 +250,7 @@ bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel)
   while((caid = channel.Ca(idx)) != 0)
   {
     provider.m_caid = caid;
-    p_it = std::find(providers->begin(), providers->end(), provider);
+    auto p_it = std::find(providers->begin(), providers->end(), provider);
     if(p_it!=providers->end())
       return true;
 
@@ -290,16 +269,15 @@ bool cVNSIChannelFilter::PassFilter(const cChannel &channel)
   if (!IsWhitelist(channel))
     return false;
 
-  std::vector<int>::iterator it;
   if (IsRadio(&channel))
   {
-    it = std::find(m_channelsRadio.begin(), m_channelsRadio.end(), CreateChannelUID(&channel));
+    auto 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));
+    auto it = std::find(m_channelsVideo.begin(), m_channelsVideo.end(), CreateChannelUID(&channel));
     if(it!=m_channelsVideo.end())
       return false;
   }
@@ -309,18 +287,30 @@ bool cVNSIChannelFilter::PassFilter(const cChannel &channel)
 
 void cVNSIChannelFilter::SortChannels()
 {
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_WRITE;
+  for (cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
+#else
   Channels.IncBeingEdited();
   Channels.Lock(true);
-
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+#endif
   {
     if(!PassFilter(*channel))
     {
+#if VDRVERSNUM >= 20301
+      for (cChannel *whitechan = Channels->Next(channel); whitechan; whitechan = Channels->Next(whitechan))
+#else
       for (cChannel *whitechan = Channels.Next(channel); whitechan; whitechan = Channels.Next(whitechan))
+#endif
       {
         if(PassFilter(*whitechan))
         {
+#if VDRVERSNUM >= 20301
+          Channels->Move(whitechan, channel);
+#else
           Channels.Move(whitechan, channel);
+#endif
           channel = whitechan;
           break;
         }
@@ -328,9 +318,13 @@ void cVNSIChannelFilter::SortChannels()
     }
   }
 
+#if VDRVERSNUM >= 20301
+  Channels->SetModifiedByUser();
+#else
   Channels.SetModified(true);
   Channels.Unlock();
   Channels.DecBeingEdited();
+#endif
 }
 
 cVNSIChannelFilter VNSIChannelFilter;
diff --git a/channelfilter.h b/channelfilter.h
index 58dc659..8b9554b 100644
--- a/channelfilter.h
+++ b/channelfilter.h
@@ -26,6 +26,7 @@
 
 #include <string>
 #include <vector>
+#include <set>
 #include <vdr/thread.h>
 #include <vdr/channels.h>
 
@@ -34,7 +35,7 @@ class cVNSIProvider
 public:
   cVNSIProvider();
   cVNSIProvider(std::string name, int caid);
-  bool operator==(const cVNSIProvider &rhs);
+  bool operator==(const cVNSIProvider &rhs) const;
   std::string m_name;
   int m_caid;
 };
@@ -51,8 +52,8 @@ public:
   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;
+  std::set<int> m_channelsVideo;
+  std::set<int> m_channelsRadio;
   cMutex m_Mutex;
 };
 
diff --git a/channelscancontrol.c b/channelscancontrol.c
index eac2eef..7ca2d4c 100644
--- a/channelscancontrol.c
+++ b/channelscancontrol.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <vdr/menu.h>
-#include <vdr/status.h>
-
 #include "channelscancontrol.h"
 #include "vnsiclient.h"
 
+#include <vdr/menu.h>
+#include <vdr/status.h>
+
 using namespace WIRBELSCAN_SERVICE;
 
 /*!
@@ -54,8 +54,6 @@ using namespace WIRBELSCAN_SERVICE;
 #define SCANDONE 2
 #define CHECKVERSION(a,b,c) p=strchr((char *) m_scanInformation->a,'#') + 1; sscanf(p,"%d ",&version); if (version < b) c = true;
 #define CHECKLIMITS(a,v,_min,_max,_def) a=v; if ((a<_min) || (a>_max)) a=_def;
-#define freeAndNull(p)   if(p) { free(p);   p=NULL; }
-#define deleteAndNull(p) if(p) { delete(p); p=NULL; }
 
 CScanControl::CScanControl(cVNSIClient *client)
   : m_client(client),
@@ -71,10 +69,9 @@ CScanControl::CScanControl(cVNSIClient *client)
 
 CScanControl::~CScanControl()
 {
-  if (m_scanInformation)
-    delete m_scanInformation;
-  freeAndNull(m_cbuf);
-  freeAndNull(m_sbuf);
+  delete m_scanInformation;
+  free(m_cbuf);
+  free(m_sbuf);
 }
 
 bool CScanControl::IsSupported()
@@ -198,7 +195,14 @@ bool CScanControl::StartScan(sScanServiceData &data)
   m_setup.scanflags |= data.scan_fta       ? SCAN_FTA       : 0;
   m_setup.scanflags |= data.scan_hd        ? SCAN_HD        : 0;
 
+#if VDRVERSNUM >= 20301
+  {
+    LOCK_CHANNELS_READ;
+    m_lastChannelCount = Channels->Count();
+  }
+#else
   m_lastChannelCount = Channels.Count();
+#endif
 
   char *s;
   if (asprintf(&s, "%sSet%s", SPlugin, SSetup) < 0)
@@ -291,12 +295,36 @@ void CScanControl::Action(void)
     m_client->processSCAN_SetDeviceInfo(m_scanStatus.curr_device);
     m_client->processSCAN_SetTransponder(m_scanStatus.transponder);
 
-    for (int i = 0; i < Channels.Count()-m_lastChannelCount; i++)
+
+    int noOfChannels;
+#if VDRVERSNUM >= 20301
+    {
+      LOCK_CHANNELS_READ;
+      noOfChannels = Channels->Count();
+    }
+#else
+    noOfChannels = Channels.Count();
+#endif
+
+    for (int i = 0; i < noOfChannels-m_lastChannelCount; i++)
     {
+#if VDRVERSNUM >= 20301
+      LOCK_CHANNELS_READ;
+      const cChannel *channel = Channels->GetByNumber(Channels->Count()-i);
+#else
       cChannel *channel = Channels.GetByNumber(Channels.Count()-i);
+#endif
       m_client->processSCAN_NewChannel(channel->Name(), channel->Vpid() == 0, channel->Ca() > 0, channel->Vtype() > 2);
     }
-    m_lastChannelCount = Channels.Count();
+
+#if VDRVERSNUM >= 20301
+  {
+    LOCK_CHANNELS_READ;
+    m_lastChannelCount = Channels->Count();
+  }
+#else
+  m_lastChannelCount = Channels.Count();
+#endif
 
     if (m_scanStatus.status == StatusStopped)
     {
diff --git a/config.h b/config.h
index 933bf96..fa02c4e 100644
--- a/config.h
+++ b/config.h
@@ -34,19 +34,13 @@
 // log output configuration
 
 #ifdef CONSOLEDEBUG
-#define DEBUGLOG(x...) printf("VNSI: "x)
-#elif defined  DEBUG
-#define DEBUGLOG(x...) dsyslog("VNSI: "x)
+#define INFOLOG(x...) printf("VNSI: " x)
+#define ERRORLOG(x...) printf("VNSI-Error: " x)
+#define DEBUGLOG(x...) printf("VNSI-Debug: " x)
 #else
-#define DEBUGLOG(x...)
-#endif
-
-#ifdef CONSOLEDEBUG
-#define INFOLOG(x...) printf("VNSI: "x)
-#define ERRORLOG(x...) printf("VNSI-Error: "x)
-#else
-#define INFOLOG(x...) isyslog("VNSI: "x)
-#define ERRORLOG(x...) esyslog("VNSI-Error: "x)
+#define INFOLOG(x...) isyslog("VNSI: " x)
+#define ERRORLOG(x...) esyslog("VNSI-Error: " x)
+#define DEBUGLOG(x...) (SysLogLevel > 3) ? dsyslog("VNSI-Debug: " x) : void()
 #endif
 
 // default settings
@@ -69,6 +63,7 @@
 #define ERROR_PES_SCRAMBLE  0x02
 #define ERROR_PES_STARTCODE 0x04
 #define ERROR_DEMUX_NODATA  0x10
+#define ERROR_CAM_ERROR     0x20
 
 class cVNSIServerConfig
 {
diff --git a/cxsocket.c b/cxsocket.c
index e50bfab..e9b5cad 100644
--- a/cxsocket.c
+++ b/cxsocket.c
@@ -31,6 +31,9 @@
  *
  */
 
+#include "cxsocket.h"
+#include "config.h"
+
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
 
@@ -46,33 +49,25 @@
 #include <vdr/config.h>
 #include <vdr/tools.h>
 
-#include "config.h"
-#include "cxsocket.h"
-
 #ifndef MSG_MORE
 #define MSG_MORE 0
 #endif
 
-cxSocket::~cxSocket()
+cxSocket::cxSocket(int h)
+  :m_fd(h),
+   m_pollerRead(m_fd),
+   m_pollerWrite(m_fd, true)
 {
-  close();
-  delete m_pollerRead;
-  delete m_pollerWrite;
 }
 
-void cxSocket::close() {
-  if(m_fd >= 0) { 
-    ::close(m_fd);
-    m_fd=-1; 
-  }
+cxSocket::~cxSocket()
+{
+  close(m_fd);
 }
 
 void cxSocket::Shutdown()
 {
-  if(m_fd >= 0)
-  {
-    ::shutdown(m_fd, SHUT_RD);
-  }
+  ::shutdown(m_fd, SHUT_RD);
 }
 
 void cxSocket::LockWrite()
@@ -89,17 +84,14 @@ ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms, bool mo
 {
   cMutexLock CmdLock(&m_MutexWrite);
 
-  if(m_fd == -1)
-    return -1;
-
   ssize_t written = (ssize_t)size;
   const unsigned char *ptr = (const unsigned char *)buffer;
 
   while (size > 0)
   {
-    if(!m_pollerWrite->Poll(timeout_ms))
+    if(!m_pollerWrite.Poll(timeout_ms))
     {
-      ERRORLOG("cxSocket::write: poll() failed");
+      ERRORLOG("cxSocket::write(fd=%d): poll() failed", m_fd);
       return written-size;
     }
 
@@ -109,11 +101,11 @@ ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms, bool mo
     {
       if (errno == EINTR || errno == EAGAIN)
       {
-        DEBUGLOG("cxSocket::write: EINTR during write(), retrying");
+        DEBUGLOG("cxSocket::write(fd=%d): EINTR during write(), retrying", m_fd);
         continue;
       }
       else if (errno != EPIPE)
-        ERRORLOG("cxSocket::write: write() error");
+        ERRORLOG("cxSocket::write(fd=%d): write() error", m_fd);
       return p;
     }
 
@@ -128,17 +120,14 @@ ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms)
 {
   int retryCounter = 0;
 
-  if(m_fd == -1)
-    return -1;
-
   ssize_t missing = (ssize_t)size;
   unsigned char *ptr = (unsigned char *)buffer;
 
   while (missing > 0)
   {
-    if(!m_pollerRead->Poll(timeout_ms))
+    if(!m_pollerRead.Poll(timeout_ms))
     {
-      ERRORLOG("cxSocket::read: poll() failed at %d/%d", (int)(size-missing), (int)size);
+      ERRORLOG("cxSocket::read(fd=%d): poll() failed at %d/%d", m_fd, (int)(size-missing), (int)size);
       return size-missing;
     }
 
@@ -148,16 +137,16 @@ ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms)
     {
       if (retryCounter < 10 && (errno == EINTR || errno == EAGAIN))
       {
-        DEBUGLOG("cxSocket::read: EINTR/EAGAIN during read(), retrying");
+        DEBUGLOG("cxSocket::read(fd=%d): EINTR/EAGAIN during read(), retrying", m_fd);
         retryCounter++;
         continue;
       }
-      ERRORLOG("cxSocket::read: read() error at %d/%d", (int)(size-missing), (int)size);
+      ERRORLOG("cxSocket::read(fd=%d): read() error at %d/%d", m_fd, (int)(size-missing), (int)size);
       return 0;
     }
     else if (p == 0)
     {
-      INFOLOG("cxSocket::read: eof, connection closed");
+      INFOLOG("cxSocket::read(fd=%d): eof, connection closed", m_fd);
       return 0;
     }
 
@@ -169,17 +158,6 @@ ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms)
   return size;
 }
 
-void cxSocket::SetHandle(int h) {
-  if(h != m_fd) {
-    close();
-    m_fd = h;
-    delete m_pollerRead;
-    delete m_pollerWrite;
-    m_pollerRead = new cPoller(m_fd);
-    m_pollerWrite = new cPoller(m_fd, true);
-  }
-}
-
 char *cxSocket::ip2txt(uint32_t ip, unsigned int port, char *str)
 {
   // inet_ntoa is not thread-safe (?)
diff --git a/cxsocket.h b/cxsocket.h
index d144411..d8f7eab 100644
--- a/cxsocket.h
+++ b/cxsocket.h
@@ -39,17 +39,18 @@
 
 class cxSocket
 {
- private:
-  int m_fd;
+  const int m_fd;
   cMutex m_MutexWrite;
-  cPoller *m_pollerRead;
-  cPoller *m_pollerWrite;
+  cPoller m_pollerRead;
+  cPoller m_pollerWrite;
 
  public:
-  cxSocket() : m_fd(-1), m_pollerRead(NULL), m_pollerWrite(NULL) {}
+  cxSocket(int h);
   ~cxSocket();
-  void SetHandle(int h);
-  void close(void);
+
+  cxSocket(const cxSocket &) = delete;
+  cxSocket &operator=(const cxSocket &) = delete;
+
   void Shutdown(void);
   void LockWrite();
   void UnlockWrite();
diff --git a/demuxer.c b/demuxer.c
index 94859ec..3521656 100644
--- a/demuxer.c
+++ b/demuxer.c
@@ -31,10 +31,34 @@
 #include <vdr/channels.h>
 #include <libsi/si.h>
 
+cStreamInfo::cStreamInfo()
+{
+
+}
+
+cStreamInfo::cStreamInfo(const cStreamInfo& info)
+{
+  pID = info.pID;
+  type = info.type;
+  content = info.content;
+  subtitlingType = info.subtitlingType;
+  compositionPageId = info.compositionPageId;
+  ancillaryPageId = info.ancillaryPageId;
+  handleRDS = info.handleRDS;
+  SetLanguage(info.language);
+}
+
+void cStreamInfo::SetLanguage(const char* lang)
+{
+  language[0] = lang[0];
+  language[1] = lang[1];
+  language[2] = lang[2];
+  language[3] = 0;
+}
+
 cVNSIDemuxer::cVNSIDemuxer(bool bAllowRDS)
  : m_bAllowRDS(bAllowRDS)
 {
-  m_OldPmtVersion = -1;
 }
 
 cVNSIDemuxer::~cVNSIDemuxer()
@@ -48,7 +72,6 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
 
   m_CurrentChannel = channel;
   m_VideoBuffer = videoBuffer;
-  m_OldPmtVersion = -1;
 
   if (m_CurrentChannel.Vpid())
     m_WaitIFrame = true;
@@ -61,16 +84,17 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
   m_MuxPacketSerial = 0;
   m_Error = ERROR_DEMUX_NODATA;
   m_SetRefTime = true;
+  m_seenFirstPacket = false;
 }
 
 void cVNSIDemuxer::Close()
 {
   cMutexLock lock(&m_Mutex);
 
-  for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
+  for (auto *i : m_Streams)
   {
-    DEBUGLOG("Deleting stream parser for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type());
-    delete (*it);
+    DEBUGLOG("Deleting stream parser for pid=%i and type=%i", i->GetPID(), i->Type());
+    delete i;
   }
   m_Streams.clear();
   m_StreamInfos.clear();
@@ -84,6 +108,9 @@ int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
 
   cMutexLock lock(&m_Mutex);
 
+  if (!m_CurrentChannel.Vpid())
+    m_WaitIFrame = false;
+
   // clear packet
   if (!packet)
     return -1;
@@ -118,18 +145,14 @@ int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
     m_PatPmtParser.ParsePmt(buf, TS_SIZE);
     if (m_PatPmtParser.GetVersions(patVersion, pmtVersion))
     {
-      if (pmtVersion != m_OldPmtVersion)
+      cChannel pmtChannel(m_CurrentChannel);
+      SetChannelPids(&pmtChannel, &m_PatPmtParser);
+      SetChannelStreamInfos(&pmtChannel);
+      m_PatPmtParser.Reset();
+      if (EnsureParsers())
       {
-        cChannel pmtChannel(m_CurrentChannel);
-        SetChannelPids(&pmtChannel, &m_PatPmtParser);
-        SetChannelStreams(&pmtChannel);
-        m_PatPmtParser.Reset();
-        m_OldPmtVersion = pmtVersion;
-        if (EnsureParsers())
-        {
-          packet->pmtChange = true;
-            return 1;
-        }
+        packet->pmtChange = true;
+        return 1;
       }
     }
   }
@@ -139,6 +162,7 @@ int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
     if (error == 0)
     {
       m_WaitIFrame = false;
+      m_seenFirstPacket = true;
 
       packet->serial = m_MuxPacketSerial;
       if (m_SetRefTime)
@@ -152,6 +176,15 @@ int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
     else if (error < 0)
     {
       m_Error |= abs(error);
+      if (m_Error & ERROR_PES_SCRAMBLE)
+      {
+        if (m_seenFirstPacket)
+        {
+          ResetParsers();
+          m_Error |= ERROR_CAM_ERROR;
+          m_WaitIFrame = true;
+        }
+      }
     }
   }
 
@@ -362,107 +395,107 @@ cTSStream *cVNSIDemuxer::GetNextStream()
 
 cTSStream *cVNSIDemuxer::FindStream(int Pid)
 {
-  for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
+  for (auto *i : m_Streams)
   {
-    if (Pid == (*it)->GetPID())
-      return *it;
+    if (Pid == i->GetPID())
+      return i;
   }
   return NULL;
 }
 
 void cVNSIDemuxer::ResetParsers()
 {
-  for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
+  for (auto *i : m_Streams)
   {
-    (*it)->ResetParser();
+    i->ResetParser();
   }
+  m_seenFirstPacket = false;
 }
 
-void cVNSIDemuxer::AddStreamInfo(sStreamInfo &stream)
+static bool Contains(const std::list<cStreamInfo> &list, int pID, eStreamType type)
 {
-  m_StreamInfos.push_back(stream);
+  for (const auto &i : list)
+    if (i.pID == pID && i.type == type)
+      return true;
+
+  return false;
 }
 
 bool cVNSIDemuxer::EnsureParsers()
 {
   bool streamChange = false;
 
-  std::list<cTSStream*>::iterator it = m_Streams.begin();
+  auto it = m_Streams.begin();
   while (it != m_Streams.end())
   {
-    std::list<sStreamInfo>::iterator its;
-    for (its = m_StreamInfos.begin(); its != m_StreamInfos.end(); ++its)
-    {
-      if ((its->pID == (*it)->GetPID()) && (its->type == (*it)->Type()))
-      {
-        break;
-      }
-    }
-    if (its == m_StreamInfos.end())
+    if (!Contains(m_StreamInfos, (*it)->GetPID(), (*it)->Type()))
     {
       INFOLOG("Deleting stream for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type());
-      m_Streams.erase(it);
-      it = m_Streams.begin();
+      it = m_Streams.erase(it);
       streamChange = true;
     }
     else
       ++it;
   }
 
-  for (std::list<sStreamInfo>::iterator it = m_StreamInfos.begin(); it != m_StreamInfos.end(); ++it)
+  for (const auto &i : m_StreamInfos)
   {
-    cTSStream *stream = FindStream(it->pID);
+    cTSStream *stream = FindStream(i.pID);
     if (stream)
     {
       // TODO: check for change in lang
-      stream->SetLanguage(it->language);
+      stream->SetLanguage(i.language);
       continue;
     }
 
-    if (it->type == stH264)
+    if (i.type == stH264)
+    {
+      stream = new cTSStream(stH264, i.pID, &m_PtsWrap);
+    }
+    else if (i.type == stHEVC)
     {
-      stream = new cTSStream(stH264, it->pID, &m_PtsWrap);
+      stream = new cTSStream(stHEVC, i.pID, &m_PtsWrap);
     }
-    else if (it->type == stMPEG2VIDEO)
+    else if (i.type == stMPEG2VIDEO)
     {
-      stream = new cTSStream(stMPEG2VIDEO, it->pID, &m_PtsWrap);
+      stream = new cTSStream(stMPEG2VIDEO, i.pID, &m_PtsWrap);
     }
-    else if (it->type == stMPEG2AUDIO)
+    else if (i.type == stMPEG2AUDIO)
     {
-      stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap, it->handleRDS);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stMPEG2AUDIO, i.pID, &m_PtsWrap, i.handleRDS);
+      stream->SetLanguage(i.language);
     }
-    else if (it->type == stAACADTS)
+    else if (i.type == stAACADTS)
     {
-      stream = new cTSStream(stAACADTS, it->pID, &m_PtsWrap);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stAACADTS, i.pID, &m_PtsWrap);
+      stream->SetLanguage(i.language);
     }
-    else if (it->type == stAACLATM)
+    else if (i.type == stAACLATM)
     {
-      stream = new cTSStream(stAACLATM, it->pID, &m_PtsWrap);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stAACLATM, i.pID, &m_PtsWrap);
+      stream->SetLanguage(i.language);
     }
-    else if (it->type == stAC3)
+    else if (i.type == stAC3)
     {
-      stream = new cTSStream(stAC3, it->pID, &m_PtsWrap);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stAC3, i.pID, &m_PtsWrap);
+      stream->SetLanguage(i.language);
     }
-    else if (it->type == stEAC3)
+    else if (i.type == stEAC3)
     {
-      stream = new cTSStream(stEAC3, it->pID, &m_PtsWrap);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stEAC3, i.pID, &m_PtsWrap);
+      stream->SetLanguage(i.language);
     }
-    else if (it->type == stDVBSUB)
+    else if (i.type == stDVBSUB)
     {
-      stream = new cTSStream(stDVBSUB, it->pID, &m_PtsWrap);
-      stream->SetLanguage(it->language);
+      stream = new cTSStream(stDVBSUB, i.pID, &m_PtsWrap);
+      stream->SetLanguage(i.language);
 #if APIVERSNUM >= 10709
-      stream->SetSubtitlingDescriptor(it->subtitlingType, it->compositionPageId, it->ancillaryPageId);
+      stream->SetSubtitlingDescriptor(i.subtitlingType, i.compositionPageId, i.ancillaryPageId);
 #endif
     }
-    else if (it->type == stTELETEXT)
+    else if (i.type == stTELETEXT)
     {
-      stream = new cTSStream(stTELETEXT, it->pID, &m_PtsWrap);
+      stream = new cTSStream(stTELETEXT, i.pID, &m_PtsWrap);
     }
     else
       continue;
@@ -476,9 +509,11 @@ bool cVNSIDemuxer::EnsureParsers()
   return streamChange;
 }
 
-void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
+void cVNSIDemuxer::SetChannelStreamInfos(const cChannel *channel)
 {
-  sStreamInfo newStream;
+  m_StreamInfos.clear();
+
+  cStreamInfo newStream;
   bool containsVideo = false;
   int index = 0;
   if (channel->Vpid())
@@ -487,11 +522,13 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
 #if APIVERSNUM >= 10701
     if (channel->Vtype() == 0x1B)
       newStream.type = stH264;
+    else if (channel->Vtype() == 0x24)
+      newStream.type = stHEVC;
     else
 #endif
       newStream.type = stMPEG2VIDEO;
 
-    AddStreamInfo(newStream);
+    m_StreamInfos.push_back(newStream);
     containsVideo = true;
   }
 
@@ -499,17 +536,12 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
   index = 0;
   for ( ; *DPids; DPids++)
   {
-    if (!FindStream(*DPids))
-    {
-      newStream.pID = *DPids;
-      newStream.type = stAC3;
-#if APIVERSNUM >= 10715
-      if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
-        newStream.type = stEAC3;
-#endif
-      newStream.SetLanguage(channel->Dlang(index));
-      AddStreamInfo(newStream);
-    }
+    newStream.pID = *DPids;
+    newStream.type = stAC3;
+    if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
+      newStream.type = stEAC3;
+    newStream.SetLanguage(channel->Dlang(index));
+    m_StreamInfos.push_back(newStream);
     index++;
   }
 
@@ -517,20 +549,15 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
   index = 0;
   for ( ; *APids; APids++)
   {
-    if (!FindStream(*APids))
-    {
-      newStream.pID = *APids;
-      newStream.type = stMPEG2AUDIO;
-#if APIVERSNUM >= 10715
-      if (channel->Atype(index) == 0x0F)
-        newStream.type = stAACADTS;
-      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);
-    }
+    newStream.pID = *APids;
+    newStream.type = stMPEG2AUDIO;
+    if (channel->Atype(index) == 0x0F)
+      newStream.type = stAACADTS;
+    else if (channel->Atype(index) == 0x11)
+      newStream.type = stAACLATM;
+    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));
+    m_StreamInfos.push_back(newStream);
     index++;
   }
 
@@ -540,27 +567,22 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
     index = 0;
     for ( ; *SPids; SPids++)
     {
-      if (!FindStream(*SPids))
-      {
-        newStream.pID = *SPids;
-        newStream.type = stDVBSUB;
-        newStream.SetLanguage(channel->Slang(index));
-#if APIVERSNUM >= 10709
-        newStream.subtitlingType = channel->SubtitlingType(index);
-        newStream.compositionPageId = channel->CompositionPageId(index);
-        newStream.ancillaryPageId = channel->AncillaryPageId(index);
-#endif
-        AddStreamInfo(newStream);
-      }
-      index++;
+      newStream.pID = *SPids;
+      newStream.type = stDVBSUB;
+      newStream.SetLanguage(channel->Slang(index));
+      newStream.subtitlingType = channel->SubtitlingType(index);
+      newStream.compositionPageId = channel->CompositionPageId(index);
+      newStream.ancillaryPageId = channel->AncillaryPageId(index);
+      m_StreamInfos.push_back(newStream);
     }
+    index++;
   }
 
   if (channel->Tpid())
   {
     newStream.pID = channel->Tpid();
     newStream.type = stTELETEXT;
-    AddStreamInfo(newStream);
+    m_StreamInfos.push_back(newStream);
   }
 }
 
@@ -571,9 +593,9 @@ void cVNSIDemuxer::SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser
   int Dpids[MAXDPIDS + 1] = { 0 };
   int Dtypes[MAXDPIDS + 1] = { 0 };
   int Spids[MAXSPIDS + 1] = { 0 };
-  char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" };
-  char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
-  char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
+  char ALangs[MAXAPIDS][MAXLANGCODE2] = { 0 };
+  char DLangs[MAXDPIDS][MAXLANGCODE2] = { 0 };
+  char SLangs[MAXSPIDS][MAXLANGCODE2] = { 0 };
   int index = 0;
 
   const int *aPids = patPmtParser->Apids();
@@ -625,7 +647,7 @@ bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time)
 
   m_VideoBuffer->SetPos(*pos);
   ResetParsers();
-  while (len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime) == 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 fe83f02..73be2bd 100644
--- a/demuxer.h
+++ b/demuxer.h
@@ -33,23 +33,22 @@ class cChannel;
 class cPatPmtParser;
 class cVideoBuffer;
 
-struct sStreamInfo
+class cStreamInfo
 {
-  int pID;
-  eStreamType type;
+public:
+  cStreamInfo();
+  cStreamInfo(const cStreamInfo& info);
+  cStreamInfo& operator=(const cStreamInfo& info) = delete;
+  void SetLanguage(const char* lang);
+
+  int pID = 0;
+  eStreamType type = eStreamType::stNone;
   eStreamContent content;
-  char language[MAXLANGCODE2];
+  char language[MAXLANGCODE2] = { 0 };
   int subtitlingType;
   int compositionPageId;
   int ancillaryPageId;
-  bool handleRDS;
-  void SetLanguage(const char* lang)
-  {
-    language[0] = lang[0];
-    language[1] = lang[1];
-    language[2] = lang[2];
-    language[3] = 0;
-  }
+  bool handleRDS = false;
 };
 
 class cVNSIDemuxer
@@ -57,6 +56,10 @@ class cVNSIDemuxer
 public:
   cVNSIDemuxer(bool bAllowRDS);
   virtual ~cVNSIDemuxer();
+
+  cVNSIDemuxer(const cVNSIDemuxer &) = delete;
+  cVNSIDemuxer &operator=(const cVNSIDemuxer &) = delete;
+
   int Read(sStreamPacket *packet, sStreamPacket *packet_side_data);
   cTSStream *GetFirstStream();
   cTSStream *GetNextStream();
@@ -71,17 +74,15 @@ public:
 protected:
   bool EnsureParsers();
   void ResetParsers();
-  void SetChannelStreams(const cChannel *channel);
+  void SetChannelStreamInfos(const cChannel *channel);
   void SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser);
   cTSStream *FindStream(int Pid);
-  void AddStreamInfo(sStreamInfo &stream);
   bool GetTimeAtPos(off_t *pos, int64_t *time);
   std::list<cTSStream*> m_Streams;
   std::list<cTSStream*>::iterator m_StreamsIterator;
-  std::list<sStreamInfo> m_StreamInfos;
+  std::list<cStreamInfo> m_StreamInfos;
   cChannel m_CurrentChannel;
   cPatPmtParser m_PatPmtParser;
-  int m_OldPmtVersion;
   bool m_WaitIFrame;
   cVideoBuffer *m_VideoBuffer;
   cMutex m_Mutex;
@@ -91,4 +92,5 @@ protected:
   bool m_SetRefTime;
   time_t m_refTime, m_endTime, m_wrapTime;
   bool m_bAllowRDS;
+  bool m_seenFirstPacket;
 };
diff --git a/hash.c b/hash.c
index f4dee4e..5c6ca21 100644
--- a/hash.c
+++ b/hash.c
@@ -23,11 +23,11 @@
  *
  */
 
+#include "hash.h"
+
 #include <vdr/tools.h>
 #include <vdr/channels.h>
 
-#include "hash.h"
-
 static uint32_t crc32_tab[] = {
 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@@ -98,9 +98,21 @@ uint32_t CreateChannelUID(const cChannel* channel) {
 }
 
 const cChannel* FindChannelByUID(uint32_t channelUID) {
-  cChannel* result = NULL;
+  const cChannel* result = NULL;
 
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  // maybe we need to use a lookup table
+  for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel)) {
+    cString channelid = channel->GetChannelID().ToString();
+    if(channelUID == CreateStringHash(channelid)) {
+      result = channel;
+      break;
+    }
+  }
+#else
   // maybe we need to use a lookup table
+  Channels.Lock(false);
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
     cString channelid = channel->GetChannelID().ToString();
     if(channelUID == CreateStringHash(channelid)) {
@@ -108,6 +120,8 @@ const cChannel* FindChannelByUID(uint32_t channelUID) {
       break;
     }
   }
+  Channels.Unlock();
+#endif
 
   return result;
 }
diff --git a/parser.c b/parser.c
index ab8dadf..7740e0f 100644
--- a/parser.c
+++ b/parser.c
@@ -32,6 +32,7 @@
 #include "parser_AC3.h"
 #include "parser_DTS.h"
 #include "parser_h264.h"
+#include "parser_hevc.h"
 #include "parser_MPEGAudio.h"
 #include "parser_MPEGVideo.h"
 #include "parser_Subtitle.h"
@@ -58,8 +59,7 @@ cParser::cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsW
 
 cParser::~cParser()
 {
-  if (m_PesBuffer)
-    free(m_PesBuffer);
+  free(m_PesBuffer);
 }
 
 void cParser::Reset()
@@ -370,13 +370,15 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
       return false;
     }
     m_PesBufferSize += m_PesBufferInitialSize / 10;
-    m_PesBuffer = (uint8_t*)realloc(m_PesBuffer, m_PesBufferSize);
-    if (m_PesBuffer == NULL)
+    uint8_t *new_buffer = (uint8_t*)realloc(m_PesBuffer, m_PesBufferSize);
+    if (new_buffer == NULL)
     {
       ERRORLOG("cParser::AddPESPacket - realloc failed");
       Reset();
       return false;
     }
+
+    m_PesBuffer = new_buffer;
   }
 
   // copy first packet of new frame to front
@@ -401,7 +403,7 @@ inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
     return false;
 
   uint32_t startcode = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
-  if (m_Stream->Type() == stH264 || m_Stream->Type() == stMPEG2VIDEO)
+  if (m_Stream->Type() == stH264 || m_Stream->Type() == stHEVC ||m_Stream->Type() == stMPEG2VIDEO)
   {
     if (startcode >= 0x000001e0 && startcode <= 0x000001ef)
       return true;
@@ -469,6 +471,11 @@ cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSi
     m_pesParser = new cParserH264(m_pID, this, ptsWrap, true);
     m_streamContent = scVIDEO;
   }
+  else if (m_streamType == stHEVC)
+  {
+    m_pesParser = new cParserHEVC(m_pID, this, ptsWrap, true);
+    m_streamContent = scVIDEO;
+  }
   else if (m_streamType == stMPEG2AUDIO)
   {
     m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true, handleSideData);
@@ -694,7 +701,10 @@ bool cTSStream::SetVideoInformation(int FpsScale, int FpsRate, int Height, int W
       (m_Height != Height) ||
       (m_Width != Width) ||
       (m_Aspect != Aspect))
+  {
+    INFOLOG("Video stream change, pid: %d, width: %d, height: %d, aspect: %f", m_pID, Width, Height, Aspect);
     m_IsStreamChange = true;
+  }
 
   m_FpsScale        = FpsScale;
   m_FpsRate         = FpsRate;
@@ -723,7 +733,10 @@ bool cTSStream::SetAudioInformation(int Channels, int SampleRate, int BitRate, i
       (m_BlockAlign != BlockAlign) ||
       (m_BitRate != BitRate) ||
       (m_BitsPerSample != BitsPerSample))
+  {
+    INFOLOG("Audio stream change, pid: %d, channels: %d, samplerate: %d", m_pID, Channels, SampleRate);
     m_IsStreamChange = true;
+  }
 
   m_Channels        = Channels;
   m_SampleRate      = SampleRate;
diff --git a/parser.h b/parser.h
index f15bd2e..b9455b0 100644
--- a/parser.h
+++ b/parser.h
@@ -104,6 +104,7 @@ enum eStreamType
   stDTS,
   stMPEG2VIDEO,
   stH264,
+  stHEVC,
   stDVBSUB,
   stTEXTSUB,
   stTELETEXT,
@@ -149,6 +150,9 @@ public:
   cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParser();
 
+  cParser(const cParser &) = delete;
+  cParser &operator=(const cParser &) = delete;
+
   bool AddPESPacket(uint8_t *data, int size);
   virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) = 0;
 //  void ClearFrame() {m_PesBufferPtr = 0;}
@@ -227,19 +231,22 @@ public:
   cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData = false);
   virtual ~cTSStream();
 
+  cTSStream(const cTSStream &) = delete;
+  cTSStream &operator=(const cTSStream &) = delete;
+
   int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe);
   bool ReadTime(uint8_t *data, int64_t *dts);
   void ResetParser();
 
   void SetLanguage(const char *language);
   const char *GetLanguage() { return m_language; }
-  const eStreamContent Content() const { return m_streamContent; }
-  const eStreamType Type() const { return m_streamType; }
+  eStreamContent Content() const { return m_streamContent; }
+  eStreamType Type() const { return m_streamType; }
   void SetType(eStreamType type) { m_streamType = type; }
-  const int GetPID() const { return m_pID; }
+  int GetPID() const { return m_pID; }
 
   uint32_t AddSideDataType(eStreamContent content);
-  const std::vector< std::pair<uint32_t, eStreamContent> > *GetSideDataTypes() const { return &m_SideDataTypes; }
+  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);
diff --git a/parser_AAC.c b/parser_AAC.c
index 5dab502..9fec5ce 100644
--- a/parser_AAC.c
+++ b/parser_AAC.c
@@ -22,11 +22,11 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
+#include "parser_AAC.h"
 #include "config.h"
 
-#include "parser_AAC.h"
+#include <stdlib.h>
+#include <assert.h>
 
 static int aac_sample_rates[16] =
 {
@@ -154,6 +154,9 @@ int cParserAAC::FindHeaders(uint8_t *buf, int buf_size)
       m_FrameSize = bs.readBits(13);
       m_SampleRate    = aac_sample_rates[SampleRateIndex & 0x0E];
 
+      if (!m_SampleRate)
+        m_SampleRate = aac_sample_rates[4];
+
       m_FoundFrame = true;
       m_DTS = m_curPTS;
       m_PTS = m_curPTS;
diff --git a/parser_AC3.c b/parser_AC3.c
index f401953..9ca7124 100644
--- a/parser_AC3.c
+++ b/parser_AC3.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
-
 #include "parser_AC3.h"
 #include "bitstream.h"
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
 
 #define AC3_HEADER_SIZE 7
 
diff --git a/parser_DTS.c b/parser_DTS.c
index f464892..5395c3b 100644
--- a/parser_DTS.c
+++ b/parser_DTS.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
-
 #include "parser_DTS.h"
 #include "bitstream.h"
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
 
 cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
  : cParser(pID, stream, ptsWrap, observePtsWraps)
diff --git a/parser_MPEGAudio.c b/parser_MPEGAudio.c
index fbbb0d0..2b1ec67 100644
--- a/parser_MPEGAudio.c
+++ b/parser_MPEGAudio.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
-
 #include "parser_MPEGAudio.h"
 #include "bitstream.h"
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
 
 #define MAX_RDS_BUFFER_SIZE 100000
 
@@ -65,11 +65,7 @@ cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWr
 
 cParserMPEG2Audio::~cParserMPEG2Audio()
 {
-  if (m_RDSBuffer)
-  {
-    delete m_RDSBuffer;
-    m_RDSBuffer = NULL;
-  }
+  free(m_RDSBuffer);
 }
 
 void cParserMPEG2Audio::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
@@ -140,14 +136,16 @@ void cParserMPEG2Audio::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
             return;
           }
           m_RDSBufferSize += m_RDSBufferInitialSize / 10;
-          m_RDSBuffer = (uint8_t*)realloc(m_RDSBuffer, m_RDSBufferSize);
-          if (m_RDSBuffer == NULL)
+          uint8_t *new_buffer = (uint8_t *)realloc(m_RDSBuffer, m_RDSBufferSize);
+          if (new_buffer == NULL)
           {
             ERRORLOG("PVR Parser MPEG2-Audio - %s - realloc for RDS data failed", __FUNCTION__);
             m_RDSEnabled = false;
             return;
           }
-        }
+
+          m_RDSBuffer = new_buffer;
+      }
 
         int pes_buffer_ptr = 0;
         for (int i = m_FrameSize-3; i > m_FrameSize-3-rdsl; i--)    // <-- data reverse, from end to start
diff --git a/parser_MPEGVideo.c b/parser_MPEGVideo.c
index 663d09d..b27dd1b 100644
--- a/parser_MPEGVideo.c
+++ b/parser_MPEGVideo.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
+#include "parser_MPEGVideo.h"
 #include "bitstream.h"
+#include "config.h"
 
-#include "parser_MPEGVideo.h"
+#include <stdlib.h>
+#include <assert.h>
 
 using namespace std;
 
diff --git a/parser_Subtitle.c b/parser_Subtitle.c
index bd8e19f..032ec88 100644
--- a/parser_Subtitle.c
+++ b/parser_Subtitle.c
@@ -22,11 +22,11 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
+#include "parser_Subtitle.h"
 #include "config.h"
 
-#include "parser_Subtitle.h"
+#include <stdlib.h>
+#include <assert.h>
 
 cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
  : cParser(pID, stream, ptsWrap, observePtsWraps)
diff --git a/parser_Teletext.c b/parser_Teletext.c
index a5c65a6..8e32dce 100644
--- a/parser_Teletext.c
+++ b/parser_Teletext.c
@@ -22,10 +22,10 @@
  *
  */
 
-#include <stdlib.h>
+#include "parser_Teletext.h"
 #include "config.h"
 
-#include "parser_Teletext.h"
+#include <stdlib.h>
 
 cParserTeletext::cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
  : cParser(pID, stream, ptsWrap, observePtsWraps)
diff --git a/parser_h264.c b/parser_h264.c
index c7f8dde..ced5d88 100644
--- a/parser_h264.c
+++ b/parser_h264.c
@@ -22,12 +22,12 @@
  *
  */
 
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
+#include "parser_h264.h"
 #include "bitstream.h"
+#include "config.h"
 
-#include "parser_h264.h"
+#include <stdlib.h>
+#include <assert.h>
 
 static const int h264_lev2cpbsize[][2] =
 {
diff --git a/parser_hevc.c b/parser_hevc.c
new file mode 100644
index 0000000..34b5901
--- /dev/null
+++ b/parser_hevc.c
@@ -0,0 +1,323 @@
+/*
+ *      vdr-plugin-vnsi - KODI server plugin for VDR
+ *
+ *      Copyright (C) 2005-2012 Team XBMC
+ *      Copyright (C) 2015 Team KODI
+ *
+ *      http://kodi.tv
+ *
+ *  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 KODI; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Warning: This is an unfinished port from H.264 to HEVC in alpha state
+// Tested with German DVB-T2 HD channels
+
+#include "parser_hevc.h"
+#include "bitstream.h"
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+
+cParserHEVC::cParserHEVC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
+{
+  m_Height            = 0;
+  m_Width             = 0;
+  m_FpsScale          = 0;
+  m_PixelAspect.den   = 1;
+  m_PixelAspect.num   = 0;
+  memset(&m_streamData, 0, sizeof(m_streamData));
+  m_PesBufferInitialSize = 240000;
+
+  m_IsVideo = true;
+  Reset();
+}
+
+cParserHEVC::~cParserHEVC()
+{
+}
+
+void cParserHEVC::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
+{
+  if (m_PesBufferPtr < 10) // 2*startcode + header + trail bits
+    return;
+
+  int p = m_PesParserPtr;
+  uint32_t startcode = m_StartCode;
+  bool frameComplete = false;
+
+  while (m_PesBufferPtr - p)
+  {
+    startcode = startcode << 8 | m_PesBuffer[p++];
+    if ((startcode & 0x00ffffff) == 0x00000001)
+    {
+      if (m_LastStartPos != -1)
+         Parse_HEVC(m_LastStartPos, p-m_LastStartPos, &frameComplete);
+      m_LastStartPos = p;
+      if (frameComplete)
+        break;
+    }
+  }
+  m_PesParserPtr = p;
+  m_StartCode = startcode;
+
+  if (frameComplete)
+  {
+    if (!m_NeedSPS && m_FrameValid)
+    {
+      double PAR = (double)m_PixelAspect.num/(double)m_PixelAspect.den;
+      double DAR = (PAR * m_Width) / m_Height;
+      DEBUGLOG("HEVC SPS: PAR %i:%i", m_PixelAspect.num, m_PixelAspect.den);
+      DEBUGLOG("HEVC SPS: DAR %.2f", DAR);
+
+      int duration;
+      if (m_curDTS != DVD_NOPTS_VALUE && m_prevDTS != DVD_NOPTS_VALUE && m_curDTS > m_prevDTS)
+        duration = m_curDTS - m_prevDTS;
+      else
+        duration = m_Stream->Rescale(20000, 90000, DVD_TIME_BASE);
+
+      if (m_FpsScale == 0)
+        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);
+
+      pkt->id       = m_pID;
+      pkt->size     = m_PesNextFramePtr;
+      pkt->data     = m_PesBuffer;
+      pkt->dts      = m_DTS;
+      pkt->pts      = m_PTS;
+      pkt->duration = duration;
+      pkt->streamChange = streamChange;
+
+    }
+    m_StartCode = 0xffffffff;
+    m_LastStartPos = -1;
+    m_PesParserPtr = 0;
+    m_FoundFrame = false;
+    m_FrameValid = true;
+  }
+}
+
+void cParserHEVC::Reset()
+{
+  cParser::Reset();
+  m_StartCode = 0xffffffff;
+  m_LastStartPos = -1;
+  m_NeedSPS = true;
+  m_NeedPPS = true;
+  memset(&m_streamData, 0, sizeof(m_streamData));
+}
+
+
+void cParserHEVC::Parse_HEVC(int buf_ptr, unsigned int NumBytesInNalUnit, bool *complete)
+{
+  uint8_t *buf = m_PesBuffer + buf_ptr;
+  uint16_t header;
+  HDR_NAL hdr;
+
+  // nal_unit_header
+  header = (buf[0] << 8) | buf[1];
+  if (header & 0x8000) // ignore forbidden_bit == 1
+    return;
+  hdr.nal_unit_type   = (header & 0x7e00) >> 9;
+  hdr.nuh_layer_id    = (header &  0x1f8) >> 3;
+  hdr.nuh_temporal_id = (header &    0x7) - 1;
+
+  switch (hdr.nal_unit_type)
+  {
+  case NAL_TRAIL_N ... NAL_RASL_R:
+  case NAL_BLA_W_LP ... NAL_CRA_NUT:
+  {
+    if (m_NeedSPS || m_NeedPPS)
+    {
+      m_FoundFrame = true;
+      return;
+    }
+    hevc_private::VCL_NAL vcl;
+    memset(&vcl, 0, sizeof(hevc_private::VCL_NAL));
+    Parse_SLH(buf, NumBytesInNalUnit, hdr, vcl);
+ 
+    // check for the beginning of a new access unit
+    if (m_FoundFrame && IsFirstVclNal(vcl))
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr - 3;
+      return;
+    }
+
+    if (!m_FoundFrame)
+    {
+      if (buf_ptr - 3 >= m_PesTimePos)
+      {
+        m_DTS = m_curDTS;
+        m_PTS = m_curPTS;
+      }
+      else
+      {
+        m_DTS = m_prevDTS;
+        m_PTS = m_prevPTS;
+      }
+    }
+
+    m_streamData.vcl_nal = vcl;
+    m_FoundFrame = true;
+    break;
+  }
+
+  case NAL_PFX_SEI_NUT:
+    if (m_FoundFrame)
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr - 3;
+    }
+    break;
+
+  case NAL_VPS_NUT:
+     break;
+
+  case NAL_SPS_NUT:
+  {
+    if (m_FoundFrame)
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr - 3;
+      return;
+    }
+    Parse_SPS(buf, NumBytesInNalUnit, hdr);
+    m_NeedSPS = false;
+    break;
+  }
+
+  case NAL_PPS_NUT:
+  {
+    if (m_FoundFrame)
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr - 3;
+      return;
+    }
+    Parse_PPS(buf, NumBytesInNalUnit);
+    m_NeedPPS = false;
+    break;
+  }
+
+  case NAL_AUD_NUT:
+    if (m_FoundFrame && (m_prevPTS != DVD_NOPTS_VALUE))
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr - 3;
+    }
+    break;
+
+  case NAL_EOS_NUT:
+    if (m_FoundFrame)
+    {
+      *complete = true;
+      m_PesNextFramePtr = buf_ptr + 2;
+    }
+    break;
+
+  case NAL_FD_NUT:
+  case NAL_SFX_SEI_NUT:
+     break;
+
+  default:
+    INFOLOG("HEVC fixme: nal unknown %i", hdr.nal_unit_type);
+    break;
+  }
+}
+
+void cParserHEVC::Parse_PPS(uint8_t *buf, int len)
+{
+  cBitstream bs(buf, len*8, true);
+
+  int pps_id = bs.readGolombUE();
+  int sps_id = bs.readGolombUE();
+  m_streamData.pps[pps_id].sps = sps_id;
+  m_streamData.pps[pps_id].dependent_slice_segments_enabled_flag = bs.readBits(1);
+}
+
+void cParserHEVC::Parse_SLH(uint8_t *buf, int len, HDR_NAL hdr, hevc_private::VCL_NAL &vcl)
+{
+  cBitstream bs(buf, len*8, true);
+
+  vcl.nal_unit_type = hdr.nal_unit_type;
+
+  vcl.first_slice_segment_in_pic_flag = bs.readBits(1);
+
+  if ((hdr.nal_unit_type >= NAL_BLA_W_LP) && (hdr.nal_unit_type <= NAL_RSV_IRAP_VCL23))
+    bs.skipBits(1); // no_output_of_prior_pics_flag
+
+  vcl.pic_parameter_set_id = bs.readGolombUE();
+}
+
+// 7.3.2.2.1 General sequence parameter set RBSP syntax
+void cParserHEVC::Parse_SPS(uint8_t *buf, int len, HDR_NAL hdr)
+{
+  cBitstream bs(buf, len*8, true);
+  unsigned int i;
+  int sub_layer_profile_present_flag[8], sub_layer_level_present_flag[8];
+
+  bs.skipBits(4); // sps_video_parameter_set_id
+
+  unsigned int sps_max_sub_layers_minus1 = bs.readBits(3);
+  bs.skipBits(1); // sps_temporal_id_nesting_flag
+
+  // skip over profile_tier_level
+  bs.skipBits(8 + 32 + 4 + 43 + 1 +8);
+  for (i=0; i<sps_max_sub_layers_minus1; i++)
+  {
+    sub_layer_profile_present_flag[i] = bs.readBits(1);
+    sub_layer_level_present_flag[i] = bs.readBits(1);
+  }
+  if (sps_max_sub_layers_minus1 > 0)
+  {
+    for (i=sps_max_sub_layers_minus1; i<8; i++)
+      bs.skipBits(2);
+  }
+  for (i=0; i<sps_max_sub_layers_minus1; i++)
+  {
+    if (sub_layer_profile_present_flag[i])
+      bs.skipBits(8 + 32 + 4 + 43 + 1);
+    if (sub_layer_level_present_flag[i])
+      bs.skipBits(8);
+  }
+  // end skip over profile_tier_level
+
+  bs.readGolombUE(); // sps_seq_parameter_set_id
+  unsigned int chroma_format_idc = bs.readGolombUE();
+ 
+  if (chroma_format_idc == 3)
+    bs.skipBits(1); // separate_colour_plane_flag
+
+  m_Width  = bs.readGolombUE();
+  m_Height = bs.readGolombUE();
+  m_PixelAspect.num = 1;
+}
+
+bool cParserHEVC::IsFirstVclNal(hevc_private::VCL_NAL &vcl)
+{
+  if (m_streamData.vcl_nal.pic_parameter_set_id != vcl.pic_parameter_set_id)
+    return true;
+
+  if (vcl.first_slice_segment_in_pic_flag)
+    return true;
+
+  return false;
+}
+
diff --git a/parser_hevc.h b/parser_hevc.h
new file mode 100644
index 0000000..e29b775
--- /dev/null
+++ b/parser_hevc.h
@@ -0,0 +1,123 @@
+/*
+ *      vdr-plugin-vnsi - KODI server plugin for VDR
+ *
+ *      Copyright (C) 2005-2012 Team XBMC
+ *      Copyright (C) 2015 Team KODI
+ *
+ *      http://kodi.tv
+ *
+ *  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 KODI; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef VNSI_DEMUXER_HEVC_H
+#define VNSI_DEMUXER_HEVC_H
+
+#include "parser.h"
+
+class cBitstream;
+
+// --- cParserHEVC -------------------------------------------------
+
+class cParserHEVC : public cParser
+{
+private:
+  typedef struct hevc_private
+  {
+    struct PPS
+    {
+      int sps;
+      int dependent_slice_segments_enabled_flag;
+    } pps[64];
+
+    struct VCL_NAL
+    {
+      int pic_parameter_set_id; // slice
+      unsigned int first_slice_segment_in_pic_flag;
+      unsigned int nal_unit_type;
+    } vcl_nal;
+
+  } hevc_private_t;
+
+  typedef struct HDR_NAL_t
+  {
+     uint nal_unit_type;
+     uint nuh_layer_id;
+     uint nuh_temporal_id;
+  } HDR_NAL;
+
+  typedef struct mpeg_rational_s {
+    int num;
+    int den;
+  } mpeg_rational_t;
+
+  enum
+  {
+    NAL_TRAIL_N  = 0x00, // Coded slice segment of trailing picture
+    NAL_TRAIL_R  = 0x01, // Coded slice segment of trailing picture
+    NAL_TSA_N    = 0x02, // Coded slice segment of TSA picture
+    NAL_TSA_R    = 0x03, // Coded slice segment of TSA picture
+    NAL_STSA_N   = 0x04, // Coded slice segment of STSA picture
+    NAL_STSA_R   = 0x05, // Coded slice segment of STSA picture
+    NAL_RADL_N   = 0x06, // Coded slice segment of RADL picture
+    NAL_RADL_R   = 0x07, // Coded slice segment of RADL picture
+    NAL_RASL_N   = 0x08, // Coded slice segment of RASL picture
+    NAL_RASL_R   = 0x09, // Coded slice segment of RASL picture
+
+    NAL_BLA_W_LP = 0x10, // Coded slice segment of a BLA picture
+    NAL_CRA_NUT  = 0x15, // Coded slice segment of a CRA picture
+    NAL_RSV_IRAP_VCL22 = 0x16, // Reserved IRAP VCL NAL unit types
+    NAL_RSV_IRAP_VCL23 = 0x17, // Reserved IRAP VCL NAL unit types
+
+    NAL_VPS_NUT  = 0x20, // Video Parameter SET
+    NAL_SPS_NUT  = 0x21, // Sequence Parameter Set
+    NAL_PPS_NUT  = 0x22, // Picture Parameter Set
+    NAL_AUD_NUT  = 0x23, // Access Unit Delimiter
+    NAL_EOS_NUT  = 0x24, // End of Sequence
+    NAL_EOB_NUT  = 0x25, // End of Bitstream
+    NAL_FD_NUT   = 0x26, // Filler Data
+    NAL_PFX_SEI_NUT  = 0x27, // Supplemental Enhancement Information 
+    NAL_SFX_SEI_NUT  = 0x28, // Supplemental Enhancement Information
+
+  };
+
+  uint32_t        m_StartCode;
+  int             m_LastStartPos;
+  bool            m_NeedSPS;
+  bool            m_NeedPPS;
+  int             m_Width;
+  int             m_Height;
+  int             m_FpsScale;
+  mpeg_rational_t m_PixelAspect;
+  hevc_private    m_streamData;
+  int64_t         m_DTS;
+  int64_t         m_PTS;
+
+  void Parse_HEVC(int buf_ptr, unsigned int NumBytesInNalUnit, bool *complete);
+  void Parse_PPS(uint8_t *buf, int len);
+  void Parse_SLH(uint8_t *buf, int len, HDR_NAL hdr, hevc_private::VCL_NAL &vcl);
+  void Parse_SPS(uint8_t *buf, int len, HDR_NAL hdr);
+  bool IsFirstVclNal(hevc_private::VCL_NAL &vcl);
+
+public:
+  cParserHEVC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
+  virtual ~cParserHEVC();
+
+  virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
+  virtual void Reset();
+};
+
+
+#endif // VNSI_DEMUXER_HEVC_H
diff --git a/po/de_DE.po b/po/de_DE.po
index 0edfa03..00d9411 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VNSI-Server 1.0.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2015-04-04 14:32+0200\n"
+"POT-Creation-Date: 2016-04-23 09:08+0200\n"
 "PO-Revision-Date: 2015-01-23 21:46+0100\n"
 "Last-Translator: Alwin Esch\n"
 "Language-Team: German\n"
@@ -21,9 +21,6 @@ msgstr ""
 msgid "Your scanner version is to old - Please upgrade."
 msgstr ""
 
-msgid "PMT Timeout (0-10)"
-msgstr "PMT Auszeit (0-10)"
-
 msgid "Off"
 msgstr "Aus"
 
@@ -51,6 +48,9 @@ msgstr "Wiedergeben als Aufzeichnung statt Live"
 msgid "Avoid EPG scan while streaming"
 msgstr "Keine EPG suche während der Wiedergabe durchführen"
 
+msgid "Disable scramble timeout"
+msgstr ""
+
 msgid "Recording with the same name exists"
 msgstr "Aufnahme mit der selben größe existiert"
 
@@ -65,3 +65,6 @@ msgstr "Fehler beim Zugriff der Indexdatei"
 
 msgid "Deleted recording vanished"
 msgstr "Gelöschte Aufnahme verschwunden"
+
+#~ msgid "PMT Timeout (0-10)"
+#~ msgstr "PMT Auszeit (0-10)"
diff --git a/po/lt_LT.po b/po/lt_LT.po
index a3c985d..2f979df 100644
--- a/po/lt_LT.po
+++ b/po/lt_LT.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VNSI-Server 1.0.0\n"
 "Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2015-04-04 14:32+0200\n"
+"POT-Creation-Date: 2016-04-23 09:08+0200\n"
 "PO-Revision-Date: 2015-02-11 22:30+0200\n"
 "Last-Translator: Valdemaras Pipiras\n"
 "Language-Team: Lithuanian\n"
@@ -21,9 +21,6 @@ msgstr ""
 msgid "Your scanner version is to old - Please upgrade."
 msgstr ""
 
-msgid "PMT Timeout (0-10)"
-msgstr "PMT  (0-10)"
-
 msgid "Off"
 msgstr "Išjungta"
 
@@ -51,6 +48,9 @@ msgstr "Groti įrašą vietoj gyvos transliacijos"
 msgid "Avoid EPG scan while streaming"
 msgstr "Vengti EPG skanavimo kol vyksta transliacija"
 
+msgid "Disable scramble timeout"
+msgstr ""
+
 msgid "Recording with the same name exists"
 msgstr "Jau yra įrašų tokiu pat pavadinimu"
 
@@ -65,3 +65,6 @@ msgstr "Klaida bandant atidaryti index failą"
 
 msgid "Deleted recording vanished"
 msgstr "Ištrintas įrašas galutinai išvalytas"
+
+#~ msgid "PMT Timeout (0-10)"
+#~ msgstr "PMT  (0-10)"
diff --git a/recordingscache.c b/recordingscache.c
index b2892d8..1c4ff6e 100644
--- a/recordingscache.c
+++ b/recordingscache.c
@@ -22,8 +22,8 @@
  *
  */
 
-#include "config.h"
 #include "recordingscache.h"
+#include "config.h"
 #include "vnsiclient.h"
 #include "hash.h"
 
@@ -38,7 +38,7 @@ cRecordingsCache& cRecordingsCache::GetInstance() {
   return singleton;
 }
 
-uint32_t cRecordingsCache::Register(cRecording* recording, bool deleted) {
+uint32_t cRecordingsCache::Register(const cRecording* recording, bool deleted) {
   cString filename = recording->FileName();
   uint32_t uid = CreateStringHash(filename);
 
@@ -54,7 +54,7 @@ uint32_t cRecordingsCache::Register(cRecording* recording, bool deleted) {
   return uid;
 }
 
-cRecording* cRecordingsCache::Lookup(uint32_t uid) {
+const cRecording* cRecordingsCache::Lookup(uint32_t uid) {
   DEBUGLOG("%s - lookup uid: %08x", __FUNCTION__, uid);
 
   if(m_recordings.find(uid) == m_recordings.end()) {
@@ -66,11 +66,65 @@ cRecording* cRecordingsCache::Lookup(uint32_t uid) {
   cString filename = m_recordings[uid].filename;
   DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
 
+  const cRecording* r;
+  if (!m_recordings[uid].isDeleted)
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_RECORDINGS_READ;
+    r = Recordings->GetByName(filename);
+#else
+    r = Recordings.GetByName(filename);
+#endif
+  }
+  else
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_DELETEDRECORDINGS_READ;
+    r = DeletedRecordings->GetByName(filename);
+#else
+    r = DeletedRecordings.GetByName(filename);
+#endif
+  }
+
+  DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found");
+  m_mutex.Unlock();
+
+  return r;
+}
+
+cRecording* cRecordingsCache::LookupWrite(uint32_t uid)
+{
+  DEBUGLOG("%s - lookup uid: %08x", __FUNCTION__, uid);
+
+  if(m_recordings.find(uid) == m_recordings.end())
+  {
+    DEBUGLOG("%s - not found !", __FUNCTION__);
+    return NULL;
+  }
+
+  m_mutex.Lock();
+  cString filename = m_recordings[uid].filename;
+  DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
+
   cRecording* r;
   if (!m_recordings[uid].isDeleted)
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_RECORDINGS_WRITE;
+    r = Recordings->GetByName(filename);
+#else
     r = Recordings.GetByName(filename);
+#endif
+  }
   else
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_DELETEDRECORDINGS_WRITE;
+    r = DeletedRecordings->GetByName(filename);
+#else
     r = DeletedRecordings.GetByName(filename);
+#endif
+  }
 
   DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found");
   m_mutex.Unlock();
diff --git a/recordingscache.h b/recordingscache.h
index 7fb9180..2dac71d 100644
--- a/recordingscache.h
+++ b/recordingscache.h
@@ -43,9 +43,10 @@ public:
 
   static cRecordingsCache& GetInstance();
 
-  uint32_t Register(cRecording* recording, bool deleted = false);
+  uint32_t Register(const cRecording* recording, bool deleted = false);
 
-  cRecording* Lookup(uint32_t uid);
+  const cRecording* Lookup(uint32_t uid);
+  cRecording* LookupWrite(uint32_t uid);
 
 private:
   struct RecordingsInfo
diff --git a/recplayer.c b/recplayer.c
index 870d388..710b2c5 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -38,31 +38,23 @@
 #define O_NOATIME 0
 #endif
 
-cRecPlayer::cRecPlayer(cRecording* rec, bool inProgress)
+cRecPlayer::cRecPlayer(const cRecording* rec, bool inProgress)
+  :m_inProgress(inProgress),
+   m_recordingFilename(rec->FileName()),
+   m_pesrecording(rec->IsPesRecording()),
+   m_indexFile(m_recordingFilename.c_str(), false, m_pesrecording),
+   m_file(-1), m_fileOpen(-1)
 {
-  m_file          = -1;
-  m_fileOpen      = -1;
-  m_recordingFilename = strdup(rec->FileName());
-  m_inProgress = inProgress;
-
   // FIXME find out max file path / name lengths
-#if VDRVERSNUM < 10703
-  m_pesrecording = true;
-  m_indexFile = new cIndexFile(m_recordingFilename, false);
-#else
-  m_pesrecording = rec->IsPesRecording();
-  if(m_pesrecording) INFOLOG("recording '%s' is a PES recording", m_recordingFilename);
-  m_indexFile = new cIndexFile(m_recordingFilename, false, m_pesrecording);
-#endif
+
+  if(m_pesrecording)
+    INFOLOG("recording '%s' is a PES recording", m_recordingFilename.c_str());
 
   scan();
 }
 
 void cRecPlayer::cleanup() {
-  for(int i = 0; i != m_segments.Size(); i++) {
-    delete m_segments[i];
-  }
-  m_segments.Clear();
+  m_segments.clear();
 }
 
 void cRecPlayer::scan()
@@ -85,17 +77,17 @@ void cRecPlayer::scan()
       break;
     }
 
-    cSegment* segment = new cSegment();
-    segment->start = m_totalLength;
-    segment->end = segment->start + s.st_size;
+    cSegment segment;
+    segment.start = m_totalLength;
+    segment.end = segment.start + s.st_size;
 
-    m_segments.Append(segment);
+    m_segments.push_back(segment);
 
     m_totalLength += s.st_size;
     INFOLOG("File %i found, size: %lu, totalLength now %lu", i, s.st_size, m_totalLength);
   }
 
-  m_totalFrames = m_indexFile->Last();
+  m_totalFrames = m_indexFile.Last();
   INFOLOG("total frames: %u", m_totalFrames);
 }
 
@@ -105,7 +97,7 @@ void cRecPlayer::reScan()
 
   m_totalLength = 0;
 
-  for(int i = 0; ; i++) // i think we only need one possible loop
+  for(size_t i = 0; ; i++) // i think we only need one possible loop
   {
     fileNameFromIndex(i);
 
@@ -114,21 +106,21 @@ void cRecPlayer::reScan()
     }
 
     cSegment* segment;
-    if (m_segments.Size() < i+1)
+    if (m_segments.size() < i+1)
     {
-      segment = new cSegment();
-      m_segments.Append(segment);
+      m_segments.push_back(cSegment());
+      segment = &m_segments.back();
       segment->start = m_totalLength;
     }
     else
-      segment = m_segments[i];
+      segment = &m_segments[i];
 
     segment->end = segment->start + s.st_size;
 
     m_totalLength += s.st_size;
   }
 
-  m_totalFrames = m_indexFile->Last();
+  m_totalFrames = m_indexFile.Last();
 }
 
 
@@ -136,14 +128,13 @@ cRecPlayer::~cRecPlayer()
 {
   cleanup();
   closeFile();
-  free(m_recordingFilename);
 }
 
 char* cRecPlayer::fileNameFromIndex(int index) {
   if (m_pesrecording)
-    snprintf(m_fileName, sizeof(m_fileName), "%s/%03i.vdr", m_recordingFilename, index+1);
+    snprintf(m_fileName, sizeof(m_fileName), "%s/%03i.vdr", m_recordingFilename.c_str(), index+1);
   else
-    snprintf(m_fileName, sizeof(m_fileName), "%s/%05i.ts", m_recordingFilename, index+1);
+    snprintf(m_fileName, sizeof(m_fileName), "%s/%05i.ts", m_recordingFilename.c_str(), index+1);
 
   return m_fileName;
 }
@@ -212,25 +203,25 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
     amount = m_totalLength - position;
 
   // work out what block "position" is in
-  int segmentNumber = -1;
-  for(int i = 0; i < m_segments.Size(); i++)
-  {
-    if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) {
-      segmentNumber = i;
+  std::vector<cSegment>::iterator begin = m_segments.begin(),
+    end = m_segments.end(), segmentIterator = end;
+  for (std::vector<cSegment>::iterator i = begin; i != end; ++i) {
+    if ((position >= i->start) && (position < i->end)) {
+      segmentIterator = i;
       break;
     }
   }
 
   // segment not found / invalid position
-  if (segmentNumber == -1)
+  if (segmentIterator == end)
     return 0;
 
   // open file (if not already open)
-  if (!openFile(segmentNumber))
+  if (!openFile(std::distance(begin, segmentIterator)))
     return 0;
 
   // work out position in current file
-  uint64_t filePosition = position - m_segments[segmentNumber]->start;
+  uint64_t filePosition = position - segmentIterator->start;
 
   // seek to position
   if(lseek(m_file, filePosition, SEEK_SET) == -1)
@@ -264,55 +255,44 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
 
 uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
 {
-  if (!m_indexFile)
-    return 0;
-#if VDRVERSNUM < 10703
-  unsigned char retFileNumber;
-  int retFileOffset;
-  unsigned char retPicType;
-#else
   uint16_t retFileNumber;
   off_t retFileOffset;
   bool retPicType;
-#endif
   int retLength;
 
-
-  if (!m_indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
+  if (!m_indexFile.Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
     return 0;
 
-  if (retFileNumber >= m_segments.Size()) 
+  if (retFileNumber >= m_segments.size()) 
     return 0;
 
-  uint64_t position = m_segments[retFileNumber]->start + retFileOffset;
+  uint64_t position = m_segments[retFileNumber].start + retFileOffset;
   return position;
 }
 
 uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
 {
-  if (!m_indexFile) return 0;
-
   if (position >= m_totalLength)
   {
     DEBUGLOG("Client asked for data starting past end of recording!");
     return m_totalFrames;
   }
 
-  int segmentNumber = -1;
-  for(int i = 0; i < m_segments.Size(); i++)
-  {
-    if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) {
-      segmentNumber = i;
+  std::vector<cSegment>::iterator begin = m_segments.begin(),
+    end = m_segments.end(), segmentIterator = end;
+  for (std::vector<cSegment>::iterator i = begin; i != end; ++i) {
+    if ((position >= i->start) && (position < i->end)) {
+      segmentIterator = i;
       break;
     }
   }
 
-  if(segmentNumber == -1) {
+  if (segmentIterator == end)
     return m_totalFrames;
-  }
 
-  uint32_t askposition = position - m_segments[segmentNumber]->start;
-  return m_indexFile->Get((int)segmentNumber, askposition);
+  uint32_t askposition = position - segmentIterator->start;
+  int segmentNumber = std::distance(begin, segmentIterator);
+  return m_indexFile.Get((int)segmentNumber, askposition);
 }
 
 
@@ -321,20 +301,13 @@ bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_
   // 0 = backwards
   // 1 = forwards
 
-  if (!m_indexFile) return false;
-
-#if VDRVERSNUM < 10703
-  unsigned char waste1;
-  int waste2;
-#else
   uint16_t waste1;
   off_t waste2;
-#endif
 
   int iframeLength;
   int indexReturnFrameNumber;
 
-  indexReturnFrameNumber = (uint32_t)m_indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
+  indexReturnFrameNumber = (uint32_t)m_indexFile.GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
   DEBUGLOG("GNIF input framenumber:%u, direction=%u, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
 
   if (indexReturnFrameNumber == -1) return false;
diff --git a/recplayer.h b/recplayer.h
index 3d7b520..ce04cf6 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -33,10 +33,12 @@
 
 #include <stdio.h>
 #include <vdr/recording.h>
-#include <vdr/tools.h>
 
 #include "config.h"
 
+#include <vector>
+#include <string>
+
 class cSegment
 {
   public:
@@ -47,7 +49,7 @@ class cSegment
 class cRecPlayer
 {
 public:
-  cRecPlayer(cRecording* rec, bool inProgress = false);
+  cRecPlayer(const cRecording* rec, bool inProgress = false);
   ~cRecPlayer();
   uint64_t getLengthBytes();
   uint32_t getLengthFrames();
@@ -67,16 +69,16 @@ private:
   char* fileNameFromIndex(int index);
   void checkBufferSize(int s);
 
-  char        m_fileName[512];
-  cIndexFile *m_indexFile;
-  int         m_file;
-  int         m_fileOpen;
-  cVector<cSegment*> m_segments;
-  uint64_t    m_totalLength;
-  uint32_t    m_totalFrames;
-  char       *m_recordingFilename;
-  bool        m_pesrecording;
-  bool        m_inProgress;
+  const bool m_inProgress;
+  const std::string m_recordingFilename;
+  const bool m_pesrecording;
+  cIndexFile m_indexFile;
+  int m_file;
+  int m_fileOpen;
+  char m_fileName[512];
+  std::vector<cSegment> m_segments;
+  uint64_t m_totalLength;
+  uint32_t m_totalFrames;
 };
 
 #endif // VNSI_RECPLAYER_H
diff --git a/requestpacket.c b/requestpacket.c
index 4ab2b30..86fec04 100644
--- a/requestpacket.c
+++ b/requestpacket.c
@@ -24,8 +24,11 @@
  *
  */
 
+#include "requestpacket.h"
+#include "vnsicommand.h"
+#include "config.h"
+
 #include <stdlib.h>
-#include <stdint.h>
 #include <string.h>
 
 #ifndef __FreeBSD__
@@ -36,15 +39,10 @@
 #define __cpu_to_be64 htobe64
 #endif
 
-#include "config.h"
-#include "requestpacket.h"
-#include "vnsicommand.h"
-
-cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength)
+cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, size_t dataLength)
  : userData(data), userDataLength(dataLength), opCode(opcode), requestID(requestID)
 {
   packetPos       = 0;
-  ownBlock        = true;
   channelID       = 0;
   streamID        = 0;
   flag            = 0;
@@ -52,37 +50,31 @@ cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* dat
 
 cRequestPacket::~cRequestPacket()
 {
-  if (!ownBlock) return; // don't free if it's a getblock
-
-  if (userData) free(userData);
+  delete[] userData;
 }
 
-bool cRequestPacket::end()
+bool cRequestPacket::end() const
 {
   return (packetPos >= userDataLength);
 }
 
-int cRequestPacket::serverError()
-{
-  if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1;
-  else return 0;
-}
-
 char* cRequestPacket::extract_String()
 {
-  if (serverError()) return NULL;
+  char *p = (char *)&userData[packetPos];
+  const char *end = (const char *)memchr(p, '\0', userDataLength - packetPos);
+  if (end == NULL)
+    /* string is not terminated - fail */
+    throw MalformedVNSIPacket();
 
-  int length = strlen((char*)&userData[packetPos]);
-  if ((packetPos + length) > userDataLength) return NULL;
-  char* str = new char[length + 1];
-  strcpy(str, (char*)&userData[packetPos]);
+  int length = end - p;
   packetPos += length + 1;
-  return str;
+  return p;
 }
 
 uint8_t cRequestPacket::extract_U8()
 {
-  if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(uint8_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   uint8_t uc = userData[packetPos];
   packetPos += sizeof(uint8_t);
   return uc;
@@ -90,7 +82,8 @@ uint8_t cRequestPacket::extract_U8()
 
 uint32_t cRequestPacket::extract_U32()
 {
-  if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(uint32_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   uint32_t ul;
   memcpy(&ul, &userData[packetPos], sizeof(uint32_t));
   ul = ntohl(ul);
@@ -100,7 +93,8 @@ uint32_t cRequestPacket::extract_U32()
 
 uint64_t cRequestPacket::extract_U64()
 {
-  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(uint64_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   uint64_t ull;
   memcpy(&ull, &userData[packetPos], sizeof(uint64_t));
   ull = __be64_to_cpu(ull);
@@ -110,7 +104,8 @@ uint64_t cRequestPacket::extract_U64()
 
 int64_t cRequestPacket::extract_S64()
 {
-  if ((packetPos + sizeof(int64_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(int64_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   int64_t ll;
   memcpy(&ll, &userData[packetPos], sizeof(int64_t));
   ll = __be64_to_cpu(ll);
@@ -120,7 +115,8 @@ int64_t cRequestPacket::extract_S64()
 
 double cRequestPacket::extract_Double()
 {
-  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(uint64_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   uint64_t ull;
   memcpy(&ull, &userData[packetPos], sizeof(uint64_t));
   ull = __be64_to_cpu(ull);
@@ -132,7 +128,8 @@ double cRequestPacket::extract_Double()
 
 int32_t cRequestPacket::extract_S32()
 {
-  if ((packetPos + sizeof(int32_t)) > userDataLength) return 0;
+  if ((packetPos + sizeof(int32_t)) > userDataLength)
+    throw MalformedVNSIPacket();
   int32_t l;
   memcpy(&l, &userData[packetPos], sizeof(int32_t));
   l = ntohl(l);
@@ -142,6 +139,5 @@ int32_t cRequestPacket::extract_S32()
 
 uint8_t* cRequestPacket::getData()
 {
-  ownBlock = false;
   return userData;
 }
diff --git a/requestpacket.h b/requestpacket.h
index 368f971..65d0f66 100644
--- a/requestpacket.h
+++ b/requestpacket.h
@@ -27,20 +27,29 @@
 #ifndef VNSI_REQUESTPACKET_H
 #define VNSI_REQUESTPACKET_H
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <stdexcept>
+
+class MalformedVNSIPacket : public std::runtime_error {
+public:
+  MalformedVNSIPacket()
+    :std::runtime_error("Malformed VNSI packet") {}
+};
+
 class cRequestPacket
 {
 public:
-  cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength);
+  cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, size_t dataLength);
   ~cRequestPacket();
 
-  int  serverError();
-
-  uint32_t  getDataLength()     { return userDataLength; }
-  uint32_t  getChannelID()      { return channelID; }
-  uint32_t  getRequestID()      { return requestID; }
-  uint32_t  getStreamID()       { return streamID; }
-  uint32_t  getFlag()           { return flag; }
-  uint32_t  getOpCode()         { return opCode; }
+  size_t    getDataLength() const { return userDataLength; }
+  uint32_t  getChannelID() const { return channelID; }
+  uint32_t  getRequestID() const { return requestID; }
+  uint32_t  getStreamID() const { return streamID; }
+  uint32_t  getFlag() const { return flag; }
+  uint32_t  getOpCode() const { return opCode; }
 
   char*     extract_String();
   uint8_t   extract_U8();
@@ -50,15 +59,15 @@ public:
   int32_t   extract_S32();
   double    extract_Double();
 
-  bool      end();
+  bool      end() const;
 
   // If you call this, the memory becomes yours. Free with free()
   uint8_t* getData();
 
 private:
   uint8_t* userData;
-  uint32_t userDataLength;
-  uint32_t packetPos;
+  size_t userDataLength;
+  size_t packetPos;
   uint32_t opCode;
 
   uint32_t channelID;
@@ -67,8 +76,6 @@ private:
   uint32_t streamID;
 
   uint32_t flag; // stream only
-
-  bool ownBlock;
 };
 
 #endif // VNSI_REQUESTPACKET_H
diff --git a/responsepacket.c b/responsepacket.c
index 92ad9ae..8e944f1 100644
--- a/responsepacket.c
+++ b/responsepacket.c
@@ -28,10 +28,13 @@
  * This code is taken from VOMP for VDR plugin.
  */
 
+#include "responsepacket.h"
+#include "vnsicommand.h"
+#include "config.h"
+
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <string.h>
-#include <inttypes.h>
 
 #ifndef __FreeBSD__
 #include <asm/byteorder.h>
@@ -41,10 +44,6 @@
 #define __cpu_to_be64 htobe64
 #endif
 
-#include "responsepacket.h"
-#include "vnsicommand.h"
-#include "config.h"
-
 /* Packet format for an RR channel response:
 
 4 bytes = channel ID = 1 (request/response channel)
@@ -73,7 +72,7 @@ void cResponsePacket::initBuffers()
   }
 }
 
-bool cResponsePacket::init(uint32_t requestID)
+void cResponsePacket::init(uint32_t requestID)
 {
   initBuffers();
 
@@ -87,11 +86,9 @@ bool cResponsePacket::init(uint32_t requestID)
   memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t));
 
   bufUsed = headerLength;
-
-  return true;
 }
 
-bool cResponsePacket::initScan(uint32_t opCode)
+void cResponsePacket::initScan(uint32_t opCode)
 {
   initBuffers();
 
@@ -105,11 +102,9 @@ bool cResponsePacket::initScan(uint32_t opCode)
   memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t));
 
   bufUsed = headerLength;
-
-  return true;
 }
 
-bool cResponsePacket::initStatus(uint32_t opCode)
+void cResponsePacket::initStatus(uint32_t opCode)
 {
   initBuffers();
 
@@ -123,11 +118,9 @@ bool cResponsePacket::initStatus(uint32_t opCode)
   memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t));
 
   bufUsed = headerLength;
-
-  return true;
 }
 
-bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial)
+void cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial)
 {
   initBuffers();
 
@@ -152,11 +145,9 @@ bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t du
   memcpy(&buffer[userDataLenPosStream], &ul, sizeof(uint32_t));
 
   bufUsed = headerLengthStream;
-
-  return true;
 }
 
-bool cResponsePacket::initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1)
+void cResponsePacket::initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1)
 {
   initBuffers();
 
@@ -183,8 +174,6 @@ bool cResponsePacket::initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32
   memcpy(&buffer[userDataLenPosOSD], &ul, sizeof(uint32_t));
 
   bufUsed = headerLengthOSD;
-
-  return true;
 }
 
 void cResponsePacket::finalise()
diff --git a/responsepacket.h b/responsepacket.h
index 256a70a..6d7af8f 100644
--- a/responsepacket.h
+++ b/responsepacket.h
@@ -31,17 +31,19 @@
 #ifndef VNSI_RESPONSEPACKET_H
 #define VNSI_RESPONSEPACKET_H
 
+#include <stdint.h>
+
 class cResponsePacket
 {
 public:
   cResponsePacket();
   ~cResponsePacket();
 
-  bool init(uint32_t requestID);
-  bool initScan(uint32_t opCode);
-  bool initStatus(uint32_t opCode);
-  bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial);
-  bool initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+  void init(uint32_t requestID);
+  void initScan(uint32_t opCode);
+  void initStatus(uint32_t opCode);
+  void initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial);
+  void initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
   void finalise();
   void finaliseStream();
   void finaliseOSD();
diff --git a/setup.c b/setup.c
index acc0aa3..008122d 100644
--- a/setup.c
+++ b/setup.c
@@ -32,12 +32,11 @@ int TimeshiftBufferFileSize = 6;
 char TimeshiftBufferDir[PATH_MAX] = "\0";
 int PlayRecording = 0;
 int AvoidEPGScan = 1;
+int DisableScrambleTimeout = 0;
+int DisableCamBlacklist = 0;
 
 cMenuSetupVNSI::cMenuSetupVNSI(void)
 {
-  newPmtTimeout = PmtTimeout;
-  Add(new cMenuEditIntItem( tr("PMT Timeout (0-10)"), &newPmtTimeout));
-
   timeshiftModesTexts[0] = tr("Off");
   timeshiftModesTexts[1] = tr("RAM");
   timeshiftModesTexts[2] = tr("File");
@@ -58,6 +57,12 @@ cMenuSetupVNSI::cMenuSetupVNSI(void)
 
   newAvoidEPGScan = AvoidEPGScan;
   Add(new cMenuEditBoolItem( tr("Avoid EPG scan while streaming"), &newAvoidEPGScan));
+
+  newDisableScrambleTimeout = DisableScrambleTimeout;
+  Add(new cMenuEditBoolItem( tr("Disable scramble timeout"), &newDisableScrambleTimeout));
+
+  newDisableCamBlacklist = DisableCamBlacklist;
+  Add(new cMenuEditBoolItem( tr("Disable cam blacklist"), &newDisableCamBlacklist));
 }
 
 void cMenuSetupVNSI::Store(void)
@@ -80,9 +85,18 @@ void cMenuSetupVNSI::Store(void)
     newTimeshiftBufferFileSize = 1;
   SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize);
 
-  SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir)));
+  strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir));
+  if (*TimeshiftBufferDir && TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+    /* strip trailing slash */
+    TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] = 0;
+
+  SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, TimeshiftBufferDir);
 
   SetupStore(CONFNAME_PLAYRECORDING, PlayRecording = newPlayRecording);
 
   SetupStore(CONFNAME_AVOIDEPGSCAN, AvoidEPGScan = newAvoidEPGScan);
+
+  SetupStore(CONFNAME_DISABLESCRAMBLETIMEOUT, DisableScrambleTimeout = newDisableScrambleTimeout);
+
+  SetupStore(CONFNAME_DISABLECAMBLACKLIST, DisableCamBlacklist = newDisableCamBlacklist);
 }
diff --git a/setup.h b/setup.h
index cd4f0dc..e37ad10 100644
--- a/setup.h
+++ b/setup.h
@@ -38,6 +38,8 @@ private:
   char newTimeshiftBufferDir[PATH_MAX];
   int newPlayRecording;
   int newAvoidEPGScan;
+  int newDisableScrambleTimeout;
+  int newDisableCamBlacklist;
 protected:
   virtual void Store(void);
 public:
diff --git a/status.c b/status.c
index b8f41e1..3c28934 100644
--- a/status.c
+++ b/status.c
@@ -22,70 +22,137 @@
  *
  */
 
-#include "vnsi.h"
 #include "status.h"
-#include "vnsiclient.h"
+#include "vnsi.h"
+
 #include <vdr/tools.h>
 #include <vdr/recording.h>
 #include <vdr/videodir.h>
 #include <vdr/shutdown.h>
 
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+cVNSIStatus::cVNSIStatus() : cThread("VNSIStatus")
+{
+}
+
 cVNSIStatus::~cVNSIStatus()
 {
   Shutdown();
 }
 
+void cVNSIStatus::Init(CVNSITimers *timers)
+{
+  m_vnsiTimers = timers;
+  Start();
+}
+
 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());
+  m_clients.clear();
 }
 
-void cVNSIStatus::AddClient(cVNSIClient* client)
+static bool CheckFileSuffix(const char *name,
+                            const char *suffix, size_t suffix_length)
 {
-  cMutexLock lock(&m_mutex);
-  m_clients.push_back(client);
+  size_t name_length = strlen(name);
+  return name_length > suffix_length &&
+    memcmp(name + name_length - suffix_length, suffix, suffix_length) == 0;
+}
+
+static void DeleteFiles(const char *directory_path, const char *suffix)
+{
+  const size_t suffix_length = strlen(suffix);
+
+  DIR *dir = opendir(directory_path);
+  if (dir == nullptr)
+    return;
+
+  std::string path(directory_path);
+  path.push_back('/');
+  const size_t start = path.size();
+
+  while (auto *e = readdir(dir))
+  {
+    if (CheckFileSuffix(e->d_name, suffix, suffix_length))
+    {
+      path.replace(start, path.size(), e->d_name);
+
+      if (unlink(path.c_str()) < 0)
+      {
+        ERRORLOG("Failed to delete %s: %s", path.c_str(), strerror(errno));
+      }
+    }
+  }
+
+  closedir(dir);
 }
 
 void cVNSIStatus::Action(void)
 {
   cTimeMs chanTimer(0);
+  cTimeMs epgTimer(0);
 
   // get initial state of the recordings
+#if VDRVERSNUM >= 20301
+  cStateKey chanState;
+  const cChannels *channels = cChannels::GetChannelsRead(chanState);
+  chanState.Remove(false);
+#endif
+
+  // get initial state of the recordings
+#if VDRVERSNUM >= 20301
+  cStateKey recState;
+  const cRecordings *recordings = cRecordings::GetRecordingsRead(recState);
+  recState.Remove(false);
+#else
   int recState = -1;
   Recordings.StateChanged(recState);
+#endif
 
   // get initial state of the timers
+#if VDRVERSNUM >= 20301
+  cStateKey timerState;
+  const cTimers *timers = cTimers::GetTimersRead(timerState);
+  timerState.Remove(false);
+#else
   int timerState = -1;
   Timers.Modified(timerState);
+#endif
+
+  // vnsitimer
+  int vnsitimerState;
+  m_vnsiTimers->StateChange(vnsitimerState);
 
   // last update of epg
+#if VDRVERSNUM >= 20301
+  cStateKey epgState;
+  const cSchedules *epg = cSchedules::GetSchedulesRead(epgState);
+  epgState.Remove(false);
+#else
   time_t epgUpdate = cSchedules::Modified();
+#endif
 
   // 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);
+    DeleteFiles(TimeshiftBufferDir, ".vnsi");
   }
   else
   {
 #if VDRVERSNUM >= 20102
-    cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
+    DeleteFiles(cVideoDirectory::Name(), ".vnsi");
 #else
-    cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
+    DeleteFiles(VideoDirectory, ".vnsi");
 #endif
   }
-  int ret = system(cmd);
 
   // set thread priority
   SetPriority(1);
@@ -97,10 +164,9 @@ void cVNSIStatus::Action(void)
     // remove disconnected clients
     for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
     {
-      if (!(*i)->Active())
+      if (!i->Active())
       {
-        INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
-        delete (*i);
+        INFOLOG("Client with ID %u seems to be disconnected, removing from client list", i->GetID());
         i = m_clients.erase(i);
       }
       else {
@@ -114,33 +180,99 @@ void cVNSIStatus::Action(void)
      */
     if (!cVNSIClient::InhibidDataUpdates())
     {
+      // reset inactivity timeout as long as there are clients connected
+      if (!m_clients.empty())
+      {
+        ShutdownHandler.SetUserInactiveTimeout();
+      }
+
       // trigger clients to reload the modified channel list
-      if(m_clients.size() > 0 && chanTimer.TimedOut())
+      if(chanTimer.TimedOut())
       {
+#if VDRVERSNUM >= 20301
+        if (channels->Lock(chanState))
+        {
+          chanState.Remove(false);
+          INFOLOG("Requesting clients to reload channel list");
+          for (auto &i : m_clients)
+            i.ChannelsChange();
+          chanTimer.Set(5000);
+        }
+#else
         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();
+          for (auto &i : m_clients)
+            i.ChannelsChange();
         }
         chanTimer.Set(5000);
+#endif
       }
 
-      // reset inactivity timeout as long as there are clients connected
-      if(m_clients.size() > 0)
+
+#if VDRVERSNUM >= 20301
+      if (recordings->Lock(recState))
       {
-        ShutdownHandler.SetUserInactiveTimeout();
+        recState.Remove();
+        INFOLOG("Requesting clients to reload recordings list");
+        for (auto &i : m_clients)
+        {
+          i.RecordingsChange();
+        }
       }
 
+      if (timers->Lock(timerState))
+      {
+        timerState.Remove(false);
+        INFOLOG("Requesting clients to reload timers");
+        for (auto &i : m_clients)
+        {
+          i.SignalTimerChange();
+        }
+      }
+
+      if (m_vnsiTimers->StateChange(vnsitimerState))
+      {
+        INFOLOG("Requesting clients to reload vnsi-timers");
+        for (auto &i : m_clients)
+        {
+          i.SignalTimerChange();
+        }
+      }
+
+      if (epgTimer.TimedOut())
+      {
+        if (epg->Lock(epgState))
+        {
+          epgState.Remove(false);
+          INFOLOG("Requesting clients to load epg");
+          bool callAgain = false;
+          for (auto &i : m_clients)
+          {
+            callAgain |= i.EpgChange();
+          }
+          if (callAgain)
+          {
+            epgTimer.Set(100);
+            epgState.Reset();
+          }
+          else
+          {
+            epgTimer.Set(5000);
+            m_vnsiTimers->Scan();
+          }
+        }
+      }
+#else
       // 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();
+        for (auto &i : m_clients)
+          i.RecordingsChange();
       }
 
       // update timers
@@ -148,21 +280,22 @@ void cVNSIStatus::Action(void)
       {
         INFOLOG("Timers state changed (%i)", timerState);
         INFOLOG("Requesting clients to reload timers");
-        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+        for (auto &i : m_clients)
         {
-          (*i)->TimerChange();
+          i.SignalTimerChange();
         }
       }
 
       // update epg
       if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300)
       {
-        for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+        for (auto &i : m_clients)
         {
-          (*i)->EpgChange();
+          i.EpgChange();
         }
         epgUpdate = time(NULL);
       }
+#endif
     }
 
     m_mutex.Unlock();
diff --git a/status.h b/status.h
index 94015a2..aaea7f5 100644
--- a/status.h
+++ b/status.h
@@ -26,21 +26,35 @@
 
 #include <vdr/thread.h>
 #include <list>
+#include "vnsitimer.h"
+#include "vnsiclient.h"
 
 class cVNSIClient;
 
-typedef std::list<cVNSIClient*> ClientList;
+typedef std::list<cVNSIClient> ClientList;
 
 class cVNSIStatus : public cThread
 {
 public:
+  cVNSIStatus();
   virtual ~cVNSIStatus();
+
+  cVNSIStatus(const cVNSIStatus &) = delete;
+  cVNSIStatus &operator=(const cVNSIStatus &) = delete;
+
+  void Init(CVNSITimers *timers);
   void Shutdown();
-  void AddClient(cVNSIClient* client);
+
+  template<typename... Args>
+  void AddClient(Args&&... args) {
+    cMutexLock lock(&m_mutex);
+    m_clients.emplace_back(std::forward<Args>(args)...);
+  }
 
 protected:
   virtual void Action(void);
 
   ClientList m_clients;
   cMutex m_mutex;
+  CVNSITimers *m_vnsiTimers;
 };
diff --git a/streamer.c b/streamer.c
index 782bfb6..3a35212 100644
--- a/streamer.c
+++ b/streamer.c
@@ -46,19 +46,9 @@ cLiveStreamer::cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, ui
  , m_ClientID(clientID)
  , m_scanTimeout(timeout)
  , m_Demuxer(bAllowRDS)
- , m_VideoInput(m_Event, m_Mutex, m_IsRetune)
+ , m_VideoInput(m_Event)
 {
-  m_Channel         = NULL;
-  m_Socket          = NULL;
-  m_Frontend        = -1;
-  m_IsAudioOnly     = false;
-  m_IsMPEGPS        = false;
-  m_startup         = true;
-  m_SignalLost      = false;
-  m_IFrameSeen      = false;
-  m_VideoBuffer     = NULL;
   m_Timeshift       = timeshift;
-  m_IsRetune        = false;
 
   memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
 
@@ -97,12 +87,30 @@ bool cLiveStreamer::Open(int serial)
   }
   else if (PlayRecording && serial == -1)
   {
+#if VDRVERSNUM >= 20301
+    LOCK_TIMERS_READ;
+    for (const cTimer *timer = Timers->First(); timer; timer = Timers->Next(timer))
+#else
     for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer))
+#endif
     {
       if (timer &&
           timer->Recording() &&
           timer->Channel() == m_Channel)
       {
+#if VDRVERSNUM >= 20301
+        LOCK_RECORDINGS_READ;
+        cTimer t(*timer);
+        cRecording matchRec(&t, t.Event());
+        const cRecording *rec;
+        {
+          rec = Recordings->GetByName(matchRec.FileName());
+          if (!rec)
+          {
+            return false;
+          }
+        }
+#else
         Recordings.Load();
         cRecording matchRec(timer, timer->Event());
         cRecording *rec;
@@ -114,6 +122,7 @@ bool cLiveStreamer::Open(int serial)
             return false;
           }
         }
+#endif
         m_VideoBuffer = cVideoBuffer::Create(rec);
         recording = true;
         break;
@@ -133,7 +142,6 @@ 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());
@@ -177,13 +185,17 @@ void cLiveStreamer::Action(void)
   bool requestStreamChangeSideData = false;
   cTimeMs last_info(1000);
   cTimeMs bufferStatsTimer(1000);
+  int openFailCount = 0;
 
   while (Running())
   {
-    if (m_IsRetune)
+    auto retune = m_VideoInput.ReceivingStatus();
+    if (retune == cVideoInput::RETUNE)
+      // allow timeshift playback when retune == cVideoInput::CLOSE
       ret = -1;
     else
       ret = m_Demuxer.Read(&pkt_data, &pkt_side_data);
+
     if (ret > 0)
     {
       if (pkt_data.pmtChange)
@@ -241,29 +253,39 @@ void cLiveStreamer::Action(void)
     else if (ret == -1)
     {
       // no data
+      if (retune == cVideoInput::CLOSE)
       {
-        bool retune = false;
-        {
-          cMutexLock lock(&m_Mutex);
-          retune = m_IsRetune;
-          if (!retune)
-            m_Event.TimedWait(m_Mutex, 10);
-        }
-        if (retune)
+        m_Socket->Shutdown();
+        break;
+      }
+      if (m_Demuxer.GetError() & ERROR_CAM_ERROR)
+      {
+        INFOLOG("CAM error, try reset");
+        cCamSlot *cs = m_Device->CamSlot();
+        if (cs)
+          cs->StopDecrypting();
+        retune = cVideoInput::RETUNE;
+      }
+      if (retune == cVideoInput::RETUNE)
+      {
+        INFOLOG("re-tuning...");
+        m_VideoInput.Close();
+        if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer))
         {
-          m_VideoInput.Close();
-          if (m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer))
+          if (++openFailCount == 3)
           {
-            cMutexLock lock(&m_Mutex);
-            m_IsRetune = false;
+            openFailCount = 0;
+            cCondWait::SleepMs(2000);
           }
           else
-          {
-            cMutexLock lock(&m_Mutex);
-            m_Event.TimedWait(m_Mutex, 100);
-          }
+            cCondWait::SleepMs(100);
         }
+        else
+          openFailCount = 0;
       }
+      else
+        m_Event.Wait(10);
+
       if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
       {
         sendStreamStatus();
@@ -295,6 +317,9 @@ bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocke
   m_Priority  = priority;
   m_Socket    = Socket;
 
+  if (m_Priority < 0)
+    m_Priority = 0;
+
   if (!Open())
     return false;
 
@@ -331,11 +356,7 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
   if(pkt->size == 0)
     return;
 
-  if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts, pkt->serial))
-  {
-    ERRORLOG("stream response packet init fail");
-    return;
-  }
+  m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts, pkt->serial);
   m_streamHeader.setLen(m_streamHeader.getStreamHeaderLength() + pkt->size);
   m_streamHeader.finaliseStream();
 
@@ -350,136 +371,140 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
 
 void cLiveStreamer::sendStreamChange()
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0, 0))
-  {
-    ERRORLOG("stream response packet init fail");
-    delete resp;
-    return;
-  }
+  cResponsePacket resp;
+  resp.initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0, 0);
 
   uint32_t FpsScale, FpsRate, Height, Width;
   double Aspect;
   uint32_t Channels, SampleRate, BitRate, BitsPerSample, BlockAlign;
   for (cTSStream* stream = m_Demuxer.GetFirstStream(); stream; stream = m_Demuxer.GetNextStream())
   {
-    resp->add_U32(stream->GetPID());
+    resp.add_U32(stream->GetPID());
     if (stream->Type() == stMPEG2AUDIO)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("MPEG2AUDIO");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
-
-      for (unsigned int i = 0; i < stream->GetSideDataTypes()->size(); i++)
+      resp.add_String("MPEG2AUDIO");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
+
+      for (const auto &i : stream->GetSideDataTypes())
       {
-        resp->add_U32(stream->GetSideDataTypes()->at(i).first);
-        if (stream->GetSideDataTypes()->at(i).second == scRDS)
+        resp.add_U32(i.first);
+        if (i.second == scRDS)
         {
-          resp->add_String("RDS");
-          resp->add_String(stream->GetLanguage());
-          resp->add_U32(stream->GetPID());
+          resp.add_String("RDS");
+          resp.add_String(stream->GetLanguage());
+          resp.add_U32(stream->GetPID());
         }
       }
     }
     else if (stream->Type() == stMPEG2VIDEO)
     {
       stream->GetVideoInformation(FpsScale, FpsRate, Height, Width, Aspect);
-      resp->add_String("MPEG2VIDEO");
-      resp->add_U32(FpsScale);
-      resp->add_U32(FpsRate);
-      resp->add_U32(Height);
-      resp->add_U32(Width);
-      resp->add_double(Aspect);
+      resp.add_String("MPEG2VIDEO");
+      resp.add_U32(FpsScale);
+      resp.add_U32(FpsRate);
+      resp.add_U32(Height);
+      resp.add_U32(Width);
+      resp.add_double(Aspect);
     }
     else if (stream->Type() == stAC3)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("AC3");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
+      resp.add_String("AC3");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
     }
     else if (stream->Type() == stH264)
     {
       stream->GetVideoInformation(FpsScale, FpsRate, Height, Width, Aspect);
-      resp->add_String("H264");
-      resp->add_U32(FpsScale);
-      resp->add_U32(FpsRate);
-      resp->add_U32(Height);
-      resp->add_U32(Width);
-      resp->add_double(Aspect);
+      resp.add_String("H264");
+      resp.add_U32(FpsScale);
+      resp.add_U32(FpsRate);
+      resp.add_U32(Height);
+      resp.add_U32(Width);
+      resp.add_double(Aspect);
+    }
+    else if (stream->Type() == stHEVC)
+    {
+      stream->GetVideoInformation(FpsScale, FpsRate, Height, Width, Aspect);
+      resp.add_String("HEVC");
+      resp.add_U32(FpsScale);
+      resp.add_U32(FpsRate);
+      resp.add_U32(Height);
+      resp.add_U32(Width);
+      resp.add_double(Aspect);
     }
     else if (stream->Type() == stDVBSUB)
     {
-      resp->add_String("DVBSUB");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(stream->CompositionPageId());
-      resp->add_U32(stream->AncillaryPageId());
+      resp.add_String("DVBSUB");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(stream->CompositionPageId());
+      resp.add_U32(stream->AncillaryPageId());
     }
     else if (stream->Type() == stTELETEXT)
     {
-      resp->add_String("TELETEXT");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(stream->CompositionPageId());
-      resp->add_U32(stream->AncillaryPageId());
+      resp.add_String("TELETEXT");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(stream->CompositionPageId());
+      resp.add_U32(stream->AncillaryPageId());
     }
     else if (stream->Type() == stAACADTS)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("AAC");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
+      resp.add_String("AAC");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
     }
     else if (stream->Type() == stAACLATM)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("AAC_LATM");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
+      resp.add_String("AAC_LATM");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
     }
     else if (stream->Type() == stEAC3)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("EAC3");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
+      resp.add_String("EAC3");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
     }
     else if (stream->Type() == stDTS)
     {
       stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
-      resp->add_String("DTS");
-      resp->add_String(stream->GetLanguage());
-      resp->add_U32(Channels);
-      resp->add_U32(SampleRate);
-      resp->add_U32(BlockAlign);
-      resp->add_U32(BitRate);
-      resp->add_U32(BitsPerSample);
+      resp.add_String("DTS");
+      resp.add_String(stream->GetLanguage());
+      resp.add_U32(Channels);
+      resp.add_U32(SampleRate);
+      resp.add_U32(BlockAlign);
+      resp.add_U32(BitRate);
+      resp.add_U32(BitsPerSample);
     }
   }
 
-  resp->finaliseStream();
-  m_Socket->write(resp->getPtr(), resp->getLen());
-  delete resp;
+  resp.finaliseStream();
+  m_Socket->write(resp.getPtr(), resp.getLen());
 }
 
 void cLiveStreamer::sendSignalInfo()
@@ -488,24 +513,17 @@ void cLiveStreamer::sendSignalInfo()
      return a empty signalinfo package */
   if (m_Frontend == -2)
   {
-    cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
-    {
-      ERRORLOG("stream response packet init fail");
-      delete resp;
-      return;
-    }
-
-    resp->add_String(*cString::sprintf("Unknown"));
-    resp->add_String(*cString::sprintf("Unknown"));
-    resp->add_U32(0);
-    resp->add_U32(0);
-    resp->add_U32(0);
-    resp->add_U32(0);
-
-    resp->finaliseStream();
-    m_Socket->write(resp->getPtr(), resp->getLen());
-    delete resp;
+    cResponsePacket resp;
+    resp.initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0);
+    resp.add_String(*cString::sprintf("Unknown"));
+    resp.add_String(*cString::sprintf("Unknown"));
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_U32(0);
+
+    resp.finaliseStream();
+    m_Socket->write(resp.getPtr(), resp.getLen());
     return;
   }
 
@@ -536,23 +554,17 @@ void cLiveStreamer::sendSignalInfo()
 
     if (m_Frontend >= 0)
     {
-      cResponsePacket *resp = new cResponsePacket();
-      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
-      {
-        ERRORLOG("stream response packet init fail");
-        delete resp;
-        return;
-      }
-      resp->add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver));
-      resp->add_String("");
-      resp->add_U32(0);
-      resp->add_U32(0);
-      resp->add_U32(0);
-      resp->add_U32(0);
-
-      resp->finaliseStream();
-      m_Socket->write(resp->getPtr(), resp->getLen());
-      delete resp;
+      cResponsePacket resp;
+      resp.initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0);
+      resp.add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver));
+      resp.add_String("");
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_U32(0);
+
+      resp.finaliseStream();
+      m_Socket->write(resp.getPtr(), resp.getLen());
     }
   }
   else
@@ -576,13 +588,8 @@ void cLiveStreamer::sendSignalInfo()
 
     if (m_Frontend >= 0)
     {
-      cResponsePacket *resp = new cResponsePacket();
-      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
-      {
-        ERRORLOG("stream response packet init fail");
-        delete resp;
-        return;
-      }
+      cResponsePacket resp;
+      resp.initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0);
 
       fe_status_t status;
       uint16_t fe_snr;
@@ -605,82 +612,75 @@ void cLiveStreamer::sendSignalInfo()
       switch (m_Channel->Source() & cSource::st_Mask)
       {
         case cSource::stSat:
-          resp->add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "",  cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          resp.add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "",  cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
           break;
         case cSource::stCable:
-          resp->add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          resp.add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
           break;
         case cSource::stTerr:
-          resp->add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          resp.add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          break;
+        case cSource::stAtsc:
+          resp.add_String(*cString::sprintf("ATSC #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          break;
+        default:
+          resp.add_U8(0);
           break;
       }
-      resp->add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-"));
-      resp->add_U32(fe_snr);
-      resp->add_U32(fe_signal);
-      resp->add_U32(fe_ber);
-      resp->add_U32(fe_unc);
-
-      resp->finaliseStream();
-      m_Socket->write(resp->getPtr(), resp->getLen());
-      delete resp;
+      resp.add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-"));
+      resp.add_U32(fe_snr);
+      resp.add_U32(fe_signal);
+      resp.add_U32(fe_ber);
+      resp.add_U32(fe_unc);
+
+      resp.finaliseStream();
+      m_Socket->write(resp.getPtr(), resp.getLen());
     }
   }
 }
 
 void cLiveStreamer::sendStreamStatus()
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0, 0))
-  {
-    ERRORLOG("stream response packet init fail");
-    delete resp;
-    return;
-  }
+  cResponsePacket resp;
+  resp.initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0, 0);
   uint16_t error = m_Demuxer.GetError();
   if (error & ERROR_PES_SCRAMBLE)
   {
     INFOLOG("Channel: scrambled %d", error);
-    resp->add_String(cString::sprintf("Channel: scrambled (%d)", error));
+    resp.add_String(cString::sprintf("Channel: scrambled (%d)", error));
   }
   else if (error & ERROR_PES_STARTCODE)
   {
     INFOLOG("Channel: startcode %d", error);
-    resp->add_String(cString::sprintf("Channel: encrypted? (%d)", error));
+    resp.add_String(cString::sprintf("Channel: encrypted? (%d)", error));
   }
   else if (error & ERROR_DEMUX_NODATA)
   {
     INFOLOG("Channel: no data %d", error);
-    resp->add_String(cString::sprintf("Channel: no data"));
+    resp.add_String(cString::sprintf("Channel: no data"));
   }
   else
   {
     INFOLOG("Channel: unknown error %d", error);
-    resp->add_String(cString::sprintf("Channel: unknown error (%d)", error));
+    resp.add_String(cString::sprintf("Channel: unknown error (%d)", error));
   }
 
-  resp->finaliseStream();
-  m_Socket->write(resp->getPtr(), resp->getLen());
-  delete resp;
+  resp.finaliseStream();
+  m_Socket->write(resp.getPtr(), resp.getLen());
 }
 
 void cLiveStreamer::sendBufferStatus()
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStream(VNSI_STREAM_BUFFERSTATS, 0, 0, 0, 0, 0))
-  {
-    ERRORLOG("stream response packet init fail");
-    delete resp;
-    return;
-  }
+  cResponsePacket resp;
+  resp.initStream(VNSI_STREAM_BUFFERSTATS, 0, 0, 0, 0, 0);
   uint32_t start, end;
   bool timeshift;
   m_Demuxer.BufferStatus(timeshift, start, end);
-  resp->add_U8(timeshift);
-  resp->add_U32(start);
-  resp->add_U32(end);
-  resp->finaliseStream();
-  m_Socket->write(resp->getPtr(), resp->getLen());
-  delete resp;
+  resp.add_U8(timeshift);
+  resp.add_U32(start);
+  resp.add_U32(end);
+  resp.finaliseStream();
+  m_Socket->write(resp.getPtr(), resp.getLen());
 }
 
 void cLiveStreamer::sendRefTime(sStreamPacket *pkt)
@@ -688,19 +688,12 @@ 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;
+  cResponsePacket resp;
+  resp.initStream(VNSI_STREAM_REFTIME, 0, 0, 0, 0, 0);
+  resp.add_U32(pkt->reftime);
+  resp.add_U64(pkt->pts);
+  resp.finaliseStream();
+  m_Socket->write(resp.getPtr(), resp.getLen());
 }
 
 bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial)
@@ -716,7 +709,5 @@ void cLiveStreamer::RetuneChannel(const cChannel *channel)
     return;
 
   INFOLOG("re-tune to channel %s", m_Channel->Name());
-  cMutexLock lock(&m_Mutex);
-  m_IsRetune = true;
-  m_Event.Broadcast();
+  m_VideoInput.RequestRetune();
 }
diff --git a/streamer.h b/streamer.h
index 078e144..9445c28 100644
--- a/streamer.h
+++ b/streamer.h
@@ -32,7 +32,6 @@
 #include <vdr/device.h>
 #include <vdr/receiver.h>
 #include <vdr/thread.h>
-#include <list>
 
 #include "parser.h"
 #include "responsepacket.h"
@@ -48,10 +47,7 @@ class cVideoInput;
 
 class cLiveStreamer : public cThread
 {
-private:
   friend class cParser;
-  friend class cLivePatFilter;
-  friend class cLiveReceiver;
 
   void sendStreamPacket(sStreamPacket *pkt);
   void sendStreamChange();
@@ -60,30 +56,28 @@ private:
   void sendBufferStatus();
   void sendRefTime(sStreamPacket *pkt);
 
-  int               m_ClientID;
-  const cChannel   *m_Channel;                      /*!> Channel to stream */
+  const int         m_ClientID;
+  const cChannel   *m_Channel = nullptr;            /*!> Channel to stream */
   cDevice          *m_Device;
-  cxSocket         *m_Socket;                       /*!> The socket class to communicate with client */
-  int               m_Frontend;                     /*!> File descriptor to access used receiving device  */
+  cxSocket         *m_Socket = nullptr;             /*!> The socket class to communicate with client */
+  int               m_Frontend = -1;                /*!> File descriptor to access used receiving device  */
   dvb_frontend_info m_FrontendInfo;                 /*!> DVB Information about the receiving device (DVB only) */
   v4l2_capability   m_vcap;                         /*!> PVR Information about the receiving device (pvrinput only) */
   cString           m_DeviceString;                 /*!> The name of the receiving device */
-  bool              m_startup;
-  bool              m_IsAudioOnly;                  /*!> Set to true if streams contains only audio */
-  bool              m_IsMPEGPS;                     /*!> TS Stream contains MPEG PS data like from pvrinput */
+  bool              m_startup = true;
+  bool              m_IsAudioOnly = false;          /*!> Set to true if streams contains only audio */
+  bool              m_IsMPEGPS = false;             /*!> TS Stream contains MPEG PS data like from pvrinput */
   uint32_t          m_scanTimeout;                  /*!> Channel scanning timeout (in seconds) */
   cTimeMs           m_last_tick;
-  bool              m_SignalLost;
-  bool              m_IFrameSeen;
+  bool              m_SignalLost = false;
+  bool              m_IFrameSeen = false;
   cResponsePacket   m_streamHeader;
   cVNSIDemuxer      m_Demuxer;
-  cVideoBuffer     *m_VideoBuffer;
+  cVideoBuffer     *m_VideoBuffer = nullptr;
   cVideoInput       m_VideoInput;
   int               m_Priority;
   uint8_t           m_Timeshift;
-  cCondVar          m_Event;
-  cMutex            m_Mutex;
-  bool              m_IsRetune;
+  cCondWait         m_Event;
 
 protected:
   virtual void Action(void);
@@ -94,6 +88,9 @@ public:
   cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout = 0);
   virtual ~cLiveStreamer();
 
+  cLiveStreamer(const cLiveStreamer &) = delete;
+  cLiveStreamer &operator=(const cLiveStreamer &) = delete;
+
   void Activate(bool On);
 
   bool StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket* resp);
diff --git a/videobuffer.c b/videobuffer.c
index 131c252..c85a06e 100644
--- a/videobuffer.c
+++ b/videobuffer.c
@@ -40,32 +40,25 @@ class cVideoBufferSimple : public cVideoBuffer
 {
 friend class cVideoBuffer;
 public:
-  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual void Put(const uint8_t *buf, unsigned int size);
   virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
 
 protected:
   cVideoBufferSimple();
-  virtual ~cVideoBufferSimple();
-  cRingBufferLinear *m_Buffer;
+  cRingBufferLinear m_Buffer;
   int m_BytesConsumed;
 };
 
 cVideoBufferSimple::cVideoBufferSimple()
+  :m_Buffer(MEGABYTE(5), TS_SIZE * 2, false)
 {
-  m_Buffer = new cRingBufferLinear(MEGABYTE(3), TS_SIZE * 2, false);
-  m_Buffer->SetTimeouts(0, 100);
+  m_Buffer.SetTimeouts(0, 100);
   m_BytesConsumed = 0;
 }
 
-cVideoBufferSimple::~cVideoBufferSimple()
+void cVideoBufferSimple::Put(const uint8_t *buf, unsigned int size)
 {
-  if (m_Buffer)
-    delete m_Buffer;
-}
-
-void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size)
-{
-  m_Buffer->Put(buf, size);
+  m_Buffer.Put(buf, size);
 }
 
 int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
@@ -73,10 +66,10 @@ int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endT
   int  readBytes;
   if (m_BytesConsumed)
   {
-    m_Buffer->Del(m_BytesConsumed);
+    m_Buffer.Del(m_BytesConsumed);
   }
   m_BytesConsumed = 0;
-  *buf = m_Buffer->Get(readBytes);
+  *buf = m_Buffer.Get(readBytes);
   if (!(*buf) || readBytes < TS_SIZE)
   {
     usleep(100);
@@ -94,7 +87,7 @@ int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endT
 
   if ((*buf)[0] != TS_SYNC_BYTE)
   {
-    m_Buffer->Del(m_BytesConsumed);
+    m_Buffer.Del(m_BytesConsumed);
     m_BytesConsumed = 0;
     return 0;
   }
@@ -198,7 +191,7 @@ class cVideoBufferRAM : public cVideoBufferTimeshift
 {
 friend class cVideoBuffer;
 public:
-  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual void Put(const 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);
 
@@ -217,8 +210,7 @@ cVideoBufferRAM::cVideoBufferRAM()
 
 cVideoBufferRAM::~cVideoBufferRAM()
 {
-  if (m_Buffer)
-    free(m_Buffer);
+  free(m_Buffer);
 }
 
 bool cVideoBufferRAM::Init()
@@ -243,7 +235,7 @@ void cVideoBufferRAM::SetPos(off_t pos)
   m_BytesConsumed = 0;
 }
 
-void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
+void cVideoBufferRAM::Put(const uint8_t *buf, unsigned int size)
 {
   if (Available() + MARGIN >= m_BufferSize)
   {
@@ -303,7 +295,7 @@ int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime
   if (m_ReadPtr > (m_BufferSize - m_Margin))
   {
     int bytesToCopy = m_BufferSize - m_ReadPtr;
-    memmove(m_Buffer + (m_Margin - bytesToCopy), m_Buffer + m_ReadPtr, bytesToCopy);
+    memmove(m_Buffer + (m_Margin - bytesToCopy), m_BufferPtr + m_ReadPtr, bytesToCopy);
     *buf = m_Buffer + (m_Margin - bytesToCopy);
   }
   else
@@ -335,7 +327,7 @@ class cVideoBufferFile : public cVideoBufferTimeshift
 friend class cVideoBuffer;
 public:
   virtual off_t GetPosMax();
-  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual void Put(const 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);
 
@@ -393,10 +385,7 @@ bool cVideoBufferFile::Init()
   struct stat sb;
   if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
   {
-    if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
-      m_Filename = cString::sprintf("%sTimeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID);
-    else
-      m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID);
+    m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID);
   }
   else
 #if VDRVERSNUM >= 20102
@@ -451,7 +440,7 @@ off_t cVideoBufferFile::GetPosMax()
   return posMax;
 }
 
-void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
+void cVideoBufferFile::Put(const uint8_t *buf, unsigned int size)
 {
   if (Available() + MARGIN >= m_BufferSize)
   {
@@ -626,22 +615,22 @@ class cVideoBufferRecording : public cVideoBufferFile
 friend class cVideoBuffer;
 public:
   virtual off_t GetPosMax();
-  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual void Put(const 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);
+  cVideoBufferRecording(const cRecording *rec);
   virtual ~cVideoBufferRecording();
   virtual bool Init();
   virtual off_t Available();
   off_t GetPosEnd();
   cRecPlayer *m_RecPlayer;
-  cRecording *m_Recording;
+  const cRecording *m_Recording;
   cTimeMs m_ScanTimer;
 };
 
-cVideoBufferRecording::cVideoBufferRecording(cRecording *rec)
+cVideoBufferRecording::cVideoBufferRecording(const cRecording *rec)
 {
   m_Recording = rec;
   m_ReadCacheSize = 0;
@@ -662,7 +651,7 @@ off_t cVideoBufferRecording::GetPosMax()
   return cVideoBufferFile::GetPosMax();
 }
 
-void cVideoBufferRecording::Put(uint8_t *buf, unsigned int size)
+void cVideoBufferRecording::Put(const uint8_t *buf, unsigned int size)
 {
 
 }
@@ -778,7 +767,7 @@ class cVideoBufferTest : public cVideoBufferFile
 friend class cVideoBuffer;
 public:
   virtual off_t GetPosMax();
-  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual void Put(const uint8_t *buf, unsigned int size);
 
 protected:
   cVideoBufferTest(cString filename);
@@ -818,7 +807,7 @@ off_t cVideoBufferTest::GetPosEnd()
   return end;
 }
 
-void cVideoBufferTest::Put(uint8_t *buf, unsigned int size)
+void cVideoBufferTest::Put(const uint8_t *buf, unsigned int size)
 {
 
 }
@@ -915,7 +904,7 @@ cVideoBuffer* cVideoBuffer::Create(cString filename)
     return buffer;
 }
 
-cVideoBuffer* cVideoBuffer::Create(cRecording *rec)
+cVideoBuffer* cVideoBuffer::Create(const cRecording *rec)
 {
   INFOLOG("Open recording: %s", rec->FileName());
   cVideoBufferRecording *buffer = new cVideoBufferRecording(rec);
diff --git a/videobuffer.h b/videobuffer.h
index 2df8f57..493d377 100644
--- a/videobuffer.h
+++ b/videobuffer.h
@@ -36,8 +36,8 @@ public:
   virtual ~cVideoBuffer();
   static cVideoBuffer* Create(int clientID, uint8_t timeshift);
   static cVideoBuffer* Create(cString filename);
-  static cVideoBuffer* Create(cRecording *rec);
-  virtual void Put(uint8_t *buf, unsigned int size) = 0;
+  static cVideoBuffer* Create(const cRecording *rec);
+  virtual void Put(const 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; };
diff --git a/videoinput.c b/videoinput.c
index 98378b1..2947f14 100644
--- a/videoinput.c
+++ b/videoinput.c
@@ -29,13 +29,15 @@
 #include "vnsi.h"
 
 #include <vdr/remux.h>
-#include <vdr/channels.h>
 #include <vdr/device.h>
 #include <vdr/receiver.h>
 #include <vdr/ci.h>
 #include <vdr/config.h>
 #include <libsi/section.h>
 #include <libsi/descriptor.h>
+#include <memory>
+#include <vector>
+#include <algorithm>
 
 // --- cLiveReceiver -------------------------------------------------
 
@@ -44,11 +46,14 @@ class cLiveReceiver: public cReceiver
 public:
   cLiveReceiver(cVideoInput *VideoInput, const cChannel *Channel, int Priority);
   virtual ~cLiveReceiver();
-  cChannel m_PmtChannel;
 
 protected:
   virtual void Activate(bool On);
+#if VDRVERSNUM >= 20301
+  virtual void Receive(const uchar *Data, int Length);
+#else
   virtual void Receive(uchar *Data, int Length);
+#endif
 
   cVideoInput *m_VideoInput;
 };
@@ -66,18 +71,20 @@ cLiveReceiver::~cLiveReceiver()
 }
 
 //void cLiveReceiver
+#if VDRVERSNUM >= 20301
+void cLiveReceiver::Receive(const uchar *Data, int Length)
+#else
 void cLiveReceiver::Receive(uchar *Data, int Length)
+#endif
 {
   m_VideoInput->Receive(Data, Length);
 }
 
-inline void cLiveReceiver::Activate(bool On)
+void cLiveReceiver::Activate(bool On)
 {
-  DEBUGLOG("activate live receiver: %d", On);
+  INFOLOG("activate live receiver: %d, pmt change: %d", On, static_cast<bool>(m_VideoInput->m_PmtChange));
   if (!On && !m_VideoInput->m_PmtChange)
-  {
-    m_VideoInput->Retune();
-  }
+    m_VideoInput->RequestRetune();
 }
 
 // --- cLivePatFilter ----------------------------------------------------
@@ -110,6 +117,7 @@ cLivePatFilter::cLivePatFilter(cVideoInput *VideoInput, const cChannel *Channel)
 
 void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
 {
+#if VDRVERSNUM < 20104
   if (Pid == 0x00)
   {
     if (Tid == 0x00)
@@ -122,7 +130,12 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
       {
         if (!assoc.isNITPid())
         {
+#if VDRVERSNUM >= 20301
+          LOCK_CHANNELS_READ;
+          const cChannel *Channel =  Channels->GetByServiceID(Source(), Transponder(), assoc.getServiceId());
+#else
           const cChannel *Channel =  Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
+#endif
           if (Channel && (Channel == m_Channel))
           {
             int prevPmtPid = m_pmtPid;
@@ -160,7 +173,12 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
     }
     m_pmtVersion = pmt.getVersionNumber();
 
+#if VDRVERSNUM >= 20301
+    LOCK_CHANNELS_READ;
+    const cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), pmt.getServiceId());
+#else
     cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
+#endif
     if (Channel) {
        // Scan the stream-specific loop:
        SI::PMT::Stream stream;
@@ -389,23 +407,90 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
        pmtChannel->Modification();
        pmtChannel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid);
        pmtChannel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds);
-       m_VideoInput->PmtChange(pmtChannel->Modification(CHANNELMOD_PIDS));
+       if (pmtChannel->Modification(CHANNELMOD_PIDS))
+         m_VideoInput->RequestRetune();
     }
   }
+#endif
+}
+
+// --- cDummyReceiver ----------------------------------------------------
+
+// This dummy receiver is used to detect if a recording/streaming task
+// with a higher priority has acquired the device and detached *all*
+// receivers from it.
+class cDummyReceiver : public cReceiver
+{
+public:
+  // Return a new or existing dummy receiver attached to the device.
+  static std::shared_ptr<cDummyReceiver> Create(cDevice *device);
+  virtual ~cDummyReceiver() {Detach();}
+  bool BeenDetached() {return m_BeenDetached;}
+protected:
+#if VDRVERSNUM >= 20301
+  virtual void Receive(const uchar *Data, int Length) {}
+#else
+  virtual void Receive(uchar *Data, int Length) {}
+#endif
+  virtual void Activate(bool On);
+private:
+  static std::vector<std::weak_ptr<cDummyReceiver>> s_Pool;
+  static cMutex s_PoolMutex;
+  std::atomic<bool> m_BeenDetached;
+  cDummyReceiver() : cReceiver(NULL, MINPRIORITY), m_BeenDetached(false) {}
+};
+
+std::vector<std::weak_ptr<cDummyReceiver>> cDummyReceiver::s_Pool;
+cMutex cDummyReceiver::s_PoolMutex;
+
+void cDummyReceiver::Activate(bool On)
+{
+  INFOLOG("Dummy receiver (%p) %s", this, (On)? "activated" : "deactivated");
+  if (!On)
+    m_BeenDetached = true;
+}
+
+std::shared_ptr<cDummyReceiver> cDummyReceiver::Create(cDevice *device)
+{
+  if (!device)
+    return nullptr;
+
+  cMutexLock MutexLock(&s_PoolMutex);
+  // cleanup
+  s_Pool.erase(std::remove_if(s_Pool.begin(), s_Pool.end(),
+        [](const std::weak_ptr<cDummyReceiver> &p) -> bool
+        {return !p.lock();}), s_Pool.end());
+
+  // find an active receiver for the device
+  for (auto p : s_Pool)
+  {
+    auto recv = p.lock();
+    if (!recv->BeenDetached() && recv->Device() == device)
+      return recv;
+  }
+  auto recv = std::shared_ptr<cDummyReceiver>(new cDummyReceiver);
+  if (device->AttachReceiver(recv.get()))
+  {
+    s_Pool.push_back(recv);
+    return recv;
+  }
+  return nullptr;
 }
 
 // ----------------------------------------------------------------------------
 
-cVideoInput::cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune) :
-    m_Event(condVar), m_Mutex(mutex), m_IsRetune(retune)
+cVideoInput::cVideoInput(cCondWait &event)
+  : m_PmtChange(false)
+  , m_Event(event)
+  , m_RetuneRequested(false)
 {
-  m_Device = NULL;;
+  m_Device = NULL;
+  m_camSlot = nullptr;
   m_PatFilter = NULL;
   m_Receiver = NULL;;
   m_Channel = NULL;
   m_VideoBuffer = NULL;
   m_Priority = 0;
-  m_PmtChange = false;
 }
 
 cVideoInput::~cVideoInput()
@@ -418,27 +503,58 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
   m_VideoBuffer = videoBuffer;
   m_Channel = channel;
   m_Priority = priority;
+  m_RetuneRequested = false;
+  m_PmtChange = false;
   m_Device = cDevice::GetDevice(m_Channel, m_Priority, false);
+  m_camSlot = nullptr;
 
   if (m_Device != NULL)
   {
-    INFOLOG("Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0);
+    INFOLOG("Successfully found following device: %p (%d) for receiving, priority=%d", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0, m_Priority);
 
     if (m_Device->SwitchChannel(m_Channel, false))
     {
-      DEBUGLOG("Creating new live Receiver");
-      //m_Device->SetCurrentChannel(m_Channel);
-      m_SeenPmt = false;
+
+      m_Device->SetCurrentChannel(m_Channel);
+
+#if VDRVERSNUM < 20104
       m_PatFilter = new cLivePatFilter(this, m_Channel);
-      m_Receiver0 = new cLiveReceiver(this, m_Channel, m_Priority);
-      m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority);
-      m_Device->AttachReceiver(m_Receiver0);
       m_Device->AttachFilter(m_PatFilter);
-      cCamSlot *cs = m_Device->CamSlot();
-      if (m_Priority <= MINPRIORITY && cs)
-        cs->StartDecrypting();
+#endif
+
+      m_PmtChannel = *m_Channel;
+      m_PmtChange = true;
+
+      m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority);
+      m_Receiver->SetPids(NULL);
+      m_Receiver->SetPids(&m_PmtChannel);
+      m_Receiver->AddPid(m_PmtChannel.Tpid());
+
+      m_DummyReceiver = cDummyReceiver::Create(m_Device);
+      if (!m_DummyReceiver)
+        return false;
+
+      m_camSlot = m_Device->CamSlot();
+
+#if VDRVERSNUM >= 20107
+      if (DisableScrambleTimeout && m_camSlot)
+      {
+        // HACK cDevice::AttachReceiver() doesn't start scrambling
+        // timer if priority == MINPRIORITY
+        m_Receiver->SetPriority(MINPRIORITY);
+        if (!m_Device->AttachReceiver(m_Receiver))
+          return false;
+        if (m_camSlot)
+          m_camSlot->StartDecrypting();
+        m_Receiver->SetPriority(m_Priority);
+      }
+      else
+#endif
+      {
+        if (!m_Device->AttachReceiver(m_Receiver))
+          return false;
+      }
       m_VideoBuffer->AttachInput(true);
-      Start();
       return true;
     }
   }
@@ -448,24 +564,25 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
 void cVideoInput::Close()
 {
   INFOLOG("close video input ...");
-  Cancel(5);
 
   if (m_Device)
   {
-    if (m_Receiver)
+    if (DisableCamBlacklist)
     {
-      DEBUGLOG("Detaching Live Receiver");
-      m_Device->Detach(m_Receiver);
-    }
-    else
-    {
-      DEBUGLOG("No live receiver present");
+      // HACK Undo ChannelCamRelations.SetChecked() - see cDevice::Action().
+      // Note: m_Device->CamSlot() returns NULL after SetChecked() is called.
+      // Use m_camSlot here.
+      if (m_Receiver && m_camSlot)
+      {
+        ChannelCamRelations.ClrChecked(m_Receiver->ChannelID(),
+                                       m_camSlot->SlotNumber());
+      }
     }
 
-    if (m_Receiver0)
+    if (m_Receiver)
     {
-      DEBUGLOG("Detaching Live Receiver0");
-      m_Device->Detach(m_Receiver0);
+      DEBUGLOG("Detaching Live Receiver");
+      m_Device->Detach(m_Receiver);
     }
     else
     {
@@ -482,32 +599,19 @@ void cVideoInput::Close()
       DEBUGLOG("No live filter present");
     }
 
+    m_DummyReceiver.reset();
+
     if (m_Receiver)
     {
       DEBUGLOG("Deleting Live Receiver");
       DELETENULL(m_Receiver);
     }
 
-    if (m_Receiver0)
-    {
-      DEBUGLOG("Deleting Live Receiver0");
-      DELETENULL(m_Receiver0);
-    }
-
     if (m_PatFilter)
     {
       DEBUGLOG("Deleting Live Filter");
       DELETENULL(m_PatFilter);
     }
-
-    //m_Device->SetCurrentChannel(NULL);
-    cCamSlot *cs = m_Device->CamSlot();
-    if (m_Priority <= MINPRIORITY && cs)
-    {
-      cs->StartDecrypting();
-      if (!cs->IsDecrypting())
-        cs->Assign(NULL);
-    }
   }
   m_Channel = NULL;
   m_Device = NULL;
@@ -528,33 +632,15 @@ bool cVideoInput::IsOpen()
 
 cChannel *cVideoInput::PmtChannel()
 {
-  return &m_Receiver->m_PmtChannel;
+  return &m_PmtChannel;
 }
 
-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_Device->AttachReceiver(m_Receiver);
-    cCamSlot *cs = m_Device->CamSlot();
-    if (m_Priority <= MINPRIORITY && cs)
-      cs->StartDecrypting();
-    m_SeenPmt = true;
-  }
-}
-
-inline void cVideoInput::Receive(uchar *data, int length)
+inline void cVideoInput::Receive(const uchar *data, int length)
 {
   if (m_PmtChange)
   {
      // generate pat/pmt so we can configure parsers later
-     cPatPmtGenerator patPmtGenerator(&m_Receiver->m_PmtChannel);
+     cPatPmtGenerator patPmtGenerator(&m_PmtChannel);
      m_VideoBuffer->Put(patPmtGenerator.GetPat(), TS_SIZE);
      int Index = 0;
      while (uchar *pmt = patPmtGenerator.GetPmt(Index))
@@ -564,29 +650,23 @@ inline void cVideoInput::Receive(uchar *data, int length)
   m_VideoBuffer->Put(data, length);
 }
 
-void cVideoInput::Retune()
+void cVideoInput::RequestRetune()
 {
-  INFOLOG("call retune ...");
-  cMutexLock lock(&m_Mutex);
-  m_IsRetune = true;
-  m_Event.Broadcast();
+  m_RetuneRequested = true;
+  m_Event.Signal();
 }
 
-void cVideoInput::Action()
+cVideoInput::eReceivingStatus cVideoInput::ReceivingStatus()
 {
-  cTimeMs starttime;
-
-  while (Running())
+  if (!m_Device || !m_DummyReceiver)
+    return RETUNE;
+  if (m_RetuneRequested)
   {
-    if (starttime.Elapsed() > (unsigned int)PmtTimeout*1000)
-    {
-      INFOLOG("VideoInput: no pat/pmt within timeout, falling back to channel pids");
-      m_Receiver->m_PmtChannel = *m_Channel;
-      PmtChange(true);
-    }
-    if (m_SeenPmt)
-      break;
-
-    usleep(1000);
+    (void)m_Device->Receiving();  // wait for the receivers mutex
+    if (m_DummyReceiver->BeenDetached())  // DetachAllReceivers() was called
+      return CLOSE;
+    else
+      return RETUNE;
   }
+  return NORMAL;
 }
diff --git a/videoinput.h b/videoinput.h
index d1ebb51..3ad8749 100644
--- a/videoinput.h
+++ b/videoinput.h
@@ -24,41 +24,44 @@
 
 #pragma once
 
+#include <memory>
+#include <atomic>
+#include <vdr/channels.h>
 #include <vdr/thread.h>
 
 class cLivePatFilter;
 class cLiveReceiver;
 class cVideoBuffer;
-class cChannel;
 class cDevice;
+class cDummyReceiver;
+class cCamSlot;
 
-class cVideoInput : public cThread
+class cVideoInput
 {
 friend class cLivePatFilter;
 friend class cLiveReceiver;
 public:
-  cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune);
+  cVideoInput(cCondWait &event);
   virtual ~cVideoInput();
   bool Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer);
   void Close();
   bool IsOpen();
-
+  void RequestRetune();
+  enum eReceivingStatus {NORMAL, RETUNE, CLOSE};
+  eReceivingStatus ReceivingStatus();
 protected:
-  virtual void Action(void);
-  void PmtChange(int pidChange);
   cChannel *PmtChannel();
-  void Receive(uchar *data, int length);
-  void Retune();
+  void Receive(const uchar *data, int length);
   cDevice          *m_Device;
+  cCamSlot *m_camSlot;
   cLivePatFilter   *m_PatFilter;
   cLiveReceiver    *m_Receiver;
-  cLiveReceiver    *m_Receiver0;
   const cChannel   *m_Channel;
   cVideoBuffer     *m_VideoBuffer;
   int               m_Priority;
-  bool              m_PmtChange;
-  bool              m_SeenPmt;
-  cCondVar          &m_Event;
-  cMutex            &m_Mutex;
-  bool              &m_IsRetune;
+  std::atomic<bool> m_PmtChange;
+  cChannel m_PmtChannel;
+  cCondWait &m_Event;
+  std::shared_ptr<cDummyReceiver> m_DummyReceiver;
+  std::atomic<bool> m_RetuneRequested;
 };
diff --git a/vnsi.c b/vnsi.c
index 5078eb6..e3541ee 100644
--- a/vnsi.c
+++ b/vnsi.c
@@ -23,25 +23,19 @@
  *
  */
 
-#include <getopt.h>
-#include <vdr/plugin.h>
 #include "vnsi.h"
 #include "vnsicommand.h"
 #include "setup.h"
 
+#include <getopt.h>
+#include <vdr/plugin.h>
+
 cPluginVNSIServer* cPluginVNSIServer::VNSIServer = NULL;
 
 cPluginVNSIServer::cPluginVNSIServer(void)
 {
   Server = NULL;
   VNSIServer = NULL;
-  probe = new cDvbVsniDeviceProbe();
-}
-
-cPluginVNSIServer::~cPluginVNSIServer()
-{
-  // Clean up after yourself!
-  delete probe;
 }
 
 const char *cPluginVNSIServer::CommandLineHelp(void)
@@ -155,11 +149,19 @@ bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
   else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
     TimeshiftBufferFileSize = atoi(Value);
   else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR))
+  {
     strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir));
-  else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING))
+    if (*TimeshiftBufferDir && TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+      /* strip trailing slash */
+      TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] = 0;
+  } else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING))
     PlayRecording = atoi(Value);
   else if (!strcasecmp(Name, CONFNAME_AVOIDEPGSCAN))
     AvoidEPGScan = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_DISABLESCRAMBLETIMEOUT))
+    DisableScrambleTimeout = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_DISABLECAMBLACKLIST))
+    DisableCamBlacklist = atoi(Value);
   else
     return false;
   return true;
diff --git a/vnsi.h b/vnsi.h
index 957700e..ade3376 100644
--- a/vnsi.h
+++ b/vnsi.h
@@ -28,7 +28,7 @@
 #include <vdr/thread.h>
 #include "vnsiserver.h"
 
-static const char *VERSION        = "1.3.1";
+static const char *VERSION        = "1.5.1";
 static const char *DESCRIPTION    = "VDR-Network-Streaming-Interface (VNSI) Server";
 
 extern int PmtTimeout;
@@ -38,18 +38,23 @@ extern int TimeshiftBufferFileSize;
 extern char TimeshiftBufferDir[PATH_MAX];
 extern int PlayRecording;
 extern int AvoidEPGScan;
+extern int DisableScrambleTimeout;
+extern int DisableCamBlacklist;
 
-class cDvbVsniDeviceProbe;
+class cDvbVsniDeviceProbe : public cDvbDeviceProbe
+{
+public:
+  virtual bool Probe(int Adapter, int Frontend);
+};
 
 class cPluginVNSIServer : public cPlugin {
 private:
   cVNSIServer *Server;
   static cPluginVNSIServer *VNSIServer;
-  cDvbVsniDeviceProbe *probe;
+  cDvbVsniDeviceProbe probe;
 
 public:
   cPluginVNSIServer(void);
-  virtual ~cPluginVNSIServer();
   virtual const char *Version(void) { return VERSION; }
   virtual const char *Description(void) { return DESCRIPTION; }
   virtual const char *CommandLineHelp(void);
@@ -72,12 +77,6 @@ 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:
diff --git a/vnsiclient.c b/vnsiclient.c
index 56d3e9a..bee2bb2 100644
--- a/vnsiclient.c
+++ b/vnsiclient.c
@@ -23,23 +23,11 @@
  *
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <map>
-
-#include <vdr/recording.h>
-#include <vdr/channels.h>
-#include <vdr/videodir.h>
-#include <vdr/plugin.h>
-#include <vdr/timers.h>
-#include <vdr/menu.h>
-#include <vdr/device.h>
-
+#include "vnsiclient.h"
 #include "vnsi.h"
 #include "config.h"
 #include "vnsicommand.h"
 #include "recordingscache.h"
-#include "vnsiclient.h"
 #include "streamer.h"
 #include "vnsiserver.h"
 #include "recplayer.h"
@@ -50,24 +38,30 @@
 #include "channelfilter.h"
 #include "channelscancontrol.h"
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <map>
+#include <memory>
+
+#include <vdr/recording.h>
+#include <vdr/channels.h>
+#include <vdr/videodir.h>
+#include <vdr/plugin.h>
+#include <vdr/timers.h>
+#include <vdr/menu.h>
+#include <vdr/device.h>
+
 cMutex cVNSIClient::m_timerLock;
 bool cVNSIClient::m_inhibidDataUpdates = false;
 
-cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
+cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr, CVNSITimers &timers)
   : m_Id(id),
-    m_loggedIn(false),
-    m_StatusInterfaceEnabled(false),
-    m_Streamer(NULL),
-    m_isStreaming(false),
-    m_bSupportRDS(false),
+    m_socket(fd),
     m_ClientAddress(ClientAdr),
-    m_RecPlayer(NULL),
-    m_req(NULL),
-    m_resp(NULL),
-    m_Osd(NULL),
-    m_ChannelScanControl(this)
+    m_ChannelScanControl(this),
+    m_vnsiTimers(timers)
 {
-  m_socket.SetHandle(fd);
+  SetDescription("VNSI Client %u->%s", id, ClientAdr);
 
   Start();
 }
@@ -77,7 +71,7 @@ cVNSIClient::~cVNSIClient()
   DEBUGLOG("%s", __FUNCTION__);
   StopChannelStreaming();
   m_ChannelScanControl.StopScan();
-  m_socket.close(); // force closing connection
+  m_socket.Shutdown();
   Cancel(10);
   DEBUGLOG("done");
 }
@@ -113,9 +107,9 @@ void cVNSIClient::Action(void)
 
       if (dataLength)
       {
-        data = (uint8_t*)malloc(dataLength);
-        if (!data)
-        {
+        try {
+          data = new uint8_t[dataLength];
+        } catch (const std::bad_alloc &) {
           ERRORLOG("Extra data buffer malloc error");
           break;
         }
@@ -141,9 +135,13 @@ void cVNSIClient::Action(void)
         break;
       }
 
-      cRequestPacket* req = new cRequestPacket(requestID, opcode, data, dataLength);
-
-      processRequest(req);
+      try {
+        cRequestPacket req(requestID, opcode, data, dataLength);
+        processRequest(req);
+      } catch (const std::exception &e) {
+        ERRORLOG("%s", e.what());
+        break;
+      }
     }
     else
     {
@@ -158,51 +156,35 @@ void cVNSIClient::Action(void)
   m_ChannelScanControl.StopScan();
 
   // Shutdown OSD
-  if (m_Osd)
-  {
-    delete m_Osd;
-    m_Osd = NULL;
-  }
+  delete m_Osd;
+  m_Osd = NULL;
 }
 
-bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
+bool cVNSIClient::StartChannelStreaming(cResponsePacket &resp, const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
 {
+  delete m_Streamer;
   m_Streamer    = new cLiveStreamer(m_Id, m_bSupportRDS, timeshift, timeout);
-  m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp);
+  m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, &resp);
   return m_isStreaming;
 }
 
 void cVNSIClient::StopChannelStreaming()
 {
   m_isStreaming = false;
-  if (m_Streamer)
-  {
-    delete m_Streamer;
-    m_Streamer = NULL;
-  }
+  delete m_Streamer;
+  m_Streamer = NULL;
 }
 
-void cVNSIClient::TimerChange(const cTimer *Timer, eTimerChange Change)
-{
-  TimerChange();
-}
-
-void cVNSIClient::TimerChange()
+void cVNSIClient::SignalTimerChange()
 {
   cMutexLock lock(&m_msgLock);
 
   if (m_StatusInterfaceEnabled)
   {
-    cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStatus(VNSI_STATUS_TIMERCHANGE))
-    {
-      delete resp;
-      return;
-    }
-
-    resp->finalise();
-    m_socket.write(resp->getPtr(), resp->getLen());
-    delete resp;
+    cResponsePacket resp;
+    resp.initStatus(VNSI_STATUS_TIMERCHANGE);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
   }
 }
 
@@ -213,16 +195,10 @@ void cVNSIClient::ChannelsChange()
   if (!m_StatusInterfaceEnabled)
     return;
 
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStatus(VNSI_STATUS_CHANNELCHANGE))
-  {
-    delete resp;
-    return;
-  }
-
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initStatus(VNSI_STATUS_CHANNELCHANGE);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::RecordingsChange()
@@ -232,40 +208,49 @@ void cVNSIClient::RecordingsChange()
   if (!m_StatusInterfaceEnabled)
     return;
 
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStatus(VNSI_STATUS_RECORDINGSCHANGE))
-  {
-    delete resp;
-    return;
-  }
-
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initStatus(VNSI_STATUS_RECORDINGSCHANGE);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
-void cVNSIClient::EpgChange()
+bool cVNSIClient::EpgChange()
 {
+  bool callAgain = false;
+
   cMutexLock lock(&m_msgLock);
 
   if (!m_StatusInterfaceEnabled)
-    return;
+    return callAgain;
 
+#if VDRVERSNUM >= 20301
+  cStateKey SchedulesStateKey(true);
+  const cSchedules *schedules = cSchedules::GetSchedulesRead(SchedulesStateKey);
+  if (!schedules)
+  {
+    return callAgain;
+  }
+#else
   cSchedulesLock MutexLock;
   const cSchedules *schedules = cSchedules::Schedules(MutexLock);
   if (!schedules)
-    return;
+    return callAgain;
+#endif
 
-  std::map<int, sEpgUpdate>::iterator it;
   for (const cSchedule *schedule = schedules->First(); schedule; schedule = schedules->Next(schedule))
   {
-    cEvent *lastEvent =  schedule->Events()->Last();
+    const cEvent *lastEvent =  schedule->Events()->Last();
     if (!lastEvent)
       continue;
 
+#if VDRVERSNUM >= 20301
+    LOCK_CHANNELS_READ;
+    const cChannel *channel = Channels->GetByChannelID(schedule->ChannelID());
+#else
     Channels.Lock(false);
     const cChannel *channel = Channels.GetByChannelID(schedule->ChannelID());
     Channels.Unlock();
+#endif
 
     if (!channel)
       continue;
@@ -274,31 +259,40 @@ void cVNSIClient::EpgChange()
       continue;
 
     uint32_t channelId = CreateStringHash(schedule->ChannelID().ToString());
-    it = m_epgUpdate.find(channelId);
-    if (it != m_epgUpdate.end() && it->second.lastEvent >= lastEvent->StartTime())
+    auto it = m_epgUpdate.find(channelId);
+    if (it == m_epgUpdate.end() || it->second.attempts > 3 ||
+        it->second.lastEvent >= lastEvent->StartTime())
     {
       continue;
     }
 
-    if (it->second.attempts > 3)
+    time_t now = time(nullptr);
+    if ((now - it->second.lastTrigger) < 5)
     {
+      callAgain = true;
       continue;
     }
+
     it->second.attempts++;
+    it->second.lastTrigger = now;
 
-    INFOLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId);
+    DEBUGLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId);
 
-    cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStatus(VNSI_STATUS_EPGCHANGE))
-    {
-      delete resp;
-      return;
-    }
-    resp->add_U32(channelId);
-    resp->finalise();
-    m_socket.write(resp->getPtr(), resp->getLen());
-    delete resp;
+    cResponsePacket resp;
+    resp.initStatus(VNSI_STATUS_EPGCHANGE);
+    resp.add_U32(channelId);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
+
+    callAgain = true;
+    break;
   }
+
+#if VDRVERSNUM >= 20301
+  SchedulesStateKey.Remove();
+#endif
+
+  return callAgain;
 }
 
 void cVNSIClient::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
@@ -307,28 +301,22 @@ void cVNSIClient::Recording(const cDevice *Device, const char *Name, const char
 
   if (m_StatusInterfaceEnabled)
   {
-    cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStatus(VNSI_STATUS_RECORDING))
-    {
-      delete resp;
-      return;
-    }
-
-    resp->add_U32(Device->CardIndex());
-    resp->add_U32(On);
+    cResponsePacket resp;
+    resp.initStatus(VNSI_STATUS_RECORDING);
+    resp.add_U32(Device->CardIndex());
+    resp.add_U32(On);
     if (Name)
-      resp->add_String(Name);
+      resp.add_String(Name);
     else
-      resp->add_String("");
+      resp.add_String("");
 
     if (FileName)
-      resp->add_String(FileName);
+      resp.add_String(FileName);
     else
-      resp->add_String("");
+      resp.add_String("");
 
-    resp->finalise();
-    m_socket.write(resp->getPtr(), resp->getLen());
-    delete resp;
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
   }
 }
 
@@ -354,27 +342,24 @@ void cVNSIClient::OsdStatusMessage(const char *Message)
     else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return;
     else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return;
     else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("CAM activated!")) == 0) return;
     else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return;
     else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return;
     else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return;
     else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return;
     else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return;
+    else if (strncmp(Message, trVDR("VDR will shut down in"), 21) == 0) return;
 
-    cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStatus(VNSI_STATUS_MESSAGE))
-    {
-      delete resp;
-      return;
-    }
-
-    resp->add_U32(0);
-    resp->add_String(Message);
-    resp->finalise();
-    m_socket.write(resp->getPtr(), resp->getLen());
-    delete resp;
+    cResponsePacket resp;
+    resp.initStatus(VNSI_STATUS_MESSAGE);
+    resp.add_U32(0);
+    resp.add_String(Message);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
   }
 }
 
+#if VDRVERSNUM >= 20104
 void cVNSIClient::ChannelChange(const cChannel *Channel)
 {
   cMutexLock lock(&m_msgLock);
@@ -383,221 +368,213 @@ void cVNSIClient::ChannelChange(const cChannel *Channel)
     m_Streamer->RetuneChannel(Channel);
   }
 }
+#endif
 
-bool cVNSIClient::processRequest(cRequestPacket* req)
+bool cVNSIClient::processRequest(cRequestPacket &req)
 {
   cMutexLock lock(&m_msgLock);
 
-  m_req = req;
-  m_resp = new cResponsePacket();
-  if (!m_resp->init(m_req->getRequestID()))
-  {
-    ERRORLOG("Response packet init fail");
-    delete m_resp;
-    delete m_req;
-    m_resp = NULL;
-    m_req = NULL;
-    return false;
-  }
-
   bool result = false;
-  switch(m_req->getOpCode())
+  switch(req.getOpCode())
   {
     /** OPCODE 1 - 19: VNSI network functions for general purpose */
     case VNSI_LOGIN:
-      result = process_Login();
+      result = process_Login(req);
       break;
 
     case VNSI_GETTIME:
-      result = process_GetTime();
+      result = process_GetTime(req);
       break;
 
     case VNSI_ENABLESTATUSINTERFACE:
-      result = process_EnableStatusInterface();
+      result = process_EnableStatusInterface(req);
       break;
 
     case VNSI_PING:
-      result = process_Ping();
+      result = process_Ping(req);
       break;
 
     case VNSI_GETSETUP:
-      result = process_GetSetup();
+      result = process_GetSetup(req);
       break;
 
     case VNSI_STORESETUP:
-      result = process_StoreSetup();
+      result = process_StoreSetup(req);
       break;
 
     /** OPCODE 20 - 39: VNSI network functions for live streaming */
     case VNSI_CHANNELSTREAM_OPEN:
-      result = processChannelStream_Open();
+      result = processChannelStream_Open(req);
       break;
 
     case VNSI_CHANNELSTREAM_CLOSE:
-      result = processChannelStream_Close();
+      result = processChannelStream_Close(req);
       break;
 
     case VNSI_CHANNELSTREAM_SEEK:
-      result = processChannelStream_Seek();
+      result = processChannelStream_Seek(req);
       break;
 
     /** OPCODE 40 - 59: VNSI network functions for recording streaming */
     case VNSI_RECSTREAM_OPEN:
-      result = processRecStream_Open();
+      result = processRecStream_Open(req);
       break;
 
     case VNSI_RECSTREAM_CLOSE:
-      result = processRecStream_Close();
+      result = processRecStream_Close(req);
       break;
 
     case VNSI_RECSTREAM_GETBLOCK:
-      result = processRecStream_GetBlock();
+      result = processRecStream_GetBlock(req);
       break;
 
     case VNSI_RECSTREAM_POSTOFRAME:
-      result = processRecStream_PositionFromFrameNumber();
+      result = processRecStream_PositionFromFrameNumber(req);
       break;
 
     case VNSI_RECSTREAM_FRAMETOPOS:
-      result = processRecStream_FrameNumberFromPosition();
+      result = processRecStream_FrameNumberFromPosition(req);
       break;
 
     case VNSI_RECSTREAM_GETIFRAME:
-      result = processRecStream_GetIFrame();
+      result = processRecStream_GetIFrame(req);
       break;
 
     case VNSI_RECSTREAM_GETLENGTH:
-      result = processRecStream_GetLength();
+      result = processRecStream_GetLength(req);
       break;
 
 
     /** OPCODE 60 - 79: VNSI network functions for channel access */
     case VNSI_CHANNELS_GETCOUNT:
-      result = processCHANNELS_ChannelsCount();
+      result = processCHANNELS_ChannelsCount(req);
       break;
 
     case VNSI_CHANNELS_GETCHANNELS:
-      result = processCHANNELS_GetChannels();
+      result = processCHANNELS_GetChannels(req);
       break;
 
     case VNSI_CHANNELGROUP_GETCOUNT:
-      result = processCHANNELS_GroupsCount();
+      result = processCHANNELS_GroupsCount(req);
       break;
 
     case VNSI_CHANNELGROUP_LIST:
-      result = processCHANNELS_GroupList();
+      result = processCHANNELS_GroupList(req);
       break;
 
     case VNSI_CHANNELGROUP_MEMBERS:
-      result = processCHANNELS_GetGroupMembers();
+      result = processCHANNELS_GetGroupMembers(req);
       break;
 
     case VNSI_CHANNELS_GETCAIDS:
-      result = processCHANNELS_GetCaids();
+      result = processCHANNELS_GetCaids(req);
       break;
 
     case VNSI_CHANNELS_GETWHITELIST:
-      result = processCHANNELS_GetWhitelist();
+      result = processCHANNELS_GetWhitelist(req);
       break;
 
     case VNSI_CHANNELS_GETBLACKLIST:
-      result = processCHANNELS_GetBlacklist();
+      result = processCHANNELS_GetBlacklist(req);
       break;
 
     case VNSI_CHANNELS_SETWHITELIST:
-      result = processCHANNELS_SetWhitelist();
+      result = processCHANNELS_SetWhitelist(req);
       break;
 
     case VNSI_CHANNELS_SETBLACKLIST:
-      result = processCHANNELS_SetBlacklist();
+      result = processCHANNELS_SetBlacklist(req);
       break;
 
     /** OPCODE 80 - 99: VNSI network functions for timer access */
     case VNSI_TIMER_GETCOUNT:
-      result = processTIMER_GetCount();
+      result = processTIMER_GetCount(req);
       break;
 
     case VNSI_TIMER_GET:
-      result = processTIMER_Get();
+      result = processTIMER_Get(req);
       break;
 
     case VNSI_TIMER_GETLIST:
-      result = processTIMER_GetList();
+      result = processTIMER_GetList(req);
       break;
 
     case VNSI_TIMER_ADD:
-      result = processTIMER_Add();
+      result = processTIMER_Add(req);
       break;
 
     case VNSI_TIMER_DELETE:
-      result = processTIMER_Delete();
+      result = processTIMER_Delete(req);
       break;
 
     case VNSI_TIMER_UPDATE:
-      result = processTIMER_Update();
+      result = processTIMER_Update(req);
       break;
 
+    case VNSI_TIMER_GETTYPES:
+      result = processTIMER_GetTypes(req);
+      break;
 
     /** OPCODE 100 - 119: VNSI network functions for recording access */
     case VNSI_RECORDINGS_DISKSIZE:
-      result = processRECORDINGS_GetDiskSpace();
+      result = processRECORDINGS_GetDiskSpace(req);
       break;
 
     case VNSI_RECORDINGS_GETCOUNT:
-      result = processRECORDINGS_GetCount();
+      result = processRECORDINGS_GetCount(req);
       break;
 
     case VNSI_RECORDINGS_GETLIST:
-      result = processRECORDINGS_GetList();
+      result = processRECORDINGS_GetList(req);
       break;
 
     case VNSI_RECORDINGS_RENAME:
-      result = processRECORDINGS_Rename();
+      result = processRECORDINGS_Rename(req);
       break;
 
     case VNSI_RECORDINGS_DELETE:
-      result = processRECORDINGS_Delete();
+      result = processRECORDINGS_Delete(req);
       break;
 
     case VNSI_RECORDINGS_GETEDL:
-      result = processRECORDINGS_GetEdl();
+      result = processRECORDINGS_GetEdl(req);
       break;
 
 
     /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
     case VNSI_EPG_GETFORCHANNEL:
-      result = processEPG_GetForChannel();
+      result = processEPG_GetForChannel(req);
       break;
 
 
     /** OPCODE 140 - 159: VNSI network functions for channel scanning */
     case VNSI_SCAN_SUPPORTED:
-      result = processSCAN_ScanSupported();
+      result = processSCAN_ScanSupported(req);
       break;
 
     case VNSI_SCAN_GETCOUNTRIES:
-      result = processSCAN_GetCountries();
+      result = processSCAN_GetCountries(req);
       break;
 
     case VNSI_SCAN_GETSATELLITES:
-      result = processSCAN_GetSatellites();
+      result = processSCAN_GetSatellites(req);
       break;
 
     case VNSI_SCAN_START:
-      result = processSCAN_Start();
+      result = processSCAN_Start(req);
       break;
 
     case VNSI_SCAN_STOP:
-      result = processSCAN_Stop();
+      result = processSCAN_Stop(req);
       break;
 
     case VNSI_SCAN_SUPPORTED_TYPES:
-      result = processSCAN_GetSupportedTypes();
+      result = processSCAN_GetSupportedTypes(req);
       break;
 
     /** OPCODE 160 - 179: VNSI network functions for OSD */
     case VNSI_OSD_CONNECT:
-      result = processOSD_Connect();
+      result = processOSD_Connect(req);
       break;
 
     case VNSI_OSD_DISCONNECT:
@@ -605,55 +582,49 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       break;
 
     case VNSI_OSD_HITKEY:
-      result = processOSD_Hitkey();
+      result = processOSD_Hitkey(req);
       break;
 
 
     /** OPCODE 180 - 189: VNSI network functions for deleted recording access */
     case VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED:
-      result = processRECORDINGS_DELETED_Supported();
+      result = processRECORDINGS_DELETED_Supported(req);
       break;
 
     case VNSI_RECORDINGS_DELETED_GETCOUNT:
-      result = processRECORDINGS_DELETED_GetCount();
+      result = processRECORDINGS_DELETED_GetCount(req);
       break;
 
     case VNSI_RECORDINGS_DELETED_GETLIST:
-      result = processRECORDINGS_DELETED_GetList();
+      result = processRECORDINGS_DELETED_GetList(req);
       break;
 
     case VNSI_RECORDINGS_DELETED_DELETE:
-      result = processRECORDINGS_DELETED_Delete();
+      result = processRECORDINGS_DELETED_Delete(req);
       break;
 
     case VNSI_RECORDINGS_DELETED_UNDELETE:
-      result = processRECORDINGS_DELETED_Undelete();
+      result = processRECORDINGS_DELETED_Undelete(req);
       break;
 
     case VNSI_RECORDINGS_DELETED_DELETE_ALL:
-      result = processRECORDINGS_DELETED_DeleteAll();
+      result = processRECORDINGS_DELETED_DeleteAll(req);
       break;
   }
 
-  delete m_resp;
-  m_resp = NULL;
-
-  delete m_req;
-  m_req = NULL;
-
   return result;
 }
 
 
 /** OPCODE 1 - 19: VNSI network functions for general purpose */
 
-bool cVNSIClient::process_Login() /* OPCODE 1 */
+bool cVNSIClient::process_Login(cRequestPacket &req) /* OPCODE 1 */
 {
-  if (m_req->getDataLength() <= 4) return false;
+  if (req.getDataLength() <= 4) return false;
 
-  m_protocolVersion      = m_req->extract_U32();
-                           m_req->extract_U8();
-  const char *clientName = m_req->extract_String();
+  m_protocolVersion      = req.extract_U32();
+                           req.extract_U8();
+  const char *clientName = req.extract_String();
 
   INFOLOG("Welcome client '%s' with protocol version '%u'", clientName, m_protocolVersion);
 
@@ -662,12 +633,14 @@ bool cVNSIClient::process_Login() /* OPCODE 1 */
   struct tm* timeStruct = localtime(&timeNow);
   int timeOffset        = timeStruct->tm_gmtoff;
 
-  m_resp->add_U32(VNSI_PROTOCOLVERSION);
-  m_resp->add_U32(timeNow);
-  m_resp->add_S32(timeOffset);
-  m_resp->add_String("VDR-Network-Streaming-Interface (VNSI) Server");
-  m_resp->add_String(VNSI_SERVER_VERSION);
-  m_resp->finalise();
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(VNSI_PROTOCOLVERSION);
+  resp.add_U32(timeNow);
+  resp.add_S32(timeOffset);
+  resp.add_String("VDR-Network-Streaming-Interface (VNSI) Server");
+  resp.add_String(VNSI_SERVER_VERSION);
+  resp.finalise();
 
   if (m_protocolVersion < VNSI_MIN_PROTOCOLVERSION)
     ERRORLOG("Client '%s' have a not allowed protocol version '%u', terminating client", clientName, m_protocolVersion);
@@ -684,133 +657,145 @@ bool cVNSIClient::process_Login() /* OPCODE 1 */
     m_bSupportRDS = true;
   }
 
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
-
-  delete[] clientName;
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::process_GetTime() /* OPCODE 2 */
+bool cVNSIClient::process_GetTime(cRequestPacket &req) /* OPCODE 2 */
 {
   time_t timeNow        = time(NULL);
   struct tm* timeStruct = localtime(&timeNow);
   int timeOffset        = timeStruct->tm_gmtoff;
 
-  m_resp->add_U32(timeNow);
-  m_resp->add_S32(timeOffset);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(timeNow);
+  resp.add_S32(timeOffset);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::process_EnableStatusInterface()
+bool cVNSIClient::process_EnableStatusInterface(cRequestPacket &req)
 {
-  bool enabled = m_req->extract_U8();
+  bool enabled = req.extract_U8();
 
   SetStatusInterface(enabled);
   SetPriority(1);
 
-  m_resp->add_U32(VNSI_RET_OK);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(VNSI_RET_OK);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::process_Ping() /* OPCODE 7 */
+bool cVNSIClient::process_Ping(cRequestPacket &req) /* OPCODE 7 */
 {
-  m_resp->add_U32(1);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(1);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::process_GetSetup() /* OPCODE 8 */
+bool cVNSIClient::process_GetSetup(cRequestPacket &req) /* OPCODE 8 */
 {
-  char* name = m_req->extract_String();
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  char* name = req.extract_String();
   if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
-    m_resp->add_U32(PmtTimeout);
+    resp.add_U32(PmtTimeout);
   else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
-    m_resp->add_U32(TimeshiftMode);
+    resp.add_U32(TimeshiftMode);
   else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
-    m_resp->add_U32(TimeshiftBufferSize);
+    resp.add_U32(TimeshiftBufferSize);
   else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
-    m_resp->add_U32(TimeshiftBufferFileSize);
+    resp.add_U32(TimeshiftBufferFileSize);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */
+bool cVNSIClient::process_StoreSetup(cRequestPacket &req) /* OPCODE 9 */
 {
-  char* name = m_req->extract_String();
+  char* name = req.extract_String();
 
   if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
   {
-    int value = m_req->extract_U32();
+    int value = req.extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_PMTTIMEOUT, value);
   }
   else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
   {
-    int value = m_req->extract_U32();
+    int value = req.extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFT, value);
   }
   else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
   {
-    int value = m_req->extract_U32();
+    int value = req.extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERSIZE, value);
   }
   else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
   {
-    int value = m_req->extract_U32();
+    int value = req.extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value);
   }
   else if (!strcasecmp(name, CONFNAME_PLAYRECORDING))
   {
-    int value = m_req->extract_U32();
+    int value = req.extract_U32();
     cPluginVNSIServer::StoreSetup(CONFNAME_PLAYRECORDING, value);
   }
 
-  m_resp->add_U32(VNSI_RET_OK);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(VNSI_RET_OK);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
 /** OPCODE 20 - 39: VNSI network functions for live streaming */
 
-bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
+bool cVNSIClient::processChannelStream_Open(cRequestPacket &req) /* OPCODE 20 */
 {
-  uint32_t uid = m_req->extract_U32();
-  int32_t priority = m_req->extract_S32();
-  uint8_t timeshift = m_req->extract_U8();
-  uint32_t timeout = m_req->extract_U32();
-
-  if(timeout == 0)
-    timeout = VNSIServerConfig.stream_timeout;
+  uint32_t uid = req.extract_U32();
+  int32_t priority = req.extract_S32();
+  uint8_t timeshift = req.extract_U8();
+  uint32_t timeout = req.end()
+    ? VNSIServerConfig.stream_timeout
+    : req.extract_U32();
 
   if (m_isStreaming)
     StopChannelStreaming();
 
-  Channels.Lock(false);
-  const cChannel *channel = NULL;
-
-  // try to find channel by uid first
-  channel = FindChannelByUID(uid);
-  Channels.Unlock();
+  const cChannel *channel = FindChannelByUID(uid);
 
   // try channelnumber
   if (channel == NULL)
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_CHANNELS_READ;
+    channel = Channels->GetByNumber(uid);
+#else
     channel = Channels.GetByNumber(uid);
+#endif
+  }
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
   if (channel == NULL) {
     ERRORLOG("Can't find channel %08x", uid);
-    m_resp->add_U32(VNSI_RET_DATAINVALID);
+    resp.add_U32(VNSI_RET_DATAINVALID);
   }
   else
   {
-    if (StartChannelStreaming(channel, priority, timeshift, timeout))
+    if (StartChannelStreaming(resp, channel, priority, timeshift, timeout))
     {
       INFOLOG("Started streaming of channel %s (timeout %i seconds)", channel->Name(), timeout);
       // return here without sending the response
@@ -819,93 +804,98 @@ bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
     }
 
     DEBUGLOG("Can't stream channel %s", channel->Name());
-    m_resp->add_U32(VNSI_RET_DATALOCKED);
+    resp.add_U32(VNSI_RET_DATALOCKED);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return false;
 }
 
-bool cVNSIClient::processChannelStream_Close() /* OPCODE 21 */
+bool cVNSIClient::processChannelStream_Close(cRequestPacket &req) /* OPCODE 21 */
 {
   if (m_isStreaming)
     StopChannelStreaming();
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
+
   return true;
 }
 
-bool cVNSIClient::processChannelStream_Seek() /* OPCODE 22 */
+bool cVNSIClient::processChannelStream_Seek(cRequestPacket &req) /* OPCODE 22 */
 {
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   uint32_t serial = 0;
   if (m_isStreaming && m_Streamer)
   {
-    int64_t time = m_req->extract_S64();
+    int64_t time = req.extract_S64();
     if (m_Streamer->SeekTime(time, serial))
-      m_resp->add_U32(VNSI_RET_OK);
+      resp.add_U32(VNSI_RET_OK);
     else
-      m_resp->add_U32(VNSI_RET_ERROR);
+      resp.add_U32(VNSI_RET_ERROR);
   }
   else
-    m_resp->add_U32(VNSI_RET_ERROR);
+    resp.add_U32(VNSI_RET_ERROR);
 
-  m_resp->add_U32(serial);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.add_U32(serial);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
 /** OPCODE 40 - 59: VNSI network functions for recording streaming */
 
-bool cVNSIClient::processRecStream_Open() /* OPCODE 40 */
+bool cVNSIClient::processRecStream_Open(cRequestPacket &req) /* OPCODE 40 */
 {
-  cRecording *recording = NULL;
+  const cRecording *recording = NULL;
 
-  uint32_t uid = m_req->extract_U32();
+  uint32_t uid = req.extract_U32();
   recording = cRecordingsCache::GetInstance().Lookup(uid);
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   if (recording && m_RecPlayer == NULL)
   {
     m_RecPlayer = new cRecPlayer(recording);
 
-    m_resp->add_U32(VNSI_RET_OK);
-    m_resp->add_U32(m_RecPlayer->getLengthFrames());
-    m_resp->add_U64(m_RecPlayer->getLengthBytes());
-
-#if VDRVERSNUM < 10703
-    m_resp->add_U8(true);//added for TS
-#else
-    m_resp->add_U8(recording->IsPesRecording());//added for TS
-#endif
+    resp.add_U32(VNSI_RET_OK);
+    resp.add_U32(m_RecPlayer->getLengthFrames());
+    resp.add_U64(m_RecPlayer->getLengthBytes());
+    resp.add_U8(recording->IsPesRecording());//added for TS
   }
   else
   {
-    m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
+    resp.add_U32(VNSI_RET_DATAUNKNOWN);
     ERRORLOG("%s - unable to start recording !", __FUNCTION__);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::processRecStream_Close() /* OPCODE 41 */
+bool cVNSIClient::processRecStream_Close(cRequestPacket &req) /* OPCODE 41 */
 {
-  if (m_RecPlayer)
-  {
-    delete m_RecPlayer;
-    m_RecPlayer = NULL;
-  }
+  delete m_RecPlayer;
+  m_RecPlayer = NULL;
 
-  m_resp->add_U32(VNSI_RET_OK);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(VNSI_RET_OK);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRecStream_GetBlock() /* OPCODE 42 */
+bool cVNSIClient::processRecStream_GetBlock(cRequestPacket &req) /* OPCODE 42 */
 {
   if (m_isStreaming)
   {
@@ -919,62 +909,69 @@ bool cVNSIClient::processRecStream_GetBlock() /* OPCODE 42 */
     return false;
   }
 
-  uint64_t position  = m_req->extract_U64();
-  uint32_t amount    = m_req->extract_U32();
+  uint64_t position  = req.extract_U64();
+  uint32_t amount    = req.extract_U32();
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
-  uint8_t* p = m_resp->reserve(amount);
+  uint8_t* p = resp.reserve(amount);
   uint32_t amountReceived = m_RecPlayer->getBlock(p, position, amount);
 
-  if(amount > amountReceived) m_resp->unreserve(amount - amountReceived);
+  if(amount > amountReceived) resp.unreserve(amount - amountReceived);
 
   if (!amountReceived)
   {
-    m_resp->add_U32(0);
+    resp.add_U32(0);
     DEBUGLOG("written 4(0) as getblock got 0");
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRecStream_PositionFromFrameNumber() /* OPCODE 43 */
+bool cVNSIClient::processRecStream_PositionFromFrameNumber(cRequestPacket &req) /* OPCODE 43 */
 {
   uint64_t retval       = 0;
-  uint32_t frameNumber  = m_req->extract_U32();
+  uint32_t frameNumber  = req.extract_U32();
 
   if (m_RecPlayer)
     retval = m_RecPlayer->positionFromFrameNumber(frameNumber);
 
-  m_resp->add_U64(retval);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U64(retval);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   DEBUGLOG("Wrote posFromFrameNum reply to client");
   return true;
 }
 
-bool cVNSIClient::processRecStream_FrameNumberFromPosition() /* OPCODE 44 */
+bool cVNSIClient::processRecStream_FrameNumberFromPosition(cRequestPacket &req) /* OPCODE 44 */
 {
   uint32_t retval   = 0;
-  uint64_t position = m_req->extract_U64();
+  uint64_t position = req.extract_U64();
 
   if (m_RecPlayer)
     retval = m_RecPlayer->frameNumberFromPosition(position);
 
-  m_resp->add_U32(retval);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(retval);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   DEBUGLOG("Wrote frameNumFromPos reply to client");
   return true;
 }
 
-bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */
+bool cVNSIClient::processRecStream_GetIFrame(cRequestPacket &req) /* OPCODE 45 */
 {
   bool success            = false;
-  uint32_t frameNumber    = m_req->extract_U32();
-  uint32_t direction      = m_req->extract_U32();
+  uint32_t frameNumber    = req.extract_U32();
+  uint32_t direction      = req.extract_U32();
   uint64_t rfilePosition  = 0;
   uint32_t rframeNumber   = 0;
   uint32_t rframeLength   = 0;
@@ -982,26 +979,29 @@ bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */
   if (m_RecPlayer)
     success = m_RecPlayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   // returns file position, frame number, length
   if (success)
   {
-    m_resp->add_U64(rfilePosition);
-    m_resp->add_U32(rframeNumber);
-    m_resp->add_U32(rframeLength);
+    resp.add_U64(rfilePosition);
+    resp.add_U32(rframeNumber);
+    resp.add_U32(rframeLength);
   }
   else
   {
-    m_resp->add_U32(0);
+    resp.add_U32(0);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   DEBUGLOG("Wrote GNIF reply to client %lu %u %u", rfilePosition, rframeNumber, rframeLength);
   return true;
 }
 
-bool cVNSIClient::processRecStream_GetLength() /* OPCODE 46 */
+bool cVNSIClient::processRecStream_GetLength(cRequestPacket &req) /* OPCODE 46 */
 {
   uint64_t length = 0;
 
@@ -1011,42 +1011,66 @@ bool cVNSIClient::processRecStream_GetLength() /* OPCODE 46 */
     length = m_RecPlayer->getLengthBytes();
   }
 
-  m_resp->add_U64(length);
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U64(length);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
 /** OPCODE 60 - 79: VNSI network functions for channel access */
 
-bool cVNSIClient::processCHANNELS_ChannelsCount() /* OPCODE 61 */
+bool cVNSIClient::processCHANNELS_ChannelsCount(cRequestPacket &req) /* OPCODE 61 */
 {
+  int count = 0;
+#if VDRVERSNUM >= 20301
+  {
+    LOCK_CHANNELS_READ;
+    count = Channels->MaxNumber();
+  }
+#else
   Channels.Lock(false);
-  int count = Channels.MaxNumber();
+  count = Channels.MaxNumber();
   Channels.Unlock();
+#endif
 
-  m_resp->add_U32(count);
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(count);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
+bool cVNSIClient::processCHANNELS_GetChannels(cRequestPacket &req) /* OPCODE 63 */
 {
-  if (m_req->getDataLength() != 5) return false;
+  if (req.getDataLength() != 5) return false;
 
-  bool radio = m_req->extract_U32();
-  bool filter = m_req->extract_U8();
+  bool radio = req.extract_U32();
+  bool filter = req.extract_U8();
 
+#if VDRVERSNUM >= 20301
+  cStateKey ChannelsKey(true);
+  const cChannels *Channels = cChannels::GetChannelsRead(ChannelsKey);
+#else
   Channels.Lock(false);
+#endif
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
   cString caids;
   int caid;
   int caid_idx;
+#if VDRVERSNUM >= 20301
+  for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
+#else
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+#endif
   {
     if (radio != cVNSIChannelFilter::IsRadio(channel))
       continue;
@@ -1060,11 +1084,11 @@ bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
       continue;
 
     uint32_t uuid = CreateChannelUID(channel);
-    m_resp->add_U32(channel->Number());
-    m_resp->add_String(m_toUTF8.Convert(channel->Name()));
-    m_resp->add_String(m_toUTF8.Convert(channel->Provider()));
-    m_resp->add_U32(uuid);
-    m_resp->add_U32(channel->Ca(0));
+    resp.add_U32(channel->Number());
+    resp.add_String(m_toUTF8.Convert(channel->Name()));
+    resp.add_String(m_toUTF8.Convert(channel->Provider()));
+    resp.add_U32(uuid);
+    resp.add_U32(channel->Ca(0));
     caid_idx = 0;
     caids = "caids:";
     while((caid = channel->Ca(caid_idx)) != 0)
@@ -1072,35 +1096,31 @@ bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
       caids = cString::sprintf("%s%d;", (const char*)caids, caid);
       caid_idx++;
     }
-    m_resp->add_String((const char*)caids);
+    resp.add_String((const char*)caids);
     if (m_protocolVersion >= 6)
     {
-      m_resp->add_String(CreatePiconRef(channel));
+      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;
-    }
+    m_epgUpdate.insert(std::make_pair(uuid, sEpgUpdate()));
   }
 
+#if VDRVERSNUM >= 20301
+  ChannelsKey.Remove();
+#else
   Channels.Unlock();
+#endif
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GroupsCount()
+bool cVNSIClient::processCHANNELS_GroupsCount(cRequestPacket &req)
 {
-  uint32_t type = m_req->extract_U32();
-
-  Channels.Lock(false);
+  uint32_t type = req.extract_U32();
 
   m_channelgroups[0].clear();
   m_channelgroups[1].clear();
@@ -1118,54 +1138,63 @@ bool cVNSIClient::processCHANNELS_GroupsCount()
       break;
   }
 
-  Channels.Unlock();
-
   uint32_t count = m_channelgroups[0].size() + m_channelgroups[1].size();
 
-  m_resp->add_U32(count);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(count);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GroupList()
+bool cVNSIClient::processCHANNELS_GroupList(cRequestPacket &req)
 {
-  uint32_t radio = m_req->extract_U8();
-  std::map<std::string, ChannelGroup>::iterator i;
+  uint32_t radio = req.extract_U8();
 
-  for(i = m_channelgroups[radio].begin(); i != m_channelgroups[radio].end(); i++)
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
+  for (const auto &i : m_channelgroups[radio])
   {
-    m_resp->add_String(i->second.name.c_str());
-    m_resp->add_U8(i->second.radio);
+    resp.add_String(i.second.name.c_str());
+    resp.add_U8(i.second.radio);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GetGroupMembers()
+bool cVNSIClient::processCHANNELS_GetGroupMembers(cRequestPacket &req)
 {
-  char* groupname = m_req->extract_String();
-  uint32_t radio = m_req->extract_U8();
-  bool filter = m_req->extract_U8();
+  char* groupname = req.extract_String();
+  uint32_t radio = req.extract_U8();
+  bool filter = req.extract_U8();
   int index = 0;
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   // unknown group
   if(m_channelgroups[radio].find(groupname) == m_channelgroups[radio].end())
   {
-    delete[] groupname;
-    m_resp->finalise();
-    m_socket.write(m_resp->getPtr(), m_resp->getLen());
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
     return true;
   }
 
   bool automatic = m_channelgroups[radio][groupname].automatic;
   std::string name;
 
+#if VDRVERSNUM >= 20301
+  cStateKey ChannelsKey(true);
+  const cChannels *Channels = cChannels::GetChannelsRead(ChannelsKey);
+  for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
+#else
   Channels.Lock(false);
-
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+#endif
   {
 
     if(automatic && !channel->GroupSep())
@@ -1191,27 +1220,30 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
 
     if(name == groupname)
     {
-      m_resp->add_U32(CreateChannelUID(channel));
-      m_resp->add_U32(++index);
+      resp.add_U32(CreateChannelUID(channel));
+      resp.add_U32(++index);
     }
   }
 
+#if VDRVERSNUM >= 20301
+  ChannelsKey.Remove();
+#else
   Channels.Unlock();
+#endif
 
-  delete[] groupname;
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GetCaids()
+bool cVNSIClient::processCHANNELS_GetCaids(cRequestPacket &req)
 {
-  uint32_t uid = m_req->extract_U32();
+  uint32_t uid = req.extract_U32();
 
-  Channels.Lock(false);
-  const cChannel *channel = NULL;
-  channel = FindChannelByUID(uid);
-  Channels.Unlock();
+  const cChannel *channel = FindChannelByUID(uid);
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
   if (channel != NULL)
   {
@@ -1219,20 +1251,20 @@ bool cVNSIClient::processCHANNELS_GetCaids()
     int idx = 0;
     while((caid = channel->Ca(idx)) != 0)
     {
-      m_resp->add_U32(caid);
+      resp.add_U32(caid);
       idx++;
     }
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GetWhitelist()
+bool cVNSIClient::processCHANNELS_GetWhitelist(cRequestPacket &req)
 {
-  bool radio = m_req->extract_U8();
+  bool radio = req.extract_U8();
   std::vector<cVNSIProvider> *providers;
 
   if(radio)
@@ -1240,44 +1272,50 @@ bool cVNSIClient::processCHANNELS_GetWhitelist()
   else
     providers = &VNSIChannelFilter.m_providersVideo;
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   VNSIChannelFilter.m_Mutex.Lock();
-  for(unsigned int i=0; i<providers->size(); i++)
+  for (const auto &i : *providers)
   {
-    m_resp->add_String((*providers)[i].m_name.c_str());
-    m_resp->add_U32((*providers)[i].m_caid);
+    resp.add_String(i.m_name.c_str());
+    resp.add_U32(i.m_caid);
   }
   VNSIChannelFilter.m_Mutex.Unlock();
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_GetBlacklist()
+bool cVNSIClient::processCHANNELS_GetBlacklist(cRequestPacket &req)
 {
-  bool radio = m_req->extract_U8();
-  std::vector<int> *channels;
+  bool radio = req.extract_U8();
+  const std::set<int> *channels;
 
   if(radio)
     channels = &VNSIChannelFilter.m_channelsRadio;
   else
     channels = &VNSIChannelFilter.m_channelsVideo;
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   VNSIChannelFilter.m_Mutex.Lock();
-  for(unsigned int i=0; i<channels->size(); i++)
+  for (auto i : *channels)
   {
-    m_resp->add_U32((*channels)[i]);
+    resp.add_U32(i);
   }
   VNSIChannelFilter.m_Mutex.Unlock();
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_SetWhitelist()
+bool cVNSIClient::processCHANNELS_SetWhitelist(cRequestPacket &req)
 {
-  bool radio = m_req->extract_U8();
+  bool radio = req.extract_U8();
   cVNSIProvider provider;
   std::vector<cVNSIProvider> *providers;
 
@@ -1289,27 +1327,28 @@ bool cVNSIClient::processCHANNELS_SetWhitelist()
   VNSIChannelFilter.m_Mutex.Lock();
   providers->clear();
 
-  while(!m_req->end())
+  while(!req.end())
   {
-    char *str = m_req->extract_String();
+    char *str = req.extract_String();
     provider.m_name = str;
-    provider.m_caid = m_req->extract_U32();
-    delete [] str;
+    provider.m_caid = req.extract_U32();
     providers->push_back(provider);
   }
   VNSIChannelFilter.StoreWhitelist(radio);
   VNSIChannelFilter.m_Mutex.Unlock();
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processCHANNELS_SetBlacklist()
+bool cVNSIClient::processCHANNELS_SetBlacklist(cRequestPacket &req)
 {
-  bool radio = m_req->extract_U8();
+  bool radio = req.extract_U8();
   cVNSIProvider provider;
-  std::vector<int> *channels;
+  std::set<int> *channels;
 
   if(radio)
     channels = &VNSIChannelFilter.m_channelsRadio;
@@ -1320,16 +1359,18 @@ bool cVNSIClient::processCHANNELS_SetBlacklist()
   channels->clear();
 
   int id;
-  while(!m_req->end())
+  while(!req.end())
   {
-    id = m_req->extract_U32();
-    channels->push_back(id);
+    id = req.extract_U32();
+    channels->insert(id);
   }
   VNSIChannelFilter.StoreBlacklist(radio);
   VNSIChannelFilter.m_Mutex.Unlock();
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
@@ -1337,7 +1378,13 @@ void cVNSIClient::CreateChannelGroups(bool automatic)
 {
   std::string groupname;
 
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
+#else
+  Channels.Lock(false);
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+#endif
   {
     bool isRadio = cVNSIChannelFilter::IsRadio(channel);
 
@@ -1358,111 +1405,229 @@ void cVNSIClient::CreateChannelGroups(bool automatic)
       m_channelgroups[isRadio][groupname] = group;
     }
   }
+
+#if VDRVERSNUM < 20301
+  Channels.Unlock();
+#endif
 }
 
 /** OPCODE 80 - 99: VNSI network functions for timer access */
 
-bool cVNSIClient::processTIMER_GetCount() /* OPCODE 80 */
+bool cVNSIClient::processTIMER_GetCount(cRequestPacket &req) /* OPCODE 80 */
 {
   cMutexLock lock(&m_timerLock);
 
+#if VDRVERSNUM >= 20301
+  LOCK_TIMERS_READ;
+  int count = Timers->Count() + m_vnsiTimers.GetTimersCount();
+#else
   int count = Timers.Count();
+#endif
 
-  m_resp->add_U32(count);
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(count);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processTIMER_Get() /* OPCODE 81 */
+bool cVNSIClient::processTIMER_Get(cRequestPacket &req) /* OPCODE 81 */
 {
   cMutexLock lock(&m_timerLock);
 
-  uint32_t number = m_req->extract_U32();
+  uint32_t number = req.extract_U32();
 
-  int numTimers = Timers.Count();
-  if (numTimers > 0)
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
+  if (number & m_vnsiTimers.INDEX_MASK)
   {
-    cTimer *timer = Timers.Get(number-1);
-    if (timer)
+    CVNSITimer timer;
+    if (m_vnsiTimers.GetTimer(number, timer))
     {
-      m_resp->add_U32(VNSI_RET_OK);
-
-      m_resp->add_U32(timer->Index()+1);
-      m_resp->add_U32(timer->HasFlags(tfActive));
-      m_resp->add_U32(timer->Recording());
-      m_resp->add_U32(timer->Pending());
-      m_resp->add_U32(timer->Priority());
-      m_resp->add_U32(timer->Lifetime());
-      m_resp->add_U32(timer->Channel()->Number());
-      m_resp->add_U32(CreateChannelUID(timer->Channel()));
-      m_resp->add_U32(timer->StartTime());
-      m_resp->add_U32(timer->StopTime());
-      m_resp->add_U32(timer->Day());
-      m_resp->add_U32(timer->WeekDays());
-      m_resp->add_String(m_toUTF8.Convert(timer->File()));
+      resp.add_U32(VNSI_RET_OK);
+
+      resp.add_U32(VNSI_TIMER_TYPE_EPG_SEARCH);
+      resp.add_U32(number);
+      resp.add_U32(timer.m_enabled);
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_U32(timer.m_priority);
+      resp.add_U32(timer.m_lifetime);
+      resp.add_U32(0);
+      resp.add_U32(timer.m_channelUID);
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_U32(0);
+      resp.add_String(timer.m_name.c_str());
+      resp.add_String(timer.m_search.c_str());
     }
     else
-      m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
+    {
+      resp.add_U32(VNSI_RET_DATAUNKNOWN);
+    }
   }
   else
-    m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
+  {
+#if VDRVERSNUM >= 20301
+    LOCK_TIMERS_READ;
+    int numTimers = Timers->Count();
+    if (numTimers > 0)
+    {
+      const cTimer *timer = Timers->Get(number-1);
+#else
+    int numTimers = Timers.Count();
+    if (numTimers > 0)
+    {
+      cTimer *timer = Timers.Get(number-1);
+#endif
+      if (timer)
+      {
+        resp.add_U32(VNSI_RET_OK);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+        if (m_protocolVersion >= 9)
+        {
+          uint32_t type;
+          if (timer->HasFlags(tfVps))
+            type = VNSI_TIMER_TYPE_VPS;
+          else
+            type = VNSI_TIMER_TYPE_MAN;
+          resp.add_U32(type);
+        }
+        resp.add_U32(timer->Index()+1);
+        resp.add_U32(timer->HasFlags(tfActive));
+        resp.add_U32(timer->Recording());
+        resp.add_U32(timer->Pending());
+        resp.add_U32(timer->Priority());
+        resp.add_U32(timer->Lifetime());
+        resp.add_U32(timer->Channel()->Number());
+        resp.add_U32(CreateChannelUID(timer->Channel()));
+        resp.add_U32(timer->StartTime());
+        resp.add_U32(timer->StopTime());
+        resp.add_U32(timer->Day());
+        resp.add_U32(timer->WeekDays());
+        resp.add_String(m_toUTF8.Convert(timer->File()));
+        if (m_protocolVersion >= 9)
+        {
+          resp.add_String("");
+        }
+      }
+      else
+        resp.add_U32(VNSI_RET_DATAUNKNOWN);
+    }
+    else
+      resp.add_U32(VNSI_RET_DATAUNKNOWN);
+  }
+
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processTIMER_GetList() /* OPCODE 82 */
+bool cVNSIClient::processTIMER_GetList(cRequestPacket &req) /* OPCODE 82 */
 {
-  cMutexLock lock(&m_timerLock);
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
-  cTimer *timer;
-  int numTimers = Timers.Count();
-
-  m_resp->add_U32(numTimers);
+  cMutexLock lock(&m_timerLock);
 
+#if VDRVERSNUM >= 20301
+  LOCK_TIMERS_READ;
+  int numVdrTimers = Timers->Count();
+  int numTimers = numVdrTimers + m_vnsiTimers.GetTimersCount();
+  resp.add_U32(numTimers);
+  for (int i = 0; i < numVdrTimers; i++)
+  {
+    const cTimer *timer = Timers->Get(i);
+#else
+  int numTimers = Timers.Count() + m_vnsiTimers.GetTimersCount();
+  resp.add_U32(numTimers);
   for (int i = 0; i < numTimers; i++)
   {
-    timer = Timers.Get(i);
+    cTimer *timer = Timers.Get(i);
+#endif
     if (!timer)
       continue;
 
-    m_resp->add_U32(timer->Index()+1);
-    m_resp->add_U32(timer->HasFlags(tfActive));
-    m_resp->add_U32(timer->Recording());
-    m_resp->add_U32(timer->Pending());
-    m_resp->add_U32(timer->Priority());
-    m_resp->add_U32(timer->Lifetime());
-    m_resp->add_U32(timer->Channel()->Number());
-    m_resp->add_U32(CreateChannelUID(timer->Channel()));
-    m_resp->add_U32(timer->StartTime());
-    m_resp->add_U32(timer->StopTime());
-    m_resp->add_U32(timer->Day());
-    m_resp->add_U32(timer->WeekDays());
-    m_resp->add_String(m_toUTF8.Convert(timer->File()));
-  }
-
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+    if (m_protocolVersion >= 9)
+    {
+      uint32_t type;
+      if (timer->HasFlags(tfVps))
+        type = VNSI_TIMER_TYPE_VPS;
+      else
+        type = VNSI_TIMER_TYPE_MAN;
+      resp.add_U32(type);
+    }
+    resp.add_U32(timer->Index()+1);
+    resp.add_U32(timer->HasFlags(tfActive));
+    resp.add_U32(timer->Recording());
+    resp.add_U32(timer->Pending());
+    resp.add_U32(timer->Priority());
+    resp.add_U32(timer->Lifetime());
+    resp.add_U32(timer->Channel()->Number());
+    resp.add_U32(CreateChannelUID(timer->Channel()));
+    resp.add_U32(timer->StartTime());
+    resp.add_U32(timer->StopTime());
+    resp.add_U32(timer->Day());
+    resp.add_U32(timer->WeekDays());
+    resp.add_String(m_toUTF8.Convert(timer->File()));
+    if (m_protocolVersion >= 9)
+    {
+      resp.add_String("");
+    }
+  }
+
+  std::vector<CVNSITimer> vnsitimers = m_vnsiTimers.GetTimers();
+  int idx = m_vnsiTimers.INDEX_MASK;
+  for (auto &vnsitimer : vnsitimers)
+  {
+    resp.add_U32(VNSI_TIMER_TYPE_EPG_SEARCH);
+    resp.add_U32(idx);
+    resp.add_U32(vnsitimer.m_enabled);
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_U32(vnsitimer.m_priority);
+    resp.add_U32(vnsitimer.m_lifetime);
+    resp.add_U32(0);
+    resp.add_U32(vnsitimer.m_channelUID);
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_U32(0);
+    resp.add_String(vnsitimer.m_name.c_str());
+    resp.add_String(vnsitimer.m_search.c_str());
+    idx++;
+  }
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processTIMER_Add() /* OPCODE 83 */
+bool cVNSIClient::processTIMER_Add(cRequestPacket &req) /* OPCODE 83 */
 {
   cMutexLock lock(&m_timerLock);
 
-  uint32_t flags      = m_req->extract_U32() > 0 ? tfActive : tfNone;
-  uint32_t priority   = m_req->extract_U32();
-  uint32_t lifetime   = m_req->extract_U32();
-  uint32_t channelid  = m_req->extract_U32();
-  time_t startTime    = m_req->extract_U32();
-  time_t stopTime     = m_req->extract_U32();
-  time_t day          = m_req->extract_U32();
-  uint32_t weekdays   = m_req->extract_U32();
-  const char *file    = m_req->extract_String();
-  const char *aux     = m_req->extract_String();
+  uint32_t type = 0;
+  std::string epgsearch;
+  if (m_protocolVersion >= 9)
+  {
+    type = req.extract_U32();
+  }
+  uint32_t flags      = req.extract_U32() > 0 ? tfActive : tfNone;
+  uint32_t priority   = req.extract_U32();
+  uint32_t lifetime   = req.extract_U32();
+  uint32_t channelid  = req.extract_U32();
+  time_t startTime    = req.extract_U32();
+  time_t stopTime     = req.extract_U32();
+  time_t day          = req.extract_U32();
+  uint32_t weekdays   = req.extract_U32();
+  const char *file    = req.extract_String();
+  const char *aux     = req.extract_String();
+  if (m_protocolVersion >= 9)
+    epgsearch = req.extract_String();
 
   // handle instant timers
   if(startTime == -1 || startTime == 0)
@@ -1478,6 +1643,9 @@ bool cVNSIClient::processTIMER_Add() /* OPCODE 83 */
   time = localtime_r(&stopTime, &tm_r);
   int stop = time->tm_hour * 100 + time->tm_min;
 
+  if (type == VNSI_TIMER_TYPE_VPS)
+    flags |= tfVps;
+
   cString buffer;
   const cChannel* channel = FindChannelByUID(channelid);
   if(channel != NULL)
@@ -1485,138 +1653,254 @@ bool cVNSIClient::processTIMER_Add() /* OPCODE 83 */
     buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
   }
 
-  delete[] file;
-  delete[] aux;
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
-  cTimer *timer = new cTimer;
-  if (timer->Parse(buffer))
+  if (type == VNSI_TIMER_TYPE_EPG_SEARCH)
   {
-    cTimer *t = Timers.GetTimer(timer);
-    if (!t)
+    CVNSITimer vnsitimer;
+    vnsitimer.m_name = aux;
+    vnsitimer.m_channelUID = channelid;
+    vnsitimer.m_search = epgsearch;
+    vnsitimer.m_enabled = flags;
+    vnsitimer.m_lifetime = lifetime;
+    m_vnsiTimers.Add(std::move(vnsitimer));
+    resp.add_U32(VNSI_RET_OK);
+  }
+  else
+  {
+    std::unique_ptr<cTimer> timer(new cTimer);
+    if (timer->Parse(buffer))
     {
-      Timers.Add(timer);
-      Timers.SetModified();
-      INFOLOG("Timer %s added", *timer->ToDescr());
-      m_resp->add_U32(VNSI_RET_OK);
-      m_resp->finalise();
-      m_socket.write(m_resp->getPtr(), m_resp->getLen());
-      return true;
+#if VDRVERSNUM >= 20301
+      LOCK_TIMERS_WRITE;
+      const cTimer *t = Timers->GetTimer(timer.get());
+      if (!t)
+      {
+        INFOLOG("Timer %s added", *timer->ToDescr());
+        Timers->Add(timer.release());
+        Timers->SetModified();
+        resp.add_U32(VNSI_RET_OK);
+        resp.finalise();
+        m_socket.write(resp.getPtr(), resp.getLen());
+        return true;
+      }
+#else
+      cTimer *t = Timers.GetTimer(timer.get());
+      if (!t)
+      {
+        INFOLOG("Timer %s added", *timer->ToDescr());
+        Timers.Add(timer.release());
+        Timers.SetModified();
+        resp.add_U32(VNSI_RET_OK);
+        resp.finalise();
+        m_socket.write(resp.getPtr(), resp.getLen());
+        return true;
+      }
+#endif
+      else
+      {
+        ERRORLOG("Timer already defined: %d %s", t->Index() + 1, *t->ToText());
+        resp.add_U32(VNSI_RET_DATALOCKED);
+      }
     }
     else
     {
-      ERRORLOG("Timer already defined: %d %s", t->Index() + 1, *t->ToText());
-      m_resp->add_U32(VNSI_RET_DATALOCKED);
+      ERRORLOG("Error in timer settings");
+      resp.add_U32(VNSI_RET_DATAINVALID);
     }
   }
-  else
-  {
-    ERRORLOG("Error in timer settings");
-    m_resp->add_U32(VNSI_RET_DATAINVALID);
-  }
-
-  delete timer;
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processTIMER_Delete() /* OPCODE 84 */
+bool cVNSIClient::processTIMER_Delete(cRequestPacket &req) /* OPCODE 84 */
 {
   cMutexLock lock(&m_timerLock);
 
-  uint32_t number = m_req->extract_U32();
-  bool     force  = m_req->extract_U32();
+  uint32_t number = req.extract_U32();
+  bool     force  = req.extract_U32();
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
-  if (number <= 0 || number > (uint32_t)Timers.Count())
+  if (number & m_vnsiTimers.INDEX_MASK)
   {
-    ERRORLOG("Unable to delete timer - invalid timer identifier");
-    m_resp->add_U32(VNSI_RET_DATAINVALID);
+    if (m_vnsiTimers.DeleteTimer(number))
+    {
+      INFOLOG("Deleting vnsitimer %d", number);
+      resp.add_U32(VNSI_RET_OK);
+    }
+    else
+    {
+      ERRORLOG("Unable to delete timer - invalid timer identifier");
+      resp.add_U32(VNSI_RET_DATAINVALID);
+    }
   }
   else
   {
-    cTimer *timer = Timers.Get(number-1);
-    if (timer)
+#if VDRVERSNUM >= 20301
+    LOCK_TIMERS_WRITE;
+    int timersCount = Timers->Count();
+#else
+    int timersCount = Timers.Count();
+#endif
+
+    if (number <= 0 || number > (uint32_t)timersCount)
     {
-      if (!Timers.BeingEdited())
+      ERRORLOG("Unable to delete timer - invalid timer identifier");
+      resp.add_U32(VNSI_RET_DATAINVALID);
+    }
+    else
+    {
+#if VDRVERSNUM >= 20301
+      cTimer *timer = Timers->Get(number-1);
+      if (timer)
       {
-        if (timer->Recording())
+        Timers->SetExplicitModify();
         {
-          if (force)
+          if (timer->Recording())
           {
-            timer->Skip();
-            cRecordControls::Process(time(NULL));
+            if (force)
+            {
+              timer->Skip();
+              cRecordControls::Process(Timers, time(NULL));
+            }
+            else
+            {
+              ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
+              resp.add_U32(VNSI_RET_RECRUNNING);
+              resp.finalise();
+              m_socket.write(resp.getPtr(), resp.getLen());
+              return true;
+            }
           }
-          else
+          INFOLOG("Deleting timer %s", *timer->ToDescr());
+          Timers->Del(timer);
+          Timers->SetModified();
+          resp.add_U32(VNSI_RET_OK);
+        }
+#else
+      cTimer *timer = Timers.Get(number-1);
+      if (timer)
+      {
+        if (!Timers.BeingEdited())
+        {
+          if (timer->Recording())
           {
-            ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
-            m_resp->add_U32(VNSI_RET_RECRUNNING);
-            m_resp->finalise();
-            m_socket.write(m_resp->getPtr(), m_resp->getLen());
-            return true;
+            if (force)
+            {
+              timer->Skip();
+              cRecordControls::Process(time(NULL));
+            }
+            else
+            {
+              ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
+              resp.add_U32(VNSI_RET_RECRUNNING);
+              resp.finalise();
+              m_socket.write(resp.getPtr(), resp.getLen());
+              return true;
+            }
           }
+          INFOLOG("Deleting timer %s", *timer->ToDescr());
+          Timers.Del(timer);
+          Timers.SetModified();
+          resp.add_U32(VNSI_RET_OK);
         }
-        INFOLOG("Deleting timer %s", *timer->ToDescr());
-        Timers.Del(timer);
-        Timers.SetModified();
-        m_resp->add_U32(VNSI_RET_OK);
+        else
+        {
+          ERRORLOG("Unable to delete timer - timers being edited at VDR");
+          resp.add_U32(VNSI_RET_DATALOCKED);
+        }
+#endif
       }
       else
       {
-        ERRORLOG("Unable to delete timer - timers being edited at VDR");
-        m_resp->add_U32(VNSI_RET_DATALOCKED);
+        ERRORLOG("Unable to delete timer - invalid timer identifier");
+        resp.add_U32(VNSI_RET_DATAINVALID);
       }
     }
-    else
-    {
-      ERRORLOG("Unable to delete timer - invalid timer identifier");
-      m_resp->add_U32(VNSI_RET_DATAINVALID);
-    }
   }
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
+bool cVNSIClient::processTIMER_Update(cRequestPacket &req) /* OPCODE 85 */
 {
   cMutexLock lock(&m_timerLock);
 
-  int length      = m_req->getDataLength();
-  uint32_t index  = m_req->extract_U32();
-  bool active     = m_req->extract_U32();
-
-  cTimer *timer = Timers.Get(index - 1);
-  if (!timer)
-  {
-    ERRORLOG("Timer \"%u\" not defined", index);
-    m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
-    m_resp->finalise();
-    m_socket.write(m_resp->getPtr(), m_resp->getLen());
-    return true;
-  }
-
-  cTimer t = *timer;
-
-  if (length == 8)
-  {
-    if (active)
-      t.SetFlags(tfActive);
-    else
-      t.ClrFlags(tfActive);
+  bool active;
+  uint32_t priority, lifetime, channelid, weekdays;
+  uint32_t type = 0;
+  time_t startTime, stopTime, day;
+  const char *file;
+  const char *aux;
+  std::string epgsearch;
+
+  uint32_t index  = req.extract_U32();
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
+  type = m_protocolVersion >= 9
+    ? req.extract_U32()
+    : VNSI_TIMER_TYPE_MAN;
+
+  active = req.extract_U32();
+  priority = req.extract_U32();
+  lifetime = req.extract_U32();
+  channelid = req.extract_U32();
+  startTime = req.extract_U32();
+  stopTime = req.extract_U32();
+  day = req.extract_U32();
+  weekdays = req.extract_U32();
+  file = req.extract_String();
+  aux = req.extract_String();
+  if (m_protocolVersion >= 9)
+  {
+    epgsearch = req.extract_String();
+  }
+
+  if (index & m_vnsiTimers.INDEX_MASK)
+  {
+    CVNSITimer vnsitimer;
+    vnsitimer.m_name = aux;
+    vnsitimer.m_channelUID = channelid;
+    vnsitimer.m_search = epgsearch;
+    vnsitimer.m_enabled = active;
+    vnsitimer.m_priority = priority;
+    vnsitimer.m_lifetime = lifetime;
+    if (!m_vnsiTimers.UpdateTimer(index, vnsitimer))
+    {
+      ERRORLOG("Timer \"%u\" not defined", index);
+      resp.add_U32(VNSI_RET_DATAUNKNOWN);
+      resp.finalise();
+      m_socket.write(resp.getPtr(), resp.getLen());
+      return true;
+    }
   }
   else
   {
-    uint32_t flags      = active ? tfActive : tfNone;
-    uint32_t priority   = m_req->extract_U32();
-    uint32_t lifetime   = m_req->extract_U32();
-    uint32_t channelid  = m_req->extract_U32();
-    time_t startTime    = m_req->extract_U32();
-    time_t stopTime     = m_req->extract_U32();
-    time_t day          = m_req->extract_U32();
-    uint32_t weekdays   = m_req->extract_U32();
-    const char *file    = m_req->extract_String();
-    const char *aux     = m_req->extract_String();
+#if VDRVERSNUM >= 20301
+    LOCK_TIMERS_WRITE;
+    cTimer *timer = Timers->Get(index - 1);
+#else
+    cTimer *timer = Timers.Get(index - 1);
+#endif
+
+    if (!timer)
+    {
+      ERRORLOG("Timer \"%u\" not defined", index);
+      resp.add_U32(VNSI_RET_DATAUNKNOWN);
+      resp.finalise();
+      m_socket.write(resp.getPtr(), resp.getLen());
+      return true;
+    }
+
+    cTimer t = *timer;
 
     struct tm tm_r;
     struct tm *time = localtime_r(&startTime, &tm_r);
@@ -1626,6 +1910,10 @@ bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
     time = localtime_r(&stopTime, &tm_r);
     int stop = time->tm_hour * 100 + time->tm_min;
 
+    uint32_t flags = active > 0 ? tfActive : tfNone;
+    if (type == VNSI_TIMER_TYPE_VPS)
+      flags |= tfVps;
+
     cString buffer;
     const cChannel* channel = FindChannelByUID(channelid);
     if(channel != NULL)
@@ -1633,32 +1921,47 @@ bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
       buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
     }
 
-    delete[] file;
-    delete[] aux;
-
     if (!t.Parse(buffer))
     {
       ERRORLOG("Error in timer settings");
-      m_resp->add_U32(VNSI_RET_DATAINVALID);
-      m_resp->finalise();
-      m_socket.write(m_resp->getPtr(), m_resp->getLen());
+      resp.add_U32(VNSI_RET_DATAINVALID);
+      resp.finalise();
+      m_socket.write(resp.getPtr(), resp.getLen());
       return true;
     }
-  }
 
-  *timer = t;
-  Timers.SetModified();
+    *timer = t;
+#if VDRVERSNUM >= 20301
+    Timers->SetModified();
+#else
+    Timers.SetModified();
+#endif
+
+  }
 
-  m_resp->add_U32(VNSI_RET_OK);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.add_U32(VNSI_RET_OK);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
+bool cVNSIClient::processTIMER_GetTypes(cRequestPacket &req) /* OPCODE 80 */
+{
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+#if VDRVERSNUM >= 20301
+  resp.add_U32(VNSI_TIMER_TYPE_EPG_SEARCH);
+#else
+  resp.add_U32(0);
+#endif
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
+  return true;
+}
 
 /** OPCODE 100 - 119: VNSI network functions for recording access */
 
-bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
+bool cVNSIClient::processRECORDINGS_GetDiskSpace(cRequestPacket &req) /* OPCODE 100 */
 {
   int FreeMB;
   int UsedMB;
@@ -1669,30 +1972,52 @@ bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
 #endif
   int Total = FreeMB + UsedMB;
 
-  m_resp->add_U32(Total);
-  m_resp->add_U32(FreeMB);
-  m_resp->add_U32(Percent);
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.add_U32(Total);
+  resp.add_U32(FreeMB);
+  resp.add_U32(Percent);
+
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_GetCount() /* OPCODE 101 */
+bool cVNSIClient::processRECORDINGS_GetCount(cRequestPacket &req) /* OPCODE 101 */
 {
-  m_resp->add_U32(Recordings.Count());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  resp.add_U32(Recordings->Count());
+#else
+  resp.add_U32(Recordings.Count());
+#endif
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
+bool cVNSIClient::processRECORDINGS_GetList(cRequestPacket &req) /* OPCODE 102 */
 {
   cMutexLock lock(&m_timerLock);
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+#else
   cThreadLock RecordingsLock(&Recordings);
+#endif
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
+#if VDRVERSNUM >= 20301
+  for (const cRecording *recording = Recordings->First(); recording; recording = Recordings->Next(recording))
+#else
   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
+#endif
   {
 #if APIVERSNUM >= 10705
     const cEvent *event = recording->Info()->GetEvent();
@@ -1727,58 +2052,83 @@ bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
     DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration);
 
     // recording_time
-    m_resp->add_U32(recordingStart);
+    resp.add_U32(recordingStart);
 
     // duration
-    m_resp->add_U32(recordingDuration);
+    resp.add_U32(recordingDuration);
 
     // priority
 #if APIVERSNUM >= 10727
-    m_resp->add_U32(recording->Priority());
+    resp.add_U32(recording->Priority());
 #else
-    m_resp->add_U32(recording->priority);
+    resp.add_U32(recording->priority);
 #endif
 
     // lifetime
 #if APIVERSNUM >= 10727
-    m_resp->add_U32(recording->Lifetime());
+    resp.add_U32(recording->Lifetime());
 #else
-    m_resp->add_U32(recording->lifetime);
+    resp.add_U32(recording->lifetime);
 #endif
 
     // channel_name
-    m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+    resp.add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+    if (m_protocolVersion >= 9)
+    {
+      // channel uuid
+#if VDRVERSNUM >= 20301
+      LOCK_CHANNELS_READ;
+      const cChannel *channel = Channels->GetByChannelID(recording->Info()->ChannelID());
+#else
+      Channels.Lock(false);
+      const cChannel *channel = Channels.GetByChannelID(recording->Info()->ChannelID());
+      Channels.Unlock();
+#endif
+      if (channel)
+      {
+        resp.add_U32(CreateChannelUID(channel));
+        resp.add_U8(cVNSIChannelFilter::IsRadio(channel) ? 1 : 2);
+      }
+      else
+      {
+        resp.add_U32(0);
+        resp.add_U8(0);
+      }
+    }
 
     char* fullname = strdup(recording->Name());
     char* recname = strrchr(fullname, FOLDERDELIMCHAR);
     char* directory = NULL;
 
-    if(recname == NULL) {
+    if(recname == NULL)
+    {
       recname = fullname;
     }
-    else {
+    else
+    {
       *recname = 0;
       recname++;
       directory = fullname;
     }
 
     // title
-    m_resp->add_String(m_toUTF8.Convert(recname));
+    resp.add_String(m_toUTF8.Convert(recname));
 
     // subtitle
     if (!isempty(recording->Info()->ShortText()))
-      m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+      resp.add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
     else
-      m_resp->add_String("");
+      resp.add_String("");
 
     // description
     if (!isempty(recording->Info()->Description()))
-      m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+      resp.add_String(m_toUTF8.Convert(recording->Info()->Description()));
     else
-      m_resp->add_String("");
+      resp.add_String("");
 
     // directory
-    if(directory != NULL) {
+    if(directory != NULL)
+    {
       char* p = directory;
       while(*p != 0) {
         if(*p == FOLDERDELIMCHAR) *p = '/';
@@ -1788,68 +2138,77 @@ bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
       while(*directory == '/') directory++;
     }
 
-    m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
+    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);
+    resp.add_U32(uid);
 
     free(fullname);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_Rename() /* OPCODE 103 */
+bool cVNSIClient::processRECORDINGS_Rename(cRequestPacket &req) /* OPCODE 103 */
 {
-  uint32_t    uid          = m_req->extract_U32();
-  char*       newtitle     = m_req->extract_String();
-  cRecording* recording    = cRecordingsCache::GetInstance().Lookup(uid);
-  int         r            = VNSI_RET_DATAINVALID;
+  uint32_t uid = req.extract_U32();
+  char* newtitle = req.extract_String();
+  int r = VNSI_RET_DATAINVALID;
+
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_WRITE;
+#endif
+
+  const cRecording* recording = cRecordingsCache::GetInstance().Lookup(uid);
 
   if(recording != NULL) {
     // get filename and remove last part (recording time)
-    char* filename_old = strdup((const char*)recording->FileName());
-    char* sep = strrchr(filename_old, '/');
-    if(sep != NULL) {
-      *sep = 0;
-    }
+    std::string filename_old(recording->FileName());
+    std::string::size_type i = filename_old.rfind('/');
+    if (i != filename_old.npos)
+      filename_old.erase(i);
 
     // replace spaces in newtitle
     strreplace(newtitle, ' ', '_');
-    char* filename_new = new char[1024];
-    strncpy(filename_new, filename_old, 512);
-    sep = strrchr(filename_new, '/');
-    if(sep != NULL) {
-      sep++;
-      *sep = 0;
-    }
-    strncat(filename_new, newtitle, 512);
+    std::string filename_new(filename_old);
+    i = filename_new.rfind('/');
+    if (i != filename_new.npos)
+      filename_new.erase(i + 1);
 
-    INFOLOG("renaming recording '%s' to '%s'", filename_old, filename_new);
-    r = rename(filename_old, filename_new);
-    Recordings.Update();
+    filename_new += newtitle;
+
+    INFOLOG("renaming recording '%s' to '%s'", filename_old.c_str(), filename_new.c_str());
+    r = rename(filename_old.c_str(), filename_new.c_str());
 
-    free(filename_old);
-    delete[] filename_new;
+#if VDRVERSNUM >= 20301
+    Recordings->Update();
+#else
+    Recordings.Update();
+#endif
   }
 
-  m_resp->add_U32(r);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(r);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */
+bool cVNSIClient::processRECORDINGS_Delete(cRequestPacket &req) /* OPCODE 104 */
 {
   cString recName;
   cRecording* recording = NULL;
 
-  uint32_t uid = m_req->extract_U32();
-  recording = cRecordingsCache::GetInstance().Lookup(uid);
+  uint32_t uid = req.extract_U32();
+  recording = cRecordingsCache::GetInstance().LookupWrite(uid);
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
   if (recording)
   {
@@ -1861,42 +2220,50 @@ bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */
       if (recording->Delete())
       {
         // Copy svdrdeveldevelp's way of doing this, see if it works
+#if VDRVERSNUM >= 20301
+        LOCK_RECORDINGS_WRITE;
+        Recordings->DelByName(recording->FileName());
+#else
         Recordings.DelByName(recording->FileName());
+#endif
         INFOLOG("Recording \"%s\" deleted", recording->FileName());
-        m_resp->add_U32(VNSI_RET_OK);
+        resp.add_U32(VNSI_RET_OK);
       }
       else
       {
         ERRORLOG("Error while deleting recording!");
-        m_resp->add_U32(VNSI_RET_ERROR);
+        resp.add_U32(VNSI_RET_ERROR);
       }
     }
     else
     {
       ERRORLOG("Recording \"%s\" is in use by timer %d", recording->Name(), rc->Timer()->Index() + 1);
-      m_resp->add_U32(VNSI_RET_DATALOCKED);
+      resp.add_U32(VNSI_RET_DATALOCKED);
     }
   }
   else
   {
     ERRORLOG("Error in recording name \"%s\"", (const char*)recName);
-    m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
+    resp.add_U32(VNSI_RET_DATAUNKNOWN);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */
+bool cVNSIClient::processRECORDINGS_GetEdl(cRequestPacket &req) /* OPCODE 105 */
 {
   cString recName;
-  cRecording* recording = NULL;
+  const cRecording* recording = NULL;
 
-  uint32_t uid = m_req->extract_U32();
+  uint32_t uid = req.extract_U32();
   recording = cRecordingsCache::GetInstance().Lookup(uid);
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   if (recording)
   {
     cMarks marks;
@@ -1907,15 +2274,15 @@ bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */
       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);
+        resp.add_U64(mark->Position() *1000 / fps);
+        resp.add_U64(mark->Position() *1000 / fps);
+        resp.add_S32(2);
       }
 #endif
     }
   }
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
@@ -1923,16 +2290,21 @@ bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */
 
 /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
 
-bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
+bool cVNSIClient::processEPG_GetForChannel(cRequestPacket &req) /* OPCODE 120 */
 {
   uint32_t channelUID  = 0;
 
-  channelUID = m_req->extract_U32();
+  channelUID = req.extract_U32();
 
-  uint32_t startTime      = m_req->extract_U32();
-  uint32_t duration       = m_req->extract_U32();
+  uint32_t startTime = req.extract_U32();
+  uint32_t duration = req.extract_U32();
 
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  LOCK_SCHEDULES_READ;
+#else
   Channels.Lock(false);
+#endif
 
   const cChannel* channel = NULL;
 
@@ -1942,37 +2314,50 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
     DEBUGLOG("get schedule called for channel '%s'", (const char*)channel->GetChannelID().ToString());
   }
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   if (!channel)
   {
-    m_resp->add_U32(0);
-    m_resp->finalise();
-    m_socket.write(m_resp->getPtr(), m_resp->getLen());
+    resp.add_U32(0);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
+#if VDRVERSNUM < 20301
     Channels.Unlock();
+#endif
 
     ERRORLOG("written 0 because channel = NULL");
     return true;
   }
 
+#if VDRVERSNUM < 20301
   cSchedulesLock MutexLock;
   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
   if (!Schedules)
   {
-    m_resp->add_U32(0);
-    m_resp->finalise();
-    m_socket.write(m_resp->getPtr(), m_resp->getLen());
+    resp.add_U32(0);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
     Channels.Unlock();
 
     DEBUGLOG("written 0 because Schedule!s! = NULL");
     return true;
   }
+#endif
 
+#if VDRVERSNUM >= 20301
   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+#else
+  const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+#endif
   if (!Schedule)
   {
-    m_resp->add_U32(0);
-    m_resp->finalise();
-    m_socket.write(m_resp->getPtr(), m_resp->getLen());
+    resp.add_U32(0);
+    resp.finalise();
+    m_socket.write(resp.getPtr(), resp.getLen());
+#if VDRVERSNUM < 20301
     Channels.Unlock();
+#endif
 
     DEBUGLOG("written 0 because Schedule = NULL");
     return true;
@@ -2009,48 +2394,58 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
 #endif
 
     //in the past filter
-    if ((thisEventTime + thisEventDuration) < (uint32_t)time(NULL)) continue;
+    if ((thisEventTime + thisEventDuration) < (uint32_t)time(NULL))
+      continue;
 
     //start time filter
-    if ((thisEventTime + thisEventDuration) <= startTime) continue;
+    if ((thisEventTime + thisEventDuration) <= startTime)
+      continue;
 
     //duration filter
-    if (duration != 0 && thisEventTime >= (startTime + duration)) continue;
+    if (duration != 0 && thisEventTime >= (startTime + duration))
+      continue;
 
-    if (!thisEventTitle)        thisEventTitle        = "";
-    if (!thisEventSubTitle)     thisEventSubTitle     = "";
-    if (!thisEventDescription)  thisEventDescription  = "";
+    if (!thisEventTitle)
+      thisEventTitle = "";
+    if (!thisEventSubTitle)
+      thisEventSubTitle = "";
+    if (!thisEventDescription)
+      thisEventDescription = "";
 
-    m_resp->add_U32(thisEventID);
-    m_resp->add_U32(thisEventTime);
-    m_resp->add_U32(thisEventDuration);
-    m_resp->add_U32(thisEventContent);
-    m_resp->add_U32(thisEventRating);
+    resp.add_U32(thisEventID);
+    resp.add_U32(thisEventTime);
+    resp.add_U32(thisEventDuration);
+    resp.add_U32(thisEventContent);
+    resp.add_U32(thisEventRating);
 
-    m_resp->add_String(m_toUTF8.Convert(thisEventTitle));
-    m_resp->add_String(m_toUTF8.Convert(thisEventSubTitle));
-    m_resp->add_String(m_toUTF8.Convert(thisEventDescription));
+    resp.add_String(m_toUTF8.Convert(thisEventTitle));
+    resp.add_String(m_toUTF8.Convert(thisEventSubTitle));
+    resp.add_String(m_toUTF8.Convert(thisEventDescription));
 
     atLeastOneEvent = true;
   }
 
+#if VDRVERSNUM < 20301
   Channels.Unlock();
+#endif
+
   DEBUGLOG("Got all event data");
 
   if (!atLeastOneEvent)
   {
-    m_resp->add_U32(0);
+    resp.add_U32(0);
     DEBUGLOG("Written 0 because no data");
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
-  cEvent *lastEvent =  Schedule->Events()->Last();
+  const cEvent *lastEvent =  Schedule->Events()->Last();
   if (lastEvent)
   {
-    m_epgUpdate[channelUID].lastEvent = lastEvent->StartTime();
-    m_epgUpdate[channelUID].attempts = 0;
+    auto &u = m_epgUpdate[channelUID];
+    u.lastEvent = lastEvent->StartTime();
+    u.attempts = 0;
   }
   DEBUGLOG("written schedules packet");
 
@@ -2063,19 +2458,21 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
  * VNSI network functions for channel scanning
  */
 
-bool cVNSIClient::processSCAN_ScanSupported() /* OPCODE 140 */
+bool cVNSIClient::processSCAN_ScanSupported(cRequestPacket &req) /* OPCODE 140 */
 {
   uint32_t retValue = VNSI_RET_NOTSUPPORTED;
   if (!m_inhibidDataUpdates && m_ChannelScanControl.IsSupported())
     retValue = VNSI_RET_OK;
 
-  m_resp->add_U32(retValue);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(retValue);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processSCAN_GetSupportedTypes()
+bool cVNSIClient::processSCAN_GetSupportedTypes(cRequestPacket &req)
 {
   uint32_t retValue = 0;
   if (m_ChannelScanControl.IsSupported())
@@ -2088,241 +2485,221 @@ bool cVNSIClient::processSCAN_GetSupportedTypes()
     retValue |= m_ChannelScanControl.SupportsATSC()         ? VNSI_SCAN_SUPPORT_ATSC : 0;
   }
 
-  m_resp->add_U32(retValue);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(retValue);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processSCAN_GetCountries() /* OPCODE 141 */
+bool cVNSIClient::processSCAN_GetCountries(cRequestPacket &req) /* OPCODE 141 */
 {
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   scannerEntryList list;
   if (m_ChannelScanControl.GetCountries(list))
   {
-    m_resp->add_U32(VNSI_RET_OK);
-    for (scannerEntryList::const_iterator it = list.begin(); it != list.end(); ++it)
+    resp.add_U32(VNSI_RET_OK);
+    for (const auto &i : list)
     {
-      m_resp->add_U32(it->index);
-      m_resp->add_String(it->name);
-      m_resp->add_String(it->longName);
+      resp.add_U32(i.index);
+      resp.add_String(i.name);
+      resp.add_String(i.longName);
     }
   }
   else
   {
-    m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
+    resp.add_U32(VNSI_RET_NOTSUPPORTED);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processSCAN_GetSatellites() /* OPCODE 142 */
+bool cVNSIClient::processSCAN_GetSatellites(cRequestPacket &req) /* OPCODE 142 */
 {
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   scannerEntryList list;
   if (m_ChannelScanControl.GetSatellites(list))
   {
-    m_resp->add_U32(VNSI_RET_OK);
-    for (scannerEntryList::const_iterator it = list.begin(); it != list.end(); ++it)
+    resp.add_U32(VNSI_RET_OK);
+    for (const auto &i : list)
     {
-      m_resp->add_U32(it->index);
-      m_resp->add_String(it->name);
-      m_resp->add_String(it->longName);
+      resp.add_U32(i.index);
+      resp.add_String(i.name);
+      resp.add_String(i.longName);
     }
   }
   else
   {
-    m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
+    resp.add_U32(VNSI_RET_NOTSUPPORTED);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processSCAN_Start() /* OPCODE 143 */
+bool cVNSIClient::processSCAN_Start(cRequestPacket &req) /* OPCODE 143 */
 {
   sScanServiceData svc;
-  svc.type              = (int)m_req->extract_U32();
-  svc.scan_tv           = (bool)m_req->extract_U8();
-  svc.scan_radio        = (bool)m_req->extract_U8();
-  svc.scan_fta          = (bool)m_req->extract_U8();
-  svc.scan_scrambled    = (bool)m_req->extract_U8();
-  svc.scan_hd           = (bool)m_req->extract_U8();
-  svc.CountryIndex      = (int)m_req->extract_U32();
-  svc.DVBC_Inversion    = (int)m_req->extract_U32();
-  svc.DVBC_Symbolrate   = (int)m_req->extract_U32();
-  svc.DVBC_QAM          = (int)m_req->extract_U32();
-  svc.DVBT_Inversion    = (int)m_req->extract_U32();
-  svc.SatIndex          = (int)m_req->extract_U32();
-  svc.ATSC_Type         = (int)m_req->extract_U32();
+  svc.type              = (int)req.extract_U32();
+  svc.scan_tv           = (bool)req.extract_U8();
+  svc.scan_radio        = (bool)req.extract_U8();
+  svc.scan_fta          = (bool)req.extract_U8();
+  svc.scan_scrambled    = (bool)req.extract_U8();
+  svc.scan_hd           = (bool)req.extract_U8();
+  svc.CountryIndex      = (int)req.extract_U32();
+  svc.DVBC_Inversion    = (int)req.extract_U32();
+  svc.DVBC_Symbolrate   = (int)req.extract_U32();
+  svc.DVBC_QAM          = (int)req.extract_U32();
+  svc.DVBT_Inversion    = (int)req.extract_U32();
+  svc.SatIndex          = (int)req.extract_U32();
+  svc.ATSC_Type         = (int)req.extract_U32();
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
 
   if (!m_inhibidDataUpdates && m_ChannelScanControl.IsSupported())
   {
     if (m_ChannelScanControl.StartScan(svc))
     {
-      m_resp->add_U32(VNSI_RET_OK);
+      resp.add_U32(VNSI_RET_OK);
       m_inhibidDataUpdates = true;
     }
     else
-      m_resp->add_U32(VNSI_RET_ERROR);
+      resp.add_U32(VNSI_RET_ERROR);
   }
   else
-    m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
+    resp.add_U32(VNSI_RET_NOTSUPPORTED);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processSCAN_Stop() /* OPCODE 144 */
+bool cVNSIClient::processSCAN_Stop(cRequestPacket &req) /* OPCODE 144 */
 {
   m_inhibidDataUpdates = false;
 
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   if (m_ChannelScanControl.IsSupported())
   {
     if (m_ChannelScanControl.StopScan())
-      m_resp->add_U32(VNSI_RET_OK);
+      resp.add_U32(VNSI_RET_OK);
     else
-      m_resp->add_U32(VNSI_RET_ERROR);
+      resp.add_U32(VNSI_RET_ERROR);
   }
   else
-    m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
+    resp.add_U32(VNSI_RET_NOTSUPPORTED);
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
 void cVNSIClient::processSCAN_SetPercentage(int percent)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_PERCENTAGE))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_U32(percent);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_PERCENTAGE);
+  resp.add_U32(percent);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_SetSignalStrength(int strength, bool locked)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_SIGNAL))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_U32(strength);
-  resp->add_U32(locked);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_SIGNAL);
+  resp.add_U32(strength);
+  resp.add_U32(locked);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_SetDeviceInfo(const char *Info)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_DEVICE))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_String(Info);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_DEVICE);
+  resp.add_String(Info);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_SetTransponder(const char *Info)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_TRANSPONDER))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_String(Info);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_TRANSPONDER);
+  resp.add_String(Info);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_NEWCHANNEL))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_U32(isRadio);
-  resp->add_U32(isEncrypted);
-  resp->add_U32(isHD);
-  resp->add_String(Name);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_NEWCHANNEL);
+  resp.add_U32(isRadio);
+  resp.add_U32(isEncrypted);
+  resp.add_U32(isHD);
+  resp.add_String(Name);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_IsFinished()
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_FINISHED))
-  {
-    delete resp;
-    return;
-  }
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_FINISHED);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
 void cVNSIClient::processSCAN_SetStatus(int status)
 {
-  cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initScan(VNSI_SCANNER_STATUS))
-  {
-    delete resp;
-    return;
-  }
-  resp->add_U32(status);
-  resp->finalise();
-  m_socket.write(resp->getPtr(), resp->getLen());
-  delete resp;
+  cResponsePacket resp;
+  resp.initScan(VNSI_SCANNER_STATUS);
+  resp.add_U32(status);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 }
 
-bool cVNSIClient::processOSD_Connect() /* OPCODE 160 */
+bool cVNSIClient::processOSD_Connect(cRequestPacket &req) /* OPCODE 160 */
 {
+  delete m_Osd;
   m_Osd = new cVnsiOsdProvider(&m_socket);
   int osdWidth, osdHeight;
   double aspect;
   cDevice::PrimaryDevice()->GetOsdSize(osdWidth, osdHeight, aspect);
-  m_resp->add_U32(osdWidth);
-  m_resp->add_U32(osdHeight);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(osdWidth);
+  resp.add_U32(osdHeight);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
 bool cVNSIClient::processOSD_Disconnect() /* OPCODE 161 */
 {
-  if (m_Osd)
-  {
-    delete m_Osd;
-    m_Osd = NULL;
-  }
+  delete m_Osd;
+  m_Osd = NULL;
   return true;
 }
 
-bool cVNSIClient::processOSD_Hitkey() /* OPCODE 162 */
+bool cVNSIClient::processOSD_Hitkey(cRequestPacket &req) /* OPCODE 162 */
 {
   if (m_Osd)
   {
-    unsigned int key = m_req->extract_U32();
+    unsigned int key = req.extract_U32();
     cVnsiOsdProvider::SendKey(key);
   }
   return true;
@@ -2330,29 +2707,45 @@ bool cVNSIClient::processOSD_Hitkey() /* OPCODE 162 */
 
 /** OPCODE 180 - 189: VNSI network functions for deleted recording access */
 
-bool cVNSIClient::processRECORDINGS_DELETED_Supported() /* OPCODE 180 */
+bool cVNSIClient::processRECORDINGS_DELETED_Supported(cRequestPacket &req) /* OPCODE 180 */
 {
-  m_resp->add_U32(VNSI_RET_OK);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(VNSI_RET_OK);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_DELETED_GetCount() /* OPCODE 181 */
+bool cVNSIClient::processRECORDINGS_DELETED_GetCount(cRequestPacket &req) /* OPCODE 181 */
 {
-  DeletedRecordings.Load();
-  m_resp->add_U32(DeletedRecordings.Count());
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+#if VDRVERSNUM >= 20301
+  LOCK_DELETEDRECORDINGS_READ;
+  resp.add_U32(DeletedRecordings->Count());
+#else
+  resp.add_U32(DeletedRecordings.Count());
+#endif
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
+bool cVNSIClient::processRECORDINGS_DELETED_GetList(cRequestPacket &req) /* OPCODE 182 */
 {
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   cMutexLock lock(&m_timerLock);
-  cThreadLock RecordingsLock(&Recordings);
 
+#if VDRVERSNUM >= 20301
+  LOCK_DELETEDRECORDINGS_READ;
+  for (const cRecording *recording = DeletedRecordings->First(); recording; recording = DeletedRecordings->Next(recording))
+#else
+  cThreadLock RecordingsLock(&Recordings);
   for (cRecording *recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+#endif
   {
 #if APIVERSNUM >= 10705
     const cEvent *event = recording->Info()->GetEvent();
@@ -2387,27 +2780,27 @@ bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
     DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration);
 
     // recording_time
-    m_resp->add_U32(recordingStart);
+    resp.add_U32(recordingStart);
 
     // duration
-    m_resp->add_U32(recordingDuration);
+    resp.add_U32(recordingDuration);
 
     // priority
 #if APIVERSNUM >= 10727
-    m_resp->add_U32(recording->Priority());
+    resp.add_U32(recording->Priority());
 #else
-    m_resp->add_U32(recording->priority);
+    resp.add_U32(recording->priority);
 #endif
 
     // lifetime
 #if APIVERSNUM >= 10727
-    m_resp->add_U32(recording->Lifetime());
+    resp.add_U32(recording->Lifetime());
 #else
-    m_resp->add_U32(recording->lifetime);
+    resp.add_U32(recording->lifetime);
 #endif
 
     // channel_name
-    m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+    resp.add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
 
     char* fullname = strdup(recording->Name());
     char* recname = strrchr(fullname, FOLDERDELIMCHAR);
@@ -2423,19 +2816,19 @@ bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
     }
 
     // title
-    m_resp->add_String(m_toUTF8.Convert(recname));
+    resp.add_String(m_toUTF8.Convert(recname));
 
     // subtitle
     if (!isempty(recording->Info()->ShortText()))
-      m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+      resp.add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
     else
-      m_resp->add_String("");
+      resp.add_String("");
 
     // description
     if (!isempty(recording->Info()->Description()))
-      m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+      resp.add_String(m_toUTF8.Convert(recording->Info()->Description()));
     else
-      m_resp->add_String("");
+      resp.add_String("");
 
     // directory
     if(directory != NULL) {
@@ -2448,22 +2841,25 @@ bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
       while(*directory == '/') directory++;
     }
 
-    m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
+    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);
+    resp.add_U32(uid);
 
     free(fullname);
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */
+bool cVNSIClient::processRECORDINGS_DELETED_Delete(cRequestPacket &req) /* OPCODE 183 */
 {
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+
   cString recName;
   cRecording* recording = NULL;
 
@@ -2474,11 +2870,15 @@ bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */
 #endif
   if (LockFile.Lock())
   {
-    uint32_t uid = m_req->extract_U32();
+    uint32_t uid = req.extract_U32();
 
+#if VDRVERSNUM >= 20301
+    LOCK_DELETEDRECORDINGS_WRITE;
+    for (recording = DeletedRecordings->First(); recording; recording = DeletedRecordings->Next(recording))
+#else
     cThreadLock DeletedRecordingsLock(&DeletedRecordings);
-
     for (recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+#endif
     {
       if (uid == CreateStringHash(recording->FileName()))
       {
@@ -2489,22 +2889,27 @@ bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */
 #endif
         {
           ERRORLOG("Error while remove deleted recording (%s)", recording->FileName());
-          m_resp->add_U32(VNSI_RET_ERROR);
+          resp.add_U32(VNSI_RET_ERROR);
         }
         else
         {
+#if VDRVERSNUM >= 20301
+          DeletedRecordings->Del(recording);
+          DeletedRecordings->Update();
+#else
           DeletedRecordings.Del(recording);
           DeletedRecordings.Update();
+#endif
           INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
-          m_resp->add_U32(VNSI_RET_OK);
+          resp.add_U32(VNSI_RET_OK);
         }
         break;
       }
     }
   }
 
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
@@ -2517,7 +2922,7 @@ bool cVNSIClient::Undelete(cRecording* recording)
   char *ext = strrchr(NewName, '.');
   if (ext && strcmp(ext, ".del") == 0)
   {
-    strncpy(ext, ".rec", strlen(ext));
+    strcpy(ext, ".rec");
     if (!access(NewName, F_OK))
     {
       ERRORLOG("Recording with the same name exists (%s)", NewName);
@@ -2536,14 +2941,13 @@ bool cVNSIClient::Undelete(cRecording* recording)
           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;
+        cIndexFile index(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;
+          index.Get(LastFrame, &FileNumber, &FileOffset);
           if (FileNumber == 0)
           {
             ERRORLOG("while read last filenumber (%s)", NewName);
@@ -2564,14 +2968,21 @@ bool cVNSIClient::Undelete(cRecording* recording)
         }
         else
         {
-          delete index;
           ERRORLOG("accessing indexfile (%s)", NewName);
           OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while accessing indexfile"), NewName));
         }
 
+#if VDRVERSNUM >= 20301
+        LOCK_RECORDINGS_WRITE;
+        LOCK_DELETEDRECORDINGS_WRITE;
+        DeletedRecordings->Del(recording);
+        Recordings->Update();
+        DeletedRecordings->Update();
+#else
         DeletedRecordings.Del(recording);
         Recordings.Update();
         DeletedRecordings.Update();
+#endif
       }
       else
       {
@@ -2584,7 +2995,7 @@ bool cVNSIClient::Undelete(cRecording* recording)
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */
+bool cVNSIClient::processRECORDINGS_DELETED_Undelete(cRequestPacket &req) /* OPCODE 184 */
 {
   int ret = VNSI_RET_DATAUNKNOWN;
 
@@ -2595,11 +3006,15 @@ bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */
 #endif
   if (LockFile.Lock())
   {
-    uint32_t uid = m_req->extract_U32();
+    uint32_t uid = req.extract_U32();
 
+#if VDRVERSNUM >= 20301
+    LOCK_DELETEDRECORDINGS_WRITE;
+    for (cRecording* recording = DeletedRecordings->First(); recording; recording = DeletedRecordings->Next(recording))
+#else
     cThreadLock DeletedRecordingsLock(&DeletedRecordings);
-
     for (cRecording* recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+#endif
     {
       if (uid == CreateStringHash(recording->FileName()))
       {
@@ -2615,13 +3030,15 @@ bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */
     }
   }
 
-  m_resp->add_U32(ret);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(ret);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
   return true;
 }
 
-bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */
+bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll(cRequestPacket &req) /* OPCODE 185 */
 {
   int ret = VNSI_RET_OK;
 
@@ -2633,11 +3050,19 @@ bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */
 
   if (LockFile.Lock())
   {
+#if VDRVERSNUM >= 20301
+    LOCK_DELETEDRECORDINGS_WRITE;
+    for (cRecording *recording = DeletedRecordings->First(); recording; )
+#else
     cThreadLock DeletedRecordingsLock(&DeletedRecordings);
-
     for (cRecording *recording = DeletedRecordings.First(); recording; )
+#endif
     {
+#if VDRVERSNUM >= 20301
+      cRecording *next = DeletedRecordings->Next(recording);
+#else
       cRecording *next = DeletedRecordings.Next(recording);
+#endif
 #if VDRVERSNUM >= 20102
       if (!cVideoDirectory::RemoveVideoFile(recording->FileName()))
 #else
@@ -2652,19 +3077,26 @@ bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */
         INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
       recording = next;
     }
+#if VDRVERSNUM >= 20301
+    DeletedRecordings->Clear();
+    DeletedRecordings->Update();
+#else
     DeletedRecordings.Clear();
     DeletedRecordings.Update();
+#endif
   }
 
-  m_resp->add_U32(ret);
-  m_resp->finalise();
-  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  cResponsePacket resp;
+  resp.init(req.getRequestID());
+  resp.add_U32(ret);
+  resp.finalise();
+  m_socket.write(resp.getPtr(), resp.getLen());
 
   return true;
 }
 
 // part of this method is taken from XVDR
-cString cVNSIClient::CreatePiconRef(cChannel* channel)
+cString cVNSIClient::CreatePiconRef(const cChannel* channel)
 {
   int hash = 0;
 
diff --git a/vnsiclient.h b/vnsiclient.h
index aadcffa..92b7c48 100644
--- a/vnsiclient.h
+++ b/vnsiclient.h
@@ -46,57 +46,59 @@ class cResponsePacket;
 class cRecPlayer;
 class cCmdControl;
 class cVnsiOsdProvider;
+class CVNSITimers;
 
 class cVNSIClient : public cThread
                   , public cStatus
 {
-private:
-
-  unsigned int     m_Id;
+  const unsigned int m_Id;
   cxSocket         m_socket;
-  bool             m_loggedIn;
-  bool             m_StatusInterfaceEnabled;
-  cLiveStreamer   *m_Streamer;
-  bool             m_isStreaming;
-  bool             m_bSupportRDS;
-  cString          m_ClientAddress;
-  cRecPlayer      *m_RecPlayer;
-  cRequestPacket  *m_req;
-  cResponsePacket *m_resp;
+  bool             m_loggedIn = false;
+  bool             m_StatusInterfaceEnabled = false;
+  cLiveStreamer   *m_Streamer = nullptr;
+  bool             m_isStreaming = false;
+  bool             m_bSupportRDS = false;
+  const cString    m_ClientAddress;
+  cRecPlayer      *m_RecPlayer = nullptr;
   cCharSetConv     m_toUTF8;
   uint32_t         m_protocolVersion;
   cMutex           m_msgLock;
   static cMutex    m_timerLock;
-  cVnsiOsdProvider *m_Osd;
+  cVnsiOsdProvider *m_Osd = nullptr;
   CScanControl      m_ChannelScanControl;
   static bool       m_inhibidDataUpdates;
   typedef struct
   {
-    int attempts;
-    time_t lastEvent;
+    int attempts = 0;
+    time_t lastEvent = 0;
+    time_t lastTrigger = 0;
   } sEpgUpdate;
   std::map<int, sEpgUpdate> m_epgUpdate;
+  CVNSITimers &m_vnsiTimers;
 
 protected:
 
-  bool processRequest(cRequestPacket* req);
+  bool processRequest(cRequestPacket &req);
 
-  virtual void Action(void);
-
-  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);
+  virtual void Action(void) override;
+  virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) override;
+  virtual void OsdStatusMessage(const char *Message) override;
+#if VDRVERSNUM >= 20104
+  virtual void ChannelChange(const cChannel *Channel) override;
+#endif
 
 public:
 
-  cVNSIClient(int fd, unsigned int id, const char *ClientAdr);
+  cVNSIClient(int fd, unsigned int id, const char *ClientAdr, CVNSITimers &timers);
   virtual ~cVNSIClient();
 
+  cVNSIClient(const cVNSIClient &) = delete;
+  cVNSIClient &operator=(const cVNSIClient &) = delete;
+
   void ChannelsChange();
   void RecordingsChange();
-  void TimerChange();
-  void EpgChange();
+  void SignalTimerChange();
+  bool EpgChange();
   static bool InhibidDataUpdates() { return m_inhibidDataUpdates; }
 
   unsigned int GetID() { return m_Id; }
@@ -105,7 +107,7 @@ protected:
 
   void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
   void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
-  bool StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout);
+  bool StartChannelStreaming(cResponsePacket &resp, const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout);
   void StopChannelStreaming();
 
 private:
@@ -118,76 +120,77 @@ private:
 
   std::map<std::string, ChannelGroup> m_channelgroups[2];
 
-  bool process_Login();
-  bool process_GetTime();
-  bool process_EnableStatusInterface();
-  bool process_Ping();
-  bool process_GetSetup();
-  bool process_StoreSetup();
-
-  bool processChannelStream_Open();
-  bool processChannelStream_Close();
-  bool processChannelStream_Seek();
-
-  bool processRecStream_Open();
-  bool processRecStream_Close();
-  bool processRecStream_GetBlock();
-  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();
+  bool process_Login(cRequestPacket &r);
+  bool process_GetTime(cRequestPacket &r);
+  bool process_EnableStatusInterface(cRequestPacket &r);
+  bool process_Ping(cRequestPacket &r);
+  bool process_GetSetup(cRequestPacket &r);
+  bool process_StoreSetup(cRequestPacket &r);
+
+  bool processChannelStream_Open(cRequestPacket &r);
+  bool processChannelStream_Close(cRequestPacket &req);
+  bool processChannelStream_Seek(cRequestPacket &r);
+
+  bool processRecStream_Open(cRequestPacket &r);
+  bool processRecStream_Close(cRequestPacket &r);
+  bool processRecStream_GetBlock(cRequestPacket &r);
+  bool processRecStream_PositionFromFrameNumber(cRequestPacket &r);
+  bool processRecStream_FrameNumberFromPosition(cRequestPacket &r);
+  bool processRecStream_GetIFrame(cRequestPacket &r);
+  bool processRecStream_GetLength(cRequestPacket &r);
+
+  bool processCHANNELS_GroupsCount(cRequestPacket &r);
+  bool processCHANNELS_ChannelsCount(cRequestPacket &r);
+  bool processCHANNELS_GroupList(cRequestPacket &r);
+  bool processCHANNELS_GetChannels(cRequestPacket &r);
+  bool processCHANNELS_GetGroupMembers(cRequestPacket &r);
+  bool processCHANNELS_GetCaids(cRequestPacket &r);
+  bool processCHANNELS_GetWhitelist(cRequestPacket &r);
+  bool processCHANNELS_GetBlacklist(cRequestPacket &r);
+  bool processCHANNELS_SetWhitelist(cRequestPacket &r);
+  bool processCHANNELS_SetBlacklist(cRequestPacket &r);
 
   void CreateChannelGroups(bool automatic);
 
-  bool processTIMER_GetCount();
-  bool processTIMER_Get();
-  bool processTIMER_GetList();
-  bool processTIMER_Add();
-  bool processTIMER_Delete();
-  bool processTIMER_Update();
-
-  bool processRECORDINGS_GetDiskSpace();
-  bool processRECORDINGS_GetCount();
-  bool processRECORDINGS_GetList();
-  bool processRECORDINGS_GetInfo();
-  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();
-
-  bool processSCAN_ScanSupported();
-  bool processSCAN_GetSupportedTypes();
-  bool processSCAN_GetCountries();
-  bool processSCAN_GetSatellites();
-  bool processSCAN_Start();
-  bool processSCAN_Stop();
+  bool processTIMER_GetCount(cRequestPacket &r);
+  bool processTIMER_Get(cRequestPacket &r);
+  bool processTIMER_GetList(cRequestPacket &r);
+  bool processTIMER_Add(cRequestPacket &r);
+  bool processTIMER_Delete(cRequestPacket &r);
+  bool processTIMER_Update(cRequestPacket &r);
+  bool processTIMER_GetTypes(cRequestPacket &r);
+
+  bool processRECORDINGS_GetDiskSpace(cRequestPacket &r);
+  bool processRECORDINGS_GetCount(cRequestPacket &r);
+  bool processRECORDINGS_GetList(cRequestPacket &r);
+  bool processRECORDINGS_GetInfo(cRequestPacket &r);
+  bool processRECORDINGS_Rename(cRequestPacket &r);
+  bool processRECORDINGS_Delete(cRequestPacket &r);
+  bool processRECORDINGS_Move(cRequestPacket &r);
+  bool processRECORDINGS_GetEdl(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_Supported(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_GetCount(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_GetList(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_Delete(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_Undelete(cRequestPacket &r);
+  bool processRECORDINGS_DELETED_DeleteAll(cRequestPacket &r);
+
+  bool processEPG_GetForChannel(cRequestPacket &r);
+
+  bool processSCAN_ScanSupported(cRequestPacket &r);
+  bool processSCAN_GetSupportedTypes(cRequestPacket &r);
+  bool processSCAN_GetCountries(cRequestPacket &r);
+  bool processSCAN_GetSatellites(cRequestPacket &r);
+  bool processSCAN_Start(cRequestPacket &r);
+  bool processSCAN_Stop(cRequestPacket &r);
 
   bool Undelete(cRecording* recording);
 
-  bool processOSD_Connect();
+  bool processOSD_Connect(cRequestPacket &req);
   bool processOSD_Disconnect();
-  bool processOSD_Hitkey();
+  bool processOSD_Hitkey(cRequestPacket &req);
 
-  cString CreatePiconRef(cChannel* channel);
+  cString CreatePiconRef(const cChannel* channel);
 
 private:
   /** Static callback functions to interact with wirbelscan plugin over
diff --git a/vnsicommand.h b/vnsicommand.h
index e5be0bf..7a0b1aa 100644
--- a/vnsicommand.h
+++ b/vnsicommand.h
@@ -27,7 +27,7 @@
 #define VNSI_COMMAND_H
 
 /** Current VNSI Protocol Version number */
-#define VNSI_PROTOCOLVERSION 8
+#define VNSI_PROTOCOLVERSION 9
 
 /** Start of RDS support protocol Version */
 #define VNSI_RDS_PROTOCOLVERSION 8
@@ -53,6 +53,8 @@
 #define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir"
 #define CONFNAME_PLAYRECORDING "PlayRecording"
 #define CONFNAME_AVOIDEPGSCAN "AvoidEPGScan"
+#define CONFNAME_DISABLESCRAMBLETIMEOUT "DisableScrambleTimeout"
+#define CONFNAME_DISABLECAMBLACKLIST "DisableCamBlacklist"
 
 /* OPCODE 1 - 19: VNSI network functions for general purpose */
 #define VNSI_LOGIN                 1
@@ -95,6 +97,7 @@
 #define VNSI_TIMER_ADD             83
 #define VNSI_TIMER_DELETE          84
 #define VNSI_TIMER_UPDATE          85
+#define VNSI_TIMER_GETTYPES        86
 
 /* OPCODE 100 - 119: VNSI network functions for recording access */
 #define VNSI_RECORDINGS_DISKSIZE   100
@@ -171,6 +174,13 @@
 #define VNSI_SCAN_SUPPORT_ANALOG_RADIO 0x10
 #define VNSI_SCAN_SUPPORT_ATSC       0x20
 
+/** Timer */
+#define VNSI_TIMER_TYPE_MAN          1
+#define VNSI_TIMER_TYPE_MAN_REPEAT   2
+#define VNSI_TIMER_TYPE_EPG          3
+#define VNSI_TIMER_TYPE_VPS          4
+#define VNSI_TIMER_TYPE_EPG_SEARCH   5
+
 /** Packet return codes */
 #define VNSI_RET_OK              0
 #define VNSI_RET_RECRUNNING      1
diff --git a/vnsiosd.c b/vnsiosd.c
index 3d6f7da..e455025 100644
--- a/vnsiosd.c
+++ b/vnsiosd.c
@@ -22,8 +22,8 @@
  *
  */
 
-#include "config.h"
 #include "vnsiosd.h"
+#include "config.h"
 #include "vnsicommand.h"
 #include "responsepacket.h"
 #include "vnsi.h"
@@ -210,7 +210,6 @@ void cVnsiOsd::Flush(void)
 
 // --- cVnsiOsdProvider -------------------------------------------------------
 
-cResponsePacket cVnsiOsdProvider::m_OsdPacket;
 cxSocket *cVnsiOsdProvider::m_Socket;
 cMutex cVnsiOsdProvider::m_Mutex;
 bool cVnsiOsdProvider::m_RequestFull;
@@ -245,11 +244,8 @@ void cVnsiOsdProvider::SendOsdPacket(int cmd, int wnd, int color, int x0, int y0
   if (!m_Socket)
     return;
 
-  if (!m_OsdPacket.initOsd(cmd, wnd, color, x0, y0, x1, y1))
-  {
-    ERRORLOG("OSD response packet init fail");
-    return;
-  }
+  cResponsePacket m_OsdPacket;
+  m_OsdPacket.initOsd(cmd, wnd, color, x0, y0, x1, y1);
   m_OsdPacket.setLen(m_OsdPacket.getOSDHeaderLength() + size);
   m_OsdPacket.finaliseOSD();
 
diff --git a/vnsiosd.h b/vnsiosd.h
index 454099d..3776612 100644
--- a/vnsiosd.h
+++ b/vnsiosd.h
@@ -23,7 +23,6 @@
  */
 
 #include <vdr/osd.h>
-#include "responsepacket.h"
 
 class cxSocket;
 
@@ -37,7 +36,6 @@ public:
   static bool IsRequestFull();
   static void SendKey(unsigned int key);
 private:
-  static cResponsePacket m_OsdPacket;
   static cxSocket *m_Socket;
   static cMutex m_Mutex;
   static bool m_RequestFull;
diff --git a/vnsiserver.c b/vnsiserver.c
index ca565a5..3afe259 100644
--- a/vnsiserver.c
+++ b/vnsiserver.c
@@ -23,6 +23,11 @@
  *
  */
 
+#include "vnsiserver.h"
+#include "vnsiclient.h"
+#include "vnsi.h"
+#include "channelfilter.h"
+
 #include <netdb.h>
 #include <poll.h>
 #include <assert.h>
@@ -40,11 +45,6 @@
 
 #include <vdr/plugin.h>
 
-#include "vnsi.h"
-#include "vnsiserver.h"
-#include "vnsiclient.h"
-#include "channelfilter.h"
-
 unsigned int cVNSIServer::m_IdCnt = 0;
 
 class cAllowedHosts : public cSVDRPhosts
@@ -69,7 +69,7 @@ public:
   }
 };
 
-cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
+cVNSIServer::cVNSIServer(int listenPort) : cThread("VNSI Server")
 {
   m_ServerPort  = listenPort;
 
@@ -82,8 +82,9 @@ cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
 
 cVNSIServer::~cVNSIServer()
 {
-  m_Status.Shutdown();
   Cancel();
+  m_Status.Shutdown();
+  m_timers.Shutdown();
   INFOLOG("VNSI Server stopped");
 }
 
@@ -133,8 +134,7 @@ void cVNSIServer::NewClientConnected(int fd)
 #endif
 
   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_Status.AddClient(connection);
+  m_Status.AddClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf), m_timers);
   m_IdCnt++;
 }
 
@@ -155,7 +155,9 @@ void cVNSIServer::Action(void)
 
   VNSIChannelFilter.Load();
   VNSIChannelFilter.SortChannels();
-  m_Status.Start();
+  m_Status.Init(&m_timers);
+  m_timers.Load();
+  m_timers.Start();
 
   m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
   if(m_ServerFD == -1)
diff --git a/vnsiserver.h b/vnsiserver.h
index 43c0865..b09338a 100644
--- a/vnsiserver.h
+++ b/vnsiserver.h
@@ -30,6 +30,7 @@
 
 #include "config.h"
 #include "status.h"
+#include "vnsitimer.h"
 
 class cVNSIClient;
 
@@ -40,10 +41,11 @@ protected:
   virtual void Action(void);
   void NewClientConnected(int fd);
 
-  int           m_ServerPort;
-  int           m_ServerFD;
-  cString       m_AllowedHostsFile;
-  cVNSIStatus   m_Status;
+  int m_ServerPort;
+  int m_ServerFD;
+  cString m_AllowedHostsFile;
+  CVNSITimers m_timers;
+  cVNSIStatus m_Status;
 
   static unsigned int m_IdCnt;
 
diff --git a/vnsitimer.c b/vnsitimer.c
new file mode 100644
index 0000000..cbba00f
--- /dev/null
+++ b/vnsitimer.c
@@ -0,0 +1,360 @@
+/*
+ *      vdr-plugin-vnsi - KODI server plugin for VDR
+ *
+ *      Copyright (C) 2005-2016 Team Kodi
+ *
+ *      http://kodi.tv
+ *
+ *  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 KODI; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "hash.h"
+#include "time.h"
+#include <vdr/tools.h>
+#include <vdr/epg.h>
+#include <vdr/timers.h>
+#include <vdr/recording.h>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <regex>
+#include "vnsitimer.h"
+
+CVNSITimers::CVNSITimers() : cThread("VNSITimers")
+{
+  m_doScan = false;
+  m_state = 0;
+}
+
+void CVNSITimers::Load()
+{
+  cString filename;
+  std::string line;
+  std::ifstream rfile;
+  CVNSITimer timer;
+
+  cMutexLock lock(&m_timerLock);
+
+  filename = cString::sprintf("%s/timers.vnsi", *VNSIServerConfig.ConfigDirectory);
+  rfile.open(filename);
+  m_timers.clear();
+  if (rfile.is_open())
+  {
+    while(getline(rfile,line))
+    {
+      // timer name
+      size_t pos = line.find(";");
+      if(pos == line.npos)
+      {
+        continue;
+      }
+      timer.m_name = line.substr(0, pos);
+
+      // channelUID
+      line = line.substr(pos+1);
+      pos = line.find(";");
+      if(pos == line.npos)
+      {
+        continue;
+      }
+      char *pend;
+      std::string channeluid = line.substr(0, pos);
+      timer.m_channelUID = strtol(channeluid.c_str(), &pend, 10);
+
+      const cChannel *channel = FindChannelByUID(timer.m_channelUID);
+      if (!channel)
+      {
+        continue;
+      }
+      timer.m_channelID = channel->GetChannelID();
+
+      // enabled
+      line = line.substr(pos+1);
+      pos = line.find(";");
+      if(pos == line.npos)
+      {
+        continue;
+      }
+      std::string enabled = line.substr(0, pos);
+      timer.m_enabled = strtol(enabled.c_str(), &pend, 10);
+
+      // priority
+      line = line.substr(pos+1);
+      pos = line.find(";");
+      if(pos == line.npos)
+      {
+        continue;
+      }
+      std::string priority = line.substr(0, pos);
+      timer.m_priority = strtol(priority.c_str(), &pend, 10);
+
+      // lifetime
+      line = line.substr(pos+1);
+      pos = line.find(";");
+      if(pos == line.npos)
+      {
+        continue;
+      }
+      std::string lifetime = line.substr(0, pos);
+      timer.m_lifetime = strtol(lifetime.c_str(), &pend, 10);
+
+      timer.m_search = line.substr(pos+1);
+
+      m_timers.emplace_back(std::move(timer));
+    }
+    rfile.close();
+  }
+}
+
+void CVNSITimers::Save()
+{
+  cString filename;
+  std::ofstream wfile;
+  filename = cString::sprintf("%s/timers.vnsi", *VNSIServerConfig.ConfigDirectory);
+
+  cMutexLock lock(&m_timerLock);
+
+  wfile.open(filename);
+  if(wfile.is_open())
+  {
+    for (auto &timer : m_timers)
+    {
+      wfile << timer.m_name << ';'
+            << timer.m_channelUID << ';'
+            << timer.m_enabled << ';'
+            << timer.m_priority << ';'
+            << timer.m_lifetime << ';'
+            << timer.m_search << '\n';
+    }
+    wfile.close();
+  }
+}
+
+void CVNSITimers::Add(CVNSITimer &&timer)
+{
+  const cChannel *channel = FindChannelByUID(timer.m_channelUID);
+  if (!channel)
+    return;
+
+  timer.m_channelID = channel->GetChannelID();
+
+  cMutexLock lock(&m_timerLock);
+  m_timers.emplace_back(std::move(timer));
+  m_state++;
+
+  Save();
+}
+
+void CVNSITimers::Scan()
+{
+  m_doScan = true;
+}
+
+size_t CVNSITimers::GetTimersCount()
+{
+  cMutexLock lock(&m_timerLock);
+  return m_timers.size();
+}
+
+std::vector<CVNSITimer> CVNSITimers::GetTimers()
+{
+  cMutexLock lock(&m_timerLock);
+  return m_timers;
+}
+
+bool CVNSITimers::GetTimer(int idx, CVNSITimer &timer)
+{
+  cMutexLock lock(&m_timerLock);
+  idx &= ~INDEX_MASK;
+  if (idx < 0 || idx >= (int)m_timers.size())
+    return false;
+  timer = m_timers[idx];
+  return true;
+}
+
+bool CVNSITimers::UpdateTimer(int idx, CVNSITimer &timer)
+{
+  cMutexLock lock(&m_timerLock);
+  idx &= ~INDEX_MASK;
+  if (idx < 0 || idx >= (int)m_timers.size())
+    return false;
+  m_timers[idx] = timer;
+  m_state++;
+  Save();
+  return true;
+}
+
+bool CVNSITimers::DeleteTimer(int idx)
+{
+  cMutexLock lock(&m_timerLock);
+  idx &= ~INDEX_MASK;
+  if (idx < 0 || idx >= (int)m_timers.size())
+    return false;
+  m_timers.erase(m_timers.begin()+idx);
+  m_state++;
+  Save();
+  return true;
+}
+
+bool CVNSITimers::StateChange(int &state)
+{
+  if (state != m_state)
+  {
+    state = m_state;
+    return true;
+  }
+  return false;
+}
+
+std::string CVNSITimers::Convert(std::string search)
+{
+  std::string regex;
+  size_t pos = search.find("*");
+  while (pos != search.npos)
+  {
+    regex += search.substr(0, pos);
+    regex += ".*";
+    search = search.substr(pos + 1);
+    pos = search.find("*");
+  }
+  regex += search;
+  return regex;
+}
+
+bool CVNSITimers::IsDuplicateEvent(cTimers *timers, const cEvent *event)
+{
+  for (const cTimer *timer = timers->First(); timer; timer = timers->Next(timer))
+  {
+    const cEvent *timerEvent = timer->Event();
+    if (timerEvent == nullptr)
+      continue;
+    if (timer->HasFlags(tfActive) &&
+        strcmp(timerEvent->Title(), event->Title()) == 0)
+    {
+      if (timerEvent->ShortText() != nullptr && event->ShortText() != nullptr &&
+          strcmp(timerEvent->ShortText(), event->ShortText()) == 0)
+        return true;
+
+      if (abs(difftime(timerEvent->StartTime(), event->StartTime())) < 300)
+        return true;
+    }
+  }
+  return false;
+}
+
+void CVNSITimers::Action()
+{
+#if VDRVERSNUM >= 20301
+  bool modified;
+  cStateKey timerState;
+
+  // set thread priority (nice level)
+  SetPriority(1);
+
+  while (Running())
+  {
+    if (!m_doScan)
+    {
+      usleep(1000*1000);
+      continue;
+    }
+    m_doScan = false;
+
+    std::vector<CVNSITimer> timers;
+    {
+      cMutexLock lock(&m_timerLock);
+      timers = m_timers;
+    }
+
+    cTimers *Timers = cTimers::GetTimersWrite(timerState);
+    if (!Timers)
+      continue;
+
+    Timers->SetExplicitModify();
+    modified = false;
+    cStateKey SchedulesStateKey(true);
+    const cSchedules *schedules = cSchedules::GetSchedulesRead(SchedulesStateKey);
+    if (schedules)
+    {
+      for (const cSchedule *schedule = schedules->First(); schedule; schedule = schedules->Next(schedule))
+      {
+        for (auto &searchTimer : timers)
+        {
+          if (!searchTimer.m_enabled)
+            continue;
+
+          if (!(searchTimer.m_channelID == schedule->ChannelID()))
+            continue;
+
+          for (const cEvent *event = schedule->Events()->First(); event; event = schedule->Events()->Next(event))
+          {
+            std::string title(event->Title());
+            std::smatch m;
+            std::regex e(Convert(searchTimer.m_search));
+
+            if (std::regex_search(title, m, e,  std::regex_constants::match_not_null))
+            {
+              bool duplicate = false;
+              LOCK_RECORDINGS_READ;
+              for (const cRecording *recording = Recordings->First(); recording; recording = Recordings->Next(recording))
+              {
+                if (recording->Info() != nullptr)
+                {
+                  if (strcmp(recording->Info()->Title(), event->Title()) == 0)
+                  {
+                    if (recording->Info()->ShortText() != nullptr && event->ShortText() != nullptr &&
+                        strcmp(recording->Info()->ShortText(), event->ShortText()) == 0)
+                    {
+                      duplicate = true;
+                      break;
+                    }
+                  }
+                }
+                if (abs(difftime(event->StartTime(), recording->Start())) < 300)
+                {
+                  duplicate = true;
+                  break;
+                }
+              }
+              if (duplicate)
+                continue;
+
+              if (IsDuplicateEvent(Timers, event))
+                continue;
+
+              std::unique_ptr<cTimer> newTimer(new cTimer(event));
+              Timers->Add(newTimer.release());
+              modified = true;
+            }
+          }
+        }
+      }
+    }
+    if (modified)
+      Timers->SetModified();
+    timerState.Remove(modified);
+    SchedulesStateKey.Remove(modified);
+  }
+
+#endif
+}
+
+void CVNSITimers::Shutdown()
+{
+  Cancel(5);
+  m_timers.clear();
+}
diff --git a/vnsitimer.h b/vnsitimer.h
new file mode 100644
index 0000000..b282afd
--- /dev/null
+++ b/vnsitimer.h
@@ -0,0 +1,75 @@
+/*
+ *      vdr-plugin-vnsi - KODI server plugin for VDR
+ *
+ *      Copyright (C) 2005-2016 Team XBMC
+ *
+ *      http://kodi.tv
+ *
+ *  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 KODI; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include "stdint.h"
+#include <string>
+#include <vector>
+#include <atomic>
+#include <vdr/channels.h>
+#include <vdr/thread.h>
+
+class cEvent;
+class cTimers;
+
+class CVNSITimer
+{
+public:
+  std::string m_name;
+  uint32_t m_channelUID;
+  int32_t m_enabled;
+  int32_t m_priority;
+  int32_t m_lifetime;
+  std::string m_search;
+  tChannelID m_channelID;
+};
+
+class CVNSITimers : public cThread
+{
+public:
+  CVNSITimers();
+  void Load();
+  void Save();
+  void Shutdown();
+  void Add(CVNSITimer &&timer);
+  void Scan();
+  size_t GetTimersCount();
+  bool StateChange(int &state);
+  std::vector<CVNSITimer> GetTimers();
+  bool GetTimer(int idx, CVNSITimer &timer);
+  bool UpdateTimer(int idx, CVNSITimer &timer);
+  bool DeleteTimer(int idx);
+
+  static constexpr uint32_t INDEX_MASK = 0xF0000000;
+protected:
+  virtual void Action(void) override;
+  std::string Convert(std::string search);
+  bool IsDuplicateEvent(cTimers *timers, const cEvent *event);
+
+  std::vector<CVNSITimer> m_timers;
+  std::atomic_bool m_doScan;
+  std::atomic_int m_state;
+  cMutex m_timerLock;
+};
+

-- 
vdr-plugin-vnsiserver packaging



More information about the pkg-multimedia-commits mailing list