diff --unified vdr-2.4.1-orig/device.c vdr-2.4.1/device.c
--- vdr-2.4.1-orig/device.c	2020-03-01 13:03:03.203698201 +0100
+++ vdr-2.4.1/device.c	2020-03-01 13:03:32.455812280 +0100
@@ -1847,6 +1847,17 @@
       Detach(receiver[i]);
 }
 
+cRecorder* cDevice::GetPreRecording(const cChannel *Channel)
+{
+  cMutexLock MutexLock(&mutexReceiver);
+  for (int i = 0; i < MAXRECEIVERS; i++) {
+      if (receiver[i])
+    	  if (receiver[i]->IsPreRecording(Channel))
+    		  return (cRecorder*)receiver[i];
+      }
+  return NULL;
+}
+
 // --- cTSBuffer -------------------------------------------------------------
 
 cTSBuffer::cTSBuffer(int File, int Size, int DeviceNumber)
diff --unified vdr-2.4.1-orig/device.h vdr-2.4.1/device.h
--- vdr-2.4.1-orig/device.h	2020-03-01 13:03:03.223698280 +0100
+++ vdr-2.4.1/device.h	2020-03-01 13:03:32.455812280 +0100
@@ -85,6 +85,7 @@
 
 class cPlayer;
 class cReceiver;
+class cRecorder;
 class cLiveSubtitle;
 
 class cDeviceHook : public cListObject {
@@ -842,6 +843,8 @@
        ///< Returns true if we are currently receiving. The parameter has no meaning (for backwards compatibility only).
   bool AttachReceiver(cReceiver *Receiver);
        ///< Attaches the given receiver to this device.
+  cRecorder* GetPreRecording(const cChannel *Channel);
+       ///< Get precocious recording for the channel if there is one.
   void Detach(cReceiver *Receiver);
        ///< Detaches the given receiver from this device.
   void DetachAll(int Pid);
diff --unified vdr-2.4.1-orig/dvbplayer.c vdr-2.4.1/dvbplayer.c
--- vdr-2.4.1-orig/dvbplayer.c	2020-03-01 13:03:03.223698280 +0100
+++ vdr-2.4.1/dvbplayer.c	2020-03-01 13:03:32.455812280 +0100
@@ -249,13 +249,14 @@
   cUnbufferedFile *replayFile;
   double framesPerSecond;
   bool isPesRecording;
-  bool pauseLive;
+  ReplayState replayState;
   bool eof;
   bool firstPacket;
   ePlayModes playMode;
   ePlayDirs playDir;
   int trickSpeed;
   int readIndex;
+  int startIndex;
   bool readIndependent;
   cFrame *readFrame;
   cFrame *playFrame;
@@ -271,6 +272,8 @@
   virtual void Action(void);
 public:
   cDvbPlayer(const char *FileName, bool PauseLive);
+  cDvbPlayer(const char *FileName, ReplayState newReplayState);
+  void Construct(const char *FileName, ReplayState newReplayState);
   virtual ~cDvbPlayer();
   void SetMarks(const cMarks *Marks);
   bool Active(void) { return cThread::Running(); }
@@ -297,6 +300,17 @@
 cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
 :cThread("dvbplayer")
 {
+  Construct(FileName, PauseLive? restPauseLive : restNormal);
+}
+
+cDvbPlayer::cDvbPlayer(const char *FileName, ReplayState newReplayState)
+:cThread("dvbplayer")
+{
+  Construct(FileName, newReplayState);
+}
+
+void cDvbPlayer::Construct(const char *FileName, ReplayState newReplayState)
+{  
   nonBlockingFileReader = NULL;
   ringBuffer = NULL;
   marks = NULL;
@@ -304,7 +318,8 @@
   cRecording Recording(FileName);
   framesPerSecond = Recording.FramesPerSecond();
   isPesRecording = Recording.IsPesRecording();
-  pauseLive = PauseLive;
+  replayState = newReplayState;
+  bool reuse = (replayState == restReusePause || replayState == restReuseRewind);
   eof = false;
   firstPacket = true;
   playMode = pmPlay;
@@ -323,15 +338,21 @@
      return;
   ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
   // Create the index file:
-  index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
+  index = new cIndexFile(FileName, false, isPesRecording, replayState == restPauseLive);
   if (!index)
      esyslog("ERROR: can't allocate index");
   else if (!index->Ok()) {
      delete index;
      index = NULL;
      }
-  else if (PauseLive)
+  else if (reuse)
      framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
+  startIndex = 0;
+  if (replayState == restReuseRewind || replayState == restReusePause) {
+     int Current, Total;
+     GetIndex(Current, Total, false);
+     startIndex = max(Total - 1, 0);
+     }     
 }
 
 cDvbPlayer::~cDvbPlayer()
@@ -481,8 +502,21 @@
   bool CutIn = false;
   bool AtLastMark = false;
 
-  if (pauseLive)
-     Goto(0, true);
+  if (replayState == restPauseLive) {
+    Goto(0, true);
+    }
+  else if (replayState == restReuseRewind || replayState == restReusePause) {
+    readIndex = startIndex;
+    Goto(readIndex, true);
+    playMode = pmPlay;
+    if (replayState == restReuseRewind) {
+    	Backward();
+        }
+    else if (replayState == restReusePause) {
+    	Pause();
+        }
+    }
+  
   while (Running()) {
         if (WaitingForData)
            WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
@@ -985,6 +1019,11 @@
 {
 }
 
+cDvbPlayerControl::cDvbPlayerControl(const char *FileName, ReplayState replayState)
+:cControl(player = new cDvbPlayer(FileName, replayState))
+{
+}
+
 cDvbPlayerControl::~cDvbPlayerControl()
 {
   Stop();
diff --unified vdr-2.4.1-orig/dvbplayer.h vdr-2.4.1/dvbplayer.h
--- vdr-2.4.1-orig/dvbplayer.h	2020-03-01 13:03:03.223698280 +0100
+++ vdr-2.4.1/dvbplayer.h	2020-03-01 13:03:32.455812280 +0100
@@ -16,6 +16,14 @@
 
 class cDvbPlayer;
 
+enum ReplayState
+{
+	restNormal,
+	restPauseLive,
+	restReusePause,
+	restReuseRewind
+};
+
 class cDvbPlayerControl : public cControl {
 private:
   cDvbPlayer *player;
@@ -25,6 +33,8 @@
        // If PauseLive is true, special care is taken to make sure the index
        // file of the recording is long enough to allow the player to display
        // the first frame in still picture mode.
+  cDvbPlayerControl(const char *FileName, ReplayState replayState);
+       // Sets up a player for the given file. replayState represents the initial state.
   virtual ~cDvbPlayerControl();
   void SetMarks(const cMarks *Marks);
   bool Active(void);
Gemeinsame Unterverzeichnisse: vdr-2.4.1-orig/libsi und vdr-2.4.1/libsi.
diff --unified vdr-2.4.1-orig/menu.c vdr-2.4.1/menu.c
--- vdr-2.4.1-orig/menu.c	2020-03-01 13:03:03.215698248 +0100
+++ vdr-2.4.1/menu.c	2020-03-01 13:10:08.853186639 +0100
@@ -5214,6 +5214,16 @@
 
 cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause)
 {
+  Construct(Device, Timers, Timer, Pause, NULL);
+}
+
+cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused)
+{
+  Construct(Device, Timers, Timer, Pause, reused);
+}
+
+void cRecordControl::Construct(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused)
+{
   const char *LastReplayed = cReplayControl::LastReplayed(); // must do this before locking schedules!
   // Whatever happens here, the timers will be modified in some way...
   Timers->SetModified();
@@ -5236,6 +5246,7 @@
   timer->SetPending(true);
   timer->SetRecording(true);
   event = timer->Event();
+  if (reused != NULL) *reused = false;
 
   if (event || GetEvent())
      dsyslog("Title: '%s' Subtitle: '%s'", event->Title(), event->ShortText());
@@ -5265,8 +5276,21 @@
   if (MakeDirs(fileName, true)) {
      Recording.WriteInfo(); // we write this *before* attaching the recorder to the device, to make sure the info file is present when the recorder needs to update the fps value!
      const cChannel *ch = timer->Channel();
-     recorder = new cRecorder(fileName, ch, timer->Priority());
-     if (device->AttachReceiver(recorder)) {
+
+     if (!Timer) {
+        recorder = device->GetPreRecording(ch);
+        if (recorder != NULL) {
+           recorder->ActivatePreRecording(fileName, timer->Priority());
+           if (reused != NULL) *reused = true;
+           }
+        }
+
+     if (recorder == NULL) {
+        recorder = new cRecorder(fileName, ch, timer->Priority());
+        if (!device->AttachReceiver(recorder)) DELETENULL(recorder);
+        }
+
+     if (recorder != NULL) {
         cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
         if (!Timer && !LastReplayed) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
            cReplayControl::SetRecording(fileName);
@@ -5276,8 +5300,6 @@
         Recordings->AddByName(fileName);
         return;
         }
-     else
-        DELETENULL(recorder);
      }
   else
      timer->SetDeferred(DEFERTIMER);
@@ -5348,7 +5370,7 @@
 cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
 int cRecordControls::state = 0;
 
-bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause)
+bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause, bool* reused)
 {
   static time_t LastNoDiskSpaceMessage = 0;
   int FreeMB = 0;
@@ -5382,7 +5404,7 @@
         if (!Timer || Timer->Matches()) {
            for (int i = 0; i < MAXRECORDCONTROLS; i++) {
                if (!RecordControls[i]) {
-                  RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause);
+                  RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause, reused);
                   return RecordControls[i]->Process(time(NULL));
                   }
                }
@@ -5404,6 +5426,11 @@
   return Start(Timers, NULL, Pause);
 }
 
+bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause)
+{
+  return Start(Timers, Timer, Pause, NULL);
+}
+
 void cRecordControls::Stop(const char *InstantId)
 {
   LOCK_TIMERS_WRITE;
@@ -5439,10 +5466,17 @@
 
 bool cRecordControls::PauseLiveVideo(void)
 {
+  return PauseLiveVideo(false);
+}
+
+bool cRecordControls::PauseLiveVideo(bool rewind)
+{
   Skins.Message(mtStatus, tr("Pausing live video..."));
+  bool reused = false;  
   cReplayControl::SetRecording(NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed()
-  if (Start(true)) {
-     cReplayControl *rc = new cReplayControl(true);
+  LOCK_TIMERS_WRITE;
+  if (Start(Timers, NULL, true)) {
+     cReplayControl *rc = new cReplayControl(rewind? restReuseRewind : reused? restReusePause : restPauseLive);
      cControl::Launch(rc);
      cControl::Attach();
      Skins.Message(mtStatus, NULL);
@@ -5585,7 +5619,18 @@
 cReplayControl::cReplayControl(bool PauseLive)
 :cDvbPlayerControl(fileName, PauseLive)
 {
-  cDevice::PrimaryDevice()->SetKeepTracks(PauseLive);
+  Construct(PauseLive? restPauseLive : restNormal);
+}
+
+cReplayControl::cReplayControl(ReplayState replayState)
+:cDvbPlayerControl(fileName, replayState)
+{
+  Construct(replayState);
+}
+
+void cReplayControl::Construct(ReplayState replayState)
+{
+  cDevice::PrimaryDevice()->SetKeepTracks(replayState == restPauseLive);
   currentReplayControl = this;
   displayReplay = NULL;
   marksModified = false;
diff --unified vdr-2.4.1-orig/menu.h vdr-2.4.1/menu.h
--- vdr-2.4.1-orig/menu.h	2020-03-01 13:03:03.223698280 +0100
+++ vdr-2.4.1/menu.h	2020-03-01 13:03:32.455812280 +0100
@@ -244,6 +244,8 @@
   bool GetEvent(void);
 public:
   cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer = NULL, bool Pause = false);
+  cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused);
+  void Construct(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused);
   virtual ~cRecordControl();
   bool Process(time_t t);
   cDevice *Device(void) { return device; }
@@ -259,10 +261,12 @@
   static int state;
 public:
   static bool Start(cTimers *Timers, cTimer *Timer, bool Pause = false);
+  static bool Start(cTimers *Timers, cTimer *Timer, bool Pause, bool* reused);
   static bool Start(bool Pause = false);
   static void Stop(const char *InstantId);
   static void Stop(cTimer *Timer);
   static bool PauseLiveVideo(void);
+  static bool PauseLiveVideo(bool rewind);
   static const char *GetInstantId(const char *LastInstantId);
   static cRecordControl *GetRecordControl(const char *FileName);
   static cRecordControl *GetRecordControl(const cTimer *Timer);
@@ -318,6 +322,8 @@
   void EditTest(void);
 public:
   cReplayControl(bool PauseLive = false);
+  cReplayControl(ReplayState replayState);
+  void Construct(ReplayState replayState);
   virtual ~cReplayControl();
   void Stop(void);
   virtual cOsdObject *GetInfo(void);
Gemeinsame Unterverzeichnisse: vdr-2.4.1-orig/PLUGINS und vdr-2.4.1/PLUGINS.
Gemeinsame Unterverzeichnisse: vdr-2.4.1-orig/po und vdr-2.4.1/po.
diff --unified vdr-2.4.1-orig/receiver.h vdr-2.4.1/receiver.h
--- vdr-2.4.1-orig/receiver.h	2020-03-01 13:03:03.219698262 +0100
+++ vdr-2.4.1/receiver.h	2020-03-01 13:03:32.455812280 +0100
@@ -85,6 +85,10 @@
                ///< case the device is needed otherwise, so code that uses a cReceiver
                ///< should repeatedly check whether it is still attached, and if
                ///< it isn't, delete it (or take any other appropriate measures).
+  virtual bool IsPreRecording(const cChannel *Channel) { return false; }
+               ///< prerecords given channel; may be turned into a disc recording.
+  virtual bool ActivatePreRecording(const char* fileName, int Priority) { return false; }
+  	  	  	   ///< turn prerecording into a disc recording
   };
 
 #endif //__RECEIVER_H
diff --unified vdr-2.4.1-orig/recorder.c vdr-2.4.1/recorder.c
--- vdr-2.4.1-orig/recorder.c	2020-03-01 13:03:03.223698280 +0100
+++ vdr-2.4.1/recorder.c	2020-03-01 13:03:32.455812280 +0100
@@ -24,44 +24,55 @@
 cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
 :cReceiver(Channel, Priority)
 ,cThread("recording")
+,ringBuffer(NULL), frameDetector(NULL), fileName(NULL), index(NULL), recordFile(NULL), recordingName(NULL)
 {
-  recordingName = strdup(FileName);
+	if (FileName != NULL)
+	{
+		InitializeFile(FileName, Channel);
+	}
+}
+
+void cRecorder::InitializeFile(const char *FileName, const cChannel *Channel)
+{
+	recordingName = strdup(FileName);
+
+	// Make sure the disk is up and running:
 
-  // Make sure the disk is up and running:
+	SpinUpDisk(FileName);
 
-  SpinUpDisk(FileName);
+	ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "Recorder");
+	ringBuffer->SetTimeouts(0, 100);
+	ringBuffer->SetIoThrottle();
 
-  ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "Recorder");
-  ringBuffer->SetTimeouts(0, 100);
-  ringBuffer->SetIoThrottle();
-
-  int Pid = Channel->Vpid();
-  int Type = Channel->Vtype();
-  if (!Pid && Channel->Apid(0)) {
-     Pid = Channel->Apid(0);
-     Type = 0x04;
-     }
-  if (!Pid && Channel->Dpid(0)) {
-     Pid = Channel->Dpid(0);
-     Type = 0x06;
-     }
-  frameDetector = new cFrameDetector(Pid, Type);
-  index = NULL;
-  fileSize = 0;
-  lastDiskSpaceCheck = time(NULL);
-  fileName = new cFileName(FileName, true);
-  int PatVersion, PmtVersion;
-  if (fileName->GetLastPatPmtVersions(PatVersion, PmtVersion))
-     patPmtGenerator.SetVersions(PatVersion + 1, PmtVersion + 1);
-  patPmtGenerator.SetChannel(Channel);
-  recordFile = fileName->Open();
-  if (!recordFile)
-     return;
-  // Create the index file:
-  index = new cIndexFile(FileName, true);
-  if (!index)
-     esyslog("ERROR: can't allocate index");
-     // let's continue without index, so we'll at least have the recording
+	int Pid = Channel->Vpid();
+	int Type = Channel->Vtype();
+	if (!Pid && Channel->Apid(0)) {
+		Pid = Channel->Apid(0);
+		Type = 0x04;
+		}
+	if (!Pid && Channel->Dpid(0)) {
+		Pid = Channel->Dpid(0);
+		Type = 0x06;
+		}
+	if (frameDetector == NULL) {
+		frameDetector = new cFrameDetector(Pid, Type);
+		}
+	index = NULL;
+	fileSize = 0;
+	lastDiskSpaceCheck = time(NULL);
+	fileName = new cFileName(FileName, true);
+	int PatVersion, PmtVersion;
+	if (fileName->GetLastPatPmtVersions(PatVersion, PmtVersion))
+		patPmtGenerator.SetVersions(PatVersion + 1, PmtVersion + 1);
+	patPmtGenerator.SetChannel(Channel);
+	recordFile = fileName->Open();
+	if (!recordFile)
+		return;
+	// Create the index file:
+	index = new cIndexFile(FileName, true);
+	if (!index)
+		esyslog("ERROR: can't allocate index");
+	// let's continue without index, so we'll at least have the recording
 }
 
 cRecorder::~cRecorder()
diff --unified vdr-2.4.1-orig/recorder.h vdr-2.4.1/recorder.h
--- vdr-2.4.1-orig/recorder.h	2020-03-01 13:03:03.239698341 +0100
+++ vdr-2.4.1/recorder.h	2020-03-01 13:03:32.455812280 +0100
@@ -16,8 +16,8 @@
 #include "ringbuffer.h"
 #include "thread.h"
 
-class cRecorder : public cReceiver, cThread {
-private:
+class cRecorder : public cReceiver, protected cThread {
+protected:
   cRingBufferLinear *ringBuffer;
   cFrameDetector *frameDetector;
   cPatPmtGenerator patPmtGenerator;
@@ -37,6 +37,9 @@
        ///< destroyed.
   virtual void Receive(const uchar *Data, int Length);
   virtual void Action(void);
+  void InitializeFile(const char *FileName, const cChannel *Channel);
+       ///< Starts recording to file.
+       ///< Called in constructor if file name has been given.
 public:
   cRecorder(const char *FileName, const cChannel *Channel, int Priority);
        ///< Creates a new recorder for the given Channel and
diff --unified vdr-2.4.1-orig/ringbuffer.c vdr-2.4.1/ringbuffer.c
--- vdr-2.4.1-orig/ringbuffer.c	2020-03-01 13:03:03.219698262 +0100
+++ vdr-2.4.1/ringbuffer.c	2020-03-01 13:03:32.455812280 +0100
@@ -368,6 +368,25 @@
   return NULL;
 }
 
+uchar *cRingBufferLinear::GetRest(int &Count)
+{
+  int Head = head;
+  if (getThreadTid <= 0)
+     getThreadTid = cThread::ThreadId();
+  int rest = Size() - tail;
+  int diff = Head - tail;
+  int cont = (diff >= 0) ? diff : Size() + diff - margin;
+  if (cont > rest)
+     cont = rest;
+  uchar *p = buffer + tail;
+  if (cont > 0) {
+     Count = gotten = cont;
+     return p;
+     }
+  WaitForGet();
+  return NULL;
+}
+
 void cRingBufferLinear::Del(int Count)
 {
   if (Count > gotten) {
diff --unified vdr-2.4.1-orig/ringbuffer.h vdr-2.4.1/ringbuffer.h
--- vdr-2.4.1-orig/ringbuffer.h	2020-03-01 13:03:03.219698262 +0100
+++ vdr-2.4.1/ringbuffer.h	2020-03-01 13:03:32.455812280 +0100
@@ -98,6 +98,12 @@
     ///< The data will remain in the buffer until a call to Del() deletes it.
     ///< Returns a pointer to the data, and stores the number of bytes
     ///< actually available in Count. If the returned pointer is NULL, Count has no meaning.
+  uchar *GetRest(int &Count);
+  ///< Gets data from the ring buffer disregarding the margin.
+  ///< Might have to be called several times to get all data.
+  ///< The data will remain in the buffer until a call to Del() deletes it.
+  ///< Returns a pointer to the data, and stores the number of bytes
+  ///< actually available in Count. If the returned pointer is NULL, Count has no meaning.
   void Del(int Count);
     ///< Deletes at most Count bytes from the ring buffer.
     ///< Count must be less or equal to the number that was returned by a previous
Gemeinsame Unterverzeichnisse: vdr-2.4.1-orig/symbols und vdr-2.4.1/symbols.
diff --unified vdr-2.4.1-orig/vdr.c vdr-2.4.1/vdr.c
--- vdr-2.4.1-orig/vdr.c	2020-03-01 13:03:03.227698294 +0100
+++ vdr-2.4.1/vdr.c	2020-03-01 13:03:32.455812280 +0100
@@ -1343,16 +1343,23 @@
                key = kNone;
                break;
           // Pausing live video:
+          case kFastRew:
+	      {
+        	  // test if there's a live buffer to rewind into...
+	          LOCK_CHANNELS_READ;
+        	  if (cDevice::ActualDevice()->GetPreRecording(Channels->GetByNumber(cDevice::CurrentChannel())) == NULL) {
+        		  break;
+        	      }
+	      }
+        	  // fall through to pause
           case kPlayPause:
           case kPause:
                if (!cControl::Control()) {
                   DELETE_MENU;
-                  if (Setup.PauseKeyHandling) {
-                     if (Setup.PauseKeyHandling > 1 || Interface->Confirm(tr("Pause live video?"))) {
-                        if (!cRecordControls::PauseLiveVideo())
-                           Skins.QueueMessage(mtError, tr("No free DVB device to record!"));
-                        }
-                     }
+                  if (Setup.PauseKeyHandling)
+                     if (Setup.PauseKeyHandling > 1 || Interface->Confirm(tr("Pause live video?")))
+                        if (!cRecordControls::PauseLiveVideo(int(key) == kFastRew))
+                            Skins.QueueMessage(mtError, tr("No free DVB device to record!"));
                   key = kNone; // nobody else needs to see this key
                   }
                break;
