Guest User

vdr-livebuffer-1.7.22.patch

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