Updated patch for vdr 1.7.22 in gentoo

Message ID 4F01E54C.4030808@ekass.net
State New
Headers

Commit Message

Marc Jan. 2, 2012, 5:11 p.m. UTC
  On 01/01/2012 20:44, René wrote:
> Hi Marc,
>
> I pasted the output over here: http://pastebin.com/8eMsEuMf
>
> It looks that it can't find the files to patch. Maybe the patch was 
> not compatible with Gentoo?
>
> It would be great if you could send an updated ebuild to 1.7.22 :-) I 
> really miss the days when i updated my vdr on every release.. Now i 
> have been "stuck" with 1.6.x for ages..
>
> Do you think that it would be possible to add the livebuffer-patch 
> back to the official gentoo overlay? Even better if it could be added 
> as a basic feature of vdr (that could be turned on/off from the 
> settings :-) Klaus? Please, please, pleaaaaase :-)
>
>
> Regards,
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
The patch applies, portage try several levels with patch and it 
succeeded at p1 (log start at 255).

The modifications I've made work but you have more files that fail than 
me. Perhaps you use more use flags. I don't know if the extension 
patches are applied all the time or only when you set the use flag for 
them. You could try to build vdr without any use flag and see if it 
compiles.

I attached the patch for 1.7.22 and I removed the ifdef statement in 
Makefile so there is no more need to patch the ebuild, just to put the 
patch in VDR_LOCAL_PATCHES_DIR. You can still create a folder for each 
version ( 
http://linuxtv.org/vdrwiki/index.php/Gentoo_VDR_own_patches#Putting_patches_there 
)

To add this patch to the overlay, have you tried to contact the 
maintainer ? It could be interested, its address is in the changelog.

Regards,

Marc.
  

Comments

Rene Hertell Jan. 2, 2012, 9:44 p.m. UTC | #1
On 02.01.2012 19:11 , Marc wrote:
> The patch applies, portage try several levels with patch and it
> succeeded at p1 (log start at 255).
>
> The modifications I've made work but you have more files that fail than
> me. Perhaps you use more use flags. I don't know if the extension
> patches are applied all the time or only when you set the use flag for
> them. You could try to build vdr without any use flag and see if it
> compiles.

I got this patch installed without any errors, but it does not work 
(pressing rewind does not do anything).. In the setup-menu i see the 
LiveBuffer settings, but i can't enter the settings.

I just started a "vdrplugin-rebuild -se rebuild", so let's see if it's 
some other plugin that's screwing things up..

> To add this patch to the overlay, have you tried to contact the
> maintainer ? It could be interested, its address is in the changelog.

If i get this to work, then i'll do my best to persuade him to add this 
back to the overlay!! :-)


Regards,

René
  
Rene Hertell Jan. 2, 2012, 10:44 p.m. UTC | #2
On 02.01.2012 23:44 , René wrote:
> On 02.01.2012 19:11 , Marc wrote:
>> The patch applies, portage try several levels with patch and it
>> succeeded at p1 (log start at 255).
>>
>> The modifications I've made work but you have more files that fail than
>> me. Perhaps you use more use flags. I don't know if the extension
>> patches are applied all the time or only when you set the use flag for
>> them. You could try to build vdr without any use flag and see if it
>> compiles.
>
> I got this patch installed without any errors, but it does not work
> (pressing rewind does not do anything).. In the setup-menu i see the
> LiveBuffer settings, but i can't enter the settings.
>
> I just started a "vdrplugin-rebuild -se rebuild", so let's see if it's
> some other plugin that's screwing things up..
>
>> To add this patch to the overlay, have you tried to contact the
>> maintainer ? It could be interested, its address is in the changelog.
>
> If i get this to work, then i'll do my best to persuade him to add this
> back to the overlay!! :-)

A quick update.. Pressing the LiveBUffer menu-item in the setupmenu 
gives me the following in my logfile:

Jan  3 00:42:27 vdr vdr: [16766] ERROR: Gentoo-VDR-Setup: <menu 
System=LiveBuffer> not known

Does anyone know what this error means?

if someone knows what's messed up in the previous error-message, then i 
get the same error for BigPatch

Jan  3 00:42:29 vdr vdr: [16766] ERROR: Gentoo-VDR-Setup: <menu 
System=BigPatch> not known

Regards,

René
  
Marc Jan. 2, 2012, 11 p.m. UTC | #3
On 02/01/2012 23:44, René wrote:
> On 02.01.2012 23:44 , René wrote:
>> On 02.01.2012 19:11 , Marc wrote:
>>> The patch applies, portage try several levels with patch and it
>>> succeeded at p1 (log start at 255).
>>>
>>> The modifications I've made work but you have more files that fail than
>>> me. Perhaps you use more use flags. I don't know if the extension
>>> patches are applied all the time or only when you set the use flag for
>>> them. You could try to build vdr without any use flag and see if it
>>> compiles.
>>
>> I got this patch installed without any errors, but it does not work
>> (pressing rewind does not do anything).. In the setup-menu i see the
>> LiveBuffer settings, but i can't enter the settings.
>>
>> I just started a "vdrplugin-rebuild -se rebuild", so let's see if it's
>> some other plugin that's screwing things up..
>>
>>> To add this patch to the overlay, have you tried to contact the
>>> maintainer ? It could be interested, its address is in the changelog.
>>
>> If i get this to work, then i'll do my best to persuade him to add this
>> back to the overlay!! :-)
>
> A quick update.. Pressing the LiveBUffer menu-item in the setupmenu 
> gives me the following in my logfile:
>
> Jan  3 00:42:27 vdr vdr: [16766] ERROR: Gentoo-VDR-Setup: <menu 
> System=LiveBuffer> not known
>
> Does anyone know what this error means?
>
> if someone knows what's messed up in the previous error-message, then 
> i get the same error for BigPatch
>
> Jan  3 00:42:29 vdr vdr: [16766] ERROR: Gentoo-VDR-Setup: <menu 
> System=BigPatch> not known
>
> Regards,
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
I don't have livebuffer in the menu. I activated the livebuffer by 
setting 'Pause key handling' to 'Timeshit'.

Regards,

Marc.
  
Rene Hertell Jan. 2, 2012, 11:16 p.m. UTC | #4
On 03.01.2012 1:00 , Marc wrote:
> I don't have livebuffer in the menu. I activated the livebuffer by
> setting 'Pause key handling' to 'Timeshit'.

Ok, is this something you have to manually edit in setup.conf? I don't 
have this option in the setup for recording.. Here i have only "pause 
live video", "do not pause live video" and "confirm pause live video"..

Do you btw have to press pause to activate livebuffer, or can you just 
press rewind in the middle of playback? In the 1.6.x patch i don't have 
to press pause, i just press rewind..


Regards,

René
  
Marc Jan. 3, 2012, 8:29 a.m. UTC | #5
On 03/01/2012 00:16, René wrote:
> On 03.01.2012 1:00 , Marc wrote:
>> I don't have livebuffer in the menu. I activated the livebuffer by
>> setting 'Pause key handling' to 'Timeshit'.
>
> Ok, is this something you have to manually edit in setup.conf? I don't 
> have this option in the setup for recording.. Here i have only "pause 
> live video", "do not pause live video" and "confirm pause live video"..
It's probably because the patch is not applied, this option is added in 
the menu by it. You should have something like this when you compile vdr :
...
  * Unifdef sources 
...                                                   [ ok ]

  * Applying local patches
  * Applying livebuffer-1.7.22.patch 
...                                  [ ok ]
 >>> Source prepared.
...
>
> Do you btw have to press pause to activate livebuffer, or can you just 
> press rewind in the middle of playback? In the 1.6.x patch i don't 
> have to press pause, i just press rewind..
I checked and yes, the patch still works like this. I can either use 
pause or rewind.
>
>
> Regards,
>
> René
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
  
Rene Hertell Jan. 3, 2012, 2:12 p.m. UTC | #6
On 03.01.2012 10:29 , Marc wrote:
> * Unifdef sources ... [ ok ]
>
> * Applying local patches
> * Applying livebuffer-1.7.22.patch ... [ ok ]
>  >>> Source prepared.

Ok, then i have something fishy going on.. I have the livebufer in the 
menu, but then as you said most likely it's not applied correctly. I 
have to check the build-log if it complains something

> I checked and yes, the patch still works like this. I can either use
> pause or rewind.

Great! :-)
  
Rene Hertell Jan. 3, 2012, 11:29 p.m. UTC | #7
On 03.01.2012 16:12 , René wrote:
> On 03.01.2012 10:29 , Marc wrote:
>> * Unifdef sources ... [ ok ]
>>
>> * Applying local patches
>> * Applying livebuffer-1.7.22.patch ... [ ok ]
>> >>> Source prepared.
>
> Ok, then i have something fishy going on.. I have the livebufer in the
> menu, but then as you said most likely it's not applied correctly. I
> have to check the build-log if it complains something

Marc,

The patch is now found by the ebuild, but  it wont' get applied. The 
problem is just that it does not work. For some reasons it get's errors 
like "can't find file to patch"...

Here is the failed .out-file
http://pastebin.com/MufLFssk

My patchdir is /etc/vdr_patch_dir and this patch is here:
/etc/vdr_patch_dir/1.7.22/livebuffer-1.7.22.patch

What could here be wrong?

René
  
Marc Jan. 4, 2012, 8:36 a.m. UTC | #8
On 04/01/2012 00:29, René wrote:
> On 03.01.2012 16:12 , René wrote:
>> On 03.01.2012 10:29 , Marc wrote:
>>> * Unifdef sources ... [ ok ]
>>>
>>> * Applying local patches
>>> * Applying livebuffer-1.7.22.patch ... [ ok ]
>>> >>> Source prepared.
>>
>> Ok, then i have something fishy going on.. I have the livebufer in the
>> menu, but then as you said most likely it's not applied correctly. I
>> have to check the build-log if it complains something
>
> Marc,
>
> The patch is now found by the ebuild, but  it wont' get applied. The 
> problem is just that it does not work. For some reasons it get's 
> errors like "can't find file to patch"...
No, it's still the same problem, the patch applies but you have more 
difference in your source tree when the local patches are applied. You 
can see what parts of the patch don't apply in the log at line 205.

For me, I only have Makefile, config.c and config.h witch differ but you 
have more :
patching file device.c
Hunk #1 FAILED at 18.
patching file dvbplayer.c
Hunk #4 FAILED at 281.
patching file menu.c
Hunk #4 FAILED at 4169.
Hunk #18 FAILED at 4998.
...

I only use the noepg use flag, did you tried to compile vdr without any 
use flags ? If it works like this, try to add each use flag one by one 
to see where is the problem.

Regards,

Marc.
  
Rene Hertell Jan. 6, 2012, 2:42 p.m. UTC | #9
On 04.01.2012 10:36 , Marc wrote:

> I only use the noepg use flag, did you tried to compile vdr without any
> use flags ? If it works like this, try to add each use flag one by one
> to see where is the problem.

Indeed, looks that the following useflags messes up the livebuffer-patch

  dvlvidprefer liemikuutio jumpplay setup ttxtsubs

Now i'll wait for a good spot to try a new update to 1.7.22 (between 
timer-recordings, and shows my wife watches) :-)

René
  
Rene Hertell Jan. 6, 2012, 6:20 p.m. UTC | #10
On 06.01.2012 16:42 , René wrote:
> On 04.01.2012 10:36 , Marc wrote:
>
>> I only use the noepg use flag, did you tried to compile vdr without any
>> use flags ? If it works like this, try to add each use flag one by one
>> to see where is the problem.
>
> Indeed, looks that the following useflags messes up the livebuffer-patch
>
> dvlvidprefer liemikuutio jumpplay setup ttxtsubs
>
> Now i'll wait for a good spot to try a new update to 1.7.22 (between
> timer-recordings, and shows my wife watches) :-)

Yeah! Now i got livebuffer to work with vdr 1.7.22! FINALLY a more 
Modern vdr :-) The following use-flags did not work with the 
livebuffer-patch: dvlvidprefer liemikuutio jumpplay setup ttxtsubs

I wonder if it's a big thing to get the LiveBuffer working as an 
official patch for gentoo.. Who was it that i should e-mail and try to 
convince to accept this as a patch to vdr? If it work with an use-flag, 
then it would be really great :-) If there would be some guides on what 
i could do to help out in the process, then i'm happy to help out!


Regards,

René
  
Joerg Bornkessel Jan. 6, 2012, 10:30 p.m. UTC | #11
> I wonder if it's a big thing to get the LiveBuffer working as an
> official patch for gentoo..

Iam work on this, to integrate the patch in the extp-ng...

anyway, in any parts the the ifdef/ifndef/else construct's from the
 expt-ng patch make it not eazy...

also iam work on the ebuild to remove/add/change some crap
thats while is it package.masked in the moment...

use it of your own risk ;)
  
Rene Hertell Jan. 7, 2012, 12:13 a.m. UTC | #12
On 07.01.2012 24:30 , Joerg Bornkessel wrote:
>
>> I wonder if it's a big thing to get the LiveBuffer working as an
>> official patch for gentoo..
>
> Iam work on this, to integrate the patch in the extp-ng...
>
> anyway, in any parts the the ifdef/ifndef/else construct's from the
>   expt-ng patch make it not eazy...
>
> also iam work on the ebuild to remove/add/change some crap
> thats while is it package.masked in the moment...
>
> use it of your own risk ;)
>

Oh yeah :-) Can't wait for the patch to get ready :-) Please inform when 
you have done any progress :-) If I can help you in anyway, please don't 
hesitate to ask me!

René
  
Rene Hertell Jan. 17, 2012, 7:41 p.m. UTC | #13
On 07.01.2012 24:30 , Joerg Bornkessel wrote:
>
>> I wonder if it's a big thing to get the LiveBuffer working as an
>> official patch for gentoo..
>
> Iam work on this, to integrate the patch in the extp-ng...
>
> anyway, in any parts the the ifdef/ifndef/else construct's from the
>   expt-ng patch make it not eazy...
>
> also iam work on the ebuild to remove/add/change some crap
> thats while is it package.masked in the moment...
>
> use it of your own risk ;)
>

Hi Joerg,

Do you have good news for us Gentoo-users? :-) Have you managed to get 
livebuffer integrated into the expt-ng patch? :-)

Regards,

René
  

Patch

diff -Naur vdr-1.7.22/Makefile vdr-1.7.22-livebuffer/Makefile
--- vdr-1.7.22/Makefile	2011-12-04 15:41:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/Makefile	2011-12-31 11:16:23.000000000 +0100
@@ -62,6 +62,9 @@ 
 LIBS += $(shell pkg-config --libs fribidi)
 endif
 
+DEFINES += -DUSE_LIVEBUFFER
+OBJS += livebuffer.o
+
 LIRC_DEVICE ?= /var/run/lirc/lircd
 RCU_DEVICE  ?= /dev/ttyS1
 
diff -Naur vdr-1.7.22/config.c vdr-1.7.22-livebuffer/config.c
--- vdr-1.7.22/config.c	2011-12-03 16:21:30.000000000 +0100
+++ vdr-1.7.22-livebuffer/config.c	2011-12-31 11:16:23.000000000 +0100
@@ -461,6 +461,10 @@ 
   InitialChannel = "";
   DeviceBondings = "";
   InitialVolume = -1;
+#ifdef USE_LIVEBUFFER
+  LiveBufferSize = 30;
+  LiveBufferMaxFileSize = 100;
+#endif /*USE_LIVEBUFFER*/
   ChannelsWrap = 0;
   EmergencyExit = 1;
 }
@@ -655,6 +659,10 @@ 
   else if (!strcasecmp(Name, "InitialChannel"))      InitialChannel     = Value;
   else if (!strcasecmp(Name, "InitialVolume"))       InitialVolume      = atoi(Value);
   else if (!strcasecmp(Name, "DeviceBondings"))      DeviceBondings     = 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
@@ -752,6 +760,9 @@ 
   Store("InitialChannel",     InitialChannel);
   Store("InitialVolume",      InitialVolume);
   Store("DeviceBondings",     DeviceBondings);
+#ifdef USE_LIVEBUFFER
+  Store("LiveBufferSize",     LiveBufferSize);
+#endif  /* LIVEBUFFER */
   Store("ChannelsWrap",       ChannelsWrap);
   Store("EmergencyExit",      EmergencyExit);
 
diff -Naur vdr-1.7.22/config.h vdr-1.7.22-livebuffer/config.h
--- vdr-1.7.22/config.h	2011-12-03 15:19:52.000000000 +0100
+++ vdr-1.7.22-livebuffer/config.h	2011-12-31 11:16:23.000000000 +0100
@@ -307,6 +307,10 @@ 
   int CurrentVolume;
   int CurrentDolby;
   int InitialVolume;
+#ifdef USE_LIVEBUFFER
+  int LiveBufferSize;
+  int LiveBufferMaxFileSize;
+#endif /*USE_LIVEBUFFER*/
   int ChannelsWrap;
   int EmergencyExit;
   int __EndData__;
diff -Naur vdr-1.7.22/device.c vdr-1.7.22-livebuffer/device.c
--- vdr-1.7.22/device.c	2011-10-16 16:36:43.000000000 +0200
+++ vdr-1.7.22-livebuffer/device.c	2011-12-31 11:16:23.000000000 +0100
@@ -18,6 +18,10 @@ 
 #include "receiver.h"
 #include "status.h"
 #include "transfer.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#include "interface.h"
+#endif /*USE_LIVEBUFFER*/
 
 // --- cLiveSubtitle ---------------------------------------------------------
 
@@ -663,6 +667,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");
         }
@@ -720,8 +732,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 -Naur vdr-1.7.22/device.h vdr-1.7.22-livebuffer/device.h
--- vdr-1.7.22/device.h	2011-12-04 14:38:17.000000000 +0100
+++ vdr-1.7.22-livebuffer/device.h	2011-12-31 11:16:23.000000000 +0100
@@ -32,7 +32,11 @@ 
 #define VOLUMEDELTA         5 // used to increase/decrease the volume
 #define MAXOCCUPIEDTIMEOUT 99 // max. time (in seconds) a device may be occupied
 
+#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 -Naur vdr-1.7.22/dvbplayer.c vdr-1.7.22-livebuffer/dvbplayer.c
--- vdr-1.7.22/dvbplayer.c	2010-03-07 15:24:26.000000000 +0100
+++ vdr-1.7.22-livebuffer/dvbplayer.c	2011-12-31 11:16:23.000000000 +0100
@@ -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)
@@ -205,7 +211,12 @@ 
   cRingBufferFrame *ringBuffer;
   cPtsIndex ptsIndex;
   cFileName *fileName;
+#ifdef USE_LIVEBUFFER
+  cIndex *index;
+  cIndexFile *indexFile;
+#else
   cIndexFile *index;
+#endif /*USE_LIVEBUFFER*/
   cUnbufferedFile *replayFile;
   double framesPerSecond;
   bool isPesRecording;
@@ -270,18 +281,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()
@@ -289,7 +317,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;
 }
@@ -387,9 +419,11 @@ 
   uchar *p = NULL;
   int pc = 0;
 
+#ifndef USE_LIVEBUFFER
   readIndex = Resume();
   if (readIndex >= 0)
      isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+#endif /*USE_LIVEBUFFER*/
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
@@ -436,6 +470,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;
@@ -454,6 +492,15 @@ 
                       off_t FileOffset;
                       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;
                       }
@@ -587,7 +634,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 -Naur vdr-1.7.22/livebuffer.c vdr-1.7.22-livebuffer/livebuffer.c
--- vdr-1.7.22/livebuffer.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/livebuffer.c	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/livebuffer.h vdr-1.7.22-livebuffer/livebuffer.h
--- vdr-1.7.22/livebuffer.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/livebuffer.h	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/menu.c vdr-1.7.22-livebuffer/menu.c
--- vdr-1.7.22/menu.c	2011-12-04 15:52:38.000000000 +0100
+++ vdr-1.7.22-livebuffer/menu.c	2011-12-31 11:16:23.000000000 +0100
@@ -3066,7 +3066,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);
@@ -3077,6 +3081,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");
@@ -3086,7 +3093,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));
@@ -4157,7 +4169,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);
@@ -4242,6 +4258,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)
 {
   static time_t LastNoDiskSpaceMessage = 0;
@@ -4313,8 +4333,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)) {
@@ -4331,6 +4374,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++) {
@@ -4529,21 +4620,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;
@@ -4557,6 +4657,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);
@@ -4658,6 +4797,9 @@ 
 
 void cReplayControl::TimeSearch(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   timeSearchTime = timeSearchPos = 0;
   timeSearchHide = false;
   if (modeOnly)
@@ -4676,6 +4818,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);
@@ -4696,6 +4841,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)) {
@@ -4710,6 +4858,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);
@@ -4734,6 +4885,9 @@ 
 
 void cReplayControl::EditCut(void)
 {
+#ifdef USE_LIVEBUFFER
+  if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
   if (fileName) {
      Hide();
      if (!cCutter::Active()) {
@@ -4752,6 +4906,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);
@@ -4783,7 +4940,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;
@@ -4800,12 +4964,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;
@@ -4816,15 +5002,52 @@ 
                    if (Setup.MultiSpeedMode) break;
     case kFastFwd:
     case kRight:   Forward(); break;
-    case kRed:     TimeSearch(); 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 kBlue:    if(cRecordControls::GetLiveIndex(fileName))
+                       if(!(visible && !modeOnly))
+                           return osUnknown;
+                   //NO break
+    case kStop:    Hide();
+                   Stop();
+                   return osEnd;
+#else
+    case kRed:     TimeSearch(); break;
     case kStop:
     case kBlue:    Hide();
                    Stop();
                    return osEnd;
+#endif /*USE_LIVEBUFFER*/
+
     default: {
       DoShowMode = false;
       switch (int(Key)) {
@@ -4855,7 +5078,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 -Naur vdr-1.7.22/menu.h vdr-1.7.22-livebuffer/menu.h
--- vdr-1.7.22/menu.h	2010-03-06 17:15:59.000000000 +0100
+++ vdr-1.7.22-livebuffer/menu.h	2011-12-31 11:16:23.000000000 +0100
@@ -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:
@@ -236,10 +242,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);
@@ -248,6 +262,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 -Naur vdr-1.7.22/osdbase.h vdr-1.7.22-livebuffer/osdbase.h
--- vdr-1.7.22/osdbase.h	2010-01-16 15:25:31.000000000 +0100
+++ vdr-1.7.22-livebuffer/osdbase.h	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/player.c vdr-1.7.22-livebuffer/player.c
--- vdr-1.7.22/player.c	2007-07-20 17:25:24.000000000 +0200
+++ vdr-1.7.22-livebuffer/player.c	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/po/de_DE.po vdr-1.7.22-livebuffer/po/de_DE.po
--- vdr-1.7.22/po/de_DE.po	2011-12-03 16:35:34.000000000 +0100
+++ vdr-1.7.22-livebuffer/po/de_DE.po	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/recorder.c vdr-1.7.22-livebuffer/recorder.c
--- vdr-1.7.22/recorder.c	2011-09-04 11:26:44.000000000 +0200
+++ vdr-1.7.22-livebuffer/recorder.c	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/recorder.h vdr-1.7.22-livebuffer/recorder.h
--- vdr-1.7.22/recorder.h	2010-12-27 12:17:04.000000000 +0100
+++ vdr-1.7.22-livebuffer/recorder.h	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/recording.h vdr-1.7.22-livebuffer/recording.h
--- vdr-1.7.22/recording.h	2011-12-04 14:38:17.000000000 +0100
+++ vdr-1.7.22-livebuffer/recording.h	2011-12-31 11:16:23.000000000 +0100
@@ -264,7 +264,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 -Naur vdr-1.7.22/timers.c vdr-1.7.22-livebuffer/timers.c
--- vdr-1.7.22/timers.c	2011-08-06 15:13:54.000000000 +0200
+++ vdr-1.7.22-livebuffer/timers.c	2011-12-31 11:16:23.000000000 +0100
@@ -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 -Naur vdr-1.7.22/timers.h vdr-1.7.22-livebuffer/timers.h
--- vdr-1.7.22/timers.h	2011-08-06 14:59:32.000000000 +0200
+++ vdr-1.7.22-livebuffer/timers.h	2011-12-31 11:16:23.000000000 +0100
@@ -43,7 +43,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 -Naur vdr-1.7.22/vdr.c vdr-1.7.22-livebuffer/vdr.c
--- vdr-1.7.22/vdr.c	2011-12-03 16:35:09.000000000 +0100
+++ vdr-1.7.22-livebuffer/vdr.c	2011-12-31 11:16:23.000000000 +0100
@@ -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' },
@@ -251,10 +254,20 @@ 
     };
 
   int c;
+#ifdef USE_LIVEBUFFER
+  while ((c = getopt_long(argc, argv, "a:b:c:dD:e:E:g:hi:l: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:l: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;
@@ -420,6 +433,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"
@@ -586,9 +602,12 @@ 
 
   if (!PluginManager.LoadPlugins(true))
      EXIT(2);
-
   // Configuration data:
 
+#ifdef USE_LIVEBUFFER
+  if (!BufferDirectory)
+     BufferDirectory = VideoDirectory;
+#endif /*USE_LIVEBUFFER*/
   if (!ConfigDirectory)
      ConfigDirectory = DEFAULTCONFDIR;
 
@@ -1091,6 +1110,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()) {
@@ -1198,6 +1226,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 -Naur vdr-1.7.22/videodir.c vdr-1.7.22-livebuffer/videodir.c
--- vdr-1.7.22/videodir.c	2008-02-16 14:00:03.000000000 +0100
+++ vdr-1.7.22-livebuffer/videodir.c	2011-12-31 11:16:23.000000000 +0100
@@ -20,6 +20,9 @@ 
 #include "tools.h"
 
 const char *VideoDirectory = VIDEODIR;
+#ifdef USE_LIVEBUFFER
+const char *BufferDirectory = NULL;
+#endif /*USE_LIVEBUFFER*/
 
 class cVideoDirectory {
 private:
@@ -106,17 +109,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 -Naur vdr-1.7.22/videodir.h vdr-1.7.22-livebuffer/videodir.h
--- vdr-1.7.22/videodir.h	2008-02-16 13:53:11.000000000 +0100
+++ vdr-1.7.22-livebuffer/videodir.h	2011-12-31 11:16:23.000000000 +0100
@@ -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);