Guest User

livebuffer-1.7.22-v2.patch

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