Advertisement
Guest User

Untitled

a guest
Dec 23rd, 2011
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 56.54 KB | None | 0 0
  1. From d99a28abfd108690028d7847b8736e542b089fd0 Mon Sep 17 00:00:00 2001
  2. From: Dominic Evans <oldmanuk@gmail.com>
  3. Date: Fri, 23 Dec 2011 10:51:30 +0000
  4. Subject: [PATCH] opt-96-livebuffer12-rmm.dpatch rebased onto 1.7.21
  5.  
  6. ---
  7. Makefile | 5 +
  8. config.c | 7 +
  9. config.h | 4 +
  10. device.c | 21 +++
  11. device.h | 4 +
  12. dvbplayer.c | 51 ++++++++
  13. livebuffer.c | 403 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  14. livebuffer.h | 47 +++++++
  15. menu.c | 248 +++++++++++++++++++++++++++++++++++-
  16. menu.h | 22 +++
  17. osdbase.h | 3 +
  18. player.c | 11 ++
  19. po/de_DE.po | 3 +
  20. recorder.c | 10 ++
  21. recorder.h | 15 ++
  22. recording.h | 19 +++
  23. timers.c | 8 +
  24. timers.h | 4 +
  25. vdr.c | 52 ++++++++-
  26. videodir.c | 18 +++
  27. videodir.h | 3 +
  28. 21 files changed, 951 insertions(+), 7 deletions(-)
  29. create mode 100644 livebuffer.c
  30. create mode 100644 livebuffer.h
  31.  
  32. diff --git a/Makefile b/Makefile
  33. index 18d7eb9..bc49dac 100644
  34. --- a/Makefile
  35. +++ b/Makefile
  36. @@ -60,6 +60,11 @@ DEFINES += -DBIDI
  37. LIBS += $(shell pkg-config --libs fribidi)
  38. endif
  39.  
  40. +ifdef LIVEBUFFER
  41. +DEFINES += -DUSE_LIVEBUFFER
  42. +OBJS += livebuffer.o
  43. +endif
  44. +
  45. LIRC_DEVICE ?= /dev/lircd
  46. RCU_DEVICE ?= /dev/ttyS1
  47.  
  48. diff --git a/config.c b/config.c
  49. index 73bb00d..14146a0 100644
  50. --- a/config.c
  51. +++ b/config.c
  52. @@ -397,6 +397,10 @@
  53. CurrentDolby = 0;
  54. InitialChannel = "";
  55. InitialVolume = -1;
  56. +#ifdef USE_LIVEBUFFER
  57. + LiveBufferSize = 30;
  58. + LiveBufferMaxFileSize = 100;
  59. +#endif /*USE_LIVEBUFFER*/
  60. ChannelsWrap = 0;
  61. EmergencyExit = 1;
  62. }
  63. @@ -589,6 +589,10 @@ bool cSetup::Parse(const char *Name, const char *Value)
  64. else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
  65. else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = Value;
  66. else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
  67. +#ifdef USE_LIVEBUFFER
  68. + else if (!strcasecmp(Name, "LiveBufferSize")) LiveBufferSize = atoi(Value);
  69. + else if (!strcasecmp(Name, "LiveBufferMaxFileSize")) LiveBufferMaxFileSize = atoi(Value);
  70. +#endif /*USE_LIVEBUFFER*/
  71. else if (!strcasecmp(Name, "ChannelsWrap")) ChannelsWrap = atoi(Value);
  72. else if (!strcasecmp(Name, "EmergencyExit")) EmergencyExit = atoi(Value);
  73. else
  74. @@ -685,6 +689,9 @@
  75. Store("CurrentDolby", CurrentDolby);
  76. Store("InitialChannel", InitialChannel);
  77. Store("InitialVolume", InitialVolume);
  78. +#ifdef USE_LIVEBUFFER
  79. + Store("LiveBufferSize", LiveBufferSize);
  80. +#endif /* LIVEBUFFER */
  81. Store("ChannelsWrap", ChannelsWrap);
  82. Store("EmergencyExit", EmergencyExit);
  83.  
  84. diff --git a/config.h b/config.h
  85. index c51e3df..1972195 100644
  86. --- a/config.h
  87. +++ b/config.h
  88. @@ -288,6 +288,10 @@
  89. int CurrentVolume;
  90. int CurrentDolby;
  91. int InitialVolume;
  92. +#ifdef USE_LIVEBUFFER
  93. + int LiveBufferSize;
  94. + int LiveBufferMaxFileSize;
  95. +#endif /*USE_LIVEBUFFER*/
  96. int ChannelsWrap;
  97. int EmergencyExit;
  98. int __EndData__;
  99. diff --git a/device.c b/device.c
  100. index ba098d8..172f3b3 100644
  101. --- a/device.c
  102. +++ b/device.c
  103. @@ -18,6 +18,10 @@
  104. #include "receiver.h"
  105. #include "status.h"
  106. #include "transfer.h"
  107. +#ifdef USE_LIVEBUFFER
  108. +#include "menu.h"
  109. +#include "interface.h"
  110. +#endif /*USE_LIVEBUFFER*/
  111.  
  112. // --- cLiveSubtitle ---------------------------------------------------------
  113.  
  114. @@ -661,6 +665,14 @@ bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
  115. return false;
  116. case scrNoTransfer: Skins.Message(mtError, tr("Can't start Transfer Mode!"));
  117. return false;
  118. +#ifdef USE_LIVEBUFFER
  119. + case srcStillWritingLiveBuffer:
  120. + if(Interface->Confirm(tr("Still writing timeshift data to recording. Abort?")))
  121. + cRecordControls::CancelWritingBuffer();
  122. + else
  123. + if(cRecordControls::IsWritingBuffer()) return false;
  124. + break;
  125. +#endif /*USE_LIVEBUFFER*/
  126. case scrFailed: break; // loop will retry
  127. default: esyslog("ERROR: invalid return value from SetChannel");
  128. }
  129. @@ -718,8 +730,17 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
  130.  
  131. if (NeedsTransferMode) {
  132. if (Device && CanReplay()) {
  133. +#ifdef USE_LIVEBUFFER
  134. + if(LiveView && !cRecordControls::CanSetLiveChannel(Channel))
  135. + return cRecordControls::IsWritingBuffer() ? srcStillWritingLiveBuffer : scrFailed;
  136. +#endif /*USE_LIVEBUFFER*/
  137. cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
  138. if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
  139. +#ifdef USE_LIVEBUFFER
  140. + if(LiveView)
  141. + cRecordControls::SetLiveChannel(Device, Channel);
  142. + else
  143. +#endif /*USE_LIVEBUFFER*/
  144. cControl::Launch(new cTransferControl(Device, Channel));
  145. else
  146. Result = scrNoTransfer;
  147. diff --git a/device.h b/device.h
  148. index fd587a8..2bebe89 100644
  149. --- a/device.h
  150. +++ b/device.h
  151. @@ -31,7 +31,11 @@
  152. #define MAXVOLUME 255
  153. #define VOLUMEDELTA 5 // used to increase/decrease the volume
  154.  
  155. +#ifdef USE_LIVEBUFFER
  156. +enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed, srcStillWritingLiveBuffer };
  157. +#else
  158. enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
  159. +#endif /*USE_LIVEBUFFER*/
  160.  
  161. enum ePlayMode { pmNone, // audio/video from decoder
  162. pmAudioVideo, // audio/video from player
  163. diff --git a/dvbplayer.c b/dvbplayer.c
  164. index 017df6d..800a31d 100644
  165. --- a/dvbplayer.c
  166. +++ b/dvbplayer.c
  167. @@ -15,6 +15,9 @@
  168. #include "ringbuffer.h"
  169. #include "thread.h"
  170. #include "tools.h"
  171. +#ifdef USE_LIVEBUFFER
  172. +#include "menu.h"
  173. +#endif /*USE_LIVEBUFFER*/
  174.  
  175. // --- cPtsIndex -------------------------------------------------------------
  176.  
  177. @@ -35,6 +38,9 @@ public:
  178. void Clear(void);
  179. void Put(uint32_t Pts, int Index);
  180. int FindIndex(uint32_t Pts);
  181. +#ifdef USE_LIVEBUFFER
  182. + void SetIndex(int Index) {lastFound = Index;};
  183. +#endif /*USE_LIVEBUFFER*/
  184. };
  185.  
  186. cPtsIndex::cPtsIndex(void)
  187. @@ -205,7 +211,12 @@ private:
  188. cRingBufferFrame *ringBuffer;
  189. cPtsIndex ptsIndex;
  190. cFileName *fileName;
  191. +#ifdef USE_LIVEBUFFER
  192. + cIndex *index;
  193. + cIndexFile *indexFile;
  194. +#else
  195. cIndexFile *index;
  196. +#endif /*USE_LIVEBUFFER*/
  197. cUnbufferedFile *replayFile;
  198. double framesPerSecond;
  199. bool isPesRecording;
  200. @@ -270,18 +281,35 @@ cDvbPlayer::cDvbPlayer(const char *FileName)
  201. dropFrame = NULL;
  202. isyslog("replay %s", FileName);
  203. fileName = new cFileName(FileName, false, false, isPesRecording);
  204. +#ifndef USE_LIVEBUFFER
  205. replayFile = fileName->Open();
  206. if (!replayFile)
  207. return;
  208. +#endif /*USE_LIVEBUFFER*/
  209. ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
  210. // Create the index file:
  211. +#ifdef USE_LIVEBUFFER
  212. + indexFile = NULL;
  213. + index = cRecordControls::GetLiveIndex(FileName);
  214. + if(!index)
  215. + index = indexFile = new cIndexFile(FileName, false, isPesRecording);
  216. +#else
  217. index = new cIndexFile(FileName, false, isPesRecording);
  218. +#endif /*USE_LIVEBUFFER*/
  219. if (!index)
  220. esyslog("ERROR: can't allocate index");
  221. else if (!index->Ok()) {
  222. delete index;
  223. index = NULL;
  224. }
  225. +#ifdef USE_LIVEBUFFER
  226. + readIndex = Resume();
  227. + if (readIndex >= 0) {
  228. + ptsIndex.SetIndex(readIndex);
  229. + isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
  230. + } else
  231. + replayFile = fileName->Open();
  232. +#endif /*USE_LIVEBUFFER*/
  233. }
  234.  
  235. cDvbPlayer::~cDvbPlayer()
  236. @@ -289,7 +317,11 @@ cDvbPlayer::~cDvbPlayer()
  237. Save();
  238. Detach();
  239. delete readFrame; // might not have been stored in the buffer in Action()
  240. +#ifdef USE_LIVEBUFFER
  241. + delete indexFile;
  242. +#else
  243. delete index;
  244. +#endif /*USE_LIVEBUFFER*/
  245. delete fileName;
  246. delete ringBuffer;
  247. }
  248. @@ -387,9 +419,11 @@ void cDvbPlayer::Action(void)
  249. uchar *p = NULL;
  250. int pc = 0;
  251.  
  252. +#ifndef USE_LIVEBUFFER
  253. readIndex = Resume();
  254. if (readIndex >= 0)
  255. isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
  256. +#endif /*USE_LIVEBUFFER*/
  257.  
  258. nonBlockingFileReader = new cNonBlockingFileReader;
  259. int Length = 0;
  260. @@ -436,6 +470,10 @@ void cDvbPlayer::Action(void)
  261. if (NewIndex <= 0 && readIndex > 0)
  262. NewIndex = 1; // make sure the very first frame is delivered
  263. NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode);
  264. +#ifdef USE_LIVEBUFFER
  265. + if (NewIndex < 0 && TimeShiftMode) // Why should we wait for a timeout if not pdForward
  266. + SwitchToPlayFrame = Index;
  267. +#endif
  268. if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
  269. SwitchToPlayFrame = Index;
  270. Index = NewIndex;
  271. @@ -454,6 +492,15 @@ void cDvbPlayer::Action(void)
  272. off_t FileOffset;
  273. if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
  274. readIndex++;
  275. +#ifdef USE_LIVEBUFFER
  276. + else if(index && index->First() && (readIndex < index->First())) {
  277. + int old = readIndex;
  278. + readIndex = index->GetNextIFrame(index->First()+1, true, NULL, NULL, NULL, true);
  279. + isyslog("Jump before start of livebuffer cortrected %d->%d First %d", old, readIndex, index->First());
  280. + if(readIndex <= index->First())
  281. + eof = true;
  282. + }
  283. +#endif /*USE_LIVEBUFFER*/
  284. else
  285. eof = true;
  286. }
  287. @@ -587,7 +634,11 @@ void cDvbPlayer::Action(void)
  288. else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
  289. SwitchToPlay = true;
  290. if (SwitchToPlay) {
  291. +#ifdef USE_LIVEBUFFER
  292. + if (!SwitchToPlayFrame || (playDir == pdBackward))
  293. +#else
  294. if (!SwitchToPlayFrame)
  295. +#endif /*USE_LIVEBUFFER*/
  296. Empty();
  297. DevicePlay();
  298. playMode = pmPlay;
  299. diff --git a/livebuffer.c b/livebuffer.c
  300. new file mode 100644
  301. index 0000000..afc988d
  302. --- /dev/null
  303. +++ b/livebuffer.c
  304. @@ -0,0 +1,403 @@
  305. +#ifdef USE_LIVEBUFFER
  306. +#include "livebuffer.h"
  307. +#if VDRVERSNUM >= 10716
  308. +
  309. +#include <vector>
  310. +#include "videodir.h"
  311. +#include "recording.h"
  312. +#include "skins.h"
  313. +#include "player.h"
  314. +
  315. +#define WAIT_WRITING_COUNT 1000
  316. +#define WAIT_WRITING_SLEEP 10000
  317. +
  318. +#define WAIT_TERMINATE_COUNT 300
  319. +#define WAIT_TERMINATE_SLEEP 10000
  320. +
  321. +struct tLiveIndex {
  322. + int index;
  323. + uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!)
  324. + int reserved:7; // reserved for future use
  325. + int independent:1; // marks frames that can be displayed by themselves (for trick modes)
  326. + uint16_t number:16; // up to 64K files per recording
  327. + tLiveIndex(int Index, bool Independent, uint16_t Number, off_t Offset)
  328. + {
  329. + index = Index;
  330. + offset = Offset;
  331. + reserved = 0;
  332. + independent = Independent;
  333. + number = Number;
  334. + }
  335. +}; // tLiveIndex
  336. +
  337. +class cLiveIndex : public cIndex {
  338. +public:
  339. + cLiveIndex(const char *FileName): bufferFileName(FileName, false), bufferBaseName(FileName) {
  340. + resumePos = -1;
  341. + lastPos = lastGet = lastBuf = 0;
  342. + lastFileNumber=1;
  343. + dropFile = false;
  344. + maxSize = Setup.LiveBufferSize * 60 * DEFAULTFRAMESPERSECOND;
  345. + idx.reserve(maxSize+1);
  346. + }; // cLiveIndex
  347. + virtual ~cLiveIndex() {
  348. + }; // ~cLiveIndex
  349. + virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) {
  350. + cMutexLock lock(&idx_lock);
  351. + idx.push_back(tLiveIndex(++lastPos, Independent, FileNumber, FileOffset));
  352. + while(((idx.size() > maxSize) && (lastGet ? (lastGet > First()) : true) && (lastBuf ? (lastBuf > First()) : true)) || dropFile) {
  353. + if(idx.front().number != lastFileNumber) {
  354. + isyslog("Deleting old livebuffer file #%d (%d)", lastFileNumber, dropFile);
  355. + system(cString::sprintf("ls -l %s/%05d.ts | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", (const char *)bufferBaseName, lastFileNumber)); // for symlink video.xx
  356. + unlink(cString::sprintf("%s/%05d.ts", (const char *)bufferBaseName, lastFileNumber));
  357. + lastFileNumber = idx.front().number;
  358. + dropFile=false;
  359. + } // if
  360. + idx.erase(idx.begin());
  361. + } // if
  362. + return true;
  363. + }; // Write
  364. + virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) {
  365. + cMutexLock lock(&idx_lock);
  366. + std::vector<tLiveIndex>::iterator item = GetIndex(Index);
  367. + if(item == idx.end()) return false;
  368. + *FileNumber = item->number;
  369. + *FileOffset = item->offset;
  370. + if (Independent)
  371. + *Independent = item->independent;
  372. + item++;
  373. + if(item == idx.end()) return false;
  374. + if (Length) {
  375. + uint16_t fn = item->number;
  376. + off_t fo = item->offset;
  377. + if (fn == *FileNumber)
  378. + *Length = int(fo - *FileOffset);
  379. + else
  380. + *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
  381. + } // if
  382. + lastGet = Index;
  383. + return true;
  384. + }; // Get
  385. + virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) {
  386. + cMutexLock lock(&idx_lock);
  387. + std::vector<tLiveIndex>::iterator item = GetIndex(Index);
  388. + if(item == idx.end()) {
  389. + if(Index < First() && Forward)
  390. + item = idx.begin();
  391. + else
  392. + return -1;
  393. + }
  394. + if(Forward) {
  395. + do {
  396. + item++;
  397. + if(item == idx.end()) return -1;
  398. + } while(!item->independent);
  399. + } else {
  400. + do {
  401. + if(item == idx.begin()) return -1;
  402. + item--;
  403. + } while(!item->independent);
  404. + } // if
  405. + uint16_t fn;
  406. + if (!FileNumber)
  407. + FileNumber = &fn;
  408. + off_t fo;
  409. + if (!FileOffset)
  410. + FileOffset = &fo;
  411. + *FileNumber = item->number;
  412. + *FileOffset = item->offset;
  413. + item++;
  414. + if(item == idx.end()) return -1;
  415. + if (Length) {
  416. + // all recordings end with a non-independent frame, so the following should be safe:
  417. + uint16_t fn = item->number;
  418. + off_t fo = item->offset;
  419. + if (fn == *FileNumber) {
  420. + *Length = int(fo - *FileOffset);
  421. + } else {
  422. + esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
  423. + *Length = -1;
  424. + } // if
  425. + } // if
  426. + return Index;
  427. + }; // GetNextIFrame
  428. + virtual bool SetBufferStart(int Frames) {
  429. + cMutexLock lock(&idx_lock);
  430. + abortBuf = false;
  431. + if(Frames <= 0) {
  432. + lastBuf = 0;
  433. + return false;
  434. + } // if
  435. + lastBuf = Last()-Frames;
  436. + if(lastBuf < First())
  437. + lastBuf = First();
  438. + lastBuf = GetNextIFrame(lastBuf, true);
  439. + return true;
  440. + } // SetBufferStart
  441. + virtual cUnbufferedFile *GetNextBuffer(int &Length, bool &Independent) {
  442. + if(abortBuf || !lastBuf) return NULL;
  443. + cMutexLock lock(&idx_lock);
  444. + std::vector<tLiveIndex>::iterator buff = GetIndex(lastBuf);
  445. + if((buff == idx.end()) || ((buff+1) == idx.end())) return NULL;
  446. + off_t offset = buff->offset;
  447. + int number = buff->number;
  448. + cUnbufferedFile *ret = bufferFileName.SetOffset(number, offset);
  449. + Independent = buff->independent;
  450. + buff++;
  451. + lastBuf = buff->index;
  452. + if(number != buff->number)
  453. + Length = -1;
  454. + else
  455. + Length = buff->offset-offset;
  456. + return ret;
  457. + } // GetNextBuffer
  458. + virtual int Get(uint16_t FileNumber, off_t FileOffset) {
  459. + for ( std::vector<tLiveIndex>::iterator item = idx.begin(); item != idx.end(); item++)
  460. + if (item->number > FileNumber || ((item->number == FileNumber) && off_t(item->offset) >= FileOffset))
  461. + return item->index;
  462. + return lastPos;
  463. + }; // Get
  464. + virtual bool Ok(void) {return true;};
  465. + virtual int First(void) {return idx.size() ? idx.front().index : -1;};
  466. + virtual int Last(void) {return idx.size() ? idx.back().index : -1;};
  467. + virtual void SetResume(int Index) {resumePos = lastGet = Index;};
  468. + virtual int GetResume(void) {return resumePos;};
  469. + virtual bool StoreResume(int Index) {resumePos=Index; lastGet=0; return true;};
  470. + virtual bool IsStillRecording(void) {return true;};
  471. + virtual void Delete(void) {};
  472. + virtual void DropFile(void) {dropFile=true;};
  473. + virtual bool IsWritingBuffer(void) {return lastBuf != 0;};
  474. + virtual void CancelWritingBuffer(void) {abortBuf = true;};
  475. + virtual bool WritingBufferCanceled(void) {return abortBuf;};
  476. +protected:
  477. + int firstPos;
  478. + int lastPos;
  479. + int resumePos;
  480. + int lastFileNumber;
  481. + int lastGet;
  482. + int lastBuf;
  483. + bool abortBuf;
  484. + bool dropFile;
  485. + unsigned int maxSize;
  486. + cFileName bufferFileName;
  487. + cString bufferBaseName;
  488. + cMutex idx_lock;
  489. + std::vector<tLiveIndex> idx;
  490. + virtual std::vector<tLiveIndex>::iterator GetIndex(int Index) {
  491. + if(!idx.size()) return idx.end();
  492. + std::vector<tLiveIndex>::iterator item = idx.begin();
  493. +
  494. + unsigned int guess = Index-First(); // Try to guess the position
  495. + if(guess > 0) {
  496. + if(guess < idx.size())
  497. + item += guess;
  498. + else
  499. + item = idx.end()-1;
  500. + } // if
  501. + while(item->index < Index) {
  502. + item++;
  503. + if(item == idx.end())
  504. + return idx.end();
  505. + } // while
  506. + while(item->index > Index) {
  507. + if(item == idx.begin())
  508. + return idx.end();
  509. + item--;
  510. + } // while
  511. + if(item->index != Index)
  512. + return idx.end();
  513. + return item;
  514. + }; // GetIndex
  515. +}; // cLiveIndex
  516. +
  517. +/*****************************************************************************/
  518. +
  519. +cString cLiveRecorder::liveFileName;
  520. +
  521. +cLiveRecorder::cLiveRecorder(const cChannel *Channel):cRecorder(FileName(), Channel, -1)
  522. + ,broken(false) {
  523. + handleError = false;
  524. + if(index) delete index;
  525. + index = new cLiveIndex(FileName());
  526. + Activate(true);
  527. +}; // cLiveRecorder::cLiveRecorder
  528. +
  529. +cLiveRecorder::~cLiveRecorder() {
  530. + int maxWait = WAIT_TERMINATE_COUNT;
  531. + CancelWritingBuffer();
  532. + while(IsWritingBuffer() && maxWait--)
  533. + usleep(WAIT_TERMINATE_SLEEP);
  534. + Activate(false);
  535. + Cleanup();
  536. +}; // cLiveRecorder::~cLiveRecorder
  537. +
  538. +bool cLiveRecorder::IsWritingBuffer() {
  539. + return index && ((cLiveIndex *)index)->IsWritingBuffer();
  540. +} // cLiveRecorder::IsWritingBuffer
  541. +
  542. +void cLiveRecorder::CancelWritingBuffer() {
  543. + if(index) ((cLiveIndex *)index)->CancelWritingBuffer();
  544. +} // cLiveRecorder::CancelWritingBuffer
  545. +
  546. +bool cLiveRecorder::NextFile(void) {
  547. + if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
  548. + if(RunningLowOnDiskSpace() && index)
  549. + ((cLiveIndex *)index)->DropFile();
  550. + if (fileSize > MEGABYTE(off_t(Setup.LiveBufferMaxFileSize)) || RunningLowOnDiskSpace()) {
  551. + recordFile = fileName->NextFile();
  552. + fileSize = 0;
  553. + } // if
  554. + } // if
  555. + return recordFile != NULL;
  556. +} // cLiveRecorder::NextFile
  557. +
  558. +int cLiveRecorder::LastIFrame() {
  559. + if(!index) return 0;
  560. + int ret = index->GetNextIFrame(index->Last()-1, false);
  561. + return (ret > 0) ? ret : 0;
  562. +}; // cLiveRecorder::LastIFrame
  563. +
  564. +int cLiveRecorder::LastFrame() {
  565. + return index ? index->Last() : 0;
  566. +}; // cLiveRecorder::LastFrame
  567. +
  568. +void cLiveRecorder::SetResume(int Index) {
  569. + if(index) ((cLiveIndex *)index)->SetResume(Index);
  570. +}; // cLiveRecorder::SetResume
  571. +
  572. +bool cLiveRecorder::SetBufferStart(time_t Start) {
  573. + if(!index) return false;
  574. + if(time(NULL) <= Start) return false;
  575. + int Frames = SecondsToFrames(time(NULL)-Start, frameDetector ? frameDetector->FramesPerSecond() : DEFAULTFRAMESPERSECOND); //test stop livebuffer
  576. + return ((cLiveIndex *)index)->SetBufferStart(Frames);
  577. +} // cLiveRecorder::SetBufferStart
  578. +
  579. +cIndex *cLiveRecorder::GetIndex() {
  580. + return index;
  581. +}; // cLiveRecorder::GetIndex
  582. +
  583. +bool cLiveRecorder::Cleanup() {
  584. + if(FileName())
  585. + if(-1 == system(cString::sprintf("ls -l %s/* | grep -- '->' | sed -e's/.*-> //' | xargs rm -rf", FileName()))) // for symlink video.xx
  586. + return false;
  587. + else
  588. + if(-1 == system(cString::sprintf("rm -rf %s/*", FileName())))
  589. + return false;
  590. + return true;
  591. +}; // cLiveRecorder::Cleanup
  592. +
  593. +bool cLiveRecorder::Prepare() {
  594. + if (!MakeDirs(FileName(), true)) return false;
  595. + return Cleanup();
  596. +}; // cLiveRecorder::Prepare
  597. +
  598. +const char *cLiveRecorder::FileName() {
  599. + if(!(const char *)liveFileName && BufferDirectory)
  600. + liveFileName = cString::sprintf("%s/LiveBuffer", BufferDirectory);
  601. + return liveFileName;
  602. +}; // cLiveRecorder::FileName
  603. +
  604. +void cLiveRecorder::Activate(bool On) {
  605. + cRecorder::Activate(On);
  606. + if(!On) broken=true;
  607. +} // cLiveRecorder::Activate
  608. +
  609. +void cLiveRecorder::Receive(uchar *Data, int Length) {
  610. + if(broken) {
  611. + isyslog("Continue live recorder on broken stream (maybe due to switching to same channel on other device)");
  612. + TsSetTeiOnBrokenPackets(Data, Length);
  613. + broken = false;
  614. + } // if
  615. + cRecorder::Receive(Data, Length);
  616. +} // cLiveRecorder::Receive
  617. +
  618. +/*****************************************************************************/
  619. +
  620. +cBufferRecorder::cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex)
  621. + :cRecorder(FileName, Channel, Priority)
  622. + ,liveBufferIndex(LiveBufferIndex)
  623. + ,dropData(false) {
  624. + if(liveBufferIndex) dropData=true; // Drop new data till we have written most of the live buffer data
  625. +} // cBufferRecorder::cBufferRecorder
  626. +
  627. +cBufferRecorder::~cBufferRecorder() {
  628. + if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  629. +} // cBufferRecorder::~cBufferRecorder
  630. +
  631. +void cBufferRecorder::Action(void) {
  632. + if(liveBufferIndex)
  633. + FillInitialData(NULL, 0);
  634. + dropData=false;
  635. + cRecorder::Action();
  636. + if(liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  637. + liveBufferIndex = NULL;
  638. +} // cBufferRecorder::Action
  639. +
  640. +void cBufferRecorder::Activate(bool On) {
  641. + if(!On && liveBufferIndex) ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  642. + cRecorder::Activate(On);
  643. +} // cBufferRecorder::Activate
  644. +
  645. +void cBufferRecorder::Receive(uchar *Data, int Length) {
  646. + if(!dropData) cRecorder::Receive(Data, Length);
  647. +} // cBufferRecorder::Receive
  648. +
  649. +void cBufferRecorder::FillInitialData(uchar *Data, int Size) {
  650. + if(liveBufferIndex) {
  651. + int64_t search_pts = Data ? TsGetPts(Data, Size) : -1;
  652. + int maxWait = WAIT_WRITING_COUNT;
  653. + uchar buffer[MAXFRAMESIZE];
  654. + int Length;
  655. + bool Independent;
  656. + bool found = false;
  657. + while(!Data || (Size >= TS_SIZE)) {
  658. + cUnbufferedFile *file = ((cLiveIndex *)liveBufferIndex)->GetNextBuffer(Length, Independent);
  659. + if(!file) {
  660. + if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
  661. + isyslog("Writing buffer canceled by user");
  662. + if(fileSize) TsSetTeiOnBrokenPackets(Data, Size);
  663. + ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  664. + liveBufferIndex = NULL;
  665. + return;
  666. + } // if
  667. + if(!Data || !Size) return;
  668. + if(!maxWait--)
  669. + break;
  670. + usleep(WAIT_WRITING_SLEEP);
  671. + continue;
  672. + } // if
  673. + if (!NextFile())
  674. + break;
  675. + int len = ReadFrame(file, buffer, Length, sizeof(buffer));
  676. + if(len < TS_SIZE) {
  677. + isyslog("Failed to read live buffer data");
  678. + break;
  679. + } // if
  680. + if(Data && Independent && (search_pts == TsGetPts(buffer, len))) {
  681. + found = true;
  682. + break;
  683. + } // if
  684. + if (index)
  685. + index->Write(Independent, fileName->Number(), fileSize);
  686. + if (recordFile->Write(buffer, len) < 0) {
  687. + isyslog("Failed to write live buffer data");
  688. + break;
  689. + } // if
  690. + fileSize += len;
  691. + } // while
  692. + if(Data) {
  693. + isyslog("%lld bytes from live buffer %swritten to recording", fileSize, found ? "seamless ": "");
  694. + if(!found && fileSize) TsSetTeiOnBrokenPackets(Data, Size);
  695. + ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  696. + liveBufferIndex = NULL;
  697. + } else if(((cLiveIndex *)liveBufferIndex)->WritingBufferCanceled()) {
  698. + isyslog("%lld bytes from live buffer written to recording (aborted)", fileSize);
  699. + ((cLiveIndex *)liveBufferIndex)->SetBufferStart(0);
  700. + liveBufferIndex = NULL;
  701. + } // if
  702. + } else if (Data && fileSize)
  703. + TsSetTeiOnBrokenPackets(Data, Size);
  704. +} // cBufferRecorder::FillInitialData
  705. +
  706. +#endif /*VDRVERSNUM*/
  707. +#endif /*USE_LIVEBUFFER*/
  708. diff --git a/livebuffer.h b/livebuffer.h
  709. new file mode 100644
  710. index 0000000..8382d7d
  711. --- /dev/null
  712. +++ b/livebuffer.h
  713. @@ -0,0 +1,47 @@
  714. +#ifndef LIVEBUFFER_H
  715. +#define LIVEBUFFER_H
  716. +
  717. +#ifdef USE_LIVEBUFFER
  718. +#include "config.h"
  719. +#if VDRVERSNUM >= 10716
  720. +
  721. +#include "recorder.h"
  722. +
  723. +class cLiveRecorder : public cRecorder {
  724. +public:
  725. + cLiveRecorder(const cChannel *Channel);
  726. + virtual bool NextFile(void);
  727. + virtual ~cLiveRecorder();
  728. + virtual bool IsWritingBuffer();
  729. + virtual void CancelWritingBuffer();
  730. + virtual int LastIFrame();
  731. + virtual int LastFrame();
  732. + virtual void SetResume(int Index);
  733. + virtual bool SetBufferStart(time_t Start);
  734. + virtual cIndex *GetIndex();
  735. + static bool Cleanup();
  736. + static bool Prepare();
  737. + static const char *FileName();
  738. +protected:
  739. + virtual void Activate(bool On);
  740. + virtual void Receive(uchar *Data, int Length);
  741. + bool broken;
  742. + static cString liveFileName;
  743. +}; // cLiveRecorder
  744. +
  745. +class cBufferRecorder : public cRecorder {
  746. +public:
  747. + cBufferRecorder(const char *FileName, const cChannel *Channel, int Priority, cIndex *LiveBufferIndex);
  748. + virtual ~cBufferRecorder();
  749. + virtual void FillInitialData(uchar *Data, int Size);
  750. +protected:
  751. + virtual void Action(void);
  752. + virtual void Activate(bool On);
  753. + virtual void Receive(uchar *Data, int Length);
  754. + cIndex *liveBufferIndex;
  755. + bool dropData;
  756. +}; // cBufferRecorder
  757. +
  758. +#endif /*VDRVERSNUM*/
  759. +#endif /*USE_LIVEBUFFER*/
  760. +#endif /*LIVEBUFFER_H*/
  761. diff --git a/menu.c b/menu.c
  762. index ef2bb46..621d2ce 100644
  763. --- a/menu.c
  764. +++ b/menu.c
  765. @@ -3043,7 +3043,11 @@ eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
  766.  
  767. class cMenuSetupRecord : public cMenuSetupBase {
  768. private:
  769. - const char *pauseKeyHandlingTexts[3];
  770. +#ifdef USE_LIVEBUFFER
  771. + const char *pauseKeyHandlingTexts[4];
  772. +#else
  773. + const char *pauseKeyHandlingTexts[3];
  774. +#endif /*USE_LIVEBUFFER*/
  775. const char *delTimeshiftRecTexts[3];
  776. public:
  777. cMenuSetupRecord(void);
  778. @@ -3054,6 +3058,9 @@ cMenuSetupRecord::cMenuSetupRecord(void)
  779. pauseKeyHandlingTexts[0] = tr("do not pause live video");
  780. pauseKeyHandlingTexts[1] = tr("confirm pause live video");
  781. pauseKeyHandlingTexts[2] = tr("pause live video");
  782. +#ifdef USE_LIVEBUFFER
  783. + pauseKeyHandlingTexts[3] = tr("Timeshift");
  784. +#endif /*USE_LIVEBUFFER*/
  785. delTimeshiftRecTexts[0] = tr("no");
  786. delTimeshiftRecTexts[1] = tr("confirm");
  787. delTimeshiftRecTexts[2] = tr("yes");
  788. @@ -3063,7 +3070,12 @@ cMenuSetupRecord::cMenuSetupRecord(void)
  789. Add(new cMenuEditIntItem( tr("Setup.Recording$Primary limit"), &data.PrimaryLimit, 0, MAXPRIORITY));
  790. Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"), &data.DefaultPriority, 0, MAXPRIORITY));
  791. Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"), &data.DefaultLifetime, 0, MAXLIFETIME));
  792. - Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
  793. +#ifdef USE_LIVEBUFFER
  794. + Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 4, pauseKeyHandlingTexts));
  795. + Add(new cMenuEditIntItem( tr("Timeshift size (min)"), &data.LiveBufferSize, 1, 300)); // TODO fix name and min/max values
  796. +#else
  797. + Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
  798. +#endif /*USE_LIVEBUFFER*/
  799. Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
  800. Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
  801. Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
  802. @@ -4134,7 +4146,11 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
  803. isyslog("record %s", fileName);
  804. if (MakeDirs(fileName, true)) {
  805. const cChannel *ch = timer->Channel();
  806. +#ifdef USE_LIVEBUFFER
  807. + recorder = new cBufferRecorder(fileName, ch, timer->Priority(), cRecordControls::GetLiveBuffer(timer));
  808. +#else
  809. recorder = new cRecorder(fileName, ch, timer->Priority());
  810. +#endif
  811. if (device->AttachReceiver(recorder)) {
  812. Recording.WriteInfo();
  813. cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
  814. @@ -4219,6 +4235,10 @@ bool cRecordControl::Process(time_t t)
  815. cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
  816. int cRecordControls::state = 0;
  817.  
  818. +#ifdef USE_LIVEBUFFER
  819. +cLiveRecorder *cRecordControls::liveRecorder = NULL;
  820. +#endif /*USE_LIVEBUFFER*/
  821. +
  822. bool cRecordControls::Start(cTimer *Timer, bool Pause)
  823. {
  824. static time_t LastNoDiskSpaceMessage = 0;
  825. @@ -4290,8 +4310,31 @@ void cRecordControls::Stop(const char *InstantId)
  826. }
  827. }
  828.  
  829. +
  830. +#ifdef USE_LIVEBUFFER
  831. +bool cRecordControls::StartLiveBuffer(eKeys Key) {
  832. + if(Setup.PauseKeyHandling == 3 && liveRecorder) {
  833. + int pos = liveRecorder->LastIFrame();
  834. + isyslog("Enter timeshift at %d / %d", pos, liveRecorder->LastFrame());
  835. + liveRecorder->SetResume(pos?pos:liveRecorder->LastFrame());
  836. + cReplayControl::SetRecording(cLiveRecorder::FileName(), tr("Timeshift mode"));
  837. + cReplayControl *rc = new cReplayControl;
  838. + cControl::Launch(rc);
  839. + cControl::Attach();
  840. + rc->ProcessKey(Key);
  841. + rc->Show(); // show progressbar at the start of livebuffer
  842. + return true;
  843. + } // if
  844. + return false;
  845. +} // cRecordControls::StartLiveBuffer
  846. +#endif /*USE_LIVEBUFFER*/
  847. +
  848. bool cRecordControls::PauseLiveVideo(void)
  849. {
  850. +#ifdef USE_LIVEBUFFER
  851. + if(StartLiveBuffer(kPause))
  852. + return true;
  853. +#endif /*USE_LIVEBUFFER*/
  854. Skins.Message(mtStatus, tr("Pausing live video..."));
  855. cReplayControl::SetRecording(NULL, NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed()
  856. if (Start(NULL, true)) {
  857. @@ -4308,6 +4351,54 @@ bool cRecordControls::PauseLiveVideo(void)
  858. return false;
  859. }
  860.  
  861. +#ifdef USE_LIVEBUFFER
  862. +void cRecordControls::SetLiveChannel(cDevice *Device, const cChannel *Channel) {
  863. + if(liveRecorder) {
  864. + if(Channel && Device && (liveRecorder->ChannelID()==Channel->GetChannelID()))
  865. + Device->AttachReceiver(liveRecorder);
  866. + else
  867. + DELETENULL(liveRecorder);
  868. + } // if
  869. + if(Device && Channel) cControl::Launch(new cTransferControl(Device, Channel));
  870. + if(Setup.PauseKeyHandling == 3 && Channel && Device && !liveRecorder) {
  871. + if (cLiveRecorder::Prepare()) {
  872. + liveRecorder = new cLiveRecorder(Channel);
  873. + if(!Device->AttachReceiver(liveRecorder))
  874. + DELETENULL(liveRecorder);
  875. + } // if
  876. + } // if
  877. +} // cRecordControls::SetLiveChannel
  878. +
  879. +bool cRecordControls::CanSetLiveChannel(const cChannel *Channel) {
  880. + if(liveRecorder && Channel && (liveRecorder->ChannelID()==Channel->GetChannelID())) return true;
  881. + return !IsWritingBuffer();
  882. +} // cRecordControls::CanSetLiveChannel
  883. +
  884. +bool cRecordControls::IsWritingBuffer() {
  885. + return liveRecorder ? liveRecorder->IsWritingBuffer() : false;
  886. +} // cRecordControls::IsWritingBuffer
  887. +
  888. +void cRecordControls::CancelWritingBuffer() {
  889. + if(liveRecorder && liveRecorder->IsWritingBuffer()) {
  890. + liveRecorder->CancelWritingBuffer();
  891. + sleep(1); // allow recorder to really stop
  892. + } // if
  893. +} // cRecordControls::CancelWritingBuffer
  894. +
  895. +cIndex *cRecordControls::GetLiveBuffer(cTimer *Timer) {
  896. + if(!liveRecorder || !Timer || !Timer->Channel()) return NULL;
  897. + if(!(liveRecorder->ChannelID() == Timer->Channel()->GetChannelID())) return NULL;
  898. + if(!liveRecorder->SetBufferStart(Timer->StartTime())) return NULL;
  899. + return liveRecorder->GetIndex();
  900. +} // cRecordControls::GetLiveBuffer
  901. +
  902. +cIndex *cRecordControls::GetLiveIndex(const char *FileName) {
  903. + if(!FileName || strcmp(cLiveRecorder::FileName(), FileName)) return NULL;
  904. + return liveRecorder ? liveRecorder->GetIndex() : NULL;
  905. +} // cRecordControls::GetLiveIndex
  906. +
  907. +#endif /* USE_LIVEBUFFER */
  908. +
  909. const char *cRecordControls::GetInstantId(const char *LastInstantId)
  910. {
  911. for (int i = 0; i < MAXRECORDCONTROLS; i++) {
  912. @@ -4506,21 +4597,30 @@ void cReplayControl::Hide(void)
  913.  
  914. void cReplayControl::ShowMode(void)
  915. {
  916. - if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) {
  917. + if (visible || (Setup.ShowReplayMode && !cOsd::IsOpen())) {
  918. bool Play, Forward;
  919. int Speed;
  920. if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) {
  921. bool NormalPlay = (Play && Speed == -1);
  922. + bool Paused = (!Play && Speed == -1);
  923.  
  924. if (!visible) {
  925. if (NormalPlay)
  926. return; // no need to do indicate ">" unless there was a different mode displayed before
  927. visible = modeOnly = true;
  928. +
  929. + // if newly paused show full replay osd; ie modeOnly = false
  930. + if (Paused) {
  931. + modeOnly = (lastPlay == Play);
  932. + }
  933. +
  934. displayReplay = Skins.Current()->DisplayReplay(modeOnly);
  935. }
  936.  
  937. - if (modeOnly && !timeoutShow && NormalPlay)
  938. + // osd times out when replaying normally OR when paused and full osd is shown
  939. + if (!timeoutShow && (NormalPlay|| (!modeOnly && Paused)))
  940. timeoutShow = time(NULL) + MODETIMEOUT;
  941. +
  942. displayReplay->SetMode(Play, Forward, Speed);
  943. lastPlay = Play;
  944. lastForward = Forward;
  945. @@ -4534,6 +4634,45 @@ bool cReplayControl::ShowProgress(bool Initial)
  946. int Current, Total;
  947.  
  948. if (GetIndex(Current, Total) && Total > 0) {
  949. +#ifdef USE_LIVEBUFFER
  950. + int first=0;
  951. + cIndex *idx = cRecordControls::GetLiveIndex(fileName);
  952. + if(idx) first = idx->First(); // Normalize displayed values
  953. + Current -= first;
  954. + if(Current < 0) Current = 0;
  955. + Total -= first;
  956. + if(Total < 0) Total = 0;
  957. + time_t now = time(NULL);
  958. + static time_t last_sched_check = 0;
  959. + if(displayReplay && idx && (last_sched_check != now)) {
  960. + last_sched_check = now; // Only check every second
  961. + cSchedulesLock SchedulesLock;
  962. + const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
  963. + if (Schedules) {
  964. + const char *display_title = NULL;// = title;
  965. + const cSchedule *Schedule = Schedules->GetSchedule(Channels.GetByNumber(cDevice::CurrentChannel()));
  966. + if (Schedule) {
  967. + time_t Time = now - round(((double)Total - Current) / FramesPerSecond());
  968. + const cEvent *event = Schedule->GetEventAround(Time);
  969. + if (event) display_title = event->Title();
  970. + } // if
  971. +
  972. + // no event title; show channel name
  973. + if (!display_title) {
  974. + cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
  975. + display_title = channel->Name();
  976. + }
  977. +
  978. + // set title as "Timeshift mode: <event title> "
  979. + // OR "Timeshift mode: <channel name>"
  980. + // if neither is possible leave title as such
  981. + if (display_title)
  982. + displayReplay->SetTitle(cString::sprintf("%s: %s",
  983. + tr("Timeshift mode"),
  984. + display_title));
  985. + } // if
  986. + } // if
  987. +#endif /*USE_LIVEBUFFER*/
  988. if (!visible) {
  989. displayReplay = Skins.Current()->DisplayReplay(modeOnly);
  990. displayReplay->SetMarks(&marks);
  991. @@ -4635,6 +4774,9 @@ void cReplayControl::TimeSearchProcess(eKeys Key)
  992.  
  993. void cReplayControl::TimeSearch(void)
  994. {
  995. +#ifdef USE_LIVEBUFFER
  996. + if(cRecordControls::GetLiveIndex(fileName)) return;
  997. +#endif /*USE_LIVEBUFFER*/
  998. timeSearchTime = timeSearchPos = 0;
  999. timeSearchHide = false;
  1000. if (modeOnly)
  1001. @@ -4653,6 +4795,9 @@ void cReplayControl::TimeSearch(void)
  1002.  
  1003. void cReplayControl::MarkToggle(void)
  1004. {
  1005. +#ifdef USE_LIVEBUFFER
  1006. + if(cRecordControls::GetLiveIndex(fileName)) return;
  1007. +#endif /*USE_LIVEBUFFER*/
  1008. int Current, Total;
  1009. if (GetIndex(Current, Total, true)) {
  1010. cMark *m = marks.Get(Current);
  1011. @@ -4673,6 +4818,9 @@ void cReplayControl::MarkToggle(void)
  1012.  
  1013. void cReplayControl::MarkJump(bool Forward)
  1014. {
  1015. +#ifdef USE_LIVEBUFFER
  1016. + if(cRecordControls::GetLiveIndex(fileName)) return;
  1017. +#endif /*USE_LIVEBUFFER*/
  1018. if (marks.Count()) {
  1019. int Current, Total;
  1020. if (GetIndex(Current, Total)) {
  1021. @@ -4687,6 +4835,9 @@ void cReplayControl::MarkJump(bool Forward)
  1022.  
  1023. void cReplayControl::MarkMove(bool Forward)
  1024. {
  1025. +#ifdef USE_LIVEBUFFER
  1026. + if(cRecordControls::GetLiveIndex(fileName)) return;
  1027. +#endif /*USE_LIVEBUFFER*/
  1028. int Current, Total;
  1029. if (GetIndex(Current, Total)) {
  1030. cMark *m = marks.Get(Current);
  1031. @@ -4711,6 +4862,9 @@ void cReplayControl::MarkMove(bool Forward)
  1032.  
  1033. void cReplayControl::EditCut(void)
  1034. {
  1035. +#ifdef USE_LIVEBUFFER
  1036. + if(cRecordControls::GetLiveIndex(fileName)) return;
  1037. +#endif /*USE_LIVEBUFFER*/
  1038. if (fileName) {
  1039. Hide();
  1040. if (!cCutter::Active()) {
  1041. @@ -4729,6 +4883,9 @@ void cReplayControl::EditCut(void)
  1042.  
  1043. void cReplayControl::EditTest(void)
  1044. {
  1045. +#ifdef USE_LIVEBUFFER
  1046. + if(cRecordControls::GetLiveIndex(fileName)) return;
  1047. +#endif /*USE_LIVEBUFFER*/
  1048. int Current, Total;
  1049. if (GetIndex(Current, Total)) {
  1050. cMark *m = marks.Get(Current);
  1051. @@ -4760,7 +4917,14 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
  1052. if (Key == kNone)
  1053. marks.Update();
  1054. if (visible) {
  1055. +
  1056. + if (Key != kNone /*&& !modeOnly*/ && timeoutShow) {
  1057. + printf("timeout reset +%d\n", MODETIMEOUT);
  1058. + timeoutShow = time(NULL) + MODETIMEOUT;
  1059. + }
  1060. +
  1061. if (timeoutShow && time(NULL) > timeoutShow) {
  1062. + printf("timed out \n");
  1063. Hide();
  1064. ShowMode();
  1065. timeoutShow = 0;
  1066. @@ -4777,12 +4941,34 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
  1067. return osContinue;
  1068. }
  1069. bool DoShowMode = true;
  1070. +
  1071. +#ifdef USE_LIVEBUFFER
  1072. + if (cRecordControls::GetLiveIndex(fileName) && (Key >= k0) && (Key <= k9))
  1073. + return osSwitchChannel;
  1074. +#endif /*USE_LIVEBUFFER*/
  1075. switch (int(Key)) {
  1076. // Positioning:
  1077. +#ifdef USE_LIVEBUFFER
  1078. + case kUp: if(cRecordControls::GetLiveIndex(fileName)) {
  1079. + cDevice::SwitchChannel(1);
  1080. + return osEnd;
  1081. + } // if
  1082. + // NO break
  1083. + case kPlay:
  1084. + Play(); break;
  1085. + case kDown: if(cRecordControls::GetLiveIndex(fileName)) {
  1086. + cDevice::SwitchChannel(-1);
  1087. + return osEnd;
  1088. + } // if
  1089. + // NO break
  1090. + case kPause: Pause();
  1091. + break;
  1092. +#else
  1093. case kPlay:
  1094. case kUp: Play(); break;
  1095. case kPause:
  1096. case kDown: Pause(); break;
  1097. +#endif /*USE_LIVEBUFFER*/
  1098. case kFastRew|k_Release:
  1099. case kLeft|k_Release:
  1100. if (Setup.MultiSpeedMode) break;
  1101. @@ -4793,15 +4979,52 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
  1102. if (Setup.MultiSpeedMode) break;
  1103. case kFastFwd:
  1104. case kRight: Forward(); break;
  1105. - case kRed: TimeSearch(); break;
  1106. + //case kRed: TimeSearch(); break;
  1107. case kGreen|k_Repeat:
  1108. case kGreen: SkipSeconds(-60); break;
  1109. case kYellow|k_Repeat:
  1110. case kYellow: SkipSeconds( 60); break;
  1111. +#ifdef USE_LIVEBUFFER
  1112. + case kRed: if(cRecordControls::GetLiveIndex(fileName)) {
  1113. +
  1114. + if (!(visible && !modeOnly)) return osUnknown;
  1115. + else {} // fall through to case kRecord
  1116. + // since Timeshift ON and replay OSD is shown
  1117. + } // if
  1118. + else { //timeshift off
  1119. + TimeSearch();
  1120. + break;
  1121. + } // else
  1122. + // No break
  1123. + case kRecord: if(cRecordControls::GetLiveIndex(fileName)) {
  1124. + int frames = 0;
  1125. + int Current, Total;
  1126. + if(GetIndex(Current, Total))
  1127. + frames = Total-Current;
  1128. + cTimer *timer = new cTimer(true, false, Channels.GetByNumber(cDevice::CurrentChannel()), frames / FramesPerSecond());
  1129. + Timers.Add(timer);
  1130. + Timers.SetModified();
  1131. + if (cRecordControls::Start(timer))
  1132. + Skins.Message(mtInfo, tr("Recording started"));
  1133. + else
  1134. + Timers.Del(timer);
  1135. + } // if
  1136. + break;
  1137. + case kBlue: if(cRecordControls::GetLiveIndex(fileName))
  1138. + if(!(visible && !modeOnly))
  1139. + return osUnknown;
  1140. + //NO break
  1141. + case kStop: Hide();
  1142. + Stop();
  1143. + return osEnd;
  1144. +#else
  1145. + case kRed: TimeSearch(); break;
  1146. case kStop:
  1147. case kBlue: Hide();
  1148. Stop();
  1149. return osEnd;
  1150. +#endif /*USE_LIVEBUFFER*/
  1151. +
  1152. default: {
  1153. DoShowMode = false;
  1154. switch (int(Key)) {
  1155. @@ -4832,7 +5055,20 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
  1156. else
  1157. Show();
  1158. break;
  1159. - case kBack: if (Setup.DelTimeshiftRec) {
  1160. + case kBack:
  1161. +#ifdef USE_LIVEBUFFER
  1162. + if (visible && !modeOnly) {
  1163. + Hide();
  1164. + DoShowMode = true;
  1165. + break;
  1166. + }
  1167. + if(cRecordControls::GetLiveIndex(fileName)) {
  1168. + Hide();
  1169. + Stop();
  1170. + return osEnd;
  1171. + } // if
  1172. +#endif /*USE_LIVEBUFFER*/
  1173. + if (Setup.DelTimeshiftRec) {
  1174. cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
  1175. return rc && rc->InstantId() ? osEnd : osRecordings;
  1176. }
  1177. diff --git a/menu.h b/menu.h
  1178. index ec1c175..2d55470 100644
  1179. --- a/menu.h
  1180. +++ b/menu.h
  1181. @@ -18,6 +18,12 @@
  1182. #include "menuitems.h"
  1183. #include "recorder.h"
  1184. #include "skins.h"
  1185. +#ifdef USE_LIVEBUFFER
  1186. +#include "livebuffer.h"
  1187. +#endif /*USE_LIVEBUFFER*/
  1188. +
  1189. +
  1190. +
  1191.  
  1192. class cMenuText : public cOsdMenu {
  1193. private:
  1194. @@ -236,10 +242,18 @@ class cRecordControls {
  1195. private:
  1196. static cRecordControl *RecordControls[];
  1197. static int state;
  1198. +#ifdef USE_LIVEBUFFER
  1199. +protected:
  1200. + friend class cRecordControl;
  1201. + static cLiveRecorder *liveRecorder;
  1202. +#endif /*USE_LIVEBUFFER*/
  1203. public:
  1204. static bool Start(cTimer *Timer = NULL, bool Pause = false);
  1205. static void Stop(const char *InstantId);
  1206. static bool PauseLiveVideo(void);
  1207. +#ifdef USE_LIVEBUFFER
  1208. + static bool StartLiveBuffer(eKeys Key);
  1209. +#endif /*USE_LIVEBUFFER*/
  1210. static const char *GetInstantId(const char *LastInstantId);
  1211. static cRecordControl *GetRecordControl(const char *FileName);
  1212. static void Process(time_t t);
  1213. @@ -248,6 +262,14 @@ public:
  1214. static void Shutdown(void);
  1215. static void ChangeState(void) { state++; }
  1216. static bool StateChanged(int &State);
  1217. +#ifdef USE_LIVEBUFFER
  1218. + static void SetLiveChannel(cDevice *Device, const cChannel *Channel);
  1219. + static bool CanSetLiveChannel(const cChannel *Channel);
  1220. + static bool IsWritingBuffer();
  1221. + static void CancelWritingBuffer();
  1222. + static cIndex *GetLiveBuffer(cTimer *Timer);
  1223. + static cIndex *GetLiveIndex(const char *FileName);
  1224. +#endif /*USE_LIVEBUFFER*/
  1225. };
  1226.  
  1227. class cReplayControl : public cDvbPlayerControl {
  1228. diff --git a/osdbase.h b/osdbase.h
  1229. index 91c5ff7..27c22b7 100644
  1230. --- a/osdbase.h
  1231. +++ b/osdbase.h
  1232. @@ -33,6 +33,9 @@ enum eOSState { osUnknown,
  1233. osSwitchDvb,
  1234. osBack,
  1235. osEnd,
  1236. +#ifdef USE_LIVEBUFFER
  1237. + osSwitchChannel,
  1238. +#endif /*USE_LIVEBUFFER*/
  1239. os_User, // the following values can be used locally
  1240. osUser1,
  1241. osUser2,
  1242. diff --git a/player.c b/player.c
  1243. index 3490565..9aa2956 100644
  1244. --- a/player.c
  1245. +++ b/player.c
  1246. @@ -10,6 +10,11 @@
  1247. #include "player.h"
  1248. #include "i18n.h"
  1249.  
  1250. +#ifdef USE_LIVEBUFFER
  1251. +#include "menu.h"
  1252. +#include "transfer.h"
  1253. +#endif /*USE_LIVEBUFFER*/
  1254. +
  1255. // --- cPlayer ---------------------------------------------------------------
  1256.  
  1257. cPlayer::cPlayer(ePlayMode PlayMode)
  1258. @@ -68,6 +73,12 @@ cControl *cControl::Control(void)
  1259.  
  1260. void cControl::Launch(cControl *Control)
  1261. {
  1262. +#ifdef USE_LIVEBUFFER
  1263. + if(!dynamic_cast<cTransferControl *>(Control)) {
  1264. + if(!dynamic_cast<cReplayControl *>(Control) || strcmp(cLiveRecorder::FileName(), cReplayControl::NowReplaying()))
  1265. + cRecordControls::SetLiveChannel(NULL, NULL);
  1266. + } // if
  1267. +#endif /*USE_LIVEBUFFER*/
  1268. cMutexLock MutexLock(&mutex);
  1269. cControl *c = control; // keeps control from pointing to uninitialized memory
  1270. control = Control;
  1271. diff --git a/po/de_DE.po b/po/de_DE.po
  1272. index 6d5b822..355348b 100644
  1273. --- a/po/de_DE.po
  1274. +++ b/po/de_DE.po
  1275. @@ -25,6 +25,9 @@ msgstr "Kanal nicht verf
  1276. msgid "Can't start Transfer Mode!"
  1277. msgstr "Transfer-Mode kann nicht gestartet werden!"
  1278.  
  1279. +msgid "Still writing timeshift data to recording. Abort?"
  1280. +msgstr "Timeshift-Daten werden noch in Aufnahme kopiert. Abbrechen?"
  1281. +
  1282. msgid "off"
  1283. msgstr "aus"
  1284.  
  1285. diff --git a/recorder.c b/recorder.c
  1286. index a6cab47..70e4659 100644
  1287. --- a/recorder.c
  1288. +++ b/recorder.c
  1289. @@ -24,6 +24,9 @@
  1290. cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
  1291. :cReceiver(Channel, Priority)
  1292. ,cThread("recording")
  1293. +#ifdef USE_LIVEBUFFER
  1294. +,handleError(true)
  1295. +#endif /*USE_LIVEBUFFER*/
  1296. {
  1297. recordingName = strdup(FileName);
  1298.  
  1299. @@ -140,6 +143,9 @@ void cRecorder::Action(void)
  1300. InfoWritten = true;
  1301. }
  1302. if (FirstIframeSeen || frameDetector->IndependentFrame()) {
  1303. +#ifdef USE_LIVEBUFFER
  1304. + if(!FirstIframeSeen) FillInitialData(b, r);
  1305. +#endif /*USE_LIVEBUFFER*/
  1306. FirstIframeSeen = true; // start recording with the first I-frame
  1307. if (!NextFile())
  1308. break;
  1309. @@ -165,7 +171,11 @@ void cRecorder::Action(void)
  1310. ringBuffer->Del(Count);
  1311. }
  1312. }
  1313. +#ifdef USE_LIVEBUFFER
  1314. + if (handleError && (time(NULL) - t > MAXBROKENTIMEOUT)) {
  1315. +#else
  1316. if (time(NULL) - t > MAXBROKENTIMEOUT) {
  1317. +#endif
  1318. esyslog("ERROR: video data stream broken");
  1319. ShutdownHandler.RequestEmergencyExit();
  1320. t = time(NULL);
  1321. diff --git a/recorder.h b/recorder.h
  1322. index 05cc42b..1d2aa04 100644
  1323. --- a/recorder.h
  1324. +++ b/recorder.h
  1325. @@ -17,18 +17,33 @@
  1326. #include "thread.h"
  1327.  
  1328. class cRecorder : public cReceiver, cThread {
  1329. +#ifdef USE_LIVEBUFFER
  1330. +protected:
  1331. +#else
  1332. private:
  1333. +#endif /*USE_LIVEBUFFER*/
  1334. cRingBufferLinear *ringBuffer;
  1335. cFrameDetector *frameDetector;
  1336. cPatPmtGenerator patPmtGenerator;
  1337. cFileName *fileName;
  1338. +#ifdef USE_LIVEBUFFER
  1339. + cIndex *index;
  1340. + bool handleError;
  1341. +#else
  1342. cIndexFile *index;
  1343. +#endif /*USE_LIVEBUFFER*/
  1344. cUnbufferedFile *recordFile;
  1345. char *recordingName;
  1346. off_t fileSize;
  1347. time_t lastDiskSpaceCheck;
  1348. +#ifdef USE_LIVEBUFFER
  1349. + virtual bool RunningLowOnDiskSpace(void);
  1350. + virtual bool NextFile(void);
  1351. + virtual void FillInitialData(uchar *Data, int Size) {};
  1352. +#else
  1353. bool RunningLowOnDiskSpace(void);
  1354. bool NextFile(void);
  1355. +#endif /*USE_LIVEBUFFER*/
  1356. protected:
  1357. virtual void Activate(bool On);
  1358. virtual void Receive(uchar *Data, int Length);
  1359. diff --git a/recording.h b/recording.h
  1360. index 37979ec..52d57c8 100644
  1361. --- a/recording.h
  1362. +++ b/recording.h
  1363. @@ -264,7 +264,26 @@ public:
  1364. struct tIndexTs;
  1365. class cIndexFileGenerator;
  1366.  
  1367. +#ifdef USE_LIVEBUFFER
  1368. +class cIndex {
  1369. +public:
  1370. + virtual bool Ok(void) =0;
  1371. + virtual bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset) =0;
  1372. + virtual bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL) =0;
  1373. + virtual int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false) =0;
  1374. + virtual int Get(uint16_t FileNumber, off_t FileOffset) =0;
  1375. + virtual int First(void) {return 0;};
  1376. + virtual int Last(void) =0;
  1377. + virtual int GetResume(void) =0;
  1378. + virtual bool StoreResume(int Index) =0;
  1379. + virtual bool IsStillRecording(void) =0;
  1380. + virtual void Delete(void) =0;
  1381. + };
  1382. +
  1383. +class cIndexFile : public cIndex {
  1384. +#else
  1385. class cIndexFile {
  1386. +#endif /*USE_LIVEBUFFER*/
  1387. private:
  1388. int f;
  1389. cString fileName;
  1390. diff --git a/timers.c b/timers.c
  1391. index 54ed1ba..c2298fa 100644
  1392. --- a/timers.c
  1393. +++ b/timers.c
  1394. @@ -25,7 +25,11 @@
  1395.  
  1396. // --- cTimer ----------------------------------------------------------------
  1397.  
  1398. +#ifdef USE_LIVEBUFFER
  1399. +cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel, int Forerun)
  1400. +#else
  1401. cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
  1402. +#endif /*USE_LIVEBUFFER*/
  1403. {
  1404. startTime = stopTime = 0;
  1405. lastSetEvent = 0;
  1406. @@ -35,7 +39,11 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
  1407. if (Instant)
  1408. SetFlags(tfActive | tfInstant);
  1409. channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
  1410. +#ifdef USE_LIVEBUFFER
  1411. + time_t t = time(NULL) - Forerun;
  1412. +#else
  1413. time_t t = time(NULL);
  1414. +#endif /*USE_LIVEBUFFER*/
  1415. struct tm tm_r;
  1416. struct tm *now = localtime_r(&t, &tm_r);
  1417. day = SetTime(t, 0);
  1418. diff --git a/timers.h b/timers.h
  1419. index 1d733ee..d35e042 100644
  1420. --- a/timers.h
  1421. +++ b/timers.h
  1422. @@ -43,7 +43,11 @@ private:
  1423. char *aux;
  1424. const cEvent *event;
  1425. public:
  1426. +#ifdef USE_LIVEBUFFER
  1427. + cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL, int Forerun = 0);
  1428. +#else
  1429. cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL);
  1430. +#endif /*USE_LIVEBUFFER*/
  1431. cTimer(const cEvent *Event);
  1432. cTimer(const cTimer &Timer);
  1433. virtual ~cTimer();
  1434. diff --git a/vdr.c b/vdr.c
  1435. index c32e45f..b9c4460 100644
  1436. --- a/vdr.c
  1437. +++ b/vdr.c
  1438. @@ -218,6 +218,9 @@ int main(int argc, char *argv[])
  1439.  
  1440. static struct option long_options[] = {
  1441. { "audio", required_argument, NULL, 'a' },
  1442. +#ifdef USE_LIVEBUFFER
  1443. + { "buffer", required_argument, NULL, 'b' },
  1444. +#endif /* USE_LIVEBUFFER */
  1445. { "config", required_argument, NULL, 'c' },
  1446. { "daemon", no_argument, NULL, 'd' },
  1447. { "device", required_argument, NULL, 'D' },
  1448. @@ -251,10 +254,20 @@ int main(int argc, char *argv[])
  1449. };
  1450.  
  1451. int c;
  1452. +#ifdef USE_LIVEBUFFER
  1453. + 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) {
  1454. +#else
  1455. 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) {
  1456. +#endif /* USE_LIVEBUFFER */
  1457. switch (c) {
  1458. case 'a': AudioCommand = optarg;
  1459. break;
  1460. +#ifdef USE_LIVEBUFFER
  1461. + case 'b': BufferDirectory = optarg;
  1462. + if(optarg && *optarg && optarg[strlen(optarg)-1] == '/')
  1463. + optarg[strlen(optarg)-1] = 0;
  1464. + break;
  1465. +#endif /* USE_LIVEBUFFER */
  1466. case 'c': ConfigDirectory = optarg;
  1467. break;
  1468. case 'd': DaemonMode = true; break;
  1469. @@ -420,6 +433,9 @@ int main(int argc, char *argv[])
  1470. if (DisplayHelp) {
  1471. printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
  1472. " -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
  1473. +#ifdef USE_LIVEBUFFER
  1474. + " -b DIR, --buffer=DIR use DIR as LiveBuffer directory\n"
  1475. +#endif /*USE_LIVEBUFFER*/
  1476. " -c DIR, --config=DIR read config files from DIR (default: %s)\n"
  1477. " -d, --daemon run in daemon mode\n"
  1478. " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
  1479. @@ -586,9 +602,12 @@ int main(int argc, char *argv[])
  1480.  
  1481. if (!PluginManager.LoadPlugins(true))
  1482. EXIT(2);
  1483. -
  1484. // Configuration data:
  1485.  
  1486. +#ifdef USE_LIVEBUFFER
  1487. + if (!BufferDirectory)
  1488. + BufferDirectory = VideoDirectory;
  1489. +#endif /*USE_LIVEBUFFER*/
  1490. if (!ConfigDirectory)
  1491. ConfigDirectory = DEFAULTCONFDIR;
  1492.  
  1493. @@ -1092,6 +1111,15 @@ int main(int argc, char *argv[])
  1494. cDisplaySubtitleTracks::Process(key);
  1495. key = kNone;
  1496. break;
  1497. +#ifdef USE_LIVEBUFFER
  1498. + case kFastRew:
  1499. + if (!Interact) {
  1500. + DELETE_MENU;
  1501. + if(cRecordControls::StartLiveBuffer(key))
  1502. + key = kNone;
  1503. + } // if
  1504. + break;
  1505. +#endif /*USE_LIVEBUFFER*/
  1506. // Pausing live video:
  1507. case kPause:
  1508. if (!cControl::Control()) {
  1509. @@ -1199,6 +1227,28 @@ int main(int argc, char *argv[])
  1510. else
  1511. cControl::Shutdown();
  1512. break;
  1513. +#ifdef USE_LIVEBUFFER
  1514. + case osSwitchChannel:
  1515. + switch (key) {
  1516. + // Toggle channels:
  1517. + case kChanPrev:
  1518. + case k0: {
  1519. + if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel
  1520. + || (LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1]))
  1521. + PreviousChannelIndex ^= 1;
  1522. + Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
  1523. + break;
  1524. + }
  1525. + case k1 ... k9:
  1526. + DELETE_MENU;
  1527. + cControl::Shutdown();
  1528. + Menu = new cDisplayChannel(NORMALKEY(key));
  1529. + break;
  1530. + default:
  1531. + break;
  1532. + } // switch
  1533. + break;
  1534. +#endif /*USE_LIVEBUFFER*/
  1535. default: ;
  1536. }
  1537. }
  1538. diff --git a/videodir.c b/videodir.c
  1539. index 7331a85..14d4373 100644
  1540. --- a/videodir.c
  1541. +++ b/videodir.c
  1542. @@ -20,6 +20,9 @@
  1543. #include "tools.h"
  1544.  
  1545. const char *VideoDirectory = VIDEODIR;
  1546. +#ifdef USE_LIVEBUFFER
  1547. +const char *BufferDirectory = NULL;
  1548. +#endif /*USE_LIVEBUFFER*/
  1549.  
  1550. class cVideoDirectory {
  1551. private:
  1552. @@ -106,17 +109,32 @@ const char *cVideoDirectory::Adjust(const char *FileName)
  1553. cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags)
  1554. {
  1555. const char *ActualFileName = FileName;
  1556. +#ifdef USE_LIVEBUFFER
  1557. + bool SepBufferDir = false;
  1558.  
  1559. // Incoming name must be in base video directory:
  1560. + if (strstr(FileName, VideoDirectory) != FileName) {
  1561. + if (strstr(FileName, BufferDirectory) == FileName)
  1562. + SepBufferDir = true;
  1563. + else {
  1564. +#else
  1565. if (strstr(FileName, VideoDirectory) != FileName) {
  1566. +#endif /*USE_LIVEBUFFER*/
  1567. esyslog("ERROR: %s not in %s", FileName, VideoDirectory);
  1568. errno = ENOENT; // must set 'errno' - any ideas for a better value?
  1569. return NULL;
  1570. }
  1571. +#ifdef USE_LIVEBUFFER
  1572. + }
  1573. +#endif /*USE_LIVEBUFFER*/
  1574. // Are we going to create a new file?
  1575. if ((Flags & O_CREAT) != 0) {
  1576. cVideoDirectory Dir;
  1577. +#ifdef USE_LIVEBUFFER
  1578. + if (Dir.IsDistributed() && !SepBufferDir) {
  1579. +#else
  1580. if (Dir.IsDistributed()) {
  1581. +#endif /*USE_LIVEBUFFER*/
  1582. // Find the directory with the most free space:
  1583. int MaxFree = Dir.FreeMB();
  1584. while (Dir.Next()) {
  1585. diff --git a/videodir.h b/videodir.h
  1586. index 5e9aef5..57b8652 100644
  1587. --- a/videodir.h
  1588. +++ b/videodir.h
  1589. @@ -14,6 +14,9 @@
  1590. #include "tools.h"
  1591.  
  1592. extern const char *VideoDirectory;
  1593. +#ifdef USE_LIVEBUFFER
  1594. +extern const char *BufferDirectory;
  1595. +#endif /*USE_LIVEBUFFER*/
  1596.  
  1597. cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
  1598. int CloseVideoFile(cUnbufferedFile *File);
  1599. --
  1600. 1.7.5.4
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement