LiveBuffer for vdr 1.7.x

Message ID 4EF10857.7070500@dressler.ca
State New
Headers

Commit Message

Mlists Dec. 20, 2011, 10:12 p.m. UTC
  On 11-12-20 05:08 PM, René wrote:
> Hi All,
>
> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer 
> patch for any current vdr 1.7.x. Does anyone know if there is any work 
> going on with this great patch?
>
>
> Regards,
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr


Here you go.  I grabbed this from the yaVDR source.

Norm
#! /bin/sh /usr/share/dpatch/dpatch-run
## opt-96-livebuffer10-rmm.dpatch by  <Gerald Dachs <gda@dachsweb.de>>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

@DPATCH@
  

Comments

Rene Hertell Dec. 21, 2011, 8:48 a.m. UTC | #1
On 21.12.2011 24:12 , Lists wrote:

>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>> going on with this great patch?

> Here you go. I grabbed this from the yaVDR source.
>
> Norm

Hi Norm,

Thanks!! Do you know which vdr-build this patch is for?

I'm running gentoo, so it should be pretty easy to update an ebuild to 
contain this patch! :-)


René
  
Mlists Dec. 21, 2011, 11:34 a.m. UTC | #2
On 12/21/2011 3:48 AM, René wrote:
> On 21.12.2011 24:12 , Lists wrote:
>
>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>>> going on with this great patch?
>
>> Here you go. I grabbed this from the yaVDR source.
>>
>> Norm
>
> Hi Norm,
>
> Thanks!! Do you know which vdr-build this patch is for?
>
> I'm running gentoo, so it should be pretty easy to update an ebuild to 
> contain this patch! :-)
>
>
> René


This is for vdr 1.7.21 :)

Norm
  
Rene Hertell Dec. 21, 2011, 9:18 p.m. UTC | #3
On 21.12.2011 13:34 , Norm Dressler wrote:
> On 12/21/2011 3:48 AM, René wrote:
>> On 21.12.2011 24:12 , Lists wrote:
>>
>>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>>>> going on with this great patch?
>>
>>> Here you go. I grabbed this from the yaVDR source.
>>>
>>> Norm
>>
>> Hi Norm,
>>
>> Thanks!! Do you know which vdr-build this patch is for?
>>
>> I'm running gentoo, so it should be pretty easy to update an ebuild to
>> contain this patch! :-)
>>
>>
>> René
>
>
> This is for vdr 1.7.21 :)
>
> Norm

Oh yeah!! Great!! I have right away try to modify an ebuild for this :-)

If i'm succesful, i'll post the ebuild back to this thread!

René
  
Artem Makhutov Dec. 22, 2011, 4:04 p.m. UTC | #4
Hi,

René schrieb:
> On 21.12.2011 13:34 , Norm Dressler wrote:
>> On 12/21/2011 3:48 AM, René wrote:
>>> On 21.12.2011 24:12 , Lists wrote:
>>>
>>>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>>>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>>>>> going on with this great patch?
>>>
>>>> Here you go. I grabbed this from the yaVDR source.
>>>>
>>>> Norm
>>>
>>> Hi Norm,
>>>
>>> Thanks!! Do you know which vdr-build this patch is for?
>>>
>>> I'm running gentoo, so it should be pretty easy to update an ebuild to
>>> contain this patch! :-)
>>>
>>>
>>> René
>>
>>
>> This is for vdr 1.7.21 :)
>>
>> Norm
>
> Oh yeah!! Great!! I have right away try to modify an ebuild for this :-)
>
> If i'm succesful, i'll post the ebuild back to this thread!

You don't have to alter the ebuild for this. You can just place patch the file in a directory (I don't remember which) and it will automaticly applied when you compile vdr.

Regards, Artem
  
Marc Dec. 22, 2011, 4:38 p.m. UTC | #5
On 22/12/2011 17:04, Artem Makhutov wrote:
> Hi,
>
> René schrieb:
>> On 21.12.2011 13:34 , Norm Dressler wrote:
>>> On 12/21/2011 3:48 AM, René wrote:
>>>> On 21.12.2011 24:12 , Lists wrote:
>>>>
>>>>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>>>>> patch for any current vdr 1.7.x. Does anyone know if there is any 
>>>>>> work
>>>>>> going on with this great patch?
>>>>
>>>>> Here you go. I grabbed this from the yaVDR source.
>>>>>
>>>>> Norm
>>>>
>>>> Hi Norm,
>>>>
>>>> Thanks!! Do you know which vdr-build this patch is for?
>>>>
>>>> I'm running gentoo, so it should be pretty easy to update an ebuild to
>>>> contain this patch! :-)
>>>>
>>>>
>>>> René
>>>
>>>
>>> This is for vdr 1.7.21 :)
>>>
>>> Norm
>>
>> Oh yeah!! Great!! I have right away try to modify an ebuild for this :-)
>>
>> If i'm succesful, i'll post the ebuild back to this thread!
>
> You don't have to alter the ebuild for this. You can just place patch 
> the file in a directory (I don't remember which) and it will 
> automaticly applied when you compile vdr.
>
> Regards, Artem
>
>
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
Here it is :
VDR_LOCAL_PATCHES_DIR=/your/patch/directory emerge vdr

You can put it in make.conf

I tried but it doesn't work on the vdr-1.7.21 source tree (perhaps it 
needs some change )

Marc.
  
rollercoaster@reel-multimedia.com Dec. 22, 2011, 5:36 p.m. UTC | #6
Am Dienstag, 20. Dezember 2011, um 23:12:39 schrieb Lists:
> On 11-12-20 05:08 PM, René wrote:
> > Hi All,
> > 
> > I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
> > patch for any current vdr 1.7.x. Does anyone know if there is any work
> > going on with this great patch?


look at 
http://www.vdr-portal.de/board17-developer/board25-patches/p1003452-livebuffer-
patch-f%C3%BCr-vdr-1-7-16-aus-rmm-svn/#post1003452

in this thread there is a newer version available. But this version still is 
missing three important fixes from RMM svn repos. Could anyone update these, 
please?


r17545 | dirk | 2011-11-10 17:04:23 +0100 (Do, 10. Nov 2011) | 2 Zeilen
vdr1.7: fix crash in livebuffer on low disc space
---
r16810 | balaji | 2011-06-17 13:54:46 +0200 (Fr, 17. Jun 2011) | 11 Zeilen

vdr 1.7: fix issue #574. Handle return osSchedule and do not close control for 
osRecordings always
   
   Handle osSchedule return value from ProcessKey()
        call DirectMainFunction(osSchedule)
        needed for opening EPG menu in timeshift mode

   If timeshift mode is ON; (control belongs to livebuffer) then with 
osRecordings
       do not close the control
       allow livebuffer to run when opening Recordings list
---
r16772 | dirk | 2011-06-15 15:31:22 +0200 (Mi, 15. Jun 2011) | 5 Zeilen
vdr1.7: 
Fix crash if index is not valid
Wait some time if index exists but has no content
Allow PauseLiveLive without livebuffer
---


And no, this patch is not from Gerald D. as the header of the patch might 
imply ;)

regards,
Tim
  
Gerald Dachs Dec. 22, 2011, 8:34 p.m. UTC | #7
> And no, this patch is not from Gerald D. as the header of the patch 
might imply ;) regards, Tim

It is true that the code in the original patch is not made by me, but 
the patch is made by me, because I had to manipulate the original patch 
so that it fits to the changes the yaVDR-Team made to the vdr.
This is why my name is shown there.

If a patch author wants to be mentioned, I would apply to them to not 
only patch the code, but to add a line with their name to the changelog 
file too.

Gerald
  
Rene Hertell Dec. 22, 2011, 9:17 p.m. UTC | #8
On 22.12.2011 19:36 , Tim wrote:
> Am Dienstag, 20. Dezember 2011, um 23:12:39 schrieb Lists:
>> On 11-12-20 05:08 PM, René wrote:
>>> Hi All,
>>>
>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>>> going on with this great patch?
>
>
> look at
> http://www.vdr-portal.de/board17-developer/board25-patches/p1003452-livebuffer-
> patch-f%C3%BCr-vdr-1-7-16-aus-rmm-svn/#post1003452
>
> in this thread there is a newer version available. But this version still is
> missing three important fixes from RMM svn repos. Could anyone update these,
> please?

Is this patch "hosted" at for example github, or at vdr-developer.org? 
Github would be a good idea, because their frontend makes it easy to 
follow between revisions.. Also other people could just join by forking 
and do their own additions :-)

So did i understand correct that the patch Norm sent does not work with 
1.7.21? (i have not managed to recompile my vdr, cause had too much 
work.. Will definitively try the update during x-mas! :-)

René
  
Marc Dec. 22, 2011, 9:32 p.m. UTC | #9
On 22/12/2011 22:17, René wrote:
> On 22.12.2011 19:36 , Tim wrote:
>> Am Dienstag, 20. Dezember 2011, um 23:12:39 schrieb Lists:
>>> On 11-12-20 05:08 PM, René wrote:
>>>> Hi All,
>>>>
>>>> I'm stuck to vdr 1.6.0-2 because i can't find a current LiveBuffer
>>>> patch for any current vdr 1.7.x. Does anyone know if there is any work
>>>> going on with this great patch?
>>
>>
>> look at
>> http://www.vdr-portal.de/board17-developer/board25-patches/p1003452-livebuffer- 
>>
>> patch-f%C3%BCr-vdr-1-7-16-aus-rmm-svn/#post1003452
>>
>> in this thread there is a newer version available. But this version 
>> still is
>> missing three important fixes from RMM svn repos. Could anyone update 
>> these,
>> please?
>
> Is this patch "hosted" at for example github, or at vdr-developer.org? 
> Github would be a good idea, because their frontend makes it easy to 
> follow between revisions.. Also other people could just join by 
> forking and do their own additions :-)
>
> So did i understand correct that the patch Norm sent does not work 
> with 1.7.21? (i have not managed to recompile my vdr, cause had too 
> much work.. Will definitively try the update during x-mas! :-)
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
It's a patch from yaVDR and it doesn't apply to the vdr source tree as 
is (at least for me). The second patch works on gentoo ebuild with small 
changes (It conflicts with other patches and need some changes for 
config.c & config.h part).

I tried the second too but it crashes vdr when I try to pause. I didn't 
tested a lot.

Marc.
  
Rene Hertell Dec. 22, 2011, 10:07 p.m. UTC | #10
On 22.12.2011 23:32 , Marc wrote:

> It's a patch from yaVDR and it doesn't apply to the vdr source tree as
> is (at least for me). The second patch works on gentoo ebuild with small
> changes (It conflicts with other patches and need some changes for
> config.c & config.h part).
>
> I tried the second too but it crashes vdr when I try to pause. I didn't
> tested a lot.

Hi Marc,

It's a shame that it did not work.. I wonder in which order the patches 
are added to VDR in a normal gentoo compilation-processs. Is the 
VDR_LOCAL_PATCHES_DIR applied as last in the row of patches, or is this 
first? I assume that if eg. this patch comes in as the last patch, I 
need to adjust this patch to match possible changes in the source made 
by other patches..

It would be great if this patch could be re-added into Gentoo as a 
default use-flag... This patch was (and still IS) a really great 
feature! :-)

René
  
Marc Dec. 23, 2011, 9:47 a.m. UTC | #11
On 22/12/2011 23:07, René wrote:
> On 22.12.2011 23:32 , Marc wrote:
>
>> It's a patch from yaVDR and it doesn't apply to the vdr source tree as
>> is (at least for me). The second patch works on gentoo ebuild with small
>> changes (It conflicts with other patches and need some changes for
>> config.c & config.h part).
>>
>> I tried the second too but it crashes vdr when I try to pause. I didn't
>> tested a lot.
>
> Hi Marc,
>
> It's a shame that it did not work.. I wonder in which order the 
> patches are added to VDR in a normal gentoo compilation-processs. Is 
> the VDR_LOCAL_PATCHES_DIR applied as last in the row of patches, or is 
> this first? I assume that if eg. this patch comes in as the last 
> patch, I need to adjust this patch to match possible changes in the 
> source made by other patches..
>
> It would be great if this patch could be re-added into Gentoo as a 
> default use-flag... This patch was (and still IS) a really great 
> feature! :-)
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
Yes, it's the way the ebuild works. Almost at the end of source_prepare, 
the local patches are applied after the others. But the second patch 
needs only few changes in config.c & config.h to apply.

Report back if you succeed with this patch. For me, with the patch, I 
have the option available but when I try to pause, vdr crash. Perhaps 
the patch is to old (1.7.18 and I use 1.7.21).

The first patch is for vdr-1.7.21 but I didn't succeeded to use it.

Marc.
  
Dominic Evans Dec. 23, 2011, 11:11 a.m. UTC | #12
> It's a patch from yaVDR and it doesn't apply to the vdr source tree as is
> (at least for me).

Here's the patch from yaVDR rebased to apply to vanilla 1.7.21 sources:

https://gist.github.com/1513894

https://raw.github.com/gist/1513894
  

Patch

diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/config.c vdr-liveb/config.c
--- vdr/config.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/config.c	2011-10-27 09:56:48.312237585 +0200
@@ -419,6 +419,10 @@ 
   VerboseLNBlog = 0;
   for (int i = 0; i < MAXDEVICES; i++) CardUsesLnbNr[i] = i + 1;
 //ML-Ende
+#ifdef USE_LIVEBUFFER
+  LiveBufferSize = 15;
+  LiveBufferMaxFileSize = 100;
+#endif /*USE_LIVEBUFFER*/
 }
 
 cSetup::~cSetup()
@@ -635,6 +639,10 @@ 
   else if (!strcasecmp(Name, "InitialVolume"))       InitialVolume      = atoi(Value);
   else if (!strcasecmp(Name, "VolumeSteps"))         VolumeSteps        = atoi(Value);
   else if (!strcasecmp(Name, "VolumeLinearize"))     VolumeLinearize    = atoi(Value);
+#ifdef USE_LIVEBUFFER
+   else if (!strcasecmp(Name, "LiveBufferSize"))        LiveBufferSize        = atoi(Value);
+   else if (!strcasecmp(Name, "LiveBufferMaxFileSize")) LiveBufferMaxFileSize = atoi(Value);
+#endif /*USE_LIVEBUFFER*/
   else if (!strcasecmp(Name, "ChannelsWrap"))        ChannelsWrap       = atoi(Value);
   else if (!strcasecmp(Name, "EmergencyExit"))       EmergencyExit      = atoi(Value);
   else
@@ -765,6 +773,9 @@ 
   Store("VolumeLinearize",    VolumeLinearize);
   Store("ChannelsWrap",       ChannelsWrap);
   Store("EmergencyExit",      EmergencyExit);
+#ifdef USE_LIVEBUFFER
+  Store("LiveBufferSize",     LiveBufferSize);
+#endif  /* LIVEBUFFER */
 
 //ML
   Store("VerboseLNBlog",       VerboseLNBlog);
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/config.h vdr-liveb/config.h
--- vdr/config.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/config.h	2011-10-27 09:56:48.312237585 +0200
@@ -315,6 +315,10 @@ 
   int InitialVolume;
   int ChannelsWrap;
   int EmergencyExit;
+#ifdef USE_LIVEBUFFER
+  int LiveBufferSize;
+  int LiveBufferMaxFileSize;
+#endif /*USE_LIVEBUFFER*/
 
 //ML
   #define LNB_SHARING_VERSION "0.1.4"
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/device.c vdr-liveb/device.c
--- vdr/device.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/device.c	2011-10-27 10:02:55.742237597 +0200
@@ -20,6 +20,10 @@ 
 #include "status.h"
 #include "transfer.h"
 #include "vdrttxtsubshooks.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#include "interface.h"
+#endif /*USE_LIVEBUFFER*/
 
 // --- cLiveSubtitle ---------------------------------------------------------
 
@@ -758,6 +762,14 @@ 
                               return false;
         case scrNoTransfer:   Skins.Message(mtError, tr("Can't start Transfer Mode!"));
                               return false;
+#ifdef USE_LIVEBUFFER
+        case srcStillWritingLiveBuffer:
+           if(Interface->Confirm(tr("Still writing timeshift data to recording. Abort?")))
+              cRecordControls::CancelWritingBuffer();
+           else
+              if(cRecordControls::IsWritingBuffer()) return false;
+           break;
+#endif /*USE_LIVEBUFFER*/
         case scrFailed:       break; // loop will retry
         default:              esyslog("ERROR: invalid return value from SetChannel");
         }
@@ -893,8 +905,17 @@ 
 
   if (NeedsTransferMode) {
      if (Device && CanReplay()) {
+#ifdef USE_LIVEBUFFER
+        if(LiveView && !cRecordControls::CanSetLiveChannel(Channel))
+           return cRecordControls::IsWritingBuffer() ? srcStillWritingLiveBuffer : scrFailed;
+#endif /*USE_LIVEBUFFER*/
         cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
         if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+#ifdef USE_LIVEBUFFER
+           if(LiveView)
+              cRecordControls::SetLiveChannel(Device, Channel);
+           else
+#endif /*USE_LIVEBUFFER*/
            cControl::Launch(new cTransferControl(Device, Channel));
         else
            Result = scrNoTransfer;
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/device.h vdr-liveb/device.h
--- vdr/device.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/device.h	2011-10-27 09:56:48.322237585 +0200
@@ -33,7 +33,11 @@ 
 #define MAXVOLUME         255
 #define VOLUMEDELTA       (MAXVOLUME/Setup.VolumeSteps) // used to increase/decrease the volume 
 
+#ifdef USE_LIVEBUFFER
+enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed, srcStillWritingLiveBuffer };
+#else
 enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+#endif /*USE_LIVEBUFFER*/
 
 enum ePlayMode { pmNone,           // audio/video from decoder
                  pmAudioVideo,     // audio/video from player
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/dvbplayer.c vdr-liveb/dvbplayer.c
--- vdr/dvbplayer.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/dvbplayer.c	2011-10-27 09:56:48.322237585 +0200
@@ -15,6 +15,9 @@ 
 #include "ringbuffer.h"
 #include "thread.h"
 #include "tools.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#endif /*USE_LIVEBUFFER*/
 
 // --- cPtsIndex -------------------------------------------------------------
 
@@ -35,6 +38,9 @@ 
   void Clear(void);
   void Put(uint32_t Pts, int Index);
   int FindIndex(uint32_t Pts);
+#ifdef USE_LIVEBUFFER
+  void SetIndex(int Index) {lastFound = Index;};
+#endif /*USE_LIVEBUFFER*/
   };
 
 cPtsIndex::cPtsIndex(void)
@@ -206,7 +212,12 @@ 
   cPtsIndex ptsIndex;
   cMarksReload marks;
   cFileName *fileName;
+#ifdef USE_LIVEBUFFER
+  cIndex *index;
+  cIndexFile *indexFile;
+#else
   cIndexFile *index;
+#endif /*USE_LIVEBUFFER*/
   cUnbufferedFile *replayFile;
   double framesPerSecond;
   bool isPesRecording;
@@ -271,18 +282,35 @@ 
   dropFrame = NULL;
   isyslog("replay %s", FileName);
   fileName = new cFileName(FileName, false, false, isPesRecording);
+#ifndef USE_LIVEBUFFER
   replayFile = fileName->Open();
   if (!replayFile)
      return;
+#endif /*USE_LIVEBUFFER*/
   ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
   // Create the index file:
+#ifdef USE_LIVEBUFFER
+  indexFile = NULL;
+  index = cRecordControls::GetLiveIndex(FileName);
+  if(!index)
+     index = indexFile = new cIndexFile(FileName, false, isPesRecording);
+#else
   index = new cIndexFile(FileName, false, isPesRecording);
+#endif /*USE_LIVEBUFFER*/
   if (!index)
      esyslog("ERROR: can't allocate index");
   else if (!index->Ok()) {
      delete index;
      index = NULL;
      }
+#ifdef USE_LIVEBUFFER
+  readIndex = Resume();
+  if (readIndex >= 0) {
+     ptsIndex.SetIndex(readIndex);
+     isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+  } else
+     replayFile = fileName->Open();
+#endif /*USE_LIVEBUFFER*/
 }
 
 cDvbPlayer::~cDvbPlayer()
@@ -290,7 +318,11 @@ 
   Save();
   Detach();
   delete readFrame; // might not have been stored in the buffer in Action()
+#ifdef USE_LIVEBUFFER
+  delete indexFile;
+#else
   delete index;
+#endif /*USE_LIVEBUFFER*/
   delete fileName;
   delete ringBuffer;
 }
@@ -394,9 +426,11 @@ 
   bool cutIn = false;
   int total = -1;
 
+#ifndef USE_LIVEBUFFER
   readIndex = Resume();
   if (readIndex >= 0)
      isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+#endif /*USE_LIVEBUFFER*/
 
   if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
      int Index = marks.First()->position;
@@ -456,6 +490,10 @@ 
                          if (NewIndex <= 0 && readIndex > 0)
                             NewIndex = 1; // make sure the very first frame is delivered
                          NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode);
+#ifdef USE_LIVEBUFFER
+                         if (NewIndex < 0 && TimeShiftMode) // Why should we wait for a timeout if not pdForward
+                            SwitchToPlayFrame = Index;
+#endif
                          if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
                             SwitchToPlayFrame = Index;
                          Index = NewIndex;
@@ -512,6 +550,15 @@ 
                          total = index->Last();
                       if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
                          readIndex++;
+#ifdef USE_LIVEBUFFER
+                      else if(index && index->First() && (readIndex < index->First())) {
+                         int old = readIndex;
+                         readIndex = index->GetNextIFrame(index->First()+1, true, NULL, NULL, NULL, true);
+                         isyslog("Jump before start of livebuffer cortrected %d->%d First %d", old, readIndex, index->First());
+                         if(readIndex <= index->First())
+                            eof = true;
+                      }
+#endif /*USE_LIVEBUFFER*/
                       else
                          eof = true;
                       }
@@ -657,7 +704,11 @@ 
              else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
                 SwitchToPlay = true;
              if (SwitchToPlay) {
+#ifdef USE_LIVEBUFFER
+                if (!SwitchToPlayFrame || (playDir == pdBackward))
+#else
                 if (!SwitchToPlayFrame)
+#endif /*USE_LIVEBUFFER*/
                    Empty();
                 DevicePlay();
                 playMode = pmPlay;
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/livebuffer.c vdr-liveb/livebuffer.c
--- vdr/livebuffer.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-liveb/livebuffer.c	2011-10-27 09:56:48.322237585 +0200
@@ -0,0 +1,403 @@ 
+#ifdef USE_LIVEBUFFER
+#include "livebuffer.h"
+#if VDRVERSNUM >= 10716
+
+#include <vector>
+#include "videodir.h"
+#include "recording.h"
+#include "skins.h"
+#include "player.h"
+
+#define WAIT_WRITING_COUNT 1000
+#define WAIT_WRITING_SLEEP 10000
+
+#define WAIT_TERMINATE_COUNT 300
+#define WAIT_TERMINATE_SLEEP 10000
+
+struct tLiveIndex {
+  int index;
+  uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!)
+  int reserved:7;     // reserved for future use
+  int independent:1;  // marks frames that can be displayed by themselves (for trick modes)
+  uint16_t number:16; // up to 64K files per recording
+  tLiveIndex(int Index, bool Independent, uint16_t Number, off_t Offset)
+  {
+    index = Index;
+    offset = Offset;
+    reserved = 0;
+    independent = Independent;
+    number = Number;
+  }
+}; // tLiveIndex
+
+class cLiveIndex : public cIndex {
+public:
+	cLiveIndex(const char *FileName): bufferFileName(FileName, false), bufferBaseName(FileName) {
+		resumePos = -1;
+		lastPos = lastGet = lastBuf = 0;
+		lastFileNumber=1;
+		dropFile = false;
+		maxSize = Setup.LiveBufferSize * 60 * DEFAULTFRAMESPERSECOND;
+		idx.reserve(maxSize+1);
+	}; // cLiveIndex
+	virtual ~cLiveIndex() {
+	}; // ~cLiveIndex
+	virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) {
+		cMutexLock lock(&idx_lock);
+		idx.push_back(tLiveIndex(++lastPos, Independent, FileNumber, FileOffset));
+		while(((idx.size() > maxSize) && (lastGet ? (lastGet > First()) : true) && (lastBuf ? (lastBuf > First()) : true)) || dropFile) {
+			if(idx.front().number != lastFileNumber) {
+				isyslog("Deleting old livebuffer file #%d (%d)", lastFileNumber, dropFile);
+				system(cString::sprintf("ls -l %s/%05d.ts | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", (const char *)bufferBaseName, lastFileNumber));  // for symlink video.xx
+				unlink(cString::sprintf("%s/%05d.ts", (const char *)bufferBaseName, lastFileNumber));
+				lastFileNumber = idx.front().number;
+				dropFile=false;
+			} // if
+			idx.erase(idx.begin());
+		} // if
+		return true;
+	}; // Write
+	virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) {
+		cMutexLock lock(&idx_lock);
+		std::vector<tLiveIndex>::iterator item = GetIndex(Index);
+		if(item == idx.end()) return false;
+		*FileNumber = item->number;
+		*FileOffset = item->offset;
+		if (Independent)
+			*Independent = item->independent;
+		item++;
+		if(item == idx.end()) return false;
+		if (Length) {
+			uint16_t fn = item->number;
+			off_t fo = item->offset;
+			if (fn == *FileNumber)
+				*Length = int(fo - *FileOffset);
+			else
+				*Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
+		} // if
+		lastGet = Index;
+		return true;
+	}; // Get
+	virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) {
+		cMutexLock lock(&idx_lock);
+		std::vector<tLiveIndex>::iterator item = GetIndex(Index);
+		if(item == idx.end()) {
+			if(Index < First() && Forward)
+				item = idx.begin();
+			else
+				return -1;
+		}
+		if(Forward) {
+			do {
+				item++;
+				if(item == idx.end()) return -1;
+			} while(!item->independent);
+		} else {
+			do {
+				if(item == idx.begin()) return -1;
+				item--;
+			} while(!item->independent);
+		} // if
+		uint16_t fn;
+		if (!FileNumber)
+			FileNumber = &fn;
+		off_t fo;
+		if (!FileOffset)
+			FileOffset = &fo;
+		*FileNumber = item->number;
+		*FileOffset = item->offset;
+		item++;
+		if(item == idx.end()) return -1;
+		if (Length) {
+			// all recordings end with a non-independent frame, so the following should be safe:
+			uint16_t fn = item->number;
+			off_t fo = item->offset;
+			if (fn == *FileNumber) {
+				*Length = int(fo - *FileOffset);
+			} else {
+				esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
+				*Length = -1;
+			} // if
+		} // if
+		return Index;
+	}; // GetNextIFrame
+	virtual bool SetBufferStart(int Frames) {
+		cMutexLock lock(&idx_lock);
+		abortBuf = false;
+		if(Frames <= 0) {
+			lastBuf = 0;
+			return false;
+		} // if
+		lastBuf = Last()-Frames;
+		if(lastBuf < First())
+			lastBuf = First();
+		lastBuf = GetNextIFrame(lastBuf, true);
+		return true;
+	} // SetBufferStart
+	virtual cUnbufferedFile *GetNextBuffer(int &Length, bool &Independent) {
+		if(abortBuf || !lastBuf) return NULL;
+		cMutexLock lock(&idx_lock);
+		std::vector<tLiveIndex>::iterator buff = GetIndex(lastBuf);
+		if((buff == idx.end()) || ((buff+1) == idx.end())) return NULL;
+		off_t offset = buff->offset;
+		int number   = buff->number;
+		cUnbufferedFile *ret = bufferFileName.SetOffset(number, offset);
+		Independent = buff->independent;
+		buff++;
+		lastBuf = buff->index;
+		if(number != buff->number)
+			Length = -1;
+		else
+			Length = buff->offset-offset;
+		return ret;
+	} // GetNextBuffer
+	virtual int Get(uint16_t FileNumber, off_t FileOffset) {
+		for ( std::vector<tLiveIndex>::iterator item = idx.begin(); item != idx.end(); item++)
+			if (item->number > FileNumber || ((item->number == FileNumber) && off_t(item->offset) >= FileOffset))
+				return item->index;
+		return lastPos;
+	}; // Get
+	virtual bool Ok(void)                    {return true;};
+	virtual int  First(void)                 {return idx.size() ? idx.front().index : -1;};
+	virtual int  Last(void)                  {return idx.size() ? idx.back().index  : -1;};
+	virtual void SetResume(int Index)        {resumePos = lastGet = Index;};
+	virtual int  GetResume(void)             {return resumePos;};
+	virtual bool StoreResume(int Index)      {resumePos=Index; lastGet=0; return true;};
+	virtual bool IsStillRecording(void)      {return true;};
+	virtual void Delete(void)                {};
+	virtual void DropFile(void)              {dropFile=true;};
+	virtual bool IsWritingBuffer(void)       {return lastBuf != 0;};
+	virtual void CancelWritingBuffer(void)   {abortBuf = true;};
+	virtual bool WritingBufferCanceled(void) {return abortBuf;};
+protected:
+	int firstPos;
+	int lastPos;
+	int resumePos;
+	int lastFileNumber;
+	int lastGet;
+	int lastBuf;
+	bool abortBuf;
+	bool dropFile;
+	unsigned int maxSize;
+	cFileName bufferFileName;
+	cString bufferBaseName;
+	cMutex idx_lock;
+	std::vector<tLiveIndex> idx;
+	virtual std::vector<tLiveIndex>::iterator GetIndex(int Index) {
+		if(!idx.size()) return idx.end();
+		std::vector<tLiveIndex>::iterator item = idx.begin();
+
+		unsigned int guess = Index-First(); // Try to guess the position
+		if(guess > 0) {
+			if(guess < idx.size())
+				item += guess;
+			else
+				item = idx.end()-1;
+		} // if
+		while(item->index < Index) {
+			item++;
+			if(item == idx.end())
+				return idx.end();
+		} // while
+		while(item->index > Index) {
+			if(item == idx.begin())
+				return idx.end();
+			item--;
+		} // while
+		if(item->index != Index)
+			return idx.end();
+		return item;
+	}; // GetIndex
+}; // cLiveIndex
+
+/*****************************************************************************/
+
+cString cLiveRecorder::liveFileName;
+
+cLiveRecorder::cLiveRecorder(const cChannel *Channel):cRecorder(FileName(), Channel, -1)
+              ,broken(false) {
+	handleError = false;
+	if(index) delete index;
+	index = new cLiveIndex(FileName());
+	Activate(true);
+}; // cLiveRecorder::cLiveRecorder
+
+cLiveRecorder::~cLiveRecorder() {
+	int maxWait = WAIT_TERMINATE_COUNT;
+	CancelWritingBuffer();
+	while(IsWritingBuffer() && maxWait--)
+		usleep(WAIT_TERMINATE_SLEEP);
+	Activate(false);
+	Cleanup();
+}; // cLiveRecorder::~cLiveRecorder
+
+bool cLiveRecorder::IsWritingBuffer() {
+	return index && ((cLiveIndex *)index)->IsWritingBuffer();
+} // cLiveRecorder::IsWritingBuffer
+
+void cLiveRecorder::CancelWritingBuffer() {
+	if(index) ((cLiveIndex *)index)->CancelWritingBuffer();
+} // cLiveRecorder::CancelWritingBuffer
+
+bool cLiveRecorder::NextFile(void) {
+	if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
+		if(RunningLowOnDiskSpace() && index)
+			((cLiveIndex *)index)->DropFile();
+		if (fileSize > MEGABYTE(off_t(Setup.LiveBufferMaxFileSize)) || RunningLowOnDiskSpace()) {
+			recordFile = fileName->NextFile();
+			fileSize = 0;
+		} // if
+	} // if
+	return recordFile != NULL;
+} // cLiveRecorder::NextFile
+
+int cLiveRecorder::LastIFrame() {
+	if(!index) return 0;
+	int ret = index->GetNextIFrame(index->Last()-1, false);
+	return (ret > 0) ? ret : 0;
+}; // cLiveRecorder::LastIFrame
+
+int cLiveRecorder::LastFrame() { 
+	return index ? index->Last() : 0;
+}; // cLiveRecorder::LastFrame
+
+void cLiveRecorder::SetResume(int Index) { 
+	if(index) ((cLiveIndex *)index)->SetResume(Index);
+}; // cLiveRecorder::SetResume
+
+bool cLiveRecorder::SetBufferStart(time_t Start) {
+	if(!index) return false;
+	if(time(NULL) <= Start) return false;
+	int Frames = SecondsToFrames(time(NULL)-Start, frameDetector ? frameDetector->FramesPerSecond() : DEFAULTFRAMESPERSECOND); //test stop livebuffer 
+	return ((cLiveIndex *)index)->SetBufferStart(Frames);
+} // cLiveRecorder::SetBufferStart
+
+cIndex *cLiveRecorder::GetIndex() { 
+	return index;
+}; // cLiveRecorder::GetIndex
+
+bool cLiveRecorder::Cleanup() {
+	if(FileName()) 
+                if(-1 == system(cString::sprintf("ls -l %s/* | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", FileName()))) // for symlink video.xx
+                        return false;
+        else 
+		if(-1 == system(cString::sprintf("rm -rf %s/*", FileName())))
+			return false;
+	return true;
+}; // cLiveRecorder::Cleanup
+
+bool cLiveRecorder::Prepare() {
+	if (!MakeDirs(FileName(), true)) return false;
+	return Cleanup();
+}; // cLiveRecorder::Prepare
+
+const char *cLiveRecorder::FileName() {
+	if(!(const char *)liveFileName && BufferDirectory)
+		liveFileName = cString::sprintf("%s/LiveBuffer", BufferDirectory);
+	return liveFileName;
+}; // cLiveRecorder::FileName
+
+void cLiveRecorder::Activate(bool On) {
+	cRecorder::Activate(On);
+	if(!On) broken=true;
+} // cLiveRecorder::Activate
+
+void cLiveRecorder::Receive(uchar *Data, int Length) {
+	if(broken) {
+		isyslog("Continue live recorder on broken stream (maybe due to switching to same channel on other device)");
+		TsSetTeiOnBrokenPackets(Data, Length);
+		broken = false;
+	} // if
+	cRecorder::Receive(Data, Length);
+} // cLiveRecorder::Receive
+
+/*****************************************************************************/
+
+cBufferRecorder::cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex)
+                :cRecorder(FileName, Channel, Priority)
+                ,liveBufferIndex(LiveBufferIndex)
+                ,dropData(false) {
+	if(liveBufferIndex) dropData=true; // Drop new data till we have written most of the live buffer data
+} // cBufferRecorder::cBufferRecorder
+
+cBufferRecorder::~cBufferRecorder() {
+	if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+} // cBufferRecorder::~cBufferRecorder
+
+void cBufferRecorder::Action(void) {
+	if(liveBufferIndex)
+		FillInitialData(NULL, 0);
+	dropData=false;
+	cRecorder::Action();
+	if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+	liveBufferIndex = NULL;
+} // cBufferRecorder::Action
+
+void cBufferRecorder::Activate(bool On) {
+	if(!On && liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+	cRecorder::Activate(On);
+} // cBufferRecorder::Activate
+
+void cBufferRecorder::Receive(uchar *Data, int Length) {
+	if(!dropData) cRecorder::Receive(Data, Length);
+} // cBufferRecorder::Receive
+
+void cBufferRecorder::FillInitialData(uchar *Data, int Size) {
+	if(liveBufferIndex) {
+		int64_t search_pts = Data ? TsGetPts(Data, Size) : -1;
+		int maxWait = WAIT_WRITING_COUNT;
+		uchar buffer[MAXFRAMESIZE];
+		int Length;
+		bool Independent;
+		bool found = false;
+		while(!Data || (Size >= TS_SIZE)) {
+			cUnbufferedFile *file = ((cLiveIndex *)liveBufferIndex)->GetNextBuffer(Length, Independent);
+			if(!file) {
+				if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
+					isyslog("Writing buffer canceled by user");
+					if(fileSize) TsSetTeiOnBrokenPackets(Data, Size);
+					((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+					liveBufferIndex = NULL;
+					return;
+				} // if
+				if(!Data || !Size) return;
+				if(!maxWait--)
+					break;
+				usleep(WAIT_WRITING_SLEEP);
+				continue;
+			} // if
+			if (!NextFile())
+				break;
+			int len = ReadFrame(file, buffer, Length, sizeof(buffer));
+			if(len < TS_SIZE) {
+				isyslog("Failed to read live buffer data");
+				break;
+			} // if
+			if(Data && Independent && (search_pts == TsGetPts(buffer, len))) {
+				found = true;
+				break;
+			} // if
+			if (index)
+				index->Write(Independent, fileName->Number(), fileSize);
+			if (recordFile->Write(buffer, len) < 0) {
+				isyslog("Failed to write live buffer data");
+				break;
+			} // if
+			fileSize += len;
+		} // while
+		if(Data) {
+			isyslog("%lld bytes from live buffer %swritten to recording", fileSize, found ? "seamless ": "");
+			if(!found && fileSize) TsSetTeiOnBrokenPackets(Data, Size);
+			((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+			liveBufferIndex = NULL;
+		} else if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
+			isyslog("%lld bytes from live buffer written to recording (aborted)", fileSize);
+			((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+			liveBufferIndex = NULL;
+		} // if
+	} else if (Data && fileSize)
+		TsSetTeiOnBrokenPackets(Data, Size);
+} // cBufferRecorder::FillInitialData
+
+#endif /*VDRVERSNUM*/
+#endif /*USE_LIVEBUFFER*/
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/livebuffer.h vdr-liveb/livebuffer.h
--- vdr/livebuffer.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-liveb/livebuffer.h	2011-10-27 09:56:48.322237585 +0200
@@ -0,0 +1,47 @@ 
+#ifndef LIVEBUFFER_H
+#define LIVEBUFFER_H
+
+#ifdef USE_LIVEBUFFER
+#include "config.h"
+#if VDRVERSNUM >= 10716
+
+#include "recorder.h"
+
+class cLiveRecorder : public cRecorder {
+public:
+	cLiveRecorder(const cChannel *Channel);
+	virtual bool NextFile(void);
+	virtual ~cLiveRecorder();
+	virtual bool IsWritingBuffer();
+	virtual void CancelWritingBuffer();
+	virtual int LastIFrame();
+	virtual int LastFrame();
+	virtual void SetResume(int Index);
+	virtual bool SetBufferStart(time_t Start);
+	virtual cIndex *GetIndex();
+	static bool Cleanup();
+	static bool Prepare();
+	static const char *FileName();
+protected:
+	virtual void Activate(bool On);
+	virtual void Receive(uchar *Data, int Length);
+	bool broken;
+	static cString liveFileName;
+}; // cLiveRecorder
+
+class cBufferRecorder : public cRecorder {
+public:
+	cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex);
+	virtual ~cBufferRecorder();
+	virtual void FillInitialData(uchar *Data, int Size);
+protected:
+	virtual void Action(void);
+	virtual void Activate(bool On);
+	virtual void Receive(uchar *Data, int Length);
+	cIndex *liveBufferIndex;
+	bool dropData;
+}; // cBufferRecorder
+
+#endif /*VDRVERSNUM*/
+#endif /*USE_LIVEBUFFER*/
+#endif /*LIVEBUFFER_H*/
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/Make.config vdr-liveb/Make.config
--- vdr/Make.config	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/Make.config	2011-10-27 09:56:48.212237585 +0200
@@ -29,4 +29,6 @@ 
 
 BIDI = 1
 GRAPHTFT = 1
-DEFINES += -DUSE_GRAPHTFT
+USE_LIVEBUFFER = 1
+DEFINES += -DUSE_GRAPHTFT -DUSE_LIVEBUFFER
+
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/Makefile vdr-liveb/Makefile
--- vdr/Makefile	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/Makefile	2011-10-27 09:56:48.212237585 +0200
@@ -64,6 +64,11 @@ 
 LIBS += $(shell pkg-config --libs fribidi)
 endif
 
+ifdef USE_LIVEBUFFER
+DEFINES += -DUSE_LIVEBUFFER
+OBJS += livebuffer.o
+endif
+
 LIRC_DEVICE ?= /dev/lircd
 RCU_DEVICE  ?= /dev/ttyS1
 
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/menu.c vdr-liveb/menu.c
--- vdr/menu.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/menu.c	2011-10-27 09:56:48.332237585 +0200
@@ -3449,7 +3449,11 @@ 
 
 class cMenuSetupRecord : public cMenuSetupBase {
 private:
-  const char *pauseKeyHandlingTexts[3];
+#ifdef USE_LIVEBUFFER
+  const char *pauseKeyHandlingTexts[4];
+#else
+   const char *pauseKeyHandlingTexts[3];
+#endif /*USE_LIVEBUFFER*/
   const char *delTimeshiftRecTexts[3];
 public:
   cMenuSetupRecord(void);
@@ -3460,6 +3464,9 @@ 
   pauseKeyHandlingTexts[0] = tr("do not pause live video");
   pauseKeyHandlingTexts[1] = tr("confirm pause live video");
   pauseKeyHandlingTexts[2] = tr("pause live video");
+#ifdef USE_LIVEBUFFER
+  pauseKeyHandlingTexts[3] = tr("Timeshift");
+#endif /*USE_LIVEBUFFER*/ 
   delTimeshiftRecTexts[0] = tr("no");
   delTimeshiftRecTexts[1] = tr("confirm");
   delTimeshiftRecTexts[2] = tr("yes");
@@ -3469,7 +3476,12 @@ 
   Add(new cMenuEditIntItem( tr("Setup.Recording$Primary limit"),             &data.PrimaryLimit, 0, MAXPRIORITY));
   Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"),          &data.DefaultPriority, 0, MAXPRIORITY));
   Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"),      &data.DefaultLifetime, 0, MAXLIFETIME));
-  Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"),        &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+#ifdef USE_LIVEBUFFER
+  Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"),        &data.PauseKeyHandling, 4, pauseKeyHandlingTexts));
+  Add(new cMenuEditIntItem( tr("Timeshift size (min)"),                     &data.LiveBufferSize, 1, 300)); // TODO fix name and min/max values
+#else
+   Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"),        &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+#endif /*USE_LIVEBUFFER*/
   Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"),            &data.PausePriority, 0, MAXPRIORITY));
   Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"),        &data.PauseLifetime, 0, MAXLIFETIME));
   Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"),          &data.UseSubtitle));
@@ -4681,7 +4693,11 @@ 
   isyslog("record %s", fileName);
   if (MakeDirs(fileName, true)) {
      const cChannel *ch = timer->Channel();
+#ifdef USE_LIVEBUFFER
+     recorder = new cBufferRecorder(fileName, ch, timer->Priority(), cRecordControls::GetLiveBuffer(timer));
+#else
      recorder = new cRecorder(fileName, ch, timer->Priority());
+#endif
      if (device->AttachReceiver(recorder)) {
         Recording.WriteInfo();
         cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
@@ -4766,6 +4782,10 @@ 
 cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
 int cRecordControls::state = 0;
 
+#ifdef USE_LIVEBUFFER
+cLiveRecorder *cRecordControls::liveRecorder = NULL;
+#endif /*USE_LIVEBUFFER*/
+
 bool cRecordControls::Start(cTimer *Timer, bool Pause)
 {
   if (!Timer) {
@@ -4882,8 +4902,31 @@ 
       }
 }
 
+
+#ifdef USE_LIVEBUFFER
+bool cRecordControls::StartLiveBuffer(eKeys Key) {
+   if(Setup.PauseKeyHandling == 3 && liveRecorder) {
+     int pos = liveRecorder->LastIFrame();
+     isyslog("Enter timeshift at %d / %d", pos, liveRecorder->LastFrame());
+     liveRecorder->SetResume(pos?pos:liveRecorder->LastFrame());
+     cReplayControl::SetRecording(cLiveRecorder::FileName(), tr("Timeshift mode"));
+     cReplayControl *rc = new cReplayControl;
+     cControl::Launch(rc);
+     cControl::Attach();
+     rc->ProcessKey(Key);
+     rc->Show(); // show progressbar at the start of livebuffer
+     return true;
+  } // if
+  return false;
+} // cRecordControls::StartLiveBuffer
+#endif /*USE_LIVEBUFFER*/
+
 bool cRecordControls::PauseLiveVideo(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(StartLiveBuffer(kPause))
+     return true;
+#endif /*USE_LIVEBUFFER*/
   Skins.Message(mtStatus, tr("Pausing live video..."));
   cReplayControl::SetRecording(NULL, NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed()
   if (Start(NULL, true)) {
@@ -4900,6 +4943,54 @@ 
   return false;
 }
 
+#ifdef USE_LIVEBUFFER
+void cRecordControls::SetLiveChannel(cDevice *Device, const cChannel *Channel) {
+	if(liveRecorder) {
+		if(Channel && Device && (liveRecorder->ChannelID()==Channel->GetChannelID()))
+			Device->AttachReceiver(liveRecorder);
+		else
+			DELETENULL(liveRecorder);
+	} // if
+	if(Device && Channel) cControl::Launch(new cTransferControl(Device, Channel));
+	if(Setup.PauseKeyHandling == 3 && Channel && Device && !liveRecorder) { 
+		if (cLiveRecorder::Prepare()) {
+			liveRecorder = new cLiveRecorder(Channel);
+			if(!Device->AttachReceiver(liveRecorder))
+				DELETENULL(liveRecorder);
+		} // if
+	} // if
+} // cRecordControls::SetLiveChannel
+
+bool cRecordControls::CanSetLiveChannel(const cChannel *Channel) {
+	if(liveRecorder && Channel && (liveRecorder->ChannelID()==Channel->GetChannelID())) return true;
+	return !IsWritingBuffer();
+} // cRecordControls::CanSetLiveChannel
+
+bool cRecordControls::IsWritingBuffer() {
+	return liveRecorder ? liveRecorder->IsWritingBuffer() : false;
+} // cRecordControls::IsWritingBuffer
+
+void cRecordControls::CancelWritingBuffer() {
+	if(liveRecorder && liveRecorder->IsWritingBuffer()) {
+		liveRecorder->CancelWritingBuffer();
+		sleep(1); // allow recorder to really stop
+	} // if
+} // cRecordControls::CancelWritingBuffer
+
+cIndex *cRecordControls::GetLiveBuffer(cTimer *Timer) {
+	if(!liveRecorder || !Timer || !Timer->Channel()) return NULL;
+	if(!(liveRecorder->ChannelID() == Timer->Channel()->GetChannelID())) return NULL;
+	if(!liveRecorder->SetBufferStart(Timer->StartTime())) return NULL;
+	return liveRecorder->GetIndex();
+} // cRecordControls::GetLiveBuffer
+
+cIndex *cRecordControls::GetLiveIndex(const char *FileName) {
+	if(!FileName || strcmp(cLiveRecorder::FileName(), FileName)) return NULL;
+	return liveRecorder ? liveRecorder->GetIndex() : NULL;
+} // cRecordControls::GetLiveIndex
+
+#endif /* USE_LIVEBUFFER */
+
 const char *cRecordControls::GetInstantId(const char *LastInstantId)
 {
   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
@@ -5104,21 +5195,30 @@ 
 
 void cReplayControl::ShowMode(void)
 {
-  if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) {
+  if (visible || (Setup.ShowReplayMode && !cOsd::IsOpen())) {
      bool Play, Forward;
      int Speed;
      if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) {
         bool NormalPlay = (Play && Speed == -1);
+        bool Paused = (!Play && Speed == -1);
 
         if (!visible) {
            if (NormalPlay)
               return; // no need to do indicate ">" unless there was a different mode displayed before
            visible = modeOnly = true;
+
+           // if newly paused show full replay osd; ie modeOnly = false
+           if (Paused)  {
+               modeOnly = (lastPlay == Play);
+           }
+
            displayReplay = Skins.Current()->DisplayReplay(modeOnly);
            }
 
-        if (modeOnly && !timeoutShow && NormalPlay)
+        // osd times out when replaying normally OR when paused and full osd is shown
+        if (!timeoutShow && (NormalPlay|| (!modeOnly && Paused)))
            timeoutShow = time(NULL) + MODETIMEOUT;
+
         displayReplay->SetMode(Play, Forward, Speed);
         lastPlay = Play;
         lastForward = Forward;
@@ -5132,6 +5232,45 @@ 
   int Current, Total;
 
   if (GetIndex(Current, Total) && Total > 0) {
+#ifdef USE_LIVEBUFFER
+     int first=0;
+     cIndex *idx = cRecordControls::GetLiveIndex(fileName);
+     if(idx) first = idx->First(); // Normalize displayed values
+     Current -= first;
+     if(Current < 0) Current = 0;
+     Total   -= first;
+     if(Total < 0) Total = 0;
+     time_t now = time(NULL);
+     static time_t last_sched_check = 0;
+     if(displayReplay && idx && (last_sched_check != now)) {
+        last_sched_check = now; // Only check every second
+        cSchedulesLock SchedulesLock;
+        const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
+        if (Schedules) {
+           const char *display_title = NULL;// = title;
+           const cSchedule *Schedule = Schedules->GetSchedule(Channels.GetByNumber(cDevice::CurrentChannel()));
+           if (Schedule) {
+              time_t Time = now - round(((double)Total - Current) / FramesPerSecond());
+              const cEvent *event = Schedule->GetEventAround(Time);
+              if (event) display_title = event->Title();
+           } // if
+
+           // no event title; show channel name
+           if (!display_title) {
+               cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+               display_title = channel->Name();
+           }
+
+           // set title as "Timeshift mode: <event title> "
+           // OR "Timeshift mode: <channel name>"
+           // if neither is possible leave title as such
+           if (display_title)
+               displayReplay->SetTitle(cString::sprintf("%s: %s",
+                                                        tr("Timeshift mode"),
+                                                        display_title));
+        } // if
+     } // if
+#endif /*USE_LIVEBUFFER*/
      if (!visible) {
         displayReplay = Skins.Current()->DisplayReplay(modeOnly);
         displayReplay->SetMarks(&marks);
@@ -5233,6 +5372,9 @@ 
 
 void cReplayControl::TimeSearch(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   timeSearchTime = timeSearchPos = 0;
   timeSearchHide = false;
   if (modeOnly)
@@ -5251,6 +5393,9 @@ 
 
 void cReplayControl::MarkToggle(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   int Current, Total;
   if (GetIndex(Current, Total, true)) {
      cMark *m = marks.Get(Current);
@@ -5273,6 +5418,9 @@ 
 
 void cReplayControl::MarkJump(bool Forward)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   if (marks.Count()) {
      int Current, Total;
      if (GetIndex(Current, Total)) {
@@ -5296,6 +5444,9 @@ 
 
 void cReplayControl::MarkMove(bool Forward)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   int Current, Total;
   if (GetIndex(Current, Total)) {
      cMark *m = marks.Get(Current);
@@ -5320,6 +5471,9 @@ 
 
 void cReplayControl::EditCut(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   if (fileName) {
      Hide();
      if (!cCutter::Active()) {
@@ -5338,6 +5492,9 @@ 
 
 void cReplayControl::EditTest(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   int Current, Total;
   if (GetIndex(Current, Total)) {
      cMark *m = marks.Get(Current);
@@ -5369,7 +5526,14 @@ 
   if (Key == kNone)
      marks.Update();
   if (visible) {
+
+      if (Key != kNone /*&& !modeOnly*/ && timeoutShow) {
+          printf("timeout reset +%d\n", MODETIMEOUT);
+          timeoutShow = time(NULL) + MODETIMEOUT;
+      }
+
      if (timeoutShow && time(NULL) > timeoutShow) {
+         printf("timed out \n");
         Hide();
         ShowMode();
         timeoutShow = 0;
@@ -5386,12 +5550,34 @@ 
      return osContinue;
      }
   bool DoShowMode = true;
+
+#ifdef USE_LIVEBUFFER
+  if (cRecordControls::GetLiveIndex(fileName) && (Key >= k0) && (Key <= k9))
+      return osSwitchChannel;
+#endif /*USE_LIVEBUFFER*/
   switch (int(Key)) {
     // Positioning:
+#ifdef USE_LIVEBUFFER
+    case kUp:      if(cRecordControls::GetLiveIndex(fileName)) {
+                      cDevice::SwitchChannel(1);
+                      return osEnd;
+                   } // if
+                   // NO break
+  case kPlay:
+      Play(); break;
+    case kDown:    if(cRecordControls::GetLiveIndex(fileName)) {
+                      cDevice::SwitchChannel(-1);
+                      return osEnd;
+                   } // if
+                   // NO break
+  case kPause:   Pause();
+      break;
+#else
     case kPlay:
     case kUp:      Play(); break;
     case kPause:
     case kDown:    Pause(); break;
+#endif /*USE_LIVEBUFFER*/
     case kFastRew|k_Release:
     case kLeft|k_Release:
                    if (Setup.MultiSpeedMode) break;
@@ -5402,11 +5588,67 @@ 
                    if (Setup.MultiSpeedMode) break;
     case kFastFwd:
     case kRight:   Forward(); break;
-    case kRed:     TimeSearch(); break;
     case kGreen|k_Repeat:
     case kGreen:   SkipSeconds(-60); break;
     case kYellow|k_Repeat:
     case kYellow:  SkipSeconds( 60); break;
+#ifdef USE_LIVEBUFFER
+    case kRed:     if(cRecordControls::GetLiveIndex(fileName)) {
+
+                       if (!(visible && !modeOnly)) return osUnknown;
+                       else {} // fall through to case kRecord
+                               // since Timeshift ON and replay OSD is shown
+                       } // if
+                       else { //timeshift off
+                           TimeSearch();
+                           break;
+                       } // else
+                       // No break
+    case kRecord:  if(cRecordControls::GetLiveIndex(fileName)) {
+                      int frames = 0;
+                      int Current, Total;
+                      if(GetIndex(Current, Total))
+                         frames = Total-Current;
+                      cTimer *timer = new cTimer(true, false, Channels.GetByNumber(cDevice::CurrentChannel()), frames / FramesPerSecond());
+                      Timers.Add(timer);
+                      Timers.SetModified();
+                      if (cRecordControls::Start(timer))
+                         Skins.Message(mtInfo, tr("Recording started"));
+                      else
+                         Timers.Del(timer);
+                   } // if
+                   break;
+    case kPrev|k_Repeat:
+    case kPrev:    if (lastSkipTimeout.TimedOut()) {
+                      lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
+                      lastSkipKey = kPrev;
+                   }
+                   else if (RAWKEY(lastSkipKey) != kPrev && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
+                      lastSkipSeconds /= 2;
+                      lastSkipKey = kNone;
+                   }
+                   lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
+                   SkipSeconds(-lastSkipSeconds); break;
+    case kNext|k_Repeat:
+    case kNext:    if (lastSkipTimeout.TimedOut()) {
+                      lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
+                      lastSkipKey = kNext;	
+                   }
+                   else if (RAWKEY(lastSkipKey) != kNext && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
+                      lastSkipSeconds /= 2;
+                      lastSkipKey = kNone;
+                   }
+                   lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
+                   SkipSeconds(lastSkipSeconds); break;
+    case kBlue:    if(cRecordControls::GetLiveIndex(fileName))
+                       if(!(visible && !modeOnly))
+                           return osUnknown;
+                   //NO break
+    case kStop:    Hide();
+                   Stop();
+                   return osEnd;
+#else
+    case kRed:     TimeSearch(); break;
     case kPrev|k_Repeat:
     case kPrev:    if (lastSkipTimeout.TimedOut()) {
                       lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
@@ -5433,6 +5675,8 @@ 
     case kBlue:    Hide();
                    Stop();
                    return osEnd;
+#endif /*USE_LIVEBUFFER*/
+
     default: {
       bool play, forward;
       int speed;
@@ -5494,7 +5738,20 @@ 
                            else
                               Show();
                            break;
-            case kBack:    if (Setup.DelTimeshiftRec) { 
+            case kBack:
+#ifdef USE_LIVEBUFFER
+                           if (visible && !modeOnly) {
+                              Hide();
+                              DoShowMode = true;
+                              break;
+                           }
+                           if(cRecordControls::GetLiveIndex(fileName)) {
+                              Hide();
+                              Stop();
+                              return osEnd;
+                           } // if
+#endif /*USE_LIVEBUFFER*/
+                           if (Setup.DelTimeshiftRec) {
                               cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
                               return rc && rc->InstantId() ? osEnd : osRecordings;
                               }
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/menu.h vdr-liveb/menu.h
--- vdr/menu.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/menu.h	2011-10-27 09:56:48.332237585 +0200
@@ -18,6 +18,12 @@ 
 #include "menuitems.h"
 #include "recorder.h"
 #include "skins.h"
+#ifdef USE_LIVEBUFFER
+#include "livebuffer.h"
+#endif /*USE_LIVEBUFFER*/
+
+
+
 
 class cMenuText : public cOsdMenu {
 private:
@@ -243,10 +249,18 @@ 
 private:
   static cRecordControl *RecordControls[];
   static int state;
+#ifdef USE_LIVEBUFFER
+protected:
+  friend class cRecordControl;
+  static cLiveRecorder *liveRecorder;
+#endif /*USE_LIVEBUFFER*/
 public:
   static bool Start(cTimer *Timer = NULL, bool Pause = false);
   static void Stop(const char *InstantId);
   static bool PauseLiveVideo(void);
+#ifdef USE_LIVEBUFFER
+  static bool StartLiveBuffer(eKeys Key);
+#endif /*USE_LIVEBUFFER*/
   static const char *GetInstantId(const char *LastInstantId);
   static cRecordControl *GetRecordControl(const char *FileName);
   static void Process(time_t t);
@@ -255,6 +269,14 @@ 
   static void Shutdown(void);
   static void ChangeState(void) { state++; }
   static bool StateChanged(int &State);
+#ifdef USE_LIVEBUFFER
+  static void SetLiveChannel(cDevice *Device, const cChannel *Channel);
+  static bool CanSetLiveChannel(const cChannel *Channel);
+  static bool IsWritingBuffer();
+  static void CancelWritingBuffer();
+  static cIndex *GetLiveBuffer(cTimer *Timer);
+  static cIndex *GetLiveIndex(const char *FileName);
+#endif /*USE_LIVEBUFFER*/
   };
 
 class cReplayControl : public cDvbPlayerControl {
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/osdbase.h vdr-liveb/osdbase.h
--- vdr/osdbase.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/osdbase.h	2011-10-27 09:56:48.332237585 +0200
@@ -33,6 +33,9 @@ 
                 osSwitchDvb,
                 osBack,
                 osEnd,
+#ifdef USE_LIVEBUFFER
+                osSwitchChannel,
+#endif /*USE_LIVEBUFFER*/
                 os_User, // the following values can be used locally
                 osUser1,
                 osUser2,
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/player.c vdr-liveb/player.c
--- vdr/player.c	2011-10-17 13:40:24.000000000 +0200
+++ vdr-liveb/player.c	2011-10-27 09:56:48.332237585 +0200
@@ -10,6 +10,11 @@ 
 #include "player.h"
 #include "i18n.h"
 
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#include "transfer.h"
+#endif /*USE_LIVEBUFFER*/
+
 // --- cPlayer ---------------------------------------------------------------
 
 cPlayer::cPlayer(ePlayMode PlayMode)
@@ -68,6 +73,12 @@ 
 
 void cControl::Launch(cControl *Control)
 {
+#ifdef USE_LIVEBUFFER
+  if(!dynamic_cast<cTransferControl *>(Control)) {
+     if(!dynamic_cast<cReplayControl *>(Control) || strcmp(cLiveRecorder::FileName(), cReplayControl::NowReplaying()))
+        cRecordControls::SetLiveChannel(NULL, NULL);
+  } // if
+#endif /*USE_LIVEBUFFER*/
   cMutexLock MutexLock(&mutex);
   cControl *c = control; // keeps control from pointing to uninitialized memory
   control = Control;
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/po/de_DE.po vdr-liveb/po/de_DE.po
--- vdr/po/de_DE.po	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/po/de_DE.po	2011-10-27 09:56:48.332237585 +0200
@@ -25,6 +25,9 @@ 
 msgid "Can't start Transfer Mode!"
 msgstr "Transfer-Mode kann nicht gestartet werden!"
 
+msgid "Still writing timeshift data to recording. Abort?"
+msgstr "Timeshift-Daten werden noch in Aufnahme kopiert. Abbrechen?"
+
 msgid "off"
 msgstr "aus"
 
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/recorder.c vdr-liveb/recorder.c
--- vdr/recorder.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/recorder.c	2011-10-27 09:56:48.332237585 +0200
@@ -24,6 +24,9 @@ 
 cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
 :cReceiver(Channel, Priority)
 ,cThread("recording")
+#ifdef USE_LIVEBUFFER
+,handleError(true)
+#endif /*USE_LIVEBUFFER*/
 {
   recordingName = strdup(FileName);
 
@@ -140,6 +143,9 @@ 
                     InfoWritten = true;
                     }
                  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
+#ifdef USE_LIVEBUFFER
+                    if(!FirstIframeSeen) FillInitialData(b, r);
+#endif /*USE_LIVEBUFFER*/
                     FirstIframeSeen = true; // start recording with the first I-frame
                     if (!NextFile())
                        break;
@@ -165,7 +171,11 @@ 
               ringBuffer->Del(Count);
               }
            }
+#ifdef USE_LIVEBUFFER
+        if (handleError && (time(NULL) - t > MAXBROKENTIMEOUT)) {
+#else
         if (time(NULL) - t > MAXBROKENTIMEOUT) {
+#endif
            esyslog("ERROR: video data stream broken");
            ShutdownHandler.RequestEmergencyExit();
            t = time(NULL);
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/recorder.h vdr-liveb/recorder.h
--- vdr/recorder.h	2011-10-17 13:40:24.000000000 +0200
+++ vdr-liveb/recorder.h	2011-10-27 09:56:48.342237585 +0200
@@ -17,18 +17,33 @@ 
 #include "thread.h"
 
 class cRecorder : public cReceiver, cThread {
+#ifdef USE_LIVEBUFFER
+protected:
+#else
 private:
+#endif /*USE_LIVEBUFFER*/
   cRingBufferLinear *ringBuffer;
   cFrameDetector *frameDetector;
   cPatPmtGenerator patPmtGenerator;
   cFileName *fileName;
+#ifdef USE_LIVEBUFFER
+  cIndex *index;
+  bool handleError;
+#else
   cIndexFile *index;
+#endif /*USE_LIVEBUFFER*/
   cUnbufferedFile *recordFile;
   char *recordingName;
   off_t fileSize;
   time_t lastDiskSpaceCheck;
+#ifdef USE_LIVEBUFFER
+  virtual bool RunningLowOnDiskSpace(void);
+  virtual bool NextFile(void);
+  virtual void FillInitialData(uchar *Data, int Size) {};
+#else
   bool RunningLowOnDiskSpace(void);
   bool NextFile(void);
+#endif /*USE_LIVEBUFFER*/
 protected:
   virtual void Activate(bool On);
   virtual void Receive(uchar *Data, int Length);
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/recording.h vdr-liveb/recording.h
--- vdr/recording.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/recording.h	2011-10-27 09:56:48.342237585 +0200
@@ -288,7 +288,26 @@ 
 struct tIndexTs;
 class cIndexFileGenerator;
 
+#ifdef USE_LIVEBUFFER
+class cIndex {
+public:
+  virtual bool Ok(void) =0;
+  virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) =0;
+  virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) =0;
+  virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) =0;
+  virtual int Get(uint16_t FileNumber, off_t FileOffset) =0;
+  virtual int First(void) {return 0;};
+  virtual int Last(void) =0;
+  virtual int GetResume(void) =0;
+  virtual bool StoreResume(int Index) =0;
+  virtual bool IsStillRecording(void) =0;
+  virtual void Delete(void) =0;
+  };
+
+class cIndexFile : public cIndex {
+#else
 class cIndexFile {
+#endif /*USE_LIVEBUFFER*/
 private:
   int f;
   cString fileName;
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/timers.c vdr-liveb/timers.c
--- vdr/timers.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/timers.c	2011-10-27 09:56:48.342237585 +0200
@@ -25,7 +25,11 @@ 
 
 // --- cTimer ----------------------------------------------------------------
 
+#ifdef USE_LIVEBUFFER
+cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel, int Forerun)
+#else
 cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
+#endif /*USE_LIVEBUFFER*/
 {
   startTime = stopTime = 0;
   lastSetEvent = 0;
@@ -35,7 +39,11 @@ 
   if (Instant)
      SetFlags(tfActive | tfInstant);
   channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
+#ifdef USE_LIVEBUFFER
+  time_t t = time(NULL) - Forerun;
+#else
   time_t t = time(NULL);
+#endif /*USE_LIVEBUFFER*/
   struct tm tm_r;
   struct tm *now = localtime_r(&t, &tm_r);
   day = SetTime(t, 0);
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/timers.h vdr-liveb/timers.h
--- vdr/timers.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/timers.h	2011-10-27 09:56:48.342237585 +0200
@@ -44,7 +44,11 @@ 
   char *aux;
   const cEvent *event;
 public:
+#ifdef USE_LIVEBUFFER
+  cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL, int Forerun = 0);
+#else
   cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL);
+#endif /*USE_LIVEBUFFER*/
   cTimer(const cEvent *Event);
   cTimer(const cTimer &Timer);
   virtual ~cTimer();
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/vdr.c vdr-liveb/vdr.c
--- vdr/vdr.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/vdr.c	2011-10-27 09:56:48.342237585 +0200
@@ -218,6 +218,9 @@ 
 
   static struct option long_options[] = {
       { "audio",    required_argument, NULL, 'a' },
+#ifdef USE_LIVEBUFFER
+      { "buffer",   required_argument, NULL, 'b' },
+#endif /* USE_LIVEBUFFER */
       { "config",   required_argument, NULL, 'c' },
       { "daemon",   no_argument,       NULL, 'd' },
       { "device",   required_argument, NULL, 'D' },
@@ -254,10 +257,20 @@ 
     };
 
   int c;
+#ifdef USE_LIVEBUFFER
+  while ((c = getopt_long(argc, argv, "a:b:c:dD:e:E:g:hi:kl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
+#else
   while ((c = getopt_long(argc, argv, "a:c:dD:e:E:g:hi:kl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
+#endif /* USE_LIVEBUFFER */
         switch (c) {
           case 'a': AudioCommand = optarg;
                     break;
+#ifdef USE_LIVEBUFFER
+          case 'b': BufferDirectory = optarg;
+                    if(optarg && *optarg && optarg[strlen(optarg)-1] == '/')
+                       optarg[strlen(optarg)-1] = 0;
+                    break;
+#endif /* USE_LIVEBUFFER */
           case 'c': ConfigDirectory = optarg;
                     break;
           case 'd': DaemonMode = true; break;
@@ -427,6 +440,9 @@ 
      if (DisplayHelp) {
         printf("Usage: vdr [OPTIONS]\n\n"          // for easier orientation, this is column 80|
                "  -a CMD,   --audio=CMD    send Dolby Digital audio to stdin of command CMD\n"
+#ifdef USE_LIVEBUFFER
+               "  -b DIR,   --buffer=DIR   use DIR as LiveBuffer directory\n"
+#endif /*USE_LIVEBUFFER*/
                "  -c DIR,   --config=DIR   read config files from DIR (default: %s)\n"
                "  -d,       --daemon       run in daemon mode\n"
                "  -D NUM,   --device=NUM   use only the given DVB device (NUM = 0, 1, 2...)\n"
@@ -596,9 +612,12 @@ 
 
   if (!PluginManager.LoadPlugins(true))
      EXIT(2);
-
   // Configuration data:
 
+#ifdef USE_LIVEBUFFER
+  if (!BufferDirectory)
+     BufferDirectory = VideoDirectory;
+#endif /*USE_LIVEBUFFER*/
   if (!ConfigDirectory)
      ConfigDirectory = DEFAULTCONFDIR;
 
@@ -1116,6 +1135,15 @@ 
                   cDisplaySubtitleTracks::Process(key);
                key = kNone;
                break;
+#ifdef USE_LIVEBUFFER
+          case kFastRew:
+               if (!Interact) {
+                  DELETE_MENU;
+                  if(cRecordControls::StartLiveBuffer(key))
+                     key = kNone;
+               } // if
+               break;
+#endif /*USE_LIVEBUFFER*/
           // Pausing live video:
           case kPause:
                if (!cControl::Control()) {
@@ -1223,6 +1251,28 @@ 
                             else
                                cControl::Shutdown();
                             break;
+#ifdef USE_LIVEBUFFER
+             case osSwitchChannel:
+                            switch (key) {
+                                // Toggle channels:
+                                case kChanPrev:
+                                case k0: {
+                                    if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel
+                                        || (LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1]))
+                                        PreviousChannelIndex ^= 1;
+                                    Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
+                                    break;
+                                }
+                                case k1 ... k9:
+                                    DELETE_MENU;
+                                    cControl::Shutdown();
+                                    Menu = new cDisplayChannel(NORMALKEY(key));
+                                    break;
+                                default:
+                                    break;
+                            } // switch
+                            break;
+#endif /*USE_LIVEBUFFER*/
              default:       ;
              }
            }
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/videodir.c vdr-liveb/videodir.c
--- vdr/videodir.c	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/videodir.c	2011-10-27 09:56:48.342237585 +0200
@@ -23,6 +23,9 @@ 
 //#define HARDLINK_TEST_ONLY
 
 const char *VideoDirectory = VIDEODIR;
+#ifdef USE_LIVEBUFFER
+const char *BufferDirectory = NULL;
+#endif /*USE_LIVEBUFFER*/
 
 class cVideoDirectory {
 private:
@@ -109,17 +112,32 @@ 
 cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags)
 {
   const char *ActualFileName = FileName;
+#ifdef USE_LIVEBUFFER
+  bool SepBufferDir = false;
 
   // Incoming name must be in base video directory:
+   if (strstr(FileName, VideoDirectory) != FileName) {
+     if (strstr(FileName, BufferDirectory) == FileName)
+        SepBufferDir = true;
+     else {
+#else
   if (strstr(FileName, VideoDirectory) != FileName) {
+#endif /*USE_LIVEBUFFER*/
      esyslog("ERROR: %s not in %s", FileName, VideoDirectory);
      errno = ENOENT; // must set 'errno' - any ideas for a better value?
      return NULL;
      }
+#ifdef USE_LIVEBUFFER
+     }
+#endif /*USE_LIVEBUFFER*/
   // Are we going to create a new file?
   if ((Flags & O_CREAT) != 0) {
      cVideoDirectory Dir;
+#ifdef USE_LIVEBUFFER
+     if (Dir.IsDistributed() && !SepBufferDir) {
+#else
      if (Dir.IsDistributed()) {
+#endif /*USE_LIVEBUFFER*/
         // Find the directory with the most free space:
         int MaxFree = Dir.FreeMB();
         while (Dir.Next()) {
diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' vdr/videodir.h vdr-liveb/videodir.h
--- vdr/videodir.h	2011-10-27 09:53:31.000000000 +0200
+++ vdr-liveb/videodir.h	2011-10-27 09:56:48.342237585 +0200
@@ -14,6 +14,9 @@ 
 #include "tools.h"
 
 extern const char *VideoDirectory;
+#ifdef USE_LIVEBUFFER
+extern const char *BufferDirectory;
+#endif /*USE_LIVEBUFFER*/
 
 cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
 int CloseVideoFile(cUnbufferedFile *File);