Re: problems with playback and plugins with vdr-1.7.22

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 07/01/2012 10:59, René wrote:
On 07.01.2012 3:44 , Udo Richter wrote:

The cryptic symbol decodes to cTimer::cTimer(bool, bool, cChannel*),
found in timers.h, line 46. The actual code is in timers.c, line 28.
This function exists in this form since 1.3.38. VDR itself seems to be
fine, so your VDR seems to have a different function instead. However,
the plugins rely on the original function because they somehow compiled
against the original definition in the timers.h.

My guess is that one of the patches is optionally modifying this, and
the plugins were somehow compiled with different versions of the
timers.h, or with different compiler flags that cause some #ifdef to flip.

Ok, i'll check if this would would work without any patches.. I doubt that I would be the only one with this issue if it would be the patches.. But again i would'nt surprise me if my system is messed up because of this big jump from 1.6.x to 1.7.22. Til now VDR has been a "set and forget" installation that just works, and works, and works and wo... :-)

Regards,

René

_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
The problem come from the livebuffer patch. It redefines cTimer::cTimer to add the length of the already buffered stream so when the instant recording start, it records the buffer too :
+#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*/

This feature can be removed easily (it's not really useful), I attach a modified version of the livebuffer patch.

Thanks Udo for pointing to the right direction.

Regards,

Marc.
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()));
+                      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/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);
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux