updated patch for vdr-2.7.2
downloaded from https://www.vdr-portal.de/forum/index.php?thread/136461-announce-vdr-version-2-7-2-freigegeben/&postID=1374957#post1374957

diff --git a/device.c b/device.c
index b9c52190..15f650b2 100644
--- a/device.c
+++ b/device.c
@@ -1917,6 +1917,17 @@ void cDevice::DetachAllReceivers(void)
   ReleaseCamSlot();
 }
 
+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 --git a/device.h b/device.h
index fe60b3ef..4076f38e 100644
--- a/device.h
+++ b/device.h
@@ -85,6 +85,7 @@ struct tTrackId {
 
 class cPlayer;
 class cReceiver;
+class cRecorder;
 class cLiveSubtitle;
 
 class cDeviceHook : public cListObject {
@@ -867,6 +868,8 @@ public:
        ///< 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, bool ReleaseCam = true);
        ///< Detaches the given receiver from this device.
        ///< If ReleaseCam is true, the CAM slot will be released if it
diff --git a/dvbplayer.c b/dvbplayer.c
index c36fac70..9a0be3f6 100644
--- a/dvbplayer.c
+++ b/dvbplayer.c
@@ -249,13 +249,14 @@ private:
   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 @@ protected:
   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(); }
@@ -298,6 +301,17 @@ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
 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;
@@ -305,7 +319,8 @@ cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
   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;
@@ -324,15 +339,21 @@ cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
      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()
@@ -482,8 +503,21 @@ void cDvbPlayer::Action(void)
   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
@@ -995,6 +1029,11 @@ cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
   SetPlayer(player);
 }
 
+cDvbPlayerControl::cDvbPlayerControl(const char *FileName, ReplayState replayState)
+:cControl(player = new cDvbPlayer(FileName, replayState))
+{
+}
+
 cDvbPlayerControl::~cDvbPlayerControl()
 {
   Stop();
diff --git a/dvbplayer.h b/dvbplayer.h
index bac5b0c4..32a155b1 100644
--- a/dvbplayer.h
+++ b/dvbplayer.h
@@ -16,6 +16,14 @@
 
 class cDvbPlayer;
 
+enum ReplayState
+{
+	restNormal,
+	restPauseLive,
+	restReusePause,
+	restReuseRewind
+};
+
 class cDvbPlayerControl : public cControl {
 private:
   cDvbPlayer *player;
@@ -25,6 +33,8 @@ public:
        // 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);
diff --git a/menu.c b/menu.c
index a5c429e3..a33915f6 100644
--- a/menu.c
+++ b/menu.c
@@ -5311,6 +5311,16 @@ eOSState cDisplaySubtitleTracks::ProcessKey(eKeys Key)
 // --- cRecordControl --------------------------------------------------------
 
 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...
@@ -5340,6 +5350,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer,
   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());
@@ -5369,8 +5380,21 @@ cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer,
   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);
@@ -5380,8 +5404,6 @@ cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer,
         Recordings->AddByName(fileName);
         return;
         }
-     else
-        DELETENULL(recorder);
      }
   else
      timer->SetDeferred(DEFERTIMER);
@@ -5461,7 +5483,7 @@ bool cRecordControl::Process(time_t t)
 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;
@@ -5499,7 +5521,7 @@ bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause)
         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));
                   }
                }
@@ -5523,6 +5545,11 @@ bool cRecordControls::Start(bool Pause)
   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;
@@ -5557,11 +5584,18 @@ void cRecordControls::Stop(cTimer *Timer)
 }
 
 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, &reused)) {
+     cReplayControl *rc = new cReplayControl(rewind? restReuseRewind : reused? restReusePause : restPauseLive);
      cControl::Launch(rc);
      cControl::Attach();
      Skins.Message(mtStatus, NULL);
@@ -5704,7 +5738,18 @@ cString cReplayControl::fileName;
 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 --git a/menu.h b/menu.h
index 96c55e62..9446a70d 100644
--- a/menu.h
+++ b/menu.h
@@ -245,6 +245,8 @@ private:
   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; }
@@ -260,10 +262,12 @@ private:
   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);
@@ -320,6 +324,8 @@ private:
   void EditTest(void);
 public:
   cReplayControl(bool PauseLive = false);
+  cReplayControl(ReplayState replayState);
+  void Construct(ReplayState replayState);
   virtual ~cReplayControl();
   void Stop(void);
   virtual cOsdObject *GetInfo(void);
diff --git a/receiver.h b/receiver.h
index 9bbc6bdb..b76969bb 100644
--- a/receiver.h
+++ b/receiver.h
@@ -85,6 +85,10 @@ public:
                ///< 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 --git a/recorder.c b/recorder.c
index 0b3fdab1..dc5976c2 100644
--- a/recorder.c
+++ b/recorder.c
@@ -24,9 +24,19 @@
 cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
 :cReceiver(Channel, Priority)
 ,cThread("recording")
+,ringBuffer(NULL), frameDetector(NULL), fileName(NULL), recordingInfo(NULL), index(NULL), recordFile(NULL), recordingName(NULL)
+{
+  if (FileName != NULL) {
+     InitializeFile(FileName, Channel);
+     }
+}
+
+void cRecorder::InitializeFile(const char *FileName, const cChannel *Channel)
 {
   recordingName = strdup(FileName);
-  recordingInfo = new cRecordingInfo(recordingName);
+  if (recordingInfo == NULL) {
+     recordingInfo = new cRecordingInfo(recordingName);
+     }
   recordingInfo->Read();
   oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording
   errors = 0;
@@ -51,7 +61,9 @@ cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority
      Pid = Channel->Dpid(0);
      Type = 0x06;
      }
-  frameDetector = new cFrameDetector(Pid, Type);
+  if (frameDetector == NULL) {
+     frameDetector = new cFrameDetector(Pid, Type);
+     }
   index = NULL;
   fileSize = 0;
   lastDiskSpaceCheck = time(NULL);
diff --git a/recorder.h b/recorder.h
index a1858635..caf067f3 100644
--- a/recorder.h
+++ b/recorder.h
@@ -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;
@@ -36,7 +36,6 @@ private:
   bool RunningLowOnDiskSpace(void);
   bool NextFile(void);
   void HandleErrors(bool Force = false);
-protected:
   virtual void Activate(bool On);
        ///< If you override Activate() you need to call Detach() (which is a
        ///< member of the cReceiver class) from your own destructor in order
@@ -44,6 +43,9 @@ protected:
        ///< 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 --git a/ringbuffer.c b/ringbuffer.c
index 902c8878..89247d60 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -368,6 +368,25 @@ uchar *cRingBufferLinear::Get(int &Count)
   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 --git a/ringbuffer.h b/ringbuffer.h
index 746fc51e..6dfe6654 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -98,6 +98,12 @@ public:
     ///< 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
diff --git a/vdr.c b/vdr.c
index 6056e2f0..08eaaaae 100644
--- a/vdr.c
+++ b/vdr.c
@@ -1359,13 +1359,24 @@ int main(int argc, char *argv[])
                key = kNone;
                break;
           // Pausing live video:
+          case kFastRew:
+               if (cOsd::IsOpen())
+                  break;
+               {
+               // 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 (!Control) {
                   DELETE_MENU;
                   if (Setup.PauseKeyHandling) {
                      if (Setup.PauseKeyHandling > 1 || Interface->Confirm(tr("Pause live video?"))) {
-                        if (!cRecordControls::PauseLiveVideo())
+                        if (!cRecordControls::PauseLiveVideo(int(key) == kFastRew))
                            Skins.QueueMessage(mtError, tr("No free DVB device to record!"));
                         }
                      }
