On 01/01/2012 20:44, René wrote:
Hi Marc,
I pasted the output over here: http://pastebin.com/8eMsEuMf
It looks that it can't find the files to patch. Maybe the patch was
not compatible with Gentoo?
It would be great if you could send an updated ebuild to 1.7.22 :-) I
really miss the days when i updated my vdr on every release.. Now i
have been "stuck" with 1.6.x for ages..
Do you think that it would be possible to add the livebuffer-patch
back to the official gentoo overlay? Even better if it could be added
as a basic feature of vdr (that could be turned on/off from the
settings :-) Klaus? Please, please, pleaaaaase :-)
Regards,
René
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
The patch applies, portage try several levels with patch and it
succeeded at p1 (log start at 255).
The modifications I've made work but you have more files that fail than
me. Perhaps you use more use flags. I don't know if the extension
patches are applied all the time or only when you set the use flag for
them. You could try to build vdr without any use flag and see if it
compiles.
I attached the patch for 1.7.22 and I removed the ifdef statement in
Makefile so there is no more need to patch the ebuild, just to put the
patch in VDR_LOCAL_PATCHES_DIR. You can still create a folder for each
version (
http://linuxtv.org/vdrwiki/index.php/Gentoo_VDR_own_patches#Putting_patches_there
)
To add this patch to the overlay, have you tried to contact the
maintainer ? It could be interested, its address is in the changelog.
Regards,
Marc.
diff -Naur vdr-1.7.22/Makefile vdr-1.7.22-livebuffer/Makefile
--- vdr-1.7.22/Makefile 2011-12-04 15:41:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/Makefile 2011-12-31 11:16:23.000000000 +0100
@@ -62,6 +62,9 @@
LIBS += $(shell pkg-config --libs fribidi)
endif
+DEFINES += -DUSE_LIVEBUFFER
+OBJS += livebuffer.o
+
LIRC_DEVICE ?= /var/run/lirc/lircd
RCU_DEVICE ?= /dev/ttyS1
diff -Naur vdr-1.7.22/config.c vdr-1.7.22-livebuffer/config.c
--- vdr-1.7.22/config.c 2011-12-03 16:21:30.000000000 +0100
+++ vdr-1.7.22-livebuffer/config.c 2011-12-31 11:16:23.000000000 +0100
@@ -461,6 +461,10 @@
InitialChannel = "";
DeviceBondings = "";
InitialVolume = -1;
+#ifdef USE_LIVEBUFFER
+ LiveBufferSize = 30;
+ LiveBufferMaxFileSize = 100;
+#endif /*USE_LIVEBUFFER*/
ChannelsWrap = 0;
EmergencyExit = 1;
}
@@ -655,6 +659,10 @@
else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = Value;
else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
else if (!strcasecmp(Name, "DeviceBondings")) DeviceBondings = Value;
+#ifdef USE_LIVEBUFFER
+ else if (!strcasecmp(Name, "LiveBufferSize")) LiveBufferSize = atoi(Value);
+ else if (!strcasecmp(Name, "LiveBufferMaxFileSize")) LiveBufferMaxFileSize = atoi(Value);
+#endif /*USE_LIVEBUFFER*/
else if (!strcasecmp(Name, "ChannelsWrap")) ChannelsWrap = atoi(Value);
else if (!strcasecmp(Name, "EmergencyExit")) EmergencyExit = atoi(Value);
else
@@ -752,6 +760,9 @@
Store("InitialChannel", InitialChannel);
Store("InitialVolume", InitialVolume);
Store("DeviceBondings", DeviceBondings);
+#ifdef USE_LIVEBUFFER
+ Store("LiveBufferSize", LiveBufferSize);
+#endif /* LIVEBUFFER */
Store("ChannelsWrap", ChannelsWrap);
Store("EmergencyExit", EmergencyExit);
diff -Naur vdr-1.7.22/config.h vdr-1.7.22-livebuffer/config.h
--- vdr-1.7.22/config.h 2011-12-03 15:19:52.000000000 +0100
+++ vdr-1.7.22-livebuffer/config.h 2011-12-31 11:16:23.000000000 +0100
@@ -307,6 +307,10 @@
int CurrentVolume;
int CurrentDolby;
int InitialVolume;
+#ifdef USE_LIVEBUFFER
+ int LiveBufferSize;
+ int LiveBufferMaxFileSize;
+#endif /*USE_LIVEBUFFER*/
int ChannelsWrap;
int EmergencyExit;
int __EndData__;
diff -Naur vdr-1.7.22/device.c vdr-1.7.22-livebuffer/device.c
--- vdr-1.7.22/device.c 2011-10-16 16:36:43.000000000 +0200
+++ vdr-1.7.22-livebuffer/device.c 2011-12-31 11:16:23.000000000 +0100
@@ -18,6 +18,10 @@
#include "receiver.h"
#include "status.h"
#include "transfer.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#include "interface.h"
+#endif /*USE_LIVEBUFFER*/
// --- cLiveSubtitle ---------------------------------------------------------
@@ -663,6 +667,14 @@
return false;
case scrNoTransfer: Skins.Message(mtError, tr("Can't start Transfer Mode!"));
return false;
+#ifdef USE_LIVEBUFFER
+ case srcStillWritingLiveBuffer:
+ if(Interface->Confirm(tr("Still writing timeshift data to recording. Abort?")))
+ cRecordControls::CancelWritingBuffer();
+ else
+ if(cRecordControls::IsWritingBuffer()) return false;
+ break;
+#endif /*USE_LIVEBUFFER*/
case scrFailed: break; // loop will retry
default: esyslog("ERROR: invalid return value from SetChannel");
}
@@ -720,8 +732,17 @@
if (NeedsTransferMode) {
if (Device && CanReplay()) {
+#ifdef USE_LIVEBUFFER
+ if(LiveView && !cRecordControls::CanSetLiveChannel(Channel))
+ return cRecordControls::IsWritingBuffer() ? srcStillWritingLiveBuffer : scrFailed;
+#endif /*USE_LIVEBUFFER*/
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+#ifdef USE_LIVEBUFFER
+ if(LiveView)
+ cRecordControls::SetLiveChannel(Device, Channel);
+ else
+#endif /*USE_LIVEBUFFER*/
cControl::Launch(new cTransferControl(Device, Channel));
else
Result = scrNoTransfer;
diff -Naur vdr-1.7.22/device.h vdr-1.7.22-livebuffer/device.h
--- vdr-1.7.22/device.h 2011-12-04 14:38:17.000000000 +0100
+++ vdr-1.7.22-livebuffer/device.h 2011-12-31 11:16:23.000000000 +0100
@@ -32,7 +32,11 @@
#define VOLUMEDELTA 5 // used to increase/decrease the volume
#define MAXOCCUPIEDTIMEOUT 99 // max. time (in seconds) a device may be occupied
+#ifdef USE_LIVEBUFFER
+enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed, srcStillWritingLiveBuffer };
+#else
enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+#endif /*USE_LIVEBUFFER*/
enum ePlayMode { pmNone, // audio/video from decoder
pmAudioVideo, // audio/video from player
diff -Naur vdr-1.7.22/dvbplayer.c vdr-1.7.22-livebuffer/dvbplayer.c
--- vdr-1.7.22/dvbplayer.c 2010-03-07 15:24:26.000000000 +0100
+++ vdr-1.7.22-livebuffer/dvbplayer.c 2011-12-31 11:16:23.000000000 +0100
@@ -15,6 +15,9 @@
#include "ringbuffer.h"
#include "thread.h"
#include "tools.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#endif /*USE_LIVEBUFFER*/
// --- cPtsIndex -------------------------------------------------------------
@@ -35,6 +38,9 @@
void Clear(void);
void Put(uint32_t Pts, int Index);
int FindIndex(uint32_t Pts);
+#ifdef USE_LIVEBUFFER
+ void SetIndex(int Index) {lastFound = Index;};
+#endif /*USE_LIVEBUFFER*/
};
cPtsIndex::cPtsIndex(void)
@@ -205,7 +211,12 @@
cRingBufferFrame *ringBuffer;
cPtsIndex ptsIndex;
cFileName *fileName;
+#ifdef USE_LIVEBUFFER
+ cIndex *index;
+ cIndexFile *indexFile;
+#else
cIndexFile *index;
+#endif /*USE_LIVEBUFFER*/
cUnbufferedFile *replayFile;
double framesPerSecond;
bool isPesRecording;
@@ -270,18 +281,35 @@
dropFrame = NULL;
isyslog("replay %s", FileName);
fileName = new cFileName(FileName, false, false, isPesRecording);
+#ifndef USE_LIVEBUFFER
replayFile = fileName->Open();
if (!replayFile)
return;
+#endif /*USE_LIVEBUFFER*/
ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
// Create the index file:
+#ifdef USE_LIVEBUFFER
+ indexFile = NULL;
+ index = cRecordControls::GetLiveIndex(FileName);
+ if(!index)
+ index = indexFile = new cIndexFile(FileName, false, isPesRecording);
+#else
index = new cIndexFile(FileName, false, isPesRecording);
+#endif /*USE_LIVEBUFFER*/
if (!index)
esyslog("ERROR: can't allocate index");
else if (!index->Ok()) {
delete index;
index = NULL;
}
+#ifdef USE_LIVEBUFFER
+ readIndex = Resume();
+ if (readIndex >= 0) {
+ ptsIndex.SetIndex(readIndex);
+ isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+ } else
+ replayFile = fileName->Open();
+#endif /*USE_LIVEBUFFER*/
}
cDvbPlayer::~cDvbPlayer()
@@ -289,7 +317,11 @@
Save();
Detach();
delete readFrame; // might not have been stored in the buffer in Action()
+#ifdef USE_LIVEBUFFER
+ delete indexFile;
+#else
delete index;
+#endif /*USE_LIVEBUFFER*/
delete fileName;
delete ringBuffer;
}
@@ -387,9 +419,11 @@
uchar *p = NULL;
int pc = 0;
+#ifndef USE_LIVEBUFFER
readIndex = Resume();
if (readIndex >= 0)
isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+#endif /*USE_LIVEBUFFER*/
nonBlockingFileReader = new cNonBlockingFileReader;
int Length = 0;
@@ -436,6 +470,10 @@
if (NewIndex <= 0 && readIndex > 0)
NewIndex = 1; // make sure the very first frame is delivered
NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode);
+#ifdef USE_LIVEBUFFER
+ if (NewIndex < 0 && TimeShiftMode) // Why should we wait for a timeout if not pdForward
+ SwitchToPlayFrame = Index;
+#endif
if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
SwitchToPlayFrame = Index;
Index = NewIndex;
@@ -454,6 +492,15 @@
off_t FileOffset;
if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
readIndex++;
+#ifdef USE_LIVEBUFFER
+ else if(index && index->First() && (readIndex < index->First())) {
+ int old = readIndex;
+ readIndex = index->GetNextIFrame(index->First()+1, true, NULL, NULL, NULL, true);
+ isyslog("Jump before start of livebuffer cortrected %d->%d First %d", old, readIndex, index->First());
+ if(readIndex <= index->First())
+ eof = true;
+ }
+#endif /*USE_LIVEBUFFER*/
else
eof = true;
}
@@ -587,7 +634,11 @@
else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
SwitchToPlay = true;
if (SwitchToPlay) {
+#ifdef USE_LIVEBUFFER
+ if (!SwitchToPlayFrame || (playDir == pdBackward))
+#else
if (!SwitchToPlayFrame)
+#endif /*USE_LIVEBUFFER*/
Empty();
DevicePlay();
playMode = pmPlay;
diff -Naur vdr-1.7.22/livebuffer.c vdr-1.7.22-livebuffer/livebuffer.c
--- vdr-1.7.22/livebuffer.c 1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/livebuffer.c 2011-12-31 11:16:23.000000000 +0100
@@ -0,0 +1,403 @@
+#ifdef USE_LIVEBUFFER
+#include "livebuffer.h"
+#if VDRVERSNUM >= 10716
+
+#include <vector>
+#include "videodir.h"
+#include "recording.h"
+#include "skins.h"
+#include "player.h"
+
+#define WAIT_WRITING_COUNT 1000
+#define WAIT_WRITING_SLEEP 10000
+
+#define WAIT_TERMINATE_COUNT 300
+#define WAIT_TERMINATE_SLEEP 10000
+
+struct tLiveIndex {
+ int index;
+ uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!)
+ int reserved:7; // reserved for future use
+ int independent:1; // marks frames that can be displayed by themselves (for trick modes)
+ uint16_t number:16; // up to 64K files per recording
+ tLiveIndex(int Index, bool Independent, uint16_t Number, off_t Offset)
+ {
+ index = Index;
+ offset = Offset;
+ reserved = 0;
+ independent = Independent;
+ number = Number;
+ }
+}; // tLiveIndex
+
+class cLiveIndex : public cIndex {
+public:
+ cLiveIndex(const char *FileName): bufferFileName(FileName, false), bufferBaseName(FileName) {
+ resumePos = -1;
+ lastPos = lastGet = lastBuf = 0;
+ lastFileNumber=1;
+ dropFile = false;
+ maxSize = Setup.LiveBufferSize * 60 * DEFAULTFRAMESPERSECOND;
+ idx.reserve(maxSize+1);
+ }; // cLiveIndex
+ virtual ~cLiveIndex() {
+ }; // ~cLiveIndex
+ virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) {
+ cMutexLock lock(&idx_lock);
+ idx.push_back(tLiveIndex(++lastPos, Independent, FileNumber, FileOffset));
+ while(((idx.size() > maxSize) && (lastGet ? (lastGet > First()) : true) && (lastBuf ? (lastBuf > First()) : true)) || dropFile) {
+ if(idx.front().number != lastFileNumber) {
+ isyslog("Deleting old livebuffer file #%d (%d)", lastFileNumber, dropFile);
+ system(cString::sprintf("ls -l %s/%05d.ts | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", (const char *)bufferBaseName, lastFileNumber)); // for symlink video.xx
+ unlink(cString::sprintf("%s/%05d.ts", (const char *)bufferBaseName, lastFileNumber));
+ lastFileNumber = idx.front().number;
+ dropFile=false;
+ } // if
+ idx.erase(idx.begin());
+ } // if
+ return true;
+ }; // Write
+ virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) {
+ cMutexLock lock(&idx_lock);
+ std::vector<tLiveIndex>::iterator item = GetIndex(Index);
+ if(item == idx.end()) return false;
+ *FileNumber = item->number;
+ *FileOffset = item->offset;
+ if (Independent)
+ *Independent = item->independent;
+ item++;
+ if(item == idx.end()) return false;
+ if (Length) {
+ uint16_t fn = item->number;
+ off_t fo = item->offset;
+ if (fn == *FileNumber)
+ *Length = int(fo - *FileOffset);
+ else
+ *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
+ } // if
+ lastGet = Index;
+ return true;
+ }; // Get
+ virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) {
+ cMutexLock lock(&idx_lock);
+ std::vector<tLiveIndex>::iterator item = GetIndex(Index);
+ if(item == idx.end()) {
+ if(Index < First() && Forward)
+ item = idx.begin();
+ else
+ return -1;
+ }
+ if(Forward) {
+ do {
+ item++;
+ if(item == idx.end()) return -1;
+ } while(!item->independent);
+ } else {
+ do {
+ if(item == idx.begin()) return -1;
+ item--;
+ } while(!item->independent);
+ } // if
+ uint16_t fn;
+ if (!FileNumber)
+ FileNumber = &fn;
+ off_t fo;
+ if (!FileOffset)
+ FileOffset = &fo;
+ *FileNumber = item->number;
+ *FileOffset = item->offset;
+ item++;
+ if(item == idx.end()) return -1;
+ if (Length) {
+ // all recordings end with a non-independent frame, so the following should be safe:
+ uint16_t fn = item->number;
+ off_t fo = item->offset;
+ if (fn == *FileNumber) {
+ *Length = int(fo - *FileOffset);
+ } else {
+ esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
+ *Length = -1;
+ } // if
+ } // if
+ return Index;
+ }; // GetNextIFrame
+ virtual bool SetBufferStart(int Frames) {
+ cMutexLock lock(&idx_lock);
+ abortBuf = false;
+ if(Frames <= 0) {
+ lastBuf = 0;
+ return false;
+ } // if
+ lastBuf = Last()-Frames;
+ if(lastBuf < First())
+ lastBuf = First();
+ lastBuf = GetNextIFrame(lastBuf, true);
+ return true;
+ } // SetBufferStart
+ virtual cUnbufferedFile *GetNextBuffer(int &Length, bool &Independent) {
+ if(abortBuf || !lastBuf) return NULL;
+ cMutexLock lock(&idx_lock);
+ std::vector<tLiveIndex>::iterator buff = GetIndex(lastBuf);
+ if((buff == idx.end()) || ((buff+1) == idx.end())) return NULL;
+ off_t offset = buff->offset;
+ int number = buff->number;
+ cUnbufferedFile *ret = bufferFileName.SetOffset(number, offset);
+ Independent = buff->independent;
+ buff++;
+ lastBuf = buff->index;
+ if(number != buff->number)
+ Length = -1;
+ else
+ Length = buff->offset-offset;
+ return ret;
+ } // GetNextBuffer
+ virtual int Get(uint16_t FileNumber, off_t FileOffset) {
+ for ( std::vector<tLiveIndex>::iterator item = idx.begin(); item != idx.end(); item++)
+ if (item->number > FileNumber || ((item->number == FileNumber) && off_t(item->offset) >= FileOffset))
+ return item->index;
+ return lastPos;
+ }; // Get
+ virtual bool Ok(void) {return true;};
+ virtual int First(void) {return idx.size() ? idx.front().index : -1;};
+ virtual int Last(void) {return idx.size() ? idx.back().index : -1;};
+ virtual void SetResume(int Index) {resumePos = lastGet = Index;};
+ virtual int GetResume(void) {return resumePos;};
+ virtual bool StoreResume(int Index) {resumePos=Index; lastGet=0; return true;};
+ virtual bool IsStillRecording(void) {return true;};
+ virtual void Delete(void) {};
+ virtual void DropFile(void) {dropFile=true;};
+ virtual bool IsWritingBuffer(void) {return lastBuf != 0;};
+ virtual void CancelWritingBuffer(void) {abortBuf = true;};
+ virtual bool WritingBufferCanceled(void) {return abortBuf;};
+protected:
+ int firstPos;
+ int lastPos;
+ int resumePos;
+ int lastFileNumber;
+ int lastGet;
+ int lastBuf;
+ bool abortBuf;
+ bool dropFile;
+ unsigned int maxSize;
+ cFileName bufferFileName;
+ cString bufferBaseName;
+ cMutex idx_lock;
+ std::vector<tLiveIndex> idx;
+ virtual std::vector<tLiveIndex>::iterator GetIndex(int Index) {
+ if(!idx.size()) return idx.end();
+ std::vector<tLiveIndex>::iterator item = idx.begin();
+
+ unsigned int guess = Index-First(); // Try to guess the position
+ if(guess > 0) {
+ if(guess < idx.size())
+ item += guess;
+ else
+ item = idx.end()-1;
+ } // if
+ while(item->index < Index) {
+ item++;
+ if(item == idx.end())
+ return idx.end();
+ } // while
+ while(item->index > Index) {
+ if(item == idx.begin())
+ return idx.end();
+ item--;
+ } // while
+ if(item->index != Index)
+ return idx.end();
+ return item;
+ }; // GetIndex
+}; // cLiveIndex
+
+/*****************************************************************************/
+
+cString cLiveRecorder::liveFileName;
+
+cLiveRecorder::cLiveRecorder(const cChannel *Channel):cRecorder(FileName(), Channel, -1)
+ ,broken(false) {
+ handleError = false;
+ if(index) delete index;
+ index = new cLiveIndex(FileName());
+ Activate(true);
+}; // cLiveRecorder::cLiveRecorder
+
+cLiveRecorder::~cLiveRecorder() {
+ int maxWait = WAIT_TERMINATE_COUNT;
+ CancelWritingBuffer();
+ while(IsWritingBuffer() && maxWait--)
+ usleep(WAIT_TERMINATE_SLEEP);
+ Activate(false);
+ Cleanup();
+}; // cLiveRecorder::~cLiveRecorder
+
+bool cLiveRecorder::IsWritingBuffer() {
+ return index && ((cLiveIndex *)index)->IsWritingBuffer();
+} // cLiveRecorder::IsWritingBuffer
+
+void cLiveRecorder::CancelWritingBuffer() {
+ if(index) ((cLiveIndex *)index)->CancelWritingBuffer();
+} // cLiveRecorder::CancelWritingBuffer
+
+bool cLiveRecorder::NextFile(void) {
+ if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
+ if(RunningLowOnDiskSpace() && index)
+ ((cLiveIndex *)index)->DropFile();
+ if (fileSize > MEGABYTE(off_t(Setup.LiveBufferMaxFileSize)) || RunningLowOnDiskSpace()) {
+ recordFile = fileName->NextFile();
+ fileSize = 0;
+ } // if
+ } // if
+ return recordFile != NULL;
+} // cLiveRecorder::NextFile
+
+int cLiveRecorder::LastIFrame() {
+ if(!index) return 0;
+ int ret = index->GetNextIFrame(index->Last()-1, false);
+ return (ret > 0) ? ret : 0;
+}; // cLiveRecorder::LastIFrame
+
+int cLiveRecorder::LastFrame() {
+ return index ? index->Last() : 0;
+}; // cLiveRecorder::LastFrame
+
+void cLiveRecorder::SetResume(int Index) {
+ if(index) ((cLiveIndex *)index)->SetResume(Index);
+}; // cLiveRecorder::SetResume
+
+bool cLiveRecorder::SetBufferStart(time_t Start) {
+ if(!index) return false;
+ if(time(NULL) <= Start) return false;
+ int Frames = SecondsToFrames(time(NULL)-Start, frameDetector ? frameDetector->FramesPerSecond() : DEFAULTFRAMESPERSECOND); //test stop livebuffer
+ return ((cLiveIndex *)index)->SetBufferStart(Frames);
+} // cLiveRecorder::SetBufferStart
+
+cIndex *cLiveRecorder::GetIndex() {
+ return index;
+}; // cLiveRecorder::GetIndex
+
+bool cLiveRecorder::Cleanup() {
+ if(FileName())
+ if(-1 == system(cString::sprintf("ls -l %s/* | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", FileName()))) // for symlink video.xx
+ return false;
+ else
+ if(-1 == system(cString::sprintf("rm -rf %s/*", FileName())))
+ return false;
+ return true;
+}; // cLiveRecorder::Cleanup
+
+bool cLiveRecorder::Prepare() {
+ if (!MakeDirs(FileName(), true)) return false;
+ return Cleanup();
+}; // cLiveRecorder::Prepare
+
+const char *cLiveRecorder::FileName() {
+ if(!(const char *)liveFileName && BufferDirectory)
+ liveFileName = cString::sprintf("%s/LiveBuffer", BufferDirectory);
+ return liveFileName;
+}; // cLiveRecorder::FileName
+
+void cLiveRecorder::Activate(bool On) {
+ cRecorder::Activate(On);
+ if(!On) broken=true;
+} // cLiveRecorder::Activate
+
+void cLiveRecorder::Receive(uchar *Data, int Length) {
+ if(broken) {
+ isyslog("Continue live recorder on broken stream (maybe due to switching to same channel on other device)");
+ TsSetTeiOnBrokenPackets(Data, Length);
+ broken = false;
+ } // if
+ cRecorder::Receive(Data, Length);
+} // cLiveRecorder::Receive
+
+/*****************************************************************************/
+
+cBufferRecorder::cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex)
+ :cRecorder(FileName, Channel, Priority)
+ ,liveBufferIndex(LiveBufferIndex)
+ ,dropData(false) {
+ if(liveBufferIndex) dropData=true; // Drop new data till we have written most of the live buffer data
+} // cBufferRecorder::cBufferRecorder
+
+cBufferRecorder::~cBufferRecorder() {
+ if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+} // cBufferRecorder::~cBufferRecorder
+
+void cBufferRecorder::Action(void) {
+ if(liveBufferIndex)
+ FillInitialData(NULL, 0);
+ dropData=false;
+ cRecorder::Action();
+ if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+ liveBufferIndex = NULL;
+} // cBufferRecorder::Action
+
+void cBufferRecorder::Activate(bool On) {
+ if(!On && liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+ cRecorder::Activate(On);
+} // cBufferRecorder::Activate
+
+void cBufferRecorder::Receive(uchar *Data, int Length) {
+ if(!dropData) cRecorder::Receive(Data, Length);
+} // cBufferRecorder::Receive
+
+void cBufferRecorder::FillInitialData(uchar *Data, int Size) {
+ if(liveBufferIndex) {
+ int64_t search_pts = Data ? TsGetPts(Data, Size) : -1;
+ int maxWait = WAIT_WRITING_COUNT;
+ uchar buffer[MAXFRAMESIZE];
+ int Length;
+ bool Independent;
+ bool found = false;
+ while(!Data || (Size >= TS_SIZE)) {
+ cUnbufferedFile *file = ((cLiveIndex *)liveBufferIndex)->GetNextBuffer(Length, Independent);
+ if(!file) {
+ if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
+ isyslog("Writing buffer canceled by user");
+ if(fileSize) TsSetTeiOnBrokenPackets(Data, Size);
+ ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+ liveBufferIndex = NULL;
+ return;
+ } // if
+ if(!Data || !Size) return;
+ if(!maxWait--)
+ break;
+ usleep(WAIT_WRITING_SLEEP);
+ continue;
+ } // if
+ if (!NextFile())
+ break;
+ int len = ReadFrame(file, buffer, Length, sizeof(buffer));
+ if(len < TS_SIZE) {
+ isyslog("Failed to read live buffer data");
+ break;
+ } // if
+ if(Data && Independent && (search_pts == TsGetPts(buffer, len))) {
+ found = true;
+ break;
+ } // if
+ if (index)
+ index->Write(Independent, fileName->Number(), fileSize);
+ if (recordFile->Write(buffer, len) < 0) {
+ isyslog("Failed to write live buffer data");
+ break;
+ } // if
+ fileSize += len;
+ } // while
+ if(Data) {
+ isyslog("%lld bytes from live buffer %swritten to recording", fileSize, found ? "seamless ": "");
+ if(!found && fileSize) TsSetTeiOnBrokenPackets(Data, Size);
+ ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+ liveBufferIndex = NULL;
+ } else if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
+ isyslog("%lld bytes from live buffer written to recording (aborted)", fileSize);
+ ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
+ liveBufferIndex = NULL;
+ } // if
+ } else if (Data && fileSize)
+ TsSetTeiOnBrokenPackets(Data, Size);
+} // cBufferRecorder::FillInitialData
+
+#endif /*VDRVERSNUM*/
+#endif /*USE_LIVEBUFFER*/
diff -Naur vdr-1.7.22/livebuffer.h vdr-1.7.22-livebuffer/livebuffer.h
--- vdr-1.7.22/livebuffer.h 1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.7.22-livebuffer/livebuffer.h 2011-12-31 11:16:23.000000000 +0100
@@ -0,0 +1,47 @@
+#ifndef LIVEBUFFER_H
+#define LIVEBUFFER_H
+
+#ifdef USE_LIVEBUFFER
+#include "config.h"
+#if VDRVERSNUM >= 10716
+
+#include "recorder.h"
+
+class cLiveRecorder : public cRecorder {
+public:
+ cLiveRecorder(const cChannel *Channel);
+ virtual bool NextFile(void);
+ virtual ~cLiveRecorder();
+ virtual bool IsWritingBuffer();
+ virtual void CancelWritingBuffer();
+ virtual int LastIFrame();
+ virtual int LastFrame();
+ virtual void SetResume(int Index);
+ virtual bool SetBufferStart(time_t Start);
+ virtual cIndex *GetIndex();
+ static bool Cleanup();
+ static bool Prepare();
+ static const char *FileName();
+protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ bool broken;
+ static cString liveFileName;
+}; // cLiveRecorder
+
+class cBufferRecorder : public cRecorder {
+public:
+ cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex);
+ virtual ~cBufferRecorder();
+ virtual void FillInitialData(uchar *Data, int Size);
+protected:
+ virtual void Action(void);
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ cIndex *liveBufferIndex;
+ bool dropData;
+}; // cBufferRecorder
+
+#endif /*VDRVERSNUM*/
+#endif /*USE_LIVEBUFFER*/
+#endif /*LIVEBUFFER_H*/
diff -Naur vdr-1.7.22/menu.c vdr-1.7.22-livebuffer/menu.c
--- vdr-1.7.22/menu.c 2011-12-04 15:52:38.000000000 +0100
+++ vdr-1.7.22-livebuffer/menu.c 2011-12-31 11:16:23.000000000 +0100
@@ -3066,7 +3066,11 @@
class cMenuSetupRecord : public cMenuSetupBase {
private:
- const char *pauseKeyHandlingTexts[3];
+#ifdef USE_LIVEBUFFER
+ const char *pauseKeyHandlingTexts[4];
+#else
+ const char *pauseKeyHandlingTexts[3];
+#endif /*USE_LIVEBUFFER*/
const char *delTimeshiftRecTexts[3];
public:
cMenuSetupRecord(void);
@@ -3077,6 +3081,9 @@
pauseKeyHandlingTexts[0] = tr("do not pause live video");
pauseKeyHandlingTexts[1] = tr("confirm pause live video");
pauseKeyHandlingTexts[2] = tr("pause live video");
+#ifdef USE_LIVEBUFFER
+ pauseKeyHandlingTexts[3] = tr("Timeshift");
+#endif /*USE_LIVEBUFFER*/
delTimeshiftRecTexts[0] = tr("no");
delTimeshiftRecTexts[1] = tr("confirm");
delTimeshiftRecTexts[2] = tr("yes");
@@ -3086,7 +3093,12 @@
Add(new cMenuEditIntItem( tr("Setup.Recording$Primary limit"), &data.PrimaryLimit, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"), &data.DefaultPriority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"), &data.DefaultLifetime, 0, MAXLIFETIME));
- Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+#ifdef USE_LIVEBUFFER
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 4, pauseKeyHandlingTexts));
+ Add(new cMenuEditIntItem( tr("Timeshift size (min)"), &data.LiveBufferSize, 1, 300)); // TODO fix name and min/max values
+#else
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+#endif /*USE_LIVEBUFFER*/
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
@@ -4157,7 +4169,11 @@
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
+#ifdef USE_LIVEBUFFER
+ recorder = new cBufferRecorder(fileName, ch, timer->Priority(), cRecordControls::GetLiveBuffer(timer));
+#else
recorder = new cRecorder(fileName, ch, timer->Priority());
+#endif
if (device->AttachReceiver(recorder)) {
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
@@ -4242,6 +4258,10 @@
cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
int cRecordControls::state = 0;
+#ifdef USE_LIVEBUFFER
+cLiveRecorder *cRecordControls::liveRecorder = NULL;
+#endif /*USE_LIVEBUFFER*/
+
bool cRecordControls::Start(cTimer *Timer, bool Pause)
{
static time_t LastNoDiskSpaceMessage = 0;
@@ -4313,8 +4333,31 @@
}
}
+
+#ifdef USE_LIVEBUFFER
+bool cRecordControls::StartLiveBuffer(eKeys Key) {
+ if(Setup.PauseKeyHandling == 3 && liveRecorder) {
+ int pos = liveRecorder->LastIFrame();
+ isyslog("Enter timeshift at %d / %d", pos, liveRecorder->LastFrame());
+ liveRecorder->SetResume(pos?pos:liveRecorder->LastFrame());
+ cReplayControl::SetRecording(cLiveRecorder::FileName(), tr("Timeshift mode"));
+ cReplayControl *rc = new cReplayControl;
+ cControl::Launch(rc);
+ cControl::Attach();
+ rc->ProcessKey(Key);
+ rc->Show(); // show progressbar at the start of livebuffer
+ return true;
+ } // if
+ return false;
+} // cRecordControls::StartLiveBuffer
+#endif /*USE_LIVEBUFFER*/
+
bool cRecordControls::PauseLiveVideo(void)
{
+#ifdef USE_LIVEBUFFER
+ if(StartLiveBuffer(kPause))
+ return true;
+#endif /*USE_LIVEBUFFER*/
Skins.Message(mtStatus, tr("Pausing live video..."));
cReplayControl::SetRecording(NULL, NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed()
if (Start(NULL, true)) {
@@ -4331,6 +4374,54 @@
return false;
}
+#ifdef USE_LIVEBUFFER
+void cRecordControls::SetLiveChannel(cDevice *Device, const cChannel *Channel) {
+ if(liveRecorder) {
+ if(Channel && Device && (liveRecorder->ChannelID()==Channel->GetChannelID()))
+ Device->AttachReceiver(liveRecorder);
+ else
+ DELETENULL(liveRecorder);
+ } // if
+ if(Device && Channel) cControl::Launch(new cTransferControl(Device, Channel));
+ if(Setup.PauseKeyHandling == 3 && Channel && Device && !liveRecorder) {
+ if (cLiveRecorder::Prepare()) {
+ liveRecorder = new cLiveRecorder(Channel);
+ if(!Device->AttachReceiver(liveRecorder))
+ DELETENULL(liveRecorder);
+ } // if
+ } // if
+} // cRecordControls::SetLiveChannel
+
+bool cRecordControls::CanSetLiveChannel(const cChannel *Channel) {
+ if(liveRecorder && Channel && (liveRecorder->ChannelID()==Channel->GetChannelID())) return true;
+ return !IsWritingBuffer();
+} // cRecordControls::CanSetLiveChannel
+
+bool cRecordControls::IsWritingBuffer() {
+ return liveRecorder ? liveRecorder->IsWritingBuffer() : false;
+} // cRecordControls::IsWritingBuffer
+
+void cRecordControls::CancelWritingBuffer() {
+ if(liveRecorder && liveRecorder->IsWritingBuffer()) {
+ liveRecorder->CancelWritingBuffer();
+ sleep(1); // allow recorder to really stop
+ } // if
+} // cRecordControls::CancelWritingBuffer
+
+cIndex *cRecordControls::GetLiveBuffer(cTimer *Timer) {
+ if(!liveRecorder || !Timer || !Timer->Channel()) return NULL;
+ if(!(liveRecorder->ChannelID() == Timer->Channel()->GetChannelID())) return NULL;
+ if(!liveRecorder->SetBufferStart(Timer->StartTime())) return NULL;
+ return liveRecorder->GetIndex();
+} // cRecordControls::GetLiveBuffer
+
+cIndex *cRecordControls::GetLiveIndex(const char *FileName) {
+ if(!FileName || strcmp(cLiveRecorder::FileName(), FileName)) return NULL;
+ return liveRecorder ? liveRecorder->GetIndex() : NULL;
+} // cRecordControls::GetLiveIndex
+
+#endif /* USE_LIVEBUFFER */
+
const char *cRecordControls::GetInstantId(const char *LastInstantId)
{
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
@@ -4529,21 +4620,30 @@
void cReplayControl::ShowMode(void)
{
- if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) {
+ if (visible || (Setup.ShowReplayMode && !cOsd::IsOpen())) {
bool Play, Forward;
int Speed;
if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) {
bool NormalPlay = (Play && Speed == -1);
+ bool Paused = (!Play && Speed == -1);
if (!visible) {
if (NormalPlay)
return; // no need to do indicate ">" unless there was a different mode displayed before
visible = modeOnly = true;
+
+ // if newly paused show full replay osd; ie modeOnly = false
+ if (Paused) {
+ modeOnly = (lastPlay == Play);
+ }
+
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
}
- if (modeOnly && !timeoutShow && NormalPlay)
+ // osd times out when replaying normally OR when paused and full osd is shown
+ if (!timeoutShow && (NormalPlay|| (!modeOnly && Paused)))
timeoutShow = time(NULL) + MODETIMEOUT;
+
displayReplay->SetMode(Play, Forward, Speed);
lastPlay = Play;
lastForward = Forward;
@@ -4557,6 +4657,45 @@
int Current, Total;
if (GetIndex(Current, Total) && Total > 0) {
+#ifdef USE_LIVEBUFFER
+ int first=0;
+ cIndex *idx = cRecordControls::GetLiveIndex(fileName);
+ if(idx) first = idx->First(); // Normalize displayed values
+ Current -= first;
+ if(Current < 0) Current = 0;
+ Total -= first;
+ if(Total < 0) Total = 0;
+ time_t now = time(NULL);
+ static time_t last_sched_check = 0;
+ if(displayReplay && idx && (last_sched_check != now)) {
+ last_sched_check = now; // Only check every second
+ cSchedulesLock SchedulesLock;
+ const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
+ if (Schedules) {
+ const char *display_title = NULL;// = title;
+ const cSchedule *Schedule = Schedules->GetSchedule(Channels.GetByNumber(cDevice::CurrentChannel()));
+ if (Schedule) {
+ time_t Time = now - round(((double)Total - Current) / FramesPerSecond());
+ const cEvent *event = Schedule->GetEventAround(Time);
+ if (event) display_title = event->Title();
+ } // if
+
+ // no event title; show channel name
+ if (!display_title) {
+ cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+ display_title = channel->Name();
+ }
+
+ // set title as "Timeshift mode: <event title> "
+ // OR "Timeshift mode: <channel name>"
+ // if neither is possible leave title as such
+ if (display_title)
+ displayReplay->SetTitle(cString::sprintf("%s: %s",
+ tr("Timeshift mode"),
+ display_title));
+ } // if
+ } // if
+#endif /*USE_LIVEBUFFER*/
if (!visible) {
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
displayReplay->SetMarks(&marks);
@@ -4658,6 +4797,9 @@
void cReplayControl::TimeSearch(void)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
timeSearchTime = timeSearchPos = 0;
timeSearchHide = false;
if (modeOnly)
@@ -4676,6 +4818,9 @@
void cReplayControl::MarkToggle(void)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
int Current, Total;
if (GetIndex(Current, Total, true)) {
cMark *m = marks.Get(Current);
@@ -4696,6 +4841,9 @@
void cReplayControl::MarkJump(bool Forward)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
if (marks.Count()) {
int Current, Total;
if (GetIndex(Current, Total)) {
@@ -4710,6 +4858,9 @@
void cReplayControl::MarkMove(bool Forward)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
int Current, Total;
if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
@@ -4734,6 +4885,9 @@
void cReplayControl::EditCut(void)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
if (fileName) {
Hide();
if (!cCutter::Active()) {
@@ -4752,6 +4906,9 @@
void cReplayControl::EditTest(void)
{
+#ifdef USE_LIVEBUFFER
+ if(cRecordControls::GetLiveIndex(fileName)) return;
+#endif /*USE_LIVEBUFFER*/
int Current, Total;
if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
@@ -4783,7 +4940,14 @@
if (Key == kNone)
marks.Update();
if (visible) {
+
+ if (Key != kNone /*&& !modeOnly*/ && timeoutShow) {
+ printf("timeout reset +%d\n", MODETIMEOUT);
+ timeoutShow = time(NULL) + MODETIMEOUT;
+ }
+
if (timeoutShow && time(NULL) > timeoutShow) {
+ printf("timed out \n");
Hide();
ShowMode();
timeoutShow = 0;
@@ -4800,12 +4964,34 @@
return osContinue;
}
bool DoShowMode = true;
+
+#ifdef USE_LIVEBUFFER
+ if (cRecordControls::GetLiveIndex(fileName) && (Key >= k0) && (Key <= k9))
+ return osSwitchChannel;
+#endif /*USE_LIVEBUFFER*/
switch (int(Key)) {
// Positioning:
+#ifdef USE_LIVEBUFFER
+ case kUp: if(cRecordControls::GetLiveIndex(fileName)) {
+ cDevice::SwitchChannel(1);
+ return osEnd;
+ } // if
+ // NO break
+ case kPlay:
+ Play(); break;
+ case kDown: if(cRecordControls::GetLiveIndex(fileName)) {
+ cDevice::SwitchChannel(-1);
+ return osEnd;
+ } // if
+ // NO break
+ case kPause: Pause();
+ break;
+#else
case kPlay:
case kUp: Play(); break;
case kPause:
case kDown: Pause(); break;
+#endif /*USE_LIVEBUFFER*/
case kFastRew|k_Release:
case kLeft|k_Release:
if (Setup.MultiSpeedMode) break;
@@ -4816,15 +5002,52 @@
if (Setup.MultiSpeedMode) break;
case kFastFwd:
case kRight: Forward(); break;
- case kRed: TimeSearch(); break;
+ //case kRed: TimeSearch(); break;
case kGreen|k_Repeat:
case kGreen: SkipSeconds(-60); break;
case kYellow|k_Repeat:
case kYellow: SkipSeconds( 60); break;
+#ifdef USE_LIVEBUFFER
+ case kRed: if(cRecordControls::GetLiveIndex(fileName)) {
+
+ if (!(visible && !modeOnly)) return osUnknown;
+ else {} // fall through to case kRecord
+ // since Timeshift ON and replay OSD is shown
+ } // if
+ else { //timeshift off
+ TimeSearch();
+ break;
+ } // else
+ // No break
+ case kRecord: if(cRecordControls::GetLiveIndex(fileName)) {
+ int frames = 0;
+ int Current, Total;
+ if(GetIndex(Current, Total))
+ frames = Total-Current;
+ cTimer *timer = new cTimer(true, false, Channels.GetByNumber(cDevice::CurrentChannel()), frames / FramesPerSecond());
+ Timers.Add(timer);
+ Timers.SetModified();
+ if (cRecordControls::Start(timer))
+ Skins.Message(mtInfo, tr("Recording started"));
+ else
+ Timers.Del(timer);
+ } // if
+ break;
+ case kBlue: if(cRecordControls::GetLiveIndex(fileName))
+ if(!(visible && !modeOnly))
+ return osUnknown;
+ //NO break
+ case kStop: Hide();
+ Stop();
+ return osEnd;
+#else
+ case kRed: TimeSearch(); break;
case kStop:
case kBlue: Hide();
Stop();
return osEnd;
+#endif /*USE_LIVEBUFFER*/
+
default: {
DoShowMode = false;
switch (int(Key)) {
@@ -4855,7 +5078,20 @@
else
Show();
break;
- case kBack: if (Setup.DelTimeshiftRec) {
+ case kBack:
+#ifdef USE_LIVEBUFFER
+ if (visible && !modeOnly) {
+ Hide();
+ DoShowMode = true;
+ break;
+ }
+ if(cRecordControls::GetLiveIndex(fileName)) {
+ Hide();
+ Stop();
+ return osEnd;
+ } // if
+#endif /*USE_LIVEBUFFER*/
+ if (Setup.DelTimeshiftRec) {
cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
return rc && rc->InstantId() ? osEnd : osRecordings;
}
diff -Naur vdr-1.7.22/menu.h vdr-1.7.22-livebuffer/menu.h
--- vdr-1.7.22/menu.h 2010-03-06 17:15:59.000000000 +0100
+++ vdr-1.7.22-livebuffer/menu.h 2011-12-31 11:16:23.000000000 +0100
@@ -18,6 +18,12 @@
#include "menuitems.h"
#include "recorder.h"
#include "skins.h"
+#ifdef USE_LIVEBUFFER
+#include "livebuffer.h"
+#endif /*USE_LIVEBUFFER*/
+
+
+
class cMenuText : public cOsdMenu {
private:
@@ -236,10 +242,18 @@
private:
static cRecordControl *RecordControls[];
static int state;
+#ifdef USE_LIVEBUFFER
+protected:
+ friend class cRecordControl;
+ static cLiveRecorder *liveRecorder;
+#endif /*USE_LIVEBUFFER*/
public:
static bool Start(cTimer *Timer = NULL, bool Pause = false);
static void Stop(const char *InstantId);
static bool PauseLiveVideo(void);
+#ifdef USE_LIVEBUFFER
+ static bool StartLiveBuffer(eKeys Key);
+#endif /*USE_LIVEBUFFER*/
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);
static void Process(time_t t);
@@ -248,6 +262,14 @@
static void Shutdown(void);
static void ChangeState(void) { state++; }
static bool StateChanged(int &State);
+#ifdef USE_LIVEBUFFER
+ static void SetLiveChannel(cDevice *Device, const cChannel *Channel);
+ static bool CanSetLiveChannel(const cChannel *Channel);
+ static bool IsWritingBuffer();
+ static void CancelWritingBuffer();
+ static cIndex *GetLiveBuffer(cTimer *Timer);
+ static cIndex *GetLiveIndex(const char *FileName);
+#endif /*USE_LIVEBUFFER*/
};
class cReplayControl : public cDvbPlayerControl {
diff -Naur vdr-1.7.22/osdbase.h vdr-1.7.22-livebuffer/osdbase.h
--- vdr-1.7.22/osdbase.h 2010-01-16 15:25:31.000000000 +0100
+++ vdr-1.7.22-livebuffer/osdbase.h 2011-12-31 11:16:23.000000000 +0100
@@ -33,6 +33,9 @@
osSwitchDvb,
osBack,
osEnd,
+#ifdef USE_LIVEBUFFER
+ osSwitchChannel,
+#endif /*USE_LIVEBUFFER*/
os_User, // the following values can be used locally
osUser1,
osUser2,
diff -Naur vdr-1.7.22/player.c vdr-1.7.22-livebuffer/player.c
--- vdr-1.7.22/player.c 2007-07-20 17:25:24.000000000 +0200
+++ vdr-1.7.22-livebuffer/player.c 2011-12-31 11:16:23.000000000 +0100
@@ -10,6 +10,11 @@
#include "player.h"
#include "i18n.h"
+#ifdef USE_LIVEBUFFER
+#include "menu.h"
+#include "transfer.h"
+#endif /*USE_LIVEBUFFER*/
+
// --- cPlayer ---------------------------------------------------------------
cPlayer::cPlayer(ePlayMode PlayMode)
@@ -68,6 +73,12 @@
void cControl::Launch(cControl *Control)
{
+#ifdef USE_LIVEBUFFER
+ if(!dynamic_cast<cTransferControl *>(Control)) {
+ if(!dynamic_cast<cReplayControl *>(Control) || strcmp(cLiveRecorder::FileName(), cReplayControl::NowReplaying()))
+ cRecordControls::SetLiveChannel(NULL, NULL);
+ } // if
+#endif /*USE_LIVEBUFFER*/
cMutexLock MutexLock(&mutex);
cControl *c = control; // keeps control from pointing to uninitialized memory
control = Control;
diff -Naur vdr-1.7.22/po/de_DE.po vdr-1.7.22-livebuffer/po/de_DE.po
--- vdr-1.7.22/po/de_DE.po 2011-12-03 16:35:34.000000000 +0100
+++ vdr-1.7.22-livebuffer/po/de_DE.po 2011-12-31 11:16:23.000000000 +0100
@@ -25,6 +25,9 @@
msgid "Can't start Transfer Mode!"
msgstr "Transfer-Mode kann nicht gestartet werden!"
+msgid "Still writing timeshift data to recording. Abort?"
+msgstr "Timeshift-Daten werden noch in Aufnahme kopiert. Abbrechen?"
+
msgid "off"
msgstr "aus"
diff -Naur vdr-1.7.22/recorder.c vdr-1.7.22-livebuffer/recorder.c
--- vdr-1.7.22/recorder.c 2011-09-04 11:26:44.000000000 +0200
+++ vdr-1.7.22-livebuffer/recorder.c 2011-12-31 11:16:23.000000000 +0100
@@ -24,6 +24,9 @@
cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
:cReceiver(Channel, Priority)
,cThread("recording")
+#ifdef USE_LIVEBUFFER
+,handleError(true)
+#endif /*USE_LIVEBUFFER*/
{
recordingName = strdup(FileName);
@@ -140,6 +143,9 @@
InfoWritten = true;
}
if (FirstIframeSeen || frameDetector->IndependentFrame()) {
+#ifdef USE_LIVEBUFFER
+ if(!FirstIframeSeen) FillInitialData(b, r);
+#endif /*USE_LIVEBUFFER*/
FirstIframeSeen = true; // start recording with the first I-frame
if (!NextFile())
break;
@@ -165,7 +171,11 @@
ringBuffer->Del(Count);
}
}
+#ifdef USE_LIVEBUFFER
+ if (handleError && (time(NULL) - t > MAXBROKENTIMEOUT)) {
+#else
if (time(NULL) - t > MAXBROKENTIMEOUT) {
+#endif
esyslog("ERROR: video data stream broken");
ShutdownHandler.RequestEmergencyExit();
t = time(NULL);
diff -Naur vdr-1.7.22/recorder.h vdr-1.7.22-livebuffer/recorder.h
--- vdr-1.7.22/recorder.h 2010-12-27 12:17:04.000000000 +0100
+++ vdr-1.7.22-livebuffer/recorder.h 2011-12-31 11:16:23.000000000 +0100
@@ -17,18 +17,33 @@
#include "thread.h"
class cRecorder : public cReceiver, cThread {
+#ifdef USE_LIVEBUFFER
+protected:
+#else
private:
+#endif /*USE_LIVEBUFFER*/
cRingBufferLinear *ringBuffer;
cFrameDetector *frameDetector;
cPatPmtGenerator patPmtGenerator;
cFileName *fileName;
+#ifdef USE_LIVEBUFFER
+ cIndex *index;
+ bool handleError;
+#else
cIndexFile *index;
+#endif /*USE_LIVEBUFFER*/
cUnbufferedFile *recordFile;
char *recordingName;
off_t fileSize;
time_t lastDiskSpaceCheck;
+#ifdef USE_LIVEBUFFER
+ virtual bool RunningLowOnDiskSpace(void);
+ virtual bool NextFile(void);
+ virtual void FillInitialData(uchar *Data, int Size) {};
+#else
bool RunningLowOnDiskSpace(void);
bool NextFile(void);
+#endif /*USE_LIVEBUFFER*/
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
diff -Naur vdr-1.7.22/recording.h vdr-1.7.22-livebuffer/recording.h
--- vdr-1.7.22/recording.h 2011-12-04 14:38:17.000000000 +0100
+++ vdr-1.7.22-livebuffer/recording.h 2011-12-31 11:16:23.000000000 +0100
@@ -264,7 +264,26 @@
struct tIndexTs;
class cIndexFileGenerator;
+#ifdef USE_LIVEBUFFER
+class cIndex {
+public:
+ virtual bool Ok(void) =0;
+ virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) =0;
+ virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) =0;
+ virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) =0;
+ virtual int Get(uint16_t FileNumber, off_t FileOffset) =0;
+ virtual int First(void) {return 0;};
+ virtual int Last(void) =0;
+ virtual int GetResume(void) =0;
+ virtual bool StoreResume(int Index) =0;
+ virtual bool IsStillRecording(void) =0;
+ virtual void Delete(void) =0;
+ };
+
+class cIndexFile : public cIndex {
+#else
class cIndexFile {
+#endif /*USE_LIVEBUFFER*/
private:
int f;
cString fileName;
diff -Naur vdr-1.7.22/timers.c vdr-1.7.22-livebuffer/timers.c
--- vdr-1.7.22/timers.c 2011-08-06 15:13:54.000000000 +0200
+++ vdr-1.7.22-livebuffer/timers.c 2011-12-31 11:16:23.000000000 +0100
@@ -25,7 +25,11 @@
// --- cTimer ----------------------------------------------------------------
+#ifdef USE_LIVEBUFFER
+cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel, int Forerun)
+#else
cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
+#endif /*USE_LIVEBUFFER*/
{
startTime = stopTime = 0;
lastSetEvent = 0;
@@ -35,7 +39,11 @@
if (Instant)
SetFlags(tfActive | tfInstant);
channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
+#ifdef USE_LIVEBUFFER
+ time_t t = time(NULL) - Forerun;
+#else
time_t t = time(NULL);
+#endif /*USE_LIVEBUFFER*/
struct tm tm_r;
struct tm *now = localtime_r(&t, &tm_r);
day = SetTime(t, 0);
diff -Naur vdr-1.7.22/timers.h vdr-1.7.22-livebuffer/timers.h
--- vdr-1.7.22/timers.h 2011-08-06 14:59:32.000000000 +0200
+++ vdr-1.7.22-livebuffer/timers.h 2011-12-31 11:16:23.000000000 +0100
@@ -43,7 +43,11 @@
char *aux;
const cEvent *event;
public:
+#ifdef USE_LIVEBUFFER
+ cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL, int Forerun = 0);
+#else
cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL);
+#endif /*USE_LIVEBUFFER*/
cTimer(const cEvent *Event);
cTimer(const cTimer &Timer);
virtual ~cTimer();
diff -Naur vdr-1.7.22/vdr.c vdr-1.7.22-livebuffer/vdr.c
--- vdr-1.7.22/vdr.c 2011-12-03 16:35:09.000000000 +0100
+++ vdr-1.7.22-livebuffer/vdr.c 2011-12-31 11:16:23.000000000 +0100
@@ -218,6 +218,9 @@
static struct option long_options[] = {
{ "audio", required_argument, NULL, 'a' },
+#ifdef USE_LIVEBUFFER
+ { "buffer", required_argument, NULL, 'b' },
+#endif /* USE_LIVEBUFFER */
{ "config", required_argument, NULL, 'c' },
{ "daemon", no_argument, NULL, 'd' },
{ "device", required_argument, NULL, 'D' },
@@ -251,10 +254,20 @@
};
int c;
+#ifdef USE_LIVEBUFFER
+ while ((c = getopt_long(argc, argv, "a:b:c:dD:e:E:g:hi:l:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
+#else
while ((c = getopt_long(argc, argv, "a:c:dD:e:E:g:hi:l:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
+#endif /* USE_LIVEBUFFER */
switch (c) {
case 'a': AudioCommand = optarg;
break;
+#ifdef USE_LIVEBUFFER
+ case 'b': BufferDirectory = optarg;
+ if(optarg && *optarg && optarg[strlen(optarg)-1] == '/')
+ optarg[strlen(optarg)-1] = 0;
+ break;
+#endif /* USE_LIVEBUFFER */
case 'c': ConfigDirectory = optarg;
break;
case 'd': DaemonMode = true; break;
@@ -420,6 +433,9 @@
if (DisplayHelp) {
printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
+#ifdef USE_LIVEBUFFER
+ " -b DIR, --buffer=DIR use DIR as LiveBuffer directory\n"
+#endif /*USE_LIVEBUFFER*/
" -c DIR, --config=DIR read config files from DIR (default: %s)\n"
" -d, --daemon run in daemon mode\n"
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
@@ -586,9 +602,12 @@
if (!PluginManager.LoadPlugins(true))
EXIT(2);
-
// Configuration data:
+#ifdef USE_LIVEBUFFER
+ if (!BufferDirectory)
+ BufferDirectory = VideoDirectory;
+#endif /*USE_LIVEBUFFER*/
if (!ConfigDirectory)
ConfigDirectory = DEFAULTCONFDIR;
@@ -1091,6 +1110,15 @@
cDisplaySubtitleTracks::Process(key);
key = kNone;
break;
+#ifdef USE_LIVEBUFFER
+ case kFastRew:
+ if (!Interact) {
+ DELETE_MENU;
+ if(cRecordControls::StartLiveBuffer(key))
+ key = kNone;
+ } // if
+ break;
+#endif /*USE_LIVEBUFFER*/
// Pausing live video:
case kPause:
if (!cControl::Control()) {
@@ -1198,6 +1226,28 @@
else
cControl::Shutdown();
break;
+#ifdef USE_LIVEBUFFER
+ case osSwitchChannel:
+ switch (key) {
+ // Toggle channels:
+ case kChanPrev:
+ case k0: {
+ if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel
+ || (LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1]))
+ PreviousChannelIndex ^= 1;
+ Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
+ break;
+ }
+ case k1 ... k9:
+ DELETE_MENU;
+ cControl::Shutdown();
+ Menu = new cDisplayChannel(NORMALKEY(key));
+ break;
+ default:
+ break;
+ } // switch
+ break;
+#endif /*USE_LIVEBUFFER*/
default: ;
}
}
diff -Naur vdr-1.7.22/videodir.c vdr-1.7.22-livebuffer/videodir.c
--- vdr-1.7.22/videodir.c 2008-02-16 14:00:03.000000000 +0100
+++ vdr-1.7.22-livebuffer/videodir.c 2011-12-31 11:16:23.000000000 +0100
@@ -20,6 +20,9 @@
#include "tools.h"
const char *VideoDirectory = VIDEODIR;
+#ifdef USE_LIVEBUFFER
+const char *BufferDirectory = NULL;
+#endif /*USE_LIVEBUFFER*/
class cVideoDirectory {
private:
@@ -106,17 +109,32 @@
cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags)
{
const char *ActualFileName = FileName;
+#ifdef USE_LIVEBUFFER
+ bool SepBufferDir = false;
// Incoming name must be in base video directory:
+ if (strstr(FileName, VideoDirectory) != FileName) {
+ if (strstr(FileName, BufferDirectory) == FileName)
+ SepBufferDir = true;
+ else {
+#else
if (strstr(FileName, VideoDirectory) != FileName) {
+#endif /*USE_LIVEBUFFER*/
esyslog("ERROR: %s not in %s", FileName, VideoDirectory);
errno = ENOENT; // must set 'errno' - any ideas for a better value?
return NULL;
}
+#ifdef USE_LIVEBUFFER
+ }
+#endif /*USE_LIVEBUFFER*/
// Are we going to create a new file?
if ((Flags & O_CREAT) != 0) {
cVideoDirectory Dir;
+#ifdef USE_LIVEBUFFER
+ if (Dir.IsDistributed() && !SepBufferDir) {
+#else
if (Dir.IsDistributed()) {
+#endif /*USE_LIVEBUFFER*/
// Find the directory with the most free space:
int MaxFree = Dir.FreeMB();
while (Dir.Next()) {
diff -Naur vdr-1.7.22/videodir.h vdr-1.7.22-livebuffer/videodir.h
--- vdr-1.7.22/videodir.h 2008-02-16 13:53:11.000000000 +0100
+++ vdr-1.7.22-livebuffer/videodir.h 2011-12-31 11:16:23.000000000 +0100
@@ -14,6 +14,9 @@
#include "tools.h"
extern const char *VideoDirectory;
+#ifdef USE_LIVEBUFFER
+extern const char *BufferDirectory;
+#endif /*USE_LIVEBUFFER*/
cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
int CloseVideoFile(cUnbufferedFile *File);
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr