[vdr-plugin-vnsiserver] 01/03: Imported Upstream version 0.9.1+git20131103

Tobias Grimm tiber-guest at alioth.debian.org
Sun Nov 3 17:58:08 UTC 2013


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

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

commit 7c4b33dd65b3662e69ebc2c1b65762fbc13338d0
Author: etobi <git at e-tobi.net>
Date:   Sun Nov 3 18:56:13 2013 +0100

    Imported Upstream version 0.9.1+git20131103
---
 Makefile           |    2 +-
 config.h           |    6 +
 cxsocket.c         |   12 +
 cxsocket.h         |    5 +
 demuxer.c          |  302 +++++++++++++++++--
 demuxer.h          |   12 +
 parser.c           |  380 ++++++++++++++++++------
 parser.h           |  120 ++------
 parser_AAC.c       |   15 +-
 parser_AAC.h       |   12 +-
 parser_AC3.c       |   14 +-
 parser_AC3.h       |   12 +-
 parser_DTS.c       |   14 +-
 parser_DTS.h       |   12 +-
 parser_MPEGAudio.c |   15 +-
 parser_MPEGAudio.h |   12 +-
 parser_MPEGVideo.c |   54 ++--
 parser_MPEGVideo.h |   16 +-
 parser_Subtitle.c  |   16 +-
 parser_Subtitle.h  |   12 +-
 parser_Teletext.c  |   14 +-
 parser_Teletext.h  |   12 +-
 parser_h264.c      |   42 +--
 parser_h264.h      |   13 +-
 recplayer.c        |   62 +++-
 recplayer.h        |    4 +-
 requestpacket.c    |   16 +
 requestpacket.h    |    1 +
 responsepacket.c   |   11 +-
 responsepacket.h   |    6 +-
 setup.c            |   72 +++--
 setup.h            |    7 +-
 streamer.c         |  204 ++++++++++---
 streamer.h         |    9 +-
 videobuffer.c      |  834 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 videobuffer.h      |   24 +-
 videoinput.c       |    6 +
 videoinput.h       |    1 +
 vnsi.c             |   26 ++
 vnsi.h             |    9 +-
 vnsiclient.c       |   94 +++++-
 vnsiclient.h       |    5 +-
 vnsicommand.h      |   16 +-
 vnsiserver.c       |   50 +++-
 44 files changed, 2115 insertions(+), 466 deletions(-)

diff --git a/Makefile b/Makefile
index ad09fba..8f37c92 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
 # This name will be used in the '-P...' option of VDR to load the plugin.
 # By default the main source file also carries this name.
 
-PLUGIN = vnsiserver3
+PLUGIN = vnsiserver4
 
 ### The version number of this plugin (taken from the main source file):
 
diff --git a/config.h b/config.h
index d99deec..24657ad 100644
--- a/config.h
+++ b/config.h
@@ -64,6 +64,12 @@
 #define FOLDERDELIMCHAR '~'
 #endif
 
+// Error flags
+#define ERROR_PES_GENERAL   0x01
+#define ERROR_PES_SCRAMBLE  0x02
+#define ERROR_PES_STARTCODE 0x04
+#define ERROR_DEMUX_NODATA  0x10
+
 class cVNSIServerConfig
 {
 public:
diff --git a/cxsocket.c b/cxsocket.c
index b9638d3..8f2fa5d 100644
--- a/cxsocket.c
+++ b/cxsocket.c
@@ -49,6 +49,10 @@
 #include "config.h"
 #include "cxsocket.h"
 
+#ifndef MSG_MORE
+#define MSG_MORE 0
+#endif
+
 cxSocket::~cxSocket()
 {
   close();
@@ -63,6 +67,14 @@ void cxSocket::close() {
   }
 }
 
+void cxSocket::Shutdown()
+{
+  if(m_fd >= 0)
+  {
+    ::shutdown(m_fd, SHUT_RD);
+  }
+}
+
 void cxSocket::LockWrite()
 {
   m_MutexWrite.Lock();
diff --git a/cxsocket.h b/cxsocket.h
index 9d0ecbd..4088152 100644
--- a/cxsocket.h
+++ b/cxsocket.h
@@ -27,6 +27,10 @@
 #ifndef VNSI_CXSOCKET_H
 #define VNSI_CXSOCKET_H
 
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -46,6 +50,7 @@ class cxSocket
   ~cxSocket();
   void SetHandle(int h);
   void close(void);
+  void Shutdown(void);
   void LockWrite();
   void UnlockWrite();
   ssize_t read(void *buffer, size_t size, int timeout_ms = -1);
diff --git a/demuxer.c b/demuxer.c
index e677168..1f3c9e8 100644
--- a/demuxer.c
+++ b/demuxer.c
@@ -39,6 +39,8 @@ cVNSIDemuxer::~cVNSIDemuxer()
 
 void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
 {
+  cMutexLock lock(&m_Mutex);
+
   m_CurrentChannel = channel;
   m_VideoBuffer = videoBuffer;
   m_OldPmtVersion = -1;
@@ -47,10 +49,20 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
     m_WaitIFrame = true;
   else
     m_WaitIFrame = false;
+
+  m_FirstFrameDTS = 0;
+
+  m_PtsWrap.m_Wrap = false;
+  m_PtsWrap.m_NoOfWraps = 0;
+  m_PtsWrap.m_ConfirmCount = 0;
+  m_MuxPacketSerial = 0;
+  m_Error = ERROR_DEMUX_NODATA;
 }
 
 void cVNSIDemuxer::Close()
 {
+  cMutexLock lock(&m_Mutex);
+
   for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
   {
     DEBUGLOG("Deleting stream parser for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type());
@@ -66,6 +78,8 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
   int len;
   cTSStream *stream;
 
+  cMutexLock lock(&m_Mutex);
+
   // clear packet
   if (!packet)
     return -1;
@@ -75,9 +89,14 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
 
   // read TS Packet from buffer
   len = m_VideoBuffer->Read(&buf, TS_SIZE);
-  if (len != TS_SIZE)
+  // eof
+  if (len == -2)
+    return -2;
+  else if (len != TS_SIZE)
     return -1;
 
+  m_Error &= ~ERROR_DEMUX_NODATA;
+
   int ts_pid = TsPid(buf);
 
   // parse PAT/PMT
@@ -112,18 +131,196 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
   }
   else if (stream = FindStream(ts_pid))
   {
-    if (stream->ProcessTSPacket(buf, packet, m_WaitIFrame))
+    int error = stream->ProcessTSPacket(buf, packet, m_WaitIFrame);
+    if (error == 0)
     {
-      m_WaitIFrame = false;
+      if (m_WaitIFrame)
+      {
+        m_FirstFrameDTS = packet->dts;
+        m_WaitIFrame = false;
+      }
+
+      if (packet->dts < m_FirstFrameDTS)
+        return 0;
+
+      packet->serial = m_MuxPacketSerial;
       return 1;
     }
+    else if (error < 0)
+    {
+      m_Error |= abs(error);
+    }
   }
-  else
-    return -1;
 
   return 0;
 }
 
+bool cVNSIDemuxer::SeekTime(int64_t time)
+{
+  off_t pos, pos_min, pos_max, pos_limit, start_pos;
+  int64_t ts, ts_min, ts_max, last_ts;
+  int no_change;
+
+  if (!m_VideoBuffer->HasBuffer())
+    return false;
+
+  cMutexLock lock(&m_Mutex);
+
+//  INFOLOG("----- seek to time: %ld", time);
+
+  // rescale to 90khz
+  time = cTSStream::Rescale(time, 90000, DVD_TIME_BASE);
+
+  m_VideoBuffer->GetPositions(&pos, &pos_min, &pos_max);
+
+//  INFOLOG("----- seek to time: %ld", time);
+//  INFOLOG("------pos: %ld, pos min: %ld, pos max: %ld", pos, pos_min, pos_max);
+
+  if (!GetTimeAtPos(&pos_min, &ts_min))
+  {
+    ResetParsers();
+    m_WaitIFrame = true;
+    return false;
+  }
+
+//  INFOLOG("----time at min: %ld", ts_min);
+
+  if (ts_min >= time)
+  {
+    m_VideoBuffer->SetPos(pos_min);
+    ResetParsers();
+    m_WaitIFrame = true;
+    m_MuxPacketSerial++;
+    return true;
+  }
+
+  int64_t timecur;
+  GetTimeAtPos(&pos, &timecur);
+
+  // get time at end of buffer
+  unsigned int step= 1024;
+  bool gotTime;
+  do
+  {
+    pos_max -= step;
+    gotTime = GetTimeAtPos(&pos_max, &ts_max);
+    step += step;
+  } while (!gotTime && pos_max >= step);
+
+  if (!gotTime)
+  {
+    ResetParsers();
+    m_WaitIFrame = true;
+    return false;
+  }
+
+  if (ts_max <= time)
+  {
+    ResetParsers();
+    m_WaitIFrame = true;
+    m_MuxPacketSerial++;
+    return true;
+  }
+
+//  INFOLOG(" - time in buffer: %ld", cTSStream::Rescale(ts_max-ts_min, DVD_TIME_BASE, 90000)/1000000);
+
+  // bisect seek
+  if(ts_min > ts_max)
+  {
+    ResetParsers();
+    m_WaitIFrame = true;
+    return false;
+  }
+  else if (ts_min == ts_max)
+  {
+    pos_limit = pos_min;
+  }
+  else
+    pos_limit = pos_max;
+
+  no_change = 0;
+  ts = time;
+  last_ts = 0;
+  while (pos_min < pos_limit)
+  {
+    if (no_change==0)
+    {
+      // interpolate position
+      pos = cTSStream::Rescale(time - ts_min, pos_max - pos_min, ts_max - ts_min)
+          + pos_min - (pos_max - pos_limit);
+    }
+    else if (no_change==1)
+    {
+      // bisection, if interpolation failed to change min or max pos last time
+      pos = (pos_min + pos_limit) >> 1;
+    }
+    else
+    {
+      // linear search if bisection failed
+      pos = pos_min;
+    }
+
+    // clamp calculated pos into boundaries
+    if( pos <= pos_min)
+      pos = pos_min + 1;
+    else if (pos > pos_limit)
+      pos = pos_limit;
+    start_pos = pos;
+
+    // get time stamp at pos
+    if (!GetTimeAtPos(&pos, &ts))
+    {
+      ResetParsers();
+      m_WaitIFrame = true;
+      return false;
+    }
+    pos = m_VideoBuffer->GetPosCur();
+
+    // determine method for next calculation of pos
+    if ((last_ts == ts) || (pos >= pos_max))
+      no_change++;
+    else
+      no_change=0;
+
+//    INFOLOG("--- pos: %ld, \t time: %ld, diff time: %ld", pos, ts, time-ts);
+
+    // 0.4 sec is close enough
+    if (abs(time - ts) <= 36000)
+    {
+      break;
+    }
+    // target is to the left
+    else if (time <= ts)
+    {
+      pos_limit = start_pos - 1;
+      pos_max = pos;
+      ts_max = ts;
+    }
+    // target is to the right
+    if (time >= ts)
+    {
+      pos_min = pos;
+      ts_min = ts;
+    }
+    last_ts = ts;
+  }
+
+//  INFOLOG("----pos found: %ld", pos);
+//  INFOLOG("----time at pos: %ld, diff time: %ld", ts, cTSStream::Rescale(timecur-ts, DVD_TIME_BASE, 90000));
+
+  m_VideoBuffer->SetPos(pos);
+
+  ResetParsers();
+  m_WaitIFrame = true;
+  m_MuxPacketSerial++;
+  return true;
+}
+
+void cVNSIDemuxer::BufferStatus(bool &timeshift, int &start, int &current, int &end)
+{
+  timeshift = m_VideoBuffer->HasBuffer();
+}
+
 cTSStream *cVNSIDemuxer::GetFirstStream()
 {
   m_StreamsIterator = m_Streams.begin();
@@ -152,6 +349,14 @@ cTSStream *cVNSIDemuxer::FindStream(int Pid)
   return NULL;
 }
 
+void cVNSIDemuxer::ResetParsers()
+{
+  for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
+  {
+    (*it)->ResetParser();
+  }
+}
+
 void cVNSIDemuxer::AddStreamInfo(sStreamInfo &stream)
 {
   m_StreamInfos.push_back(stream);
@@ -195,40 +400,40 @@ bool cVNSIDemuxer::EnsureParsers()
 
     if (it->type == stH264)
     {
-      stream = new cTSStream(stH264, it->pID);
+      stream = new cTSStream(stH264, it->pID, &m_PtsWrap);
     }
     else if (it->type == stMPEG2VIDEO)
     {
-      stream = new cTSStream(stMPEG2VIDEO, it->pID);
+      stream = new cTSStream(stMPEG2VIDEO, it->pID, &m_PtsWrap);
     }
     else if (it->type == stMPEG2AUDIO)
     {
-      stream = new cTSStream(stMPEG2AUDIO, it->pID);
+      stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stAACADTS)
     {
-      stream = new cTSStream(stAACADTS, it->pID);
+      stream = new cTSStream(stAACADTS, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stAACLATM)
     {
-      stream = new cTSStream(stAACLATM, it->pID);
+      stream = new cTSStream(stAACLATM, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stAC3)
     {
-      stream = new cTSStream(stAC3, it->pID);
+      stream = new cTSStream(stAC3, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stEAC3)
     {
-      stream = new cTSStream(stEAC3, it->pID);
+      stream = new cTSStream(stEAC3, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
     }
     else if (it->type == stDVBSUB)
     {
-      stream = new cTSStream(stDVBSUB, it->pID);
+      stream = new cTSStream(stDVBSUB, it->pID, &m_PtsWrap);
       stream->SetLanguage(it->language);
 #if APIVERSNUM >= 10709
       stream->SetSubtitlingDescriptor(it->subtitlingType, it->compositionPageId, it->ancillaryPageId);
@@ -236,7 +441,7 @@ bool cVNSIDemuxer::EnsureParsers()
     }
     else if (it->type == stTELETEXT)
     {
-      stream = new cTSStream(stTELETEXT, it->pID);
+      stream = new cTSStream(stTELETEXT, it->pID, &m_PtsWrap);
     }
     else
       continue;
@@ -266,39 +471,39 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
     AddStreamInfo(newStream);
   }
 
-  const int *APids = channel->Apids();
-  for ( ; *APids; APids++)
+  const int *DPids = channel->Dpids();
+  for ( ; *DPids; DPids++)
   {
     int index = 0;
-    if (!FindStream(*APids))
+    if (!FindStream(*DPids))
     {
-      newStream.pID = *APids;
-      newStream.type = stMPEG2AUDIO;
+      newStream.pID = *DPids;
+      newStream.type = stAC3;
 #if APIVERSNUM >= 10715
-      if (channel->Atype(index) == 0x0F)
-        newStream.type = stAACADTS;
-      else if (channel->Atype(index) == 0x11)
-        newStream.type = stAACLATM;
+      if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
+        newStream.type = stEAC3;
 #endif
-      newStream.SetLanguage(channel->Alang(index));
+      newStream.SetLanguage(channel->Dlang(index));
       AddStreamInfo(newStream);
     }
     index++;
   }
 
-  const int *DPids = channel->Dpids();
-  for ( ; *DPids; DPids++)
+  const int *APids = channel->Apids();
+  for ( ; *APids; APids++)
   {
     int index = 0;
-    if (!FindStream(*DPids))
+    if (!FindStream(*APids))
     {
-      newStream.pID = *DPids;
-      newStream.type = stAC3;
+      newStream.pID = *APids;
+      newStream.type = stMPEG2AUDIO;
 #if APIVERSNUM >= 10715
-      if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
-        newStream.type = stEAC3;
+      if (channel->Atype(index) == 0x0F)
+        newStream.type = stAACADTS;
+      else if (channel->Atype(index) == 0x11)
+        newStream.type = stAACLATM;
 #endif
-      newStream.SetLanguage(channel->Dlang(index));
+      newStream.SetLanguage(channel->Alang(index));
       AddStreamInfo(newStream);
     }
     index++;
@@ -385,3 +590,36 @@ void cVNSIDemuxer::SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser
                    Spids, SLangs,
                    Tpid);
 }
+
+bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time)
+{
+  uint8_t *buf;
+  int len;
+  cTSStream *stream;
+  int ts_pid;
+
+  m_VideoBuffer->SetPos(*pos);
+  ResetParsers();
+  while (len = m_VideoBuffer->Read(&buf, TS_SIZE) == TS_SIZE)
+  {
+    ts_pid = TsPid(buf);
+    if (stream = FindStream(ts_pid))
+    {
+      // only consider video or audio streams
+      if ((stream->Content() == scVIDEO || stream->Content() == scAUDIO) &&
+          stream->ReadTime(buf, time))
+      {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+uint16_t cVNSIDemuxer::GetError()
+{
+  uint16_t ret = m_Error;
+  m_Error = ERROR_DEMUX_NODATA;
+  return ret;
+}
+
diff --git a/demuxer.h b/demuxer.h
index 8efd0ba..019595f 100644
--- a/demuxer.h
+++ b/demuxer.h
@@ -57,13 +57,20 @@ public:
   cTSStream *GetNextStream();
   void Open(const cChannel &channel, cVideoBuffer *videoBuffer);
   void Close();
+  bool SeekTime(int64_t time);
+  uint32_t GetSerial() { return m_MuxPacketSerial; }
+  void SetSerial(uint32_t serial) { m_MuxPacketSerial = serial; }
+  void BufferStatus(bool &timeshift, int &start, int &current, int &end);
+  uint16_t GetError();
 
 protected:
   bool EnsureParsers();
+  void ResetParsers();
   void SetChannelStreams(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;
@@ -71,5 +78,10 @@ protected:
   cPatPmtParser m_PatPmtParser;
   int m_OldPmtVersion;
   bool m_WaitIFrame;
+  int64_t m_FirstFrameDTS;
   cVideoBuffer *m_VideoBuffer;
+  cMutex m_Mutex;
+  uint32_t m_MuxPacketSerial;
+  sPtsWrap m_PtsWrap;
+  uint16_t m_Error;
 };
diff --git a/parser.c b/parser.c
index 55f815e..d325fae 100644
--- a/parser.c
+++ b/parser.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -46,14 +42,10 @@
 
 // --- cParser -------------------------------------------------
 
-cParser::cParser(int pID, cTSStream *stream)
- : m_pID(pID)
+cParser::cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : m_pID(pID), m_PtsWrap(ptsWrap), m_ObservePtsWraps(observePtsWraps)
 {
-  m_curPTS    = DVD_NOPTS_VALUE;
-  m_curDTS    = DVD_NOPTS_VALUE;
-  m_prevDTS   = DVD_NOPTS_VALUE;
   m_PesBuffer = NULL;
-  m_IsError = false;
   m_Stream = stream;
   m_IsVideo = false;
   m_PesBufferInitialSize = 1024;
@@ -66,23 +58,29 @@ cParser::~cParser()
     free(m_PesBuffer);
 }
 
-
+void cParser::Reset()
+{
+  m_curPTS    = DVD_NOPTS_VALUE;
+  m_curDTS    = DVD_NOPTS_VALUE;
+  m_prevDTS   = DVD_NOPTS_VALUE;
+  m_PesBufferPtr = 0;
+  m_PesParserPtr = 0;
+  m_PesNextFramePtr = 0;
+  m_FoundFrame = false;
+  m_PesPacketLength = 0;
+  m_PesHeaderPtr = 0;
+  m_Error = ERROR_PES_GENERAL;
+}
 /*
  * Extract DTS and PTS and update current values in stream
  */
 int cParser::ParsePESHeader(uint8_t *buf, size_t len)
 {
-  if (len < 6)
-    return -1;
-
   m_PesPacketLength = buf[4] << 8 | buf[5];
 
   if (!PesIsVideoPacket(buf) && !PesIsAudioPacket(buf))
     return 6;
 
-  if (len < 9)
-    return -1;
-
   unsigned int hdr_len = PesHeaderLength(buf);
 
   if (m_PesPacketLength > 0)
@@ -103,7 +101,42 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
     pts |= ((int64_t)(buf[11] & 0xFE)) << 14 ;
     pts |= ((int64_t) buf[12])         <<  7 ;
     pts |= ((int64_t)(buf[13] & 0xFE)) >>  1 ;
+
+    int64_t bit32and31 = pts >> 31;
+    if (m_ObservePtsWraps)
+    {
+      if ((bit32and31 == 3) && !m_PtsWrap->m_Wrap)
+      {
+        m_PtsWrap->m_ConfirmCount++;
+        if (m_PtsWrap->m_ConfirmCount >= 2)
+        {
+          m_PtsWrap->m_Wrap = true;
+        }
+      }
+      else if ((bit32and31 == 1) && m_PtsWrap->m_Wrap)
+      {
+        m_PtsWrap->m_ConfirmCount++;
+        if (m_PtsWrap->m_ConfirmCount >= 2)
+        {
+          m_PtsWrap->m_Wrap = false;
+          m_PtsWrap->m_NoOfWraps++;
+        }
+      }
+      else
+        m_PtsWrap->m_ConfirmCount = 0;
+    }
+
+    m_prevPTS = m_curPTS;
     m_curPTS = pts;
+    m_PesTimePos = m_PesBufferPtr;
+    if (m_PtsWrap->m_Wrap && !(bit32and31))
+    {
+      m_curPTS += 1LL<<33;
+    }
+    if (m_PtsWrap->m_NoOfWraps)
+    {
+      m_curPTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33);
+    }
   }
   else
     return hdr_len;
@@ -121,6 +154,14 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
     dts |=  (int64_t)( buf[17]          <<  7 );
     dts |=  (int64_t)((buf[18] & 0xFE)  >>  1 );
     m_curDTS = dts;
+    if (m_PtsWrap->m_Wrap && !(m_curDTS >> 31))
+    {
+      m_curDTS += 1LL<<33;
+    }
+    if (m_PtsWrap->m_NoOfWraps)
+    {
+      m_curDTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33);
+    }
   }
   else
     m_curDTS = m_curPTS;
@@ -130,15 +171,29 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
 
 int cParser::ParsePacketHeader(uint8_t *data)
 {
-  m_IsPusi  = TsPayloadStart(data);
+  if (TsIsScrambled(data))
+  {
+    m_Error = ERROR_PES_SCRAMBLE;
+    return -1;
+  }
+
+  if (TsPayloadStart(data))
+  {
+    m_IsPusi = true;
+    m_Error = 0;
+  }
+
   int  bytes = TS_SIZE - TsPayloadOffset(data);
 
   if(bytes < 0 || bytes > TS_SIZE)
+  {
+    m_Error = ERROR_PES_GENERAL;
     return -1;
+  }
 
   if (TsError(data))
   {
-    ERRORLOG("transport error");
+    m_Error = ERROR_PES_GENERAL;
     return -1;
   }
 
@@ -149,55 +204,146 @@ int cParser::ParsePacketHeader(uint8_t *data)
   }
 
   /* drop broken PES packets */
-  if (m_IsError && !m_IsPusi)
+  if (m_Error)
   {
     return -1;
   }
 
-  /* handle new payload unit */
-  if (m_IsPusi)
-  {
-    if (!PesIsHeader(data+TS_SIZE-bytes))
-    {
-      m_IsError = true;
-//      ERRORLOG("--------------- no header");
-      Reset();
-      return -1;
-    }
-    m_IsError = false;
-  }
-
   return bytes;
 }
 
 bool cParser::AddPESPacket(uint8_t *data, int size)
 {
   // check for beginning of a PES packet
+  if (m_IsPusi && m_IsVideo && !IsValidStartCode(data, 4))
+  {
+    m_IsPusi = false;
+  }
   if (m_IsPusi)
   {
-    if (IsValidStartCode(data, size))
+    int hdr_len = 6;
+    if (m_PesHeaderPtr + size < hdr_len)
     {
-      m_firstPUSIseen = true;
-      int hlen = ParsePESHeader(data, size);
-      if (hlen <= 0)
+      memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+      m_PesHeaderPtr += size;
+      return false;
+    }
+    else if (m_PesHeaderPtr)
+    {
+      int bytesNeeded = hdr_len-m_PesHeaderPtr;
+      if (bytesNeeded > 0)
+      {
+        memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+        m_PesHeaderPtr += bytesNeeded;
+        data += bytesNeeded;
+        size -= bytesNeeded;
+      }
+      if (!IsValidStartCode(m_PesHeader, hdr_len))
+      {
+        Reset();
+        m_Error |= ERROR_PES_STARTCODE;
+        return false;
+      }
+      if (PesIsVideoPacket(m_PesHeader) || PesIsAudioPacket(m_PesHeader))
+      {
+        hdr_len = 9;
+        bytesNeeded = hdr_len-m_PesHeaderPtr;
+        if (size < bytesNeeded)
+        {
+          memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+          m_PesHeaderPtr += size;
+          return false;
+        }
+        else if (bytesNeeded > 0)
+        {
+          memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+          m_PesHeaderPtr += bytesNeeded;
+          data += bytesNeeded;
+          size -= bytesNeeded;
+        }
+        if ((m_PesHeader[6] & 0x30))
+        {
+          Reset();
+          m_Error |= ERROR_PES_SCRAMBLE;
+          return false;
+        }
+        hdr_len = PesHeaderLength(m_PesHeader);
+        if (hdr_len > PES_HEADER_LENGTH)
+        {
+          Reset();
+          return false;
+        }
+      }
+      bytesNeeded = hdr_len-m_PesHeaderPtr;
+      if (size < bytesNeeded)
       {
-        INFOLOG("--------- reset");
+        memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+        m_PesHeaderPtr += size;
+        return false;
+      }
+      else if (bytesNeeded > 0)
+      {
+        memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+        m_PesHeaderPtr += bytesNeeded;
+        data += bytesNeeded;
+        size -= bytesNeeded;
+      }
+      if (ParsePESHeader(m_PesHeader, hdr_len) < 0)
+      {
+        INFOLOG("error parsing pes packet error ");
         Reset();
         return false;
       }
-      data += hlen;
-      size -= hlen;
+      m_PesHeaderPtr = 0;
+      m_IsPusi = false;
     }
-    else
+    else if (!IsValidStartCode(data, size))
     {
-      INFOLOG("--------- reset 2");
       Reset();
+      m_Error |= ERROR_PES_STARTCODE;
+      return false;
+    }
+    else
+    {
+      if (PesIsVideoPacket(data) || PesIsAudioPacket(data))
+      {
+        if (size < 9)
+        {
+          memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+          m_PesHeaderPtr += size;
+          return false;
+        }
+        if ((data[6] & 0x30))
+        {
+          Reset();
+          m_Error |= ERROR_PES_STARTCODE;
+          return false;
+        }
+        hdr_len = PesHeaderLength(data);
+        if (hdr_len > PES_HEADER_LENGTH)
+        {
+          Reset();
+          return false;
+        }
+      }
+      if (size < hdr_len)
+      {
+        memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+        m_PesHeaderPtr += size;
+        return false;
+      }
+      if (ParsePESHeader(data, hdr_len) < 0)
+      {
+        INFOLOG("error parsing pes packet error 2");
+        Reset();
+        return false;
+      }
+      data += hdr_len;
+      size -= hdr_len;
+      m_IsPusi = false;
     }
   }
 
-  if (!m_firstPUSIseen)
-    return false;
-
   if (m_PesBuffer == NULL)
   {
     m_PesBufferSize = m_PesBufferInitialSize;
@@ -212,9 +358,9 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
 
   if (m_PesBufferPtr + size >= m_PesBufferSize)
   {
-    if (m_PesBufferPtr + size >= 500000)
+    if (m_PesBufferPtr + size >= 1000000)
     {
-      ERRORLOG("cParser::AddPESPacket - max buffer size reached");
+      ERRORLOG("cParser::AddPESPacket - max buffer size reached, pid: %d", m_pID);
       Reset();
       return false;
     }
@@ -233,6 +379,7 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
   {
     memmove(m_PesBuffer, m_PesBuffer+m_PesNextFramePtr, m_PesBufferPtr-m_PesNextFramePtr);
     m_PesBufferPtr = m_PesBufferPtr-m_PesNextFramePtr;
+    m_PesTimePos -= m_PesNextFramePtr;
     m_PesNextFramePtr = 0;
   }
 
@@ -240,7 +387,7 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
   memcpy(m_PesBuffer+m_PesBufferPtr, data, size);
   m_PesBufferPtr += size;
 
-  return false;
+  return true;
 }
 
 inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
@@ -284,19 +431,9 @@ inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
   return false;
 }
 
-void cParser::Reset()
-{
-  m_PesBufferPtr = 0;
-  m_PesParserPtr = 0;
-  m_firstPUSIseen = false;
-  m_PesNextFramePtr = 0;
-  m_FoundFrame = false;
-  m_PesPacketLength = 0;
-}
-
 // --- cTSStream ----------------------------------------------------
 
-cTSStream::cTSStream(eStreamType type, int pid)
+cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
   : m_streamType(type)
   , m_pID(pid)
 {
@@ -316,25 +453,55 @@ cTSStream::cTSStream(eStreamType type, int pid)
   m_IsStreamChange  = false;
 
   if (m_streamType == stMPEG2VIDEO)
-    m_pesParser = new cParserMPEG2Video(m_pID, this);
+  {
+    m_pesParser = new cParserMPEG2Video(m_pID, this, ptsWrap, true);
+    m_streamContent = scVIDEO;
+  }
   else if (m_streamType == stH264)
-    m_pesParser = new cParserH264(m_pID, this);
+  {
+    m_pesParser = new cParserH264(m_pID, this, ptsWrap, true);
+    m_streamContent = scVIDEO;
+  }
   else if (m_streamType == stMPEG2AUDIO)
-    m_pesParser = new cParserMPEG2Audio(m_pID, this);
+  {
+    m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stAACADTS)
-    m_pesParser = new cParserAAC(m_pID, this);
+  {
+    m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stAACLATM)
-    m_pesParser = new cParserAAC(m_pID, this);
+  {
+    m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stAC3)
-    m_pesParser = new cParserAC3(m_pID, this);
+  {
+    m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stEAC3)
-    m_pesParser = new cParserAC3(m_pID, this);
+  {
+    m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stDTS)
-    m_pesParser = new cParserDTS(m_pID, this);
+  {
+    m_pesParser = new cParserDTS(m_pID, this, ptsWrap, true);
+    m_streamContent = scAUDIO;
+  }
   else if (m_streamType == stTELETEXT)
-    m_pesParser = new cParserTeletext(m_pID, this);
+  {
+    m_pesParser = new cParserTeletext(m_pID, this, ptsWrap, false);
+    m_streamContent = scTELETEXT;
+  }
   else if (m_streamType == stDVBSUB)
-    m_pesParser = new cParserSubtitle(m_pID, this);
+  {
+    m_pesParser = new cParserSubtitle(m_pID, this, ptsWrap, false);
+    m_streamContent = scSUBTITLE;
+  }
   else
   {
     ERRORLOG("Unrecognised type %i inside stream %i", m_streamType, m_pID);
@@ -351,7 +518,7 @@ cTSStream::~cTSStream()
   }
 }
 
-bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
+int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
 {
   if (!data)
     return false;
@@ -360,13 +527,21 @@ bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
     return false;
 
   int payloadSize = m_pesParser->ParsePacketHeader(data);
-  if (payloadSize < 0)
-    return false;
+  if (payloadSize == 0)
+    return 1;
+  else if (payloadSize < 0)
+  {
+    return -m_pesParser->GetError();
+  }
+
+  if (!m_pesParser->AddPESPacket(data+TS_SIZE-payloadSize, payloadSize))
+  {
+    return -m_pesParser->GetError();
+  }
 
-  m_pesParser->AddPESPacket(data+TS_SIZE-payloadSize, payloadSize);
   m_pesParser->Parse(pkt);
   if (iframe && !m_pesParser->IsVideo())
-    return false;
+    return 1;
 
   if (pkt->data)
   {
@@ -377,22 +552,57 @@ bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
       dts = pts;
 
     // Rescale for XBMC
-    pkt->dts      = Rescale(dts);
-    pkt->pts      = Rescale(pts);
-    pkt->duration = Rescale(pkt->duration);
-    return true;
+    pkt->dts      = Rescale(dts, DVD_TIME_BASE, 90000);
+    pkt->pts      = Rescale(pts, DVD_TIME_BASE, 90000);
+    pkt->duration = Rescale(pkt->duration, DVD_TIME_BASE, 90000);
+    return 0;
   }
 
+  return 1;
+}
+
+bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
+{
+  if (!data)
+    return false;
+
+  if (!m_pesParser)
+    return false;
+
+  int payloadSize = m_pesParser->ParsePacketHeader(data);
+  if (payloadSize < 0)
+    return false;
+
+  if (m_pesParser->m_IsPusi)
+  {
+    data += TS_SIZE-payloadSize;
+    if (payloadSize >= 6 && m_pesParser->IsValidStartCode(data, payloadSize))
+    {
+      m_pesParser->m_curDTS = DVD_NOPTS_VALUE;
+      m_pesParser->ParsePESHeader(data, payloadSize);
+      if (m_pesParser->m_curDTS != DVD_NOPTS_VALUE)
+      {
+        *dts = m_pesParser->m_curDTS;
+        return true;
+      }
+    }
+    m_pesParser->m_IsPusi = false;
+  }
   return false;
 }
 
-int64_t cTSStream::Rescale(int64_t a)
+void cTSStream::ResetParser()
+{
+  if (m_pesParser)
+    m_pesParser->Reset();
+}
+
+int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
 {
-  uint64_t b = DVD_TIME_BASE;
-  uint64_t c = 90000;
   uint64_t r = c/2;
 
-  if (b<=INT_MAX && c<=INT_MAX){
+  if (b<=INT_MAX && c<=INT_MAX)
+  {
     if (a<=INT_MAX)
       return (a * b + r)/c;
     else
diff --git a/parser.h b/parser.h
index f42320f..f3e8db4 100644
--- a/parser.h
+++ b/parser.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -77,88 +73,11 @@ inline bool PesIsPS1Packet(const uchar *p)
   return ((p)[3] == PRIVATE_STREAM1 || (p)[3] == PRIVATE_STREAM3 );
 }
 
-inline bool PesIsPaddingPacket(const uchar *p)
-{
-  return ((p)[3] == PADDING_STREAM);
-}
-
 inline bool PesIsAudioPacket(const uchar *p)
 {
   return (PesIsMPEGAudioPacket(p) || PesIsPS1Packet(p));
 }
 
-#if APIVERSNUM < 10701
-
-#define TS_ERROR              0x80
-#define TS_PAYLOAD_START      0x40
-#define TS_TRANSPORT_PRIORITY 0x20
-#define TS_PID_MASK_HI        0x1F
-#define TS_SCRAMBLING_CONTROL 0xC0
-#define TS_ADAPT_FIELD_EXISTS 0x20
-#define TS_PAYLOAD_EXISTS     0x10
-#define TS_CONT_CNT_MASK      0x0F
-#define TS_ADAPT_DISCONT      0x80
-#define TS_ADAPT_RANDOM_ACC   0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
-#define TS_ADAPT_ELEM_PRIO    0x20
-#define TS_ADAPT_PCR          0x10
-#define TS_ADAPT_OPCR         0x08
-#define TS_ADAPT_SPLICING     0x04
-#define TS_ADAPT_TP_PRIVATE   0x02
-#define TS_ADAPT_EXTENSION    0x01
-
-inline bool TsHasPayload(const uchar *p)
-{
-  return p[3] & TS_PAYLOAD_EXISTS;
-}
-
-inline bool TsHasAdaptationField(const uchar *p)
-{
-  return p[3] & TS_ADAPT_FIELD_EXISTS;
-}
-
-inline bool TsPayloadStart(const uchar *p)
-{
-  return p[1] & TS_PAYLOAD_START;
-}
-
-inline bool TsError(const uchar *p)
-{
-  return p[1] & TS_ERROR;
-}
-
-inline int TsPid(const uchar *p)
-{
-  return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
-}
-
-inline bool TsIsScrambled(const uchar *p)
-{
-  return p[3] & TS_SCRAMBLING_CONTROL;
-}
-
-inline int TsPayloadOffset(const uchar *p)
-{
-  return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
-}
-
-inline int TsGetPayload(const uchar **p)
-{
-  int o = TsPayloadOffset(*p);
-  *p += o;
-  return TS_SIZE - o;
-}
-
-inline int TsContinuityCounter(const uchar *p)
-{
-  return p[3] & TS_CONT_CNT_MASK;
-}
-
-inline int TsGetAdaptationField(const uchar *p)
-{
-  return TsHasAdaptationField(p) ? p[5] : 0x00;
-}
-#endif
-
 enum eStreamContent
 {
   scVIDEO,
@@ -202,27 +121,41 @@ struct sStreamPacket
   int       size;
   bool      streamChange;
   bool      pmtChange;
+  uint32_t  serial;
+};
+
+struct sPtsWrap
+{
+  bool m_Wrap;
+  int m_NoOfWraps;
+  int m_ConfirmCount;
 };
 
 class cTSStream;
 
+#define PES_HEADER_LENGTH 128
+
 class cParser
 {
+friend class cTSStream;
 public:
-  cParser(int pID, cTSStream *stream);
+  cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParser();
 
   bool AddPESPacket(uint8_t *data, int size);
   virtual void Parse(sStreamPacket *pkt) = 0;
-  void ClearFrame() {m_PesBufferPtr = 0;}
+//  void ClearFrame() {m_PesBufferPtr = 0;}
   int ParsePacketHeader(uint8_t *data);
   int ParsePESHeader(uint8_t *buf, size_t len);
   virtual void Reset();
   bool IsVideo() {return m_IsVideo; }
+  uint16_t GetError() { return m_Error; }
 
 protected:
   virtual bool IsValidStartCode(uint8_t *buf, int size);
 
+  uint8_t     m_PesHeader[PES_HEADER_LENGTH];
+  int         m_PesHeaderPtr;
   int         m_PesPacketLength;
   uint8_t    *m_PesBuffer;
   int         m_PesBufferSize;
@@ -230,20 +163,23 @@ protected:
   size_t      m_PesBufferInitialSize;
   size_t      m_PesParserPtr;
   size_t      m_PesNextFramePtr;
+  int         m_PesTimePos;
 
   bool        m_FoundFrame;
 
   int         m_pID;
   int64_t     m_curPTS;
   int64_t     m_curDTS;
+  int64_t     m_prevPTS;
   int64_t     m_prevDTS;
 
   bool        m_IsPusi;
-  bool        m_firstPUSIseen;
-  bool        m_IsError;
+  uint16_t    m_Error;
 
   cTSStream  *m_Stream;
   bool        m_IsVideo;
+  sPtsWrap   *m_PtsWrap;
+  bool        m_ObservePtsWraps;
 };
 
 
@@ -277,10 +213,12 @@ private:
   uint16_t              m_ancillaryPageId;
 
 public:
-  cTSStream(eStreamType type, int pid);
+  cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap);
   virtual ~cTSStream();
 
-  bool ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+  int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+  bool ReadTime(uint8_t *data, int64_t *dts);
+  void ResetParser();
 
   void SetLanguage(const char *language);
   const char *GetLanguage() { return m_language; }
@@ -302,7 +240,7 @@ public:
   uint16_t CompositionPageId() const { return m_compositionPageId; }
   uint16_t AncillaryPageId() const { return m_ancillaryPageId; }
 
-  int64_t Rescale(int64_t a);
+  static int64_t Rescale(int64_t a, int64_t b, int64_t c);
 };
 
 #endif // VNSI_DEMUXER_H
diff --git a/parser_AAC.c b/parser_AAC.c
index 2a3ff5d..2ef3cb1 100644
--- a/parser_AAC.c
+++ b/parser_AAC.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -35,10 +31,9 @@ static int aac_sample_rates[16] =
 };
 
 
-cParserAAC::cParserAAC(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserAAC::cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
-  m_firstPUSIseen             = false;
   m_Configured                = false;
   m_FrameLengthType           = 0;
   m_PTS                       = 0;
diff --git a/parser_AAC.h b/parser_AAC.h
index defb1d4..15c2122 100644
--- a/parser_AAC.h
+++ b/parser_AAC.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -52,7 +48,7 @@ private:
   uint32_t LATMGetValue(cBitstream *bs) { return bs->readBits(bs->readBits(2) * 8); }
 
 public:
-  cParserAAC(int pID, cTSStream *stream);
+  cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserAAC();
   virtual void Parse(sStreamPacket *pkt);
   virtual void Reset();
diff --git a/parser_AC3.c b/parser_AC3.c
index 4cb88d5..a1645a2 100644
--- a/parser_AC3.c
+++ b/parser_AC3.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -109,8 +105,8 @@ typedef enum {
   EAC3_FRAME_TYPE_RESERVED
 } EAC3FrameType;
 
-cParserAC3::cParserAC3(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserAC3::cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_PTS                       = 0;
   m_DTS                       = 0;
diff --git a/parser_AC3.h b/parser_AC3.h
index 5844437..b789423 100644
--- a/parser_AC3.h
+++ b/parser_AC3.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -43,7 +39,7 @@ private:
   int FindHeaders(uint8_t *buf, int buf_size);
 
 public:
-  cParserAC3(int pID, cTSStream *stream);
+  cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserAC3();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_DTS.c b/parser_DTS.c
index 042a1e5..2ad2385 100644
--- a/parser_DTS.c
+++ b/parser_DTS.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -29,8 +25,8 @@
 #include "parser_DTS.h"
 #include "bitstream.h"
 
-cParserDTS::cParserDTS(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
 }
 
diff --git a/parser_DTS.h b/parser_DTS.h
index c388345..c28b4ba 100644
--- a/parser_DTS.h
+++ b/parser_DTS.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -34,7 +30,7 @@ class cParserDTS : public cParser
 private:
 
 public:
-  cParserDTS(int pID, cTSStream *stream);
+  cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserDTS();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_MPEGAudio.c b/parser_MPEGAudio.c
index 2f2cc5e..2c62030 100644
--- a/parser_MPEGAudio.c
+++ b/parser_MPEGAudio.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -44,8 +40,8 @@ const uint16_t BitrateTable[2][3][15] =
   }
 };
 
-cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_PTS                       = 0;
   m_DTS                       = 0;
@@ -113,6 +109,7 @@ int cParserMPEG2Audio::FindHeaders(uint8_t *buf, int buf_size)
     int layer = bs.readBits(2);
     if (layer == 0)
       return 0;
+    layer = 4 - layer;
 
     bs.skipBits(1); // protetion bit
     int bitrate_index = bs.readBits(4);
diff --git a/parser_MPEGAudio.h b/parser_MPEGAudio.h
index b08689b..32a3989 100644
--- a/parser_MPEGAudio.h
+++ b/parser_MPEGAudio.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -43,7 +39,7 @@ private:
   int FindHeaders(uint8_t *buf, int buf_size);
 
 public:
-  cParserMPEG2Audio(int pID, cTSStream *stream);
+  cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserMPEG2Audio();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_MPEGVideo.c b/parser_MPEGVideo.c
index 59620f1..5aa911a 100644
--- a/parser_MPEGVideo.c
+++ b/parser_MPEGVideo.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -52,8 +48,8 @@ const unsigned int mpeg2video_framedurations[16] = {
   1500,
 };
 
-cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_FrameDuration     = 0;
   m_vbvDelay          = -1;
@@ -97,7 +93,7 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
   {
     if (!m_NeedSPS && !m_NeedIFrame)
     {
-      int fpsScale = m_Stream->Rescale(m_FrameDuration);
+      int fpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
       bool streamChange = m_Stream->SetVideoInformation(fpsScale,DVD_TIME_BASE, m_Height, m_Width, m_Dar);
 
       pkt->id       = m_pID;
@@ -105,14 +101,12 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
       pkt->data     = m_PesBuffer;
       pkt->dts      = m_DTS;
       pkt->pts      = m_PTS;
-      pkt->duration = m_curDTS - m_prevDTS;
+      pkt->duration = m_FrameDuration;
       pkt->streamChange = streamChange;
     }
     m_StartCode = 0xffffffff;
     m_PesParserPtr = 0;
     m_FoundFrame = false;
-    m_PTS = m_curPTS;
-    m_DTS = m_curDTS;
   }
 }
 
@@ -122,7 +116,6 @@ void cParserMPEG2Video::Reset()
   m_StartCode = 0xffffffff;
   m_NeedIFrame = true;
   m_NeedSPS = true;
-  m_DTS = DVD_NOPTS_VALUE;
 }
 
 int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete)
@@ -150,12 +143,35 @@ int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &c
     if (!Parse_MPEG2Video_PicStart(buf))
       return 0;
 
-    // if this is the first frame we see, set timestamp
-    if (m_DTS == DVD_NOPTS_VALUE)
+    if (!m_FoundFrame)
+    {
+      m_AuPrevDTS = m_AuDTS;
+      if (buf_ptr - 4 >= m_PesTimePos)
+      {
+
+        m_AuDTS = m_curDTS;
+        m_AuPTS = m_curPTS;
+      }
+      else
+      {
+        m_AuDTS = m_prevDTS;
+        m_AuPTS = m_prevPTS;
+      }
+    }
+    if (m_AuPrevDTS == m_AuDTS)
     {
-      m_PTS = m_curPTS;
-      m_DTS = m_curDTS;
+      m_DTS = m_AuDTS + m_PicNumber*m_FrameDuration;
+      m_PTS = m_AuPTS + (m_TemporalReference-m_TrLastTime)*m_FrameDuration;
     }
+    else
+    {
+      m_PTS = m_AuPTS;
+      m_DTS = m_AuDTS;
+      m_PicNumber = 0;
+      m_TrLastTime = m_TemporalReference;
+    }
+
+    m_PicNumber++;
     m_FoundFrame = true;
     break;
   }
@@ -237,7 +253,7 @@ bool cParserMPEG2Video::Parse_MPEG2Video_PicStart(uint8_t *buf)
 {
   cBitstream bs(buf, 4 * 8);
 
-  bs.skipBits(10); /* temporal reference */
+  m_TemporalReference = bs.readBits(10); /* temporal reference */
 
   int pct = bs.readBits(3);
   if (pct < PKT_I_FRAME || pct > PKT_B_FRAME)
diff --git a/parser_MPEGVideo.h b/parser_MPEGVideo.h
index 2c728c0..18da858 100644
--- a/parser_MPEGVideo.h
+++ b/parser_MPEGVideo.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -46,13 +42,17 @@ private:
   float           m_Dar;
   int64_t         m_DTS;
   int64_t         m_PTS;
+  int64_t         m_AuDTS, m_AuPTS, m_AuPrevDTS;
+  int             m_TemporalReference;
+  int             m_TrLastTime;
+  int             m_PicNumber;
 
   int Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete);
   bool Parse_MPEG2Video_SeqStart(uint8_t *buf);
   bool Parse_MPEG2Video_PicStart(uint8_t *buf);
 
 public:
-  cParserMPEG2Video(int pID, cTSStream *stream);
+  cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserMPEG2Video();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_Subtitle.c b/parser_Subtitle.c
index 2e539ed..6408fbd 100644
--- a/parser_Subtitle.c
+++ b/parser_Subtitle.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -28,8 +24,8 @@
 
 #include "parser_Subtitle.h"
 
-cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_PesBufferInitialSize = 4000;
 }
@@ -55,7 +51,7 @@ void cParserSubtitle::Parse(sStreamPacket *pkt)
     {
       pkt->id       = m_pID;
       pkt->data     = m_PesBuffer+2;
-      pkt->size     = m_PesPacketLength-2;
+      pkt->size     = m_PesPacketLength-3;
       pkt->duration = 0;
       pkt->dts      = m_curDTS;
       pkt->pts      = m_curPTS;
diff --git a/parser_Subtitle.h b/parser_Subtitle.h
index 88dfdfc..296ac7c 100644
--- a/parser_Subtitle.h
+++ b/parser_Subtitle.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -32,7 +28,7 @@
 class cParserSubtitle : public cParser
 {
 public:
-  cParserSubtitle(int pID, cTSStream *stream);
+  cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserSubtitle();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_Teletext.c b/parser_Teletext.c
index 6db09b0..b2ecaee 100644
--- a/parser_Teletext.c
+++ b/parser_Teletext.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -27,8 +23,8 @@
 
 #include "parser_Teletext.h"
 
-cParserTeletext::cParserTeletext(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserTeletext::cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_PesBufferInitialSize      = 4000;
 }
diff --git a/parser_Teletext.h b/parser_Teletext.h
index e35fd79..831e7f5 100644
--- a/parser_Teletext.h
+++ b/parser_Teletext.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -36,7 +32,7 @@ private:
   int64_t     m_lastPTS;
 
 public:
-  cParserTeletext(int pID, cTSStream *stream);
+  cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserTeletext();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_h264.c b/parser_h264.c
index cf96be9..6d45e30 100644
--- a/parser_h264.c
+++ b/parser_h264.c
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -49,8 +45,8 @@ static const int h264_lev2cpbsize[][2] =
   {-1, -1},
 };
 
-cParserH264::cParserH264(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserH264::cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
 {
   m_Height            = 0;
   m_Width             = 0;
@@ -62,7 +58,7 @@ cParserH264::cParserH264(int pID, cTSStream *stream)
   m_PixelAspect.den   = 1;
   m_PixelAspect.num   = 0;
   memset(&m_streamData, 0, sizeof(m_streamData));
-  m_PesBufferInitialSize      = 80000;
+  m_PesBufferInitialSize      = 240000;
 
   m_IsVideo = true;
   Reset();
@@ -106,7 +102,7 @@ void cParserH264::Parse(sStreamPacket *pkt)
 //      int fpsScale = DVD_TIME_BASE / m_FPS;
       if (m_FpsScale == 0)
       {
-        m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS);
+        m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS, DVD_TIME_BASE, 90000);
       }
       bool streamChange = m_Stream->SetVideoInformation(m_FpsScale,DVD_TIME_BASE, m_Height, m_Width, DAR);
 
@@ -121,8 +117,6 @@ void cParserH264::Parse(sStreamPacket *pkt)
     m_StartCode = 0xffffffff;
     m_PesParserPtr = 0;
     m_FoundFrame = false;
-    m_PTS = m_curPTS;
-    m_DTS = m_curDTS;
   }
 }
 
@@ -133,7 +127,7 @@ void cParserH264::Reset()
   m_NeedIFrame = true;
   m_NeedSPS = true;
   m_NeedPPS = true;
-  m_DTS = DVD_NOPTS_VALUE;
+  memset(&m_streamData, 0, sizeof(m_streamData));
 }
 
 int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
@@ -168,16 +162,22 @@ int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
       return -1;
     }
 
-    m_streamData.vcl_nal = vcl;
-
-    // if this is the first frame we see, set timestamp
-    if (m_DTS == DVD_NOPTS_VALUE)
+    if (!m_FoundFrame)
     {
-      m_PTS = m_curPTS;
-      m_DTS = m_curDTS;
+      if (buf_ptr - 4 >= m_PesTimePos)
+      {
+        m_DTS = m_curDTS;
+        m_PTS = m_curPTS;
+      }
+      else
+      {
+        m_DTS = m_prevDTS;
+        m_PTS = m_prevPTS;
+      }
     }
-    m_FoundFrame = true;
 
+    m_streamData.vcl_nal = vcl;
+    m_FoundFrame = true;
     break;
   }
 
diff --git a/parser_h264.h b/parser_h264.h
index 174e2f1..609ce0a 100644
--- a/parser_h264.h
+++ b/parser_h264.h
@@ -1,8 +1,5 @@
 /*
- *      vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- *      Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ *      Copyright (C) 2005-2012 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -102,7 +98,6 @@ private:
   int64_t         m_DTS;
   int64_t         m_PTS;
 
-
   int Parse_H264(uint32_t startcode, int buf_ptr, bool &complete);
   bool Parse_PPS(uint8_t *buf, int len);
   bool Parse_SLH(uint8_t *buf, int len, h264_private::VCL_NAL &vcl);
@@ -110,7 +105,7 @@ private:
   bool IsFirstVclNal(h264_private::VCL_NAL &vcl);
 
 public:
-  cParserH264(int pID, cTSStream *stream);
+  cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
   virtual ~cParserH264();
 
   virtual void Parse(sStreamPacket *pkt);
diff --git a/recplayer.c b/recplayer.c
index 9af82f7..f5af3e4 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -34,11 +34,16 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-cRecPlayer::cRecPlayer(cRecording* rec)
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+cRecPlayer::cRecPlayer(cRecording* rec, bool inProgress)
 {
   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
@@ -94,6 +99,39 @@ void cRecPlayer::scan()
   INFOLOG("total frames: %u", m_totalFrames);
 }
 
+void cRecPlayer::reScan()
+{
+  struct stat s;
+
+  m_totalLength = 0;
+
+  for(int i = 0; ; i++) // i think we only need one possible loop
+  {
+    fileNameFromIndex(i);
+
+    if(stat(m_fileName, &s) == -1) {
+      break;
+    }
+
+    cSegment* segment;
+    if (m_segments.Size() < i+1)
+    {
+      cSegment* segment = new cSegment();
+      m_segments.Append(segment);
+      segment->start = m_totalLength;
+    }
+    else
+      segment = m_segments[i];
+
+    segment->end = segment->start + s.st_size;
+
+    m_totalLength += s.st_size;
+  }
+
+  m_totalFrames = m_indexFile->Last();
+}
+
+
 cRecPlayer::~cRecPlayer()
 {
   cleanup();
@@ -118,7 +156,7 @@ bool cRecPlayer::openFile(int index)
   fileNameFromIndex(index);
   INFOLOG("openFile called for index %i string:%s", index, m_fileName);
 
-  m_file = open(m_fileName, O_RDONLY | O_NOATIME);
+  m_file = open(m_fileName, O_RDONLY);
   if (m_file == -1)
   {
     INFOLOG("file failed to open");
@@ -155,8 +193,8 @@ uint32_t cRecPlayer::getLengthFrames()
 int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
 {
   // dont let the block be larger than 256 kb
-  if (amount > 256*1024)
-    amount = 256*1024;
+  if (amount > 512*1024)
+    amount = 512*1024;
 
   if ((uint64_t)amount > m_totalLength)
     amount = m_totalLength;
@@ -195,16 +233,20 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
   // try to read the block
   int bytes_read = read(m_file, buffer, amount);
 
+  // we may got stuck at end of segment
+  if ((bytes_read == 0) && (position < m_totalLength))
+    bytes_read += getBlock(buffer, position+1 , amount);
+
   if(bytes_read <= 0) {
     return 0;
   }
 
-  // Tell linux not to bother keeping the data in the FS cache
-  posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED);
-
-  // divide and conquer
-  if(bytes_read < amount) {
-    bytes_read += getBlock(&buffer[bytes_read], position + bytes_read, amount - bytes_read);
+  if (!m_inProgress)
+  {
+#ifndef __FreeBSD__
+    // Tell linux not to bother keeping the data in the FS cache
+    posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED);
+#endif
   }
 
   return bytes_read;
diff --git a/recplayer.h b/recplayer.h
index a94e0d7..784489c 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -47,7 +47,7 @@ class cSegment
 class cRecPlayer
 {
 public:
-  cRecPlayer(cRecording* rec);
+  cRecPlayer(cRecording* rec, bool inProgress = false);
   ~cRecPlayer();
   uint64_t getLengthBytes();
   uint32_t getLengthFrames();
@@ -57,6 +57,7 @@ public:
   void closeFile();
 
   void scan();
+  void reScan();
   uint64_t positionFromFrameNumber(uint32_t frameNumber);
   uint32_t frameNumberFromPosition(uint64_t position);
   bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
@@ -75,6 +76,7 @@ private:
   uint32_t    m_totalFrames;
   char       *m_recordingFilename;
   bool        m_pesrecording;
+  bool        m_inProgress;
 };
 
 #endif // VNSI_RECPLAYER_H
diff --git a/requestpacket.c b/requestpacket.c
index cb61e6b..51245ca 100644
--- a/requestpacket.c
+++ b/requestpacket.c
@@ -28,7 +28,13 @@
 #include <stdint.h>
 #include <string.h>
 
+#ifndef __FreeBSD__
 #include <asm/byteorder.h>
+#else
+#include <sys/endian.h>
+#define __be64_to_cpu be64toh
+#define __cpu_to_be64 htobe64
+#endif
 
 #include "config.h"
 #include "requestpacket.h"
@@ -102,6 +108,16 @@ uint64_t cRequestPacket::extract_U64()
   return ull;
 }
 
+int64_t cRequestPacket::extract_S64()
+{
+  if ((packetPos + sizeof(int64_t)) > userDataLength) return 0;
+  int64_t ll;
+  memcpy(&ll, &userData[packetPos], sizeof(int64_t));
+  ll = __be64_to_cpu(ll);
+  packetPos += sizeof(int64_t);
+  return ll;
+}
+
 double cRequestPacket::extract_Double()
 {
   if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
diff --git a/requestpacket.h b/requestpacket.h
index 4e3704d..44fab53 100644
--- a/requestpacket.h
+++ b/requestpacket.h
@@ -46,6 +46,7 @@ public:
   uint8_t   extract_U8();
   uint32_t  extract_U32();
   uint64_t  extract_U64();
+  int64_t   extract_S64();
   int32_t   extract_S32();
   double    extract_Double();
 
diff --git a/responsepacket.c b/responsepacket.c
index 646e708..33ea5d0 100644
--- a/responsepacket.c
+++ b/responsepacket.c
@@ -31,8 +31,15 @@
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <string.h>
+#include <inttypes.h>
 
+#ifndef __FreeBSD__
 #include <asm/byteorder.h>
+#else
+#include <sys/endian.h>
+#define __be64_to_cpu be64toh
+#define __cpu_to_be64 htobe64
+#endif
 
 #include "responsepacket.h"
 #include "vnsicommand.h"
@@ -120,7 +127,7 @@ bool cResponsePacket::initStatus(uint32_t opCode)
   return true;
 }
 
-bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts)
+bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial)
 {
   initBuffers();
 
@@ -139,6 +146,8 @@ bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t du
   memcpy(&buffer[16], &ull, sizeof(uint64_t));
   ull = __cpu_to_be64(dts);                    // DTS
   memcpy(&buffer[24], &ull, sizeof(uint64_t));
+  ul = htonl(serial);
+  memcpy(&buffer[32], &ul, sizeof(uint32_t));
   ul = 0;
   memcpy(&buffer[userDataLenPosStream], &ul, sizeof(uint32_t));
 
diff --git a/responsepacket.h b/responsepacket.h
index 936703c..0e0c9a0 100644
--- a/responsepacket.h
+++ b/responsepacket.h
@@ -40,7 +40,7 @@ public:
   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);
+  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 finalise();
   void finaliseStream();
@@ -72,8 +72,8 @@ private:
 
   const static uint32_t headerLength          = 12;
   const static uint32_t userDataLenPos        = 8;
-  const static uint32_t headerLengthStream    = 36;
-  const static uint32_t userDataLenPosStream  = 32;
+  const static uint32_t headerLengthStream    = 40;
+  const static uint32_t userDataLenPosStream  = 36;
   const static uint32_t headerLengthOSD       = 36;
   const static uint32_t userDataLenPosOSD     = 32;
 };
diff --git a/setup.c b/setup.c
index c49d300..61df5be 100644
--- a/setup.c
+++ b/setup.c
@@ -1,31 +1,51 @@
 /*
-* Copyright (C) 2005-2012 Team XBMC
-* http://www.xbmc.org
-*
-* This Program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2, or (at your option)
-* any later version.
-*
-* This Program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with XBMC; see the file COPYING. If not, see
-* <http://www.gnu.org/licenses/>.
-*
-*/
+ *      Copyright (C) 2005-2012 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
 
 #include "setup.h"
+#include "vnsicommand.h"
 
 int PmtTimeout = 5;
+int TimeshiftMode = 0;
+int TimeshiftBufferSize = 5;
+int TimeshiftBufferFileSize = 6;
+char TimeshiftBufferDir[PATH_MAX] = "\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");
+  newTimeshiftMode = TimeshiftMode;
+  Add(new cMenuEditStraItem( tr("Time Shift Mode"), &newTimeshiftMode, 3, timeshiftModesTexts));
+
+  newTimeshiftBufferSize = TimeshiftBufferSize;
+  Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-20) x 100MB"), &newTimeshiftBufferSize));
+
+  newTimeshiftBufferFileSize = TimeshiftBufferFileSize;
+  Add(new cMenuEditIntItem( tr("TS Buffersize (File) (1-10) x 1GB"), &newTimeshiftBufferFileSize));
+
+  strn0cpy(newTimeshiftBufferDir, TimeshiftBufferDir, sizeof(newTimeshiftBufferDir));
+  Add(new cMenuEditStrItem(tr("TS Buffer Directory"), newTimeshiftBufferDir, sizeof(newTimeshiftBufferDir)));
 }
 
 void cMenuSetupVNSI::Store(void)
@@ -33,4 +53,20 @@ void cMenuSetupVNSI::Store(void)
   if (newPmtTimeout > 10 || newPmtTimeout < 0)
     newPmtTimeout = 2;
   SetupStore(CONFNAME_PMTTIMEOUT, PmtTimeout = newPmtTimeout);
+
+  SetupStore(CONFNAME_TIMESHIFT, TimeshiftMode = newTimeshiftMode);
+
+  if (newTimeshiftBufferSize > 40)
+    newTimeshiftBufferSize = 40;
+  else if (newTimeshiftBufferSize < 1)
+    newTimeshiftBufferSize = 1;
+  SetupStore(CONFNAME_TIMESHIFTBUFFERSIZE, TimeshiftBufferSize = newTimeshiftBufferSize);
+
+  if (newTimeshiftBufferFileSize > 20)
+    newTimeshiftBufferFileSize = 20;
+  else if (newTimeshiftBufferFileSize < 1)
+    newTimeshiftBufferFileSize = 1;
+  SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize);
+
+  SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir)));
 }
diff --git a/setup.h b/setup.h
index 36d0f7c..acd584f 100644
--- a/setup.h
+++ b/setup.h
@@ -23,12 +23,15 @@
 
 #include <vdr/plugin.h>
 
-#define CONFNAME_PMTTIMEOUT "PmtTimeout"
-
 class cMenuSetupVNSI : public cMenuSetupPage
 {
 private:
   int newPmtTimeout;
+  int newTimeshiftMode;
+  const char *timeshiftModesTexts[3];
+  int newTimeshiftBufferSize;
+  int newTimeshiftBufferFileSize;
+  char newTimeshiftBufferDir[PATH_MAX];
 protected:
   virtual void Store(void);
 public:
diff --git a/streamer.c b/streamer.c
index 66ceba0..b9b81a3 100644
--- a/streamer.c
+++ b/streamer.c
@@ -40,8 +40,9 @@
 
 // --- cLiveStreamer -------------------------------------------------
 
-cLiveStreamer::cLiveStreamer(uint32_t timeout)
+cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
  : cThread("cLiveStreamer stream processor")
+ , m_ClientID(clientID)
  , m_scanTimeout(timeout)
 {
   m_Channel         = NULL;
@@ -52,6 +53,8 @@ cLiveStreamer::cLiveStreamer(uint32_t timeout)
   m_startup         = true;
   m_SignalLost      = false;
   m_IFrameSeen      = false;
+  m_VideoBuffer     = NULL;
+  m_Timeshift       = timeshift;
 
   memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
 
@@ -64,18 +67,98 @@ cLiveStreamer::~cLiveStreamer()
   DEBUGLOG("Started to delete live streamer");
 
   Cancel(5);
+  Close();
 
+  DEBUGLOG("Finished to delete live streamer");
+}
+
+bool cLiveStreamer::Open(int serial)
+{
+  Close();
+
+#if APIVERSNUM >= 10725
+  m_Device = cDevice::GetDevice(m_Channel, m_Priority, true, true);
+#else
+  m_Device = cDevice::GetDevice(m_Channel, m_Priority, true);
+#endif
+
+  if (!m_Device)
+    return false;
+
+  bool recording = false;
+  if (0) // test harness
+  {
+    recording = true;
+    m_VideoBuffer = cVideoBuffer::Create("/home/xbmc/test.ts");
+  }
+  else if (serial == -1)
+  {
+    for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer))
+    {
+      if (timer &&
+          timer->Recording() &&
+          timer->Channel() == m_Channel)
+      {
+        Recordings.Load();
+        cRecording matchRec(timer, timer->Event());
+        cRecording *rec;
+        {
+          cThreadLock RecordingsLock(&Recordings);
+          rec = Recordings.GetByName(matchRec.FileName());
+          if (!rec)
+          {
+            return false;
+          }
+        }
+        m_VideoBuffer = cVideoBuffer::Create(rec);
+        recording = true;
+        break;
+      }
+    }
+  }
+  if (!recording)
+  {
+    m_VideoBuffer = cVideoBuffer::Create(m_ClientID, m_Timeshift);
+  }
+
+  if (!m_VideoBuffer)
+    return false;
+
+  if (!recording)
+  {
+    if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
+      m_IsMPEGPS = true;
+
+    if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer))
+    {
+      ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+      return false;
+    }
+  }
+
+  m_Demuxer.Open(*m_Channel, m_VideoBuffer);
+  if (serial >= 0)
+    m_Demuxer.SetSerial(serial);
+
+  return true;
+}
+
+void cLiveStreamer::Close(void)
+{
+  INFOLOG("LiveStreamer::Close - close");
   m_VideoInput.Close();
   m_Demuxer.Close();
-  delete m_VideoBuffer;
+  if (m_VideoBuffer)
+  {
+    delete m_VideoBuffer;
+    m_VideoBuffer = NULL;
+  }
 
   if (m_Frontend >= 0)
   {
     close(m_Frontend);
     m_Frontend = -1;
   }
-
-  DEBUGLOG("Finished to delete live streamer");
 }
 
 void cLiveStreamer::Action(void)
@@ -84,6 +167,7 @@ void cLiveStreamer::Action(void)
   sStreamPacket pkt;
   bool requestStreamChange = false;
   cTimeMs last_info(1000);
+  cTimeMs bufferStatsTimer(1000);
 
   while (Running())
   {
@@ -108,18 +192,33 @@ void cLiveStreamer::Action(void)
         last_info.Set(0);
         sendSignalInfo();
       }
+
+      // send buffer stats
+      if(bufferStatsTimer.TimedOut())
+      {
+        sendBufferStatus();
+        bufferStatsTimer.Set(1000);
+      }
     }
-    else
+    else if (ret == -1)
     {
       // no data
+      usleep(10000);
       if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
       {
-        INFOLOG("No Signal");
         sendStreamStatus();
         m_last_tick.Set(0);
         m_SignalLost = true;
       }
     }
+    else if (ret == -2)
+    {
+      if (!Open(m_Demuxer.GetSerial()))
+      {
+        m_Socket->Shutdown();
+        break;
+      }
+    }
   }
   INFOLOG("exit streamer thread");
 }
@@ -132,32 +231,22 @@ bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocke
     return false;
   }
 
-  m_VideoBuffer = cVideoBuffer::Create();
   m_Channel   = channel;
+  m_Priority  = priority;
   m_Socket    = Socket;
-  m_Device = cDevice::GetDevice(m_Channel, priority, true);
-
-  if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true;
 
-  if (m_VideoInput.Open(channel, priority, m_VideoBuffer))
-  {
-    m_Demuxer.Open(*m_Channel, m_VideoBuffer);
+  if (!Open())
+    return false;
 
-    // Send the OK response here, that it is before the Stream end message
-    resp->add_U32(VNSI_RET_OK);
-    resp->finalise();
-    m_Socket->write(resp->getPtr(), resp->getLen());
+  // Send the OK response here, that it is before the Stream end message
+  resp->add_U32(VNSI_RET_OK);
+  resp->finalise();
+  m_Socket->write(resp->getPtr(), resp->getLen());
 
-    Activate(true);
+  Activate(true);
 
-    INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
-    return true;
-  }
-  else
-  {
-    ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
-  }
-  return false;
+  INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+  return true;
 }
 
 inline void cLiveStreamer::Activate(bool On)
@@ -182,7 +271,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))
+  if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts, pkt->serial))
   {
     ERRORLOG("stream response packet init fail");
     return;
@@ -202,7 +291,7 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
 void cLiveStreamer::sendStreamChange()
 {
   cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0))
+  if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0, 0))
   {
     ERRORLOG("stream response packet init fail");
     delete resp;
@@ -329,7 +418,7 @@ void cLiveStreamer::sendSignalInfo()
   if (m_Frontend == -2)
   {
     cResponsePacket *resp = new cResponsePacket();
-    if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+    if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
     {
       ERRORLOG("stream response packet init fail");
       delete resp;
@@ -377,7 +466,7 @@ void cLiveStreamer::sendSignalInfo()
     if (m_Frontend >= 0)
     {
       cResponsePacket *resp = new cResponsePacket();
-      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
       {
         ERRORLOG("stream response packet init fail");
         delete resp;
@@ -417,7 +506,7 @@ void cLiveStreamer::sendSignalInfo()
     if (m_Frontend >= 0)
     {
       cResponsePacket *resp = new cResponsePacket();
-      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+      if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
       {
         ERRORLOG("stream response packet init fail");
         delete resp;
@@ -470,14 +559,63 @@ void cLiveStreamer::sendSignalInfo()
 void cLiveStreamer::sendStreamStatus()
 {
   cResponsePacket *resp = new cResponsePacket();
-  if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0))
+  if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0, 0))
+  {
+    ERRORLOG("stream response packet init fail");
+    delete resp;
+    return;
+  }
+  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));
+  }
+  else if (error & ERROR_PES_STARTCODE)
+  {
+    INFOLOG("Channel: startcode %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"));
+  }
+  else
+  {
+    INFOLOG("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;
+}
+
+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;
   }
-  resp->add_String("No Signal");
+  int32_t start, current, end;
+  bool timeshift;
+  m_Demuxer.BufferStatus(timeshift, start, current, end);
+  resp->add_U8(timeshift);
+  resp->add_S32(start);
+  resp->add_S32(current);
+  resp->add_S32(end);
   resp->finaliseStream();
   m_Socket->write(resp->getPtr(), resp->getLen());
   delete resp;
 }
+
+bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial)
+{
+  bool ret = m_Demuxer.SeekTime(time);
+  serial = m_Demuxer.GetSerial();
+  return ret;
+}
diff --git a/streamer.h b/streamer.h
index afcd7c6..3c308e5 100644
--- a/streamer.h
+++ b/streamer.h
@@ -57,7 +57,9 @@ private:
   void sendStreamChange();
   void sendSignalInfo();
   void sendStreamStatus();
+  void sendBufferStatus();
 
+  int               m_ClientID;
   const cChannel   *m_Channel;                      /*!> Channel to stream */
   cDevice          *m_Device;
   cxSocket         *m_Socket;                       /*!> The socket class to communicate with client */
@@ -76,12 +78,16 @@ private:
   cVNSIDemuxer      m_Demuxer;
   cVideoBuffer     *m_VideoBuffer;
   cVideoInput       m_VideoInput;
+  int               m_Priority;
+  uint8_t           m_Timeshift;
 
 protected:
   virtual void Action(void);
+  bool Open(int serial = -1);
+  void Close();
 
 public:
-  cLiveStreamer(uint32_t timeout = 0);
+  cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout = 0);
   virtual ~cLiveStreamer();
 
   void Activate(bool On);
@@ -90,6 +96,7 @@ public:
   bool IsStarting() { return m_startup; }
   bool IsAudioOnly() { return m_IsAudioOnly; }
   bool IsMPEGPS() { return m_IsMPEGPS; }
+  bool SeekTime(int64_t time, uint32_t &serial);
 };
 
 #endif  // VNSI_RECEIVER_H
diff --git a/videobuffer.c b/videobuffer.c
index 1ee9677..92c38f0 100644
--- a/videobuffer.c
+++ b/videobuffer.c
@@ -19,16 +19,25 @@
  */
 
 #include "videobuffer.h"
+#include "config.h"
+#include "vnsi.h"
+#include "recplayer.h"
 
 #include <vdr/ringbuffer.h>
 #include <vdr/remux.h>
+#include <vdr/videodir.h>
+#include <vdr/recording.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 class cVideoBufferSimple : public cVideoBuffer
 {
 friend class cVideoBuffer;
 public:
-  virtual void Put(uint8_t *buf, int size);
-  virtual int Read(uint8_t **buf, int size);
+  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size);
 
 protected:
   cVideoBufferSimple();
@@ -50,12 +59,12 @@ cVideoBufferSimple::~cVideoBufferSimple()
     delete m_Buffer;
 }
 
-void cVideoBufferSimple::Put(uint8_t *buf, int size)
+void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size)
 {
   m_Buffer->Put(buf, size);
 }
 
-int cVideoBufferSimple::Read(uint8_t **buf, int size)
+int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
 {
   int  readBytes;
   if (m_BytesConsumed)
@@ -92,16 +101,827 @@ int cVideoBufferSimple::Read(uint8_t **buf, int size)
 
 //-----------------------------------------------------------------------------
 
+#define MARGIN 40000
+
+class cVideoBufferTimeshift : public cVideoBuffer
+{
+friend class cVideoBuffer;
+public:
+  virtual off_t GetPosMin();
+  virtual off_t GetPosMax();
+  virtual off_t GetPosCur();
+  virtual void GetPositions(off_t *cur, off_t *min, off_t *max);
+  virtual bool HasBuffer() { return true; };
+
+protected:
+  cVideoBufferTimeshift();
+  virtual bool Init() = 0;
+  virtual off_t Available();
+  off_t m_BufferSize;
+  off_t m_WritePtr;
+  off_t m_ReadPtr;
+  bool m_BufferFull;
+  unsigned int m_Margin;
+  unsigned int m_BytesConsumed;
+  cMutex m_Mutex;
+};
+
+cVideoBufferTimeshift::cVideoBufferTimeshift()
+{
+  m_Margin = TS_SIZE*2;
+  m_BufferFull = false;
+  m_ReadPtr = 0;
+  m_WritePtr = 0;
+  m_BytesConsumed = 0;
+}
+
+off_t cVideoBufferTimeshift::GetPosMin()
+{
+  off_t ret;
+  if (!m_BufferFull)
+    return 0;
+
+  ret = m_WritePtr + MARGIN * 2;
+  if (ret >= m_BufferSize)
+    ret -= m_BufferSize;
+
+  return ret;
+}
+
+off_t cVideoBufferTimeshift::GetPosMax()
+{
+   off_t ret = m_WritePtr;
+   if (ret < GetPosMin())
+     ret += m_BufferSize;
+   return ret;
+}
+
+off_t cVideoBufferTimeshift::GetPosCur()
+{
+  off_t ret = m_ReadPtr;
+  if (ret < GetPosMin())
+    ret += m_BufferSize;
+  return ret;
+}
+
+void cVideoBufferTimeshift::GetPositions(off_t *cur, off_t *min, off_t *max)
+{
+  cMutexLock lock(&m_Mutex);
+
+  *cur = GetPosCur();
+  *min = GetPosMin();
+  *min = (*min > *cur) ? *cur : *min;
+  *max = GetPosMax();
+}
+
+off_t cVideoBufferTimeshift::Available()
+{
+  cMutexLock lock(&m_Mutex);
+
+  off_t ret;
+  if (m_ReadPtr <= m_WritePtr)
+    ret = m_WritePtr - m_ReadPtr;
+  else
+    ret = m_BufferSize - (m_ReadPtr - m_WritePtr);
+
+  return ret;
+}
+//-----------------------------------------------------------------------------
+
+class cVideoBufferRAM : public cVideoBufferTimeshift
+{
+friend class cVideoBuffer;
+public:
+  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual void SetPos(off_t pos);
+
+protected:
+  cVideoBufferRAM();
+  virtual ~cVideoBufferRAM();
+  virtual bool Init();
+  uint8_t *m_Buffer;
+  uint8_t *m_BufferPtr;
+};
+
+cVideoBufferRAM::cVideoBufferRAM()
+{
+  m_Buffer = 0;
+}
+
+cVideoBufferRAM::~cVideoBufferRAM()
+{
+  if (m_Buffer)
+    free(m_Buffer);
+}
+
+bool cVideoBufferRAM::Init()
+{
+  m_BufferSize = (off_t)TimeshiftBufferSize*100*1000*1000;
+  INFOLOG("allocated timeshift buffer with size: %ld", m_BufferSize);
+  m_Buffer = (uint8_t*)malloc(m_BufferSize + m_Margin);
+  m_BufferPtr = m_Buffer + m_Margin;
+  if (!m_Buffer)
+    return false;
+  else
+    return true;
+}
+
+void cVideoBufferRAM::SetPos(off_t pos)
+{
+  cMutexLock lock(&m_Mutex);
+
+  m_ReadPtr = pos;
+  if (m_ReadPtr >= m_BufferSize)
+    m_ReadPtr -= m_BufferSize;
+  m_BytesConsumed = 0;
+}
+
+void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
+{
+  if (Available() + MARGIN >= m_BufferSize)
+  {
+    ERRORLOG("------------- skipping data");
+    return;
+  }
+
+  if ((m_BufferSize - m_WritePtr) <= size)
+  {
+    int bytes = m_BufferSize - m_WritePtr;
+    memcpy(m_BufferPtr+m_WritePtr, buf, bytes);
+    size -= bytes;
+    buf += bytes;
+    cMutexLock lock(&m_Mutex);
+    m_WritePtr = 0;
+  }
+
+  memcpy(m_BufferPtr+m_WritePtr, buf, size);
+
+  cMutexLock lock(&m_Mutex);
+
+  m_WritePtr += size;
+  if (!m_BufferFull)
+  {
+    if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+      m_BufferFull = true;
+  }
+}
+
+int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
+{
+  // move read pointer
+  if (m_BytesConsumed)
+  {
+    cMutexLock lock(&m_Mutex);
+    m_ReadPtr += m_BytesConsumed;
+    if (m_ReadPtr >= m_BufferSize)
+      m_ReadPtr -= m_BufferSize;
+  }
+  m_BytesConsumed = 0;
+
+  // check if we have anything to read
+  off_t readBytes = Available();
+  if (readBytes < m_Margin)
+  {
+    return 0;
+  }
+
+  // if we are close to end, copy margin to front
+  if (m_ReadPtr > (m_BufferSize - m_Margin))
+  {
+    int bytesToCopy = m_BufferSize - m_ReadPtr;
+    memmove(m_Buffer + (m_Margin - bytesToCopy), m_Buffer + m_ReadPtr, bytesToCopy);
+    *buf = m_Buffer + (m_Margin - bytesToCopy);
+  }
+  else
+    *buf = m_BufferPtr + m_ReadPtr;
+
+  // Make sure we are looking at a TS packet
+  while (readBytes > TS_SIZE)
+  {
+    if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+      break;
+    m_BytesConsumed++;
+    (*buf)++;
+    readBytes--;
+  }
+
+  if ((*buf)[0] != TS_SYNC_BYTE)
+  {
+    return 0;
+  }
+
+  m_BytesConsumed += TS_SIZE;
+  return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferFile : public cVideoBufferTimeshift
+{
+friend class cVideoBuffer;
+public:
+  virtual off_t GetPosMax();
+  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+  virtual void SetPos(off_t pos);
+
+protected:
+  cVideoBufferFile();
+  cVideoBufferFile(int clientID);
+  virtual ~cVideoBufferFile();
+  virtual bool Init();
+  virtual int ReadBytes(uint8_t *buf, off_t pos, unsigned int size);
+  int m_ClientID;
+  cString m_Filename;
+  int m_Fd;
+  uint8_t *m_ReadCache;
+  unsigned int m_ReadCachePtr;
+  unsigned int m_ReadCacheSize;
+  unsigned int m_ReadCacheMaxSize;
+};
+
+cVideoBufferFile::cVideoBufferFile()
+{
+
+}
+
+cVideoBufferFile::cVideoBufferFile(int clientID)
+{
+  m_ClientID = clientID;
+  m_Fd = 0;
+  m_ReadCacheSize = 0;
+  m_ReadCache = 0;
+}
+
+cVideoBufferFile::~cVideoBufferFile()
+{
+  if (m_Fd)
+  {
+    close(m_Fd);
+    unlink(m_Filename);
+    m_Fd = 0;
+  }
+  if (m_ReadCache)
+    free(m_ReadCache);
+}
+
+bool cVideoBufferFile::Init()
+{
+  m_ReadCache = 0;
+  m_ReadCacheMaxSize = 32000;
+
+  m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+  if (!m_ReadCache)
+    return false;
+
+  m_BufferSize = (off_t)TimeshiftBufferFileSize*1000*1000*1000;
+
+  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);
+  }
+  else
+#if VDRVERSNUM >= 20102
+    m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", cVideoDirectory::Name(), m_ClientID);
+#else
+    m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", VideoDirectory, m_ClientID);
+#endif
+
+  m_Fd = open(m_Filename, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+  if (m_Fd == -1)
+  {
+    ERRORLOG("Could not open file: %s", (const char*)m_Filename);
+    return false;
+  }
+  m_WritePtr = lseek(m_Fd, m_BufferSize - 1, SEEK_SET);
+  if (m_WritePtr == -1)
+  {
+    ERRORLOG("(Init) Could not seek file: %s", (const char*)m_Filename);
+    return false;
+  }
+  char tmp = '0';
+  if (safe_write(m_Fd, &tmp, 1) < 0)
+  {
+    ERRORLOG("(Init) Could not write to file: %s", (const char*)m_Filename);
+    return false;
+  }
+
+  m_WritePtr = 0;
+  m_ReadPtr = 0;
+  m_ReadCacheSize = 0;
+  return true;
+}
+
+void cVideoBufferFile::SetPos(off_t pos)
+{
+  cMutexLock lock(&m_Mutex);
+
+  m_ReadPtr = pos;
+  if (m_ReadPtr >= m_BufferSize)
+    m_ReadPtr -= m_BufferSize;
+  m_BytesConsumed = 0;
+  m_ReadCacheSize = 0;
+}
+
+off_t cVideoBufferFile::GetPosMax()
+{
+  off_t posMax = cVideoBufferTimeshift::GetPosMax();
+  if (posMax >= m_ReadCacheMaxSize)
+    posMax -= m_ReadCacheMaxSize;
+  else
+    posMax = 0;
+  return posMax;
+}
+
+void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
+{
+  if (Available() + MARGIN >= m_BufferSize)
+  {
+    ERRORLOG("------------- skipping data");
+    return;
+  }
+
+  if ((m_BufferSize - m_WritePtr) <= size)
+  {
+    int bytes = m_BufferSize - m_WritePtr;
+
+    int p = 0;
+    off_t ptr = m_WritePtr;
+    while(bytes > 0)
+    {
+      p = pwrite(m_Fd, buf, bytes, ptr);
+      if (p < 0)
+      {
+        ERRORLOG("Could not write to file: %s", (const char*)m_Filename);
+        return;
+      }
+      size -= p;
+      bytes -= p;
+      buf += p;
+      ptr += p;
+    }
+    cMutexLock lock(&m_Mutex);
+    m_WritePtr = 0;
+  }
+
+  off_t ptr = m_WritePtr;
+  int bytes = size;
+  int p;
+  while(bytes > 0)
+  {
+    p = pwrite(m_Fd, buf, bytes, ptr);
+    if (p < 0)
+    {
+      ERRORLOG("Could not write to file: %s", (const char*)m_Filename);
+      return;
+    }
+    bytes -= p;
+    buf += p;
+    ptr += p;
+  }
+
+  cMutexLock lock(&m_Mutex);
+
+  m_WritePtr += size;
+  if (!m_BufferFull)
+  {
+    if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+      m_BufferFull = true;
+  }
+}
+
+int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
+{
+  int p;
+  for (;;)
+  {
+    p = pread(m_Fd, buf, size, pos);
+    if (p < 0 && errno == EINTR)
+    {
+      continue;
+    }
+    return p;
+  }
+}
+
+int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
+{
+  // move read pointer
+  if (m_BytesConsumed)
+  {
+    cMutexLock lock(&m_Mutex);
+    m_ReadPtr += m_BytesConsumed;
+    if (m_ReadPtr >= m_BufferSize)
+      m_ReadPtr -= m_BufferSize;
+    m_ReadCachePtr += m_BytesConsumed;
+  }
+  m_BytesConsumed = 0;
+
+  // check if we have anything to read
+  off_t readBytes;
+  if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize))
+  {
+    readBytes = m_ReadCacheSize - m_ReadCachePtr;
+    *buf = m_ReadCache + m_ReadCachePtr;
+  }
+  else if ((readBytes = Available()) >= m_ReadCacheMaxSize)
+  {
+    if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize)
+    {
+      m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize);
+      if (m_ReadCacheSize < 0)
+      {
+        ERRORLOG("Could not read file: %s", (const char*)m_Filename);
+        return 0;
+      }
+      if (m_ReadCacheSize < m_Margin)
+      {
+        ERRORLOG("Could not read file (margin): %s , read: %d", (const char*)m_Filename, m_ReadCacheSize);
+        m_ReadCacheSize = 0;
+        return 0;
+      }
+      readBytes = m_ReadCacheSize;
+      *buf = m_ReadCache;
+      m_ReadCachePtr = 0;
+    }
+    else
+    {
+      m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_BufferSize - m_ReadPtr);
+      if ((m_ReadCacheSize < m_Margin) && (m_ReadCacheSize != (m_BufferSize - m_ReadPtr)))
+      {
+        ERRORLOG("Could not read file (end): %s", (const char*)m_Filename);
+        m_ReadCacheSize = 0;
+        return 0;
+      }
+      readBytes = ReadBytes(m_ReadCache + m_ReadCacheSize, 0, m_ReadCacheMaxSize - m_ReadCacheSize);
+      if (readBytes < 0)
+      {
+        ERRORLOG("Could not read file (end): %s", (const char*)m_Filename);
+        m_ReadCacheSize = 0;
+        return 0;
+      }
+      m_ReadCacheSize += readBytes;
+      if (m_ReadCacheSize < m_Margin)
+      {
+        ERRORLOG("Could not read file (margin): %s", (const char*)m_Filename);
+        m_ReadCacheSize = 0;
+        return 0;
+      }
+      readBytes = m_ReadCacheSize;
+      *buf = m_ReadCache;
+      m_ReadCachePtr = 0;
+    }
+  }
+  else
+    return 0;
+
+  // Make sure we are looking at a TS packet
+  while (readBytes > TS_SIZE)
+  {
+    if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+      break;
+    m_BytesConsumed++;
+    (*buf)++;
+    readBytes--;
+  }
+
+  if ((*buf)[0] != TS_SYNC_BYTE)
+  {
+    return 0;
+  }
+
+  m_BytesConsumed += TS_SIZE;
+  return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferRecording : public cVideoBufferFile
+{
+friend class cVideoBuffer;
+public:
+  virtual off_t GetPosMax();
+  virtual void Put(uint8_t *buf, unsigned int size);
+  virtual int ReadBlock(uint8_t **buf, unsigned int size);
+
+protected:
+  cVideoBufferRecording(cRecording *rec);
+  virtual ~cVideoBufferRecording();
+  virtual bool Init();
+  virtual off_t Available();
+  off_t GetPosEnd();
+  cRecPlayer *m_RecPlayer;
+  cRecording *m_Recording;
+  cTimeMs m_ScanTimer;
+};
+
+cVideoBufferRecording::cVideoBufferRecording(cRecording *rec)
+{
+  m_Recording = rec;
+  m_ReadCacheSize = 0;
+  m_ReadCache = 0;
+}
+
+cVideoBufferRecording::~cVideoBufferRecording()
+{
+  INFOLOG("delete cVideoBufferRecording");
+  if (m_RecPlayer)
+    delete m_RecPlayer;
+}
+
+off_t cVideoBufferRecording::GetPosMax()
+{
+  m_RecPlayer->reScan();
+  m_WritePtr = m_RecPlayer->getLengthBytes();
+  return cVideoBufferFile::GetPosMax();
+}
+
+void cVideoBufferRecording::Put(uint8_t *buf, unsigned int size)
+{
+
+}
+
+bool cVideoBufferRecording::Init()
+{
+  m_ReadCacheMaxSize = 32000;
+
+  m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+  if (!m_ReadCache)
+    return false;
+
+  m_RecPlayer = new cRecPlayer(m_Recording, true);
+  if (!m_RecPlayer)
+    return false;
+
+  m_WritePtr = 0;
+  m_ReadPtr = 0;
+  m_ReadCacheSize = 0;
+  m_InputAttached = false;
+  m_ScanTimer.Set(0);
+  return true;
+}
+
+off_t cVideoBufferRecording::Available()
+{
+  if (m_ScanTimer.TimedOut())
+  {
+    m_RecPlayer->reScan();
+    m_ScanTimer.Set(1000);
+  }
+  m_BufferSize = m_WritePtr = m_RecPlayer->getLengthBytes();
+  return cVideoBufferTimeshift::Available();
+}
+
+int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
+{
+  // move read pointer
+  if (m_BytesConsumed)
+  {
+    m_ReadPtr += m_BytesConsumed;
+    if (m_ReadPtr >= m_BufferSize)
+    {
+      m_ReadPtr -= m_BufferSize;
+      ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error");
+    }
+    m_ReadCachePtr += m_BytesConsumed;
+  }
+  m_BytesConsumed = 0;
+
+  // check if we have anything to read
+  off_t readBytes;
+  if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize))
+  {
+    readBytes = m_ReadCacheSize - m_ReadCachePtr;
+    *buf = m_ReadCache + m_ReadCachePtr;
+  }
+  else if ((readBytes = Available()) >= m_ReadCacheMaxSize)
+  {
+    if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize)
+    {
+      m_ReadCacheSize = m_RecPlayer->getBlock(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize);
+      if (m_ReadCacheSize < 0)
+      {
+        ERRORLOG("Could not read file, size:  %d", m_ReadCacheSize);
+        m_ReadCacheSize = 0;
+        return 0;
+      }
+      readBytes = m_ReadCacheSize;
+      *buf = m_ReadCache;
+      m_ReadCachePtr = 0;
+    }
+    else
+    {
+      ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error");
+      return 0;
+    }
+  }
+  else
+    return 0;
+
+  // Make sure we are looking at a TS packet
+  while (readBytes > TS_SIZE)
+  {
+    if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+      break;
+    m_BytesConsumed++;
+    (*buf)++;
+    readBytes--;
+  }
+
+  if ((*buf)[0] != TS_SYNC_BYTE)
+  {
+    return 0;
+  }
+
+  m_BytesConsumed += TS_SIZE;
+  return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferTest : public cVideoBufferFile
+{
+friend class cVideoBuffer;
+public:
+  virtual off_t GetPosMax();
+  virtual void Put(uint8_t *buf, unsigned int size);
+
+protected:
+  cVideoBufferTest(cString filename);
+  virtual ~cVideoBufferTest();
+  virtual bool Init();
+  virtual off_t Available();
+  off_t GetPosEnd();
+};
+
+cVideoBufferTest::cVideoBufferTest(cString filename)
+{
+  m_Filename = filename;
+  m_Fd = 0;
+  m_ReadCacheSize = 0;
+}
+
+cVideoBufferTest::~cVideoBufferTest()
+{
+  if (m_Fd)
+  {
+    close(m_Fd);
+    m_Fd = 0;
+  }
+}
+
+off_t cVideoBufferTest::GetPosMax()
+{
+  m_WritePtr = GetPosEnd();
+  return cVideoBufferTimeshift::GetPosMax();
+}
+
+off_t cVideoBufferTest::GetPosEnd()
+{
+  off_t cur = lseek(m_Fd, 0, SEEK_CUR);
+  off_t end = lseek(m_Fd, 0, SEEK_END);
+  lseek(m_Fd, cur, SEEK_SET);
+  return end;
+}
+
+void cVideoBufferTest::Put(uint8_t *buf, unsigned int size)
+{
+
+}
+
+bool cVideoBufferTest::Init()
+{
+  m_ReadCache = 0;
+  m_ReadCacheMaxSize = 8000;
+
+  m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+  if (!m_ReadCache)
+    return false;
+
+  m_Fd = open(m_Filename, O_RDONLY);
+  if (m_Fd == -1)
+  {
+    ERRORLOG("Could not open file: %s", (const char*)m_Filename);
+    return false;
+  }
+
+  m_WritePtr = 0;
+  m_ReadPtr = 0;
+  m_ReadCacheSize = 0;
+  m_InputAttached = false;
+  return true;
+}
+
+off_t cVideoBufferTest::Available()
+{
+  m_BufferSize = m_WritePtr = GetPosEnd();
+  return cVideoBufferTimeshift::Available();
+}
+
+//-----------------------------------------------------------------------------
+
 cVideoBuffer::cVideoBuffer()
 {
+  m_CheckEof = false;
+  m_InputAttached = true;
 }
 
 cVideoBuffer::~cVideoBuffer()
 {
 }
 
-cVideoBuffer* cVideoBuffer::Create()
+cVideoBuffer* cVideoBuffer::Create(int clientID, uint8_t timeshift)
+{
+  // no time shift
+  if (TimeshiftMode == 0 || timeshift == 0)
+  {
+    cVideoBufferSimple *buffer = new cVideoBufferSimple();
+    return buffer;
+  }
+  // buffer in ram
+  else if (TimeshiftMode == 1)
+  {
+    cVideoBufferRAM *buffer = new cVideoBufferRAM();
+    if (!buffer->Init())
+    {
+      delete buffer;
+      return NULL;
+    }
+    else
+      return buffer;
+  }
+  // buffer in file
+  else if (TimeshiftMode == 2)
+  {
+    cVideoBufferFile *buffer = new cVideoBufferFile(clientID);
+    if (!buffer->Init())
+    {
+      delete buffer;
+      return NULL;
+    }
+    else
+      return buffer;
+  }
+  else
+    return NULL;
+}
+
+cVideoBuffer* cVideoBuffer::Create(cString filename)
+{
+  INFOLOG("Open recording: %s", (const char*)filename);
+  cVideoBufferTest *buffer = new cVideoBufferTest(filename);
+  if (!buffer->Init())
+  {
+    delete buffer;
+    return NULL;
+  }
+  else
+    return buffer;
+}
+
+cVideoBuffer* cVideoBuffer::Create(cRecording *rec)
+{
+  INFOLOG("Open recording: %s", rec->FileName());
+  cVideoBufferRecording *buffer = new cVideoBufferRecording(rec);
+  if (!buffer->Init())
+  {
+    delete buffer;
+    return NULL;
+  }
+  else
+    return buffer;
+}
+
+int cVideoBuffer::Read(uint8_t **buf, unsigned int size)
+{
+  int count = ReadBlock(buf, size);
+
+  // check for end of file
+  if (!m_InputAttached && count != TS_SIZE)
+  {
+    if (m_CheckEof && m_Timer.TimedOut())
+    {
+      INFOLOG("Recoding - end of file");
+      return -2;
+    }
+    else if (!m_CheckEof)
+    {
+      m_CheckEof = true;
+      m_Timer.Set(3000);
+    }
+  }
+  else
+    m_CheckEof = false;
+
+  return count;
+}
+
+void cVideoBuffer::AttachInput(bool attach)
 {
-  cVideoBuffer *buffer = new cVideoBufferSimple();
-  return buffer;
+  m_InputAttached = attach;
 }
diff --git a/videobuffer.h b/videobuffer.h
index 881c3fc..8ea74fc 100644
--- a/videobuffer.h
+++ b/videobuffer.h
@@ -21,14 +21,32 @@
 #pragma once
 
 #include <stdint.h>
+#include <stdlib.h>
+#include <vdr/tools.h>
+
+class cRecording;
 
 class cVideoBuffer
 {
 public:
   virtual ~cVideoBuffer();
-  static cVideoBuffer* Create();
-  virtual void Put(uint8_t *buf, int size) = 0;
-  virtual int Read(uint8_t **buf, int size) = 0;
+  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;
+  virtual int ReadBlock(uint8_t **buf, unsigned int size) = 0;
+  virtual off_t GetPosMin() { return 0; };
+  virtual off_t GetPosMax() { return 0; };
+  virtual off_t GetPosCur() { return 0; };
+  virtual void GetPositions(off_t *cur, off_t *min, off_t *max) {};
+  virtual void SetPos(off_t pos) {};
+  virtual void SetCache(bool on) {};
+  virtual bool HasBuffer() { return false; };
+  int Read(uint8_t **buf, unsigned int size);
+  void AttachInput(bool attach);
 protected:
   cVideoBuffer();
+  cTimeMs m_Timer;
+  bool m_CheckEof;
+  bool m_InputAttached;
 };
diff --git a/videoinput.c b/videoinput.c
index 7646454..45769ae 100644
--- a/videoinput.c
+++ b/videoinput.c
@@ -67,6 +67,7 @@ void cLiveReceiver::Receive(uchar *Data, int Length)
 
 inline void cLiveReceiver::Activate(bool On)
 {
+  m_VideoInput->Attach(On);
   DEBUGLOG("activate live receiver: %d", On);
 }
 
@@ -520,6 +521,11 @@ inline void cVideoInput::Receive(uchar *data, int length)
   m_VideoBuffer->Put(data, length);
 }
 
+inline void cVideoInput::Attach(bool on)
+{
+  m_VideoBuffer->AttachInput(on);
+}
+
 void cVideoInput::Action()
 {
   cTimeMs starttime;
diff --git a/videoinput.h b/videoinput.h
index a78cf10..19a1d7d 100644
--- a/videoinput.h
+++ b/videoinput.h
@@ -43,6 +43,7 @@ protected:
   void PmtChange(int pidChange);
   cChannel *PmtChannel();
   void Receive(uchar *data, int length);
+  void Attach(bool on);
   cDevice          *m_Device;
   cLivePatFilter   *m_PatFilter;
   cLiveReceiver    *m_Receiver;
diff --git a/vnsi.c b/vnsi.c
index 1a62f61..6ae4cf5 100644
--- a/vnsi.c
+++ b/vnsi.c
@@ -26,11 +26,15 @@
 #include <getopt.h>
 #include <vdr/plugin.h>
 #include "vnsi.h"
+#include "vnsicommand.h"
 #include "setup.h"
 
+cPluginVNSIServer* cPluginVNSIServer::VNSIServer = NULL;
+
 cPluginVNSIServer::cPluginVNSIServer(void)
 {
   Server = NULL;
+  VNSIServer = NULL;
 }
 
 cPluginVNSIServer::~cPluginVNSIServer()
@@ -67,6 +71,8 @@ bool cPluginVNSIServer::Initialize(void)
 {
   // Initialize any background activities the plugin shall perform.
   VNSIServerConfig.ConfigDirectory = ConfigDirectory(PLUGIN_NAME_I18N);
+
+  VNSIServer = this;
   return true;
 }
 
@@ -117,6 +123,14 @@ bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
   // Parse your own setup parameters and store their values.
   if (!strcasecmp(Name, CONFNAME_PMTTIMEOUT))
     PmtTimeout = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_TIMESHIFT))
+    TimeshiftMode = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERSIZE))
+    TimeshiftBufferSize = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+    TimeshiftBufferFileSize = atoi(Value);
+  else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR))
+    strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir));
   else
     return false;
   return true;
@@ -140,4 +154,16 @@ cString cPluginVNSIServer::SVDRPCommand(const char *Command, const char *Option,
   return NULL;
 }
 
+void cPluginVNSIServer::StoreSetup(const char *Name, int Value)
+{
+  if (VNSIServer)
+  {
+    if (VNSIServer->SetupParse(Name, itoa(Value)))
+    {
+      VNSIServer->SetupStore(Name, Value);
+      Setup.Save();
+    }
+  }
+}
+
 VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
diff --git a/vnsi.h b/vnsi.h
index 94461a4..eb7ed79 100644
--- a/vnsi.h
+++ b/vnsi.h
@@ -27,14 +27,19 @@
 #include <vdr/plugin.h>
 #include "vnsiserver.h"
 
-static const char *VERSION        = "0.9.1";
+static const char *VERSION        = "0.9.2";
 static const char *DESCRIPTION    = "VDR-Network-Streaming-Interface (VNSI) Server";
 
 extern int PmtTimeout;
+extern int TimeshiftMode;
+extern int TimeshiftBufferSize;
+extern int TimeshiftBufferFileSize;
+extern char TimeshiftBufferDir[PATH_MAX];
 
 class cPluginVNSIServer : public cPlugin {
 private:
   cVNSIServer *Server;
+  static cPluginVNSIServer *VNSIServer;
 
 public:
   cPluginVNSIServer(void);
@@ -57,5 +62,7 @@ public:
   virtual bool Service(const char *Id, void *Data = NULL);
   virtual const char **SVDRPHelpPages(void);
   virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+
+  static void StoreSetup(const char *Name, int Value);
 };
 
diff --git a/vnsiclient.c b/vnsiclient.c
index b351ef0..f88d00b 100644
--- a/vnsiclient.c
+++ b/vnsiclient.c
@@ -35,6 +35,7 @@
 #include <vdr/menu.h>
 #include <vdr/device.h>
 
+#include "vnsi.h"
 #include "config.h"
 #include "vnsicommand.h"
 #include "recordingscache.h"
@@ -176,10 +177,10 @@ void cVNSIClient::Action(void)
   }
 }
 
-bool cVNSIClient::StartChannelStreaming(const cChannel *channel, uint32_t timeout)
+bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
 {
-  m_Streamer    = new cLiveStreamer(timeout);
-  m_isStreaming = m_Streamer->StreamChannel(channel, 50, &m_socket, m_resp);
+  m_Streamer    = new cLiveStreamer(m_Id, timeshift, timeout);
+  m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp);
   return m_isStreaming;
 }
 
@@ -411,6 +412,13 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       result = process_Ping();
       break;
 
+    case VNSI_GETSETUP:
+      result = process_GetSetup();
+      break;
+
+    case VNSI_STORESETUP:
+      result = process_StoreSetup();
+      break;
 
     /** OPCODE 20 - 39: VNSI network functions for live streaming */
     case VNSI_CHANNELSTREAM_OPEN:
@@ -421,6 +429,9 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
       result = processChannelStream_Close();
       break;
 
+    case VNSI_CHANNELSTREAM_SEEK:
+      result = processChannelStream_Seek();
+      break;
 
     /** OPCODE 40 - 59: VNSI network functions for recording streaming */
     case VNSI_RECSTREAM_OPEN:
@@ -637,12 +648,61 @@ bool cVNSIClient::process_Ping() /* OPCODE 7 */
   return true;
 }
 
+bool cVNSIClient::process_GetSetup() /* OPCODE 8 */
+{
+  char* name = m_req->extract_String();
+  if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
+    m_resp->add_U32(PmtTimeout);
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
+    m_resp->add_U32(TimeshiftMode);
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
+    m_resp->add_U32(TimeshiftBufferSize);
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+    m_resp->add_U32(TimeshiftBufferFileSize);
+
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */
+{
+  char* name = m_req->extract_String();
+
+  if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
+  {
+    int value = m_req->extract_U32();
+    cPluginVNSIServer::StoreSetup(CONFNAME_PMTTIMEOUT, value);
+  }
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
+  {
+    int value = m_req->extract_U32();
+    cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFT, value);
+  }
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
+  {
+    int value = m_req->extract_U32();
+    cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERSIZE, value);
+  }
+  else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+  {
+    int value = m_req->extract_U32();
+    cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value);
+  }
+
+  m_resp->add_U32(VNSI_RET_OK);
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
 
 /** OPCODE 20 - 39: VNSI network functions for live streaming */
 
 bool cVNSIClient::processChannelStream_Open() /* 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)
@@ -668,7 +728,7 @@ bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
   }
   else
   {
-    if (StartChannelStreaming(channel, timeout))
+    if (StartChannelStreaming(channel, priority, timeshift, timeout))
     {
       INFOLOG("Started streaming of channel %s (timeout %i seconds)", channel->Name(), timeout);
       // return here without sending the response
@@ -694,6 +754,25 @@ bool cVNSIClient::processChannelStream_Close() /* OPCODE 21 */
   return true;
 }
 
+bool cVNSIClient::processChannelStream_Seek() /* OPCODE 22 */
+{
+  uint32_t serial = 0;
+  if (m_isStreaming && m_Streamer)
+  {
+    int64_t time = m_req->extract_S64();
+    if (m_Streamer->SeekTime(time, serial))
+      m_resp->add_U32(VNSI_RET_OK);
+    else
+      m_resp->add_U32(VNSI_RET_ERROR);
+  }
+  else
+    m_resp->add_U32(VNSI_RET_ERROR);
+
+  m_resp->add_U32(serial);
+  m_resp->finalise();
+  m_socket.write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
 
 /** OPCODE 40 - 59: VNSI network functions for recording streaming */
 
@@ -1324,7 +1403,11 @@ bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
 bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
 {
   int FreeMB;
+#if VDRVERSNUM >= 20102
+  int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
+#else
   int Percent = VideoDiskSpace(&FreeMB);
+#endif
   int Total   = (FreeMB / (100 - Percent)) * 100;
 
   m_resp->add_U32(Total);
@@ -1349,6 +1432,7 @@ bool cVNSIClient::processRECORDINGS_GetCount() /* OPCODE 101 */
 bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
 {
   cMutexLock lock(&m_timerLock);
+  cThreadLock RecordingsLock(&Recordings);
 
   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
   {
@@ -1477,7 +1561,7 @@ bool cVNSIClient::processRECORDINGS_Rename() /* OPCODE 103 */
 
     // replace spaces in newtitle
     strreplace(newtitle, ' ', '_');
-    char* filename_new = new char[512];
+    char* filename_new = new char[1024];
     strncpy(filename_new, filename_old, 512);
     sep = strrchr(filename_new, '/');
     if(sep != NULL) {
diff --git a/vnsiclient.h b/vnsiclient.h
index b23117f..948940a 100644
--- a/vnsiclient.h
+++ b/vnsiclient.h
@@ -94,7 +94,7 @@ protected:
 
   void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
   void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
-  bool StartChannelStreaming(const cChannel *channel, uint32_t timeout);
+  bool StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout);
   void StopChannelStreaming();
 
 private:
@@ -111,9 +111,12 @@ private:
   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();
diff --git a/vnsicommand.h b/vnsicommand.h
index a2b8000..17d3d08 100644
--- a/vnsicommand.h
+++ b/vnsicommand.h
@@ -27,7 +27,7 @@
 #define VNSI_COMMAND_H
 
 /** Current VNSI Protocol Version number */
-#define VNSI_PROTOCOLVERSION 3
+#define VNSI_PROTOCOLVERSION 4
 
 /** Packet types */
 #define VNSI_CHANNEL_REQUEST_RESPONSE 1
@@ -40,15 +40,24 @@
 
 /** Response packets operation codes */
 
+#define CONFNAME_PMTTIMEOUT "PmtTimeout"
+#define CONFNAME_TIMESHIFT "Timeshift"
+#define CONFNAME_TIMESHIFTBUFFERSIZE "TimeshiftBufferSize"
+#define CONFNAME_TIMESHIFTBUFFERFILESIZE "TimeshiftBufferFileSize"
+#define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir"
+
 /* OPCODE 1 - 19: VNSI network functions for general purpose */
 #define VNSI_LOGIN                 1
 #define VNSI_GETTIME               2
 #define VNSI_ENABLESTATUSINTERFACE 3
 #define VNSI_PING                  7
+#define VNSI_GETSETUP              8
+#define VNSI_STORESETUP            9
 
 /* OPCODE 20 - 39: VNSI network functions for live streaming */
-#define VNSI_CHANNELSTREAM_OPEN    20
-#define VNSI_CHANNELSTREAM_CLOSE   21
+#define VNSI_CHANNELSTREAM_OPEN     20
+#define VNSI_CHANNELSTREAM_CLOSE    21
+#define VNSI_CHANNELSTREAM_SEEK     22
 
 /* OPCODE 40 - 59: VNSI network functions for recording streaming */
 #define VNSI_RECSTREAM_OPEN        40
@@ -102,6 +111,7 @@
 #define VNSI_STREAM_MUXPKT       4
 #define VNSI_STREAM_SIGNALINFO   5
 #define VNSI_STREAM_CONTENTINFO  6
+#define VNSI_STREAM_BUFFERSTATS  7
 
 /** Scan packet types (server -> client) */
 #define VNSI_SCANNER_PERCENTAGE  1
diff --git a/vnsiserver.c b/vnsiserver.c
index f116163..db2e033 100644
--- a/vnsiserver.c
+++ b/vnsiserver.c
@@ -36,10 +36,13 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <sys/stat.h>
 
 #include <vdr/plugin.h>
 #include <vdr/shutdown.h>
+#include <vdr/videodir.h>
 
+#include "vnsi.h"
 #include "vnsiserver.h"
 #include "vnsiclient.h"
 
@@ -156,6 +159,7 @@ void cVNSIServer::NewClientConnected(int fd)
   int val = 1;
   setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
 
+#ifdef SOL_TCP
   val = 30;
   setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
 
@@ -167,6 +171,7 @@ void cVNSIServer::NewClientConnected(int fd)
 
   val = 1;
   setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
+#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));
@@ -179,6 +184,12 @@ void cVNSIServer::Action(void)
   fd_set fds;
   struct timeval tv;
 
+  // initial time for channels change
+  struct timespec channelsUpdate;
+  channelsUpdate.tv_sec = 0;
+  channelsUpdate.tv_nsec = 0;
+  cTimeMs chanTimer(0);
+
   // get initial state of the recordings
   int recState = -1;
   Recordings.StateChanged(recState);
@@ -190,6 +201,26 @@ void cVNSIServer::Action(void)
   // last update of epg
   time_t epgUpdate = cSchedules::Modified();
 
+  // delete old timeshift file
+  cString cmd;
+  struct stat sb;
+  if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
+  {
+    if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+      cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
+    else
+      cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
+  }
+  else
+  {
+#if VDRVERSNUM >= 20102
+    cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
+#else
+    cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
+#endif
+  }
+  int ret = system(cmd);
+
   while (Running())
   {
     FD_ZERO(&fds);
@@ -221,16 +252,21 @@ void cVNSIServer::Action(void)
       }
 
       // trigger clients to reload the modified channel list
-      if(m_clients.size() > 0)
+      if(m_clients.size() > 0 && chanTimer.TimedOut())
       {
-        Channels.Lock(false);
-        if(Channels.Modified() != 0)
+        struct stat s;
+        if(stat(Channels.FileName(), &s) != -1)
         {
-          INFOLOG("Requesting clients to reload channel list");
-          for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
-            (*i)->ChannelChange();
+          if ((s.st_mtim.tv_sec != channelsUpdate.tv_sec) &&
+              (s.st_mtim.tv_nsec != channelsUpdate.tv_nsec))
+          {
+            INFOLOG("Requesting clients to reload channel list");
+            for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+              (*i)->ChannelChange();
+            channelsUpdate = s.st_mtim;
+          }
         }
-        Channels.Unlock();
+        chanTimer.Set(5000);
       }
 
       // reset inactivity timeout as long as there are clients connected

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



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