[vdr] Update to 1.7.29.

Ville Skyttä scop at fedoraproject.org
Wed Jul 18 20:22:54 UTC 2012


commit d512308c9d5c2a370110cd767f3c543e9f0d2a2b
Author: Ville Skyttä <ville.skytta at iki.fi>
Date:   Wed Jul 18 23:22:34 2012 +0300

    Update to 1.7.29.

 .gitignore                           |    1 -
 sources                              |    7 +-
 vdr-1.7.28-epghandledexternally.diff |  118 ----
 vdr-1.7.29-hlcutter-0.2.3.diff       | 1228 ++++++++++++++++++++++++++++++++++
 vdr.spec                             |   16 +-
 5 files changed, 1239 insertions(+), 131 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 98dfeb2..c4cc295 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,3 @@
 /vdr-1.7.21-ttxtsubs.patch
 /vdr-timer-info-0.5-1.7.13.diff
 /vdr.epgsearch-exttimeredit-0.0.2.diff
-/vdr-1.7.27-hlcutter-0.2.3.diff
diff --git a/sources b/sources
index 09daafe..c1d32be 100644
--- a/sources
+++ b/sources
@@ -4,7 +4,6 @@ fae3698214bd45de1f675c2b037d5f3b  vdr-1.7.21-ttxtsubs.patch
 04511ae02243eb1bab94f3f45b59e574  vdr-timer-info-0.5-1.7.13.diff
 71f7281d55eba1957f4267f596b11e29  vdr.epgsearch-exttimeredit-0.0.2.diff
 d40db7bea1b9af94d7625e611ef3dae2  vdr_1.7.23-1.debian.tar.bz2
-ef7a35dc23c5ed700e1ce873274f7793  vdr-1.7.27-hlcutter-0.2.3.diff
-bed5d492fa7faf2065f6b2cc44e841a3  vdr-1.7.28-vasarajanauloja.patch.gz
-a086781712c57ab6c255035201fb508b  vdr-1.7.28-finnish.patch.gz
-3ccff2dcc42d112e23dd64f2c39f02f1  vdr-1.7.28.tar.bz2
+1c90e06b1de3961f901b9c35c00e51c2  vdr-1.7.29-finnish.patch.gz
+a53fe4b858611d8c21afebe7d5e03a6c  vdr-1.7.29-vasarajanauloja.patch.gz
+a3f0ae42ba456aa1865c9ed065a64d80  vdr-1.7.29.tar.bz2
diff --git a/vdr-1.7.29-hlcutter-0.2.3.diff b/vdr-1.7.29-hlcutter-0.2.3.diff
new file mode 100644
index 0000000..5d49647
--- /dev/null
+++ b/vdr-1.7.29-hlcutter-0.2.3.diff
@@ -0,0 +1,1228 @@
+diff -Nru vdr-1.7.29/config.c vdr-1.7.29-hlcutter/config.c
+--- vdr-1.7.29/config.c	2012-06-17 15:27:07.000000000 +0300
++++ vdr-1.7.29-hlcutter/config.c	2012-07-16 17:47:24.632631338 +0300
+@@ -446,8 +446,10 @@
+   FontSmlSize = 18;
+   FontFixSize = 20;
+   MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
++  MaxRecordingSize = DEFAULTRECORDINGSIZE;
+   SplitEditedFiles = 0;
+   DelTimeshiftRec = 0;
++  HardLinkCutter = 0;
+   MinEventTimeout = 30;
+   MinUserInactivity = 300;
+   NextWakeupTime = 0;
+@@ -642,8 +644,10 @@
+   else if (!strcasecmp(Name, "FontSmlSize"))         FontSmlSize        = atoi(Value);
+   else if (!strcasecmp(Name, "FontFixSize"))         FontFixSize        = atoi(Value);
+   else if (!strcasecmp(Name, "MaxVideoFileSize"))    MaxVideoFileSize   = atoi(Value);
++  else if (!strcasecmp(Name, "MaxRecordingSize"))    MaxRecordingSize   = atoi(Value);
+   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = atoi(Value);
+   else if (!strcasecmp(Name, "DelTimeshiftRec"))     DelTimeshiftRec    = atoi(Value);
++  else if (!strcasecmp(Name, "HardLinkCutter"))      HardLinkCutter     = atoi(Value);
+   else if (!strcasecmp(Name, "MinEventTimeout"))     MinEventTimeout    = atoi(Value);
+   else if (!strcasecmp(Name, "MinUserInactivity"))   MinUserInactivity  = atoi(Value);
+   else if (!strcasecmp(Name, "NextWakeupTime"))      NextWakeupTime     = atoi(Value);
+@@ -741,8 +745,10 @@
+   Store("FontSmlSize",        FontSmlSize);
+   Store("FontFixSize",        FontFixSize);
+   Store("MaxVideoFileSize",   MaxVideoFileSize);
++  Store("MaxRecordingSize",   MaxRecordingSize);
+   Store("SplitEditedFiles",   SplitEditedFiles);
+   Store("DelTimeshiftRec",    DelTimeshiftRec);
++  Store("HardLinkCutter",     HardLinkCutter);
+   Store("MinEventTimeout",    MinEventTimeout);
+   Store("MinUserInactivity",  MinUserInactivity);
+   Store("NextWakeupTime",     NextWakeupTime);
+diff -Nru vdr-1.7.29/config.h vdr-1.7.29-hlcutter/config.h
+--- vdr-1.7.29/config.h	2012-06-17 14:14:50.000000000 +0300
++++ vdr-1.7.29-hlcutter/config.h	2012-07-16 17:47:24.635631290 +0300
+@@ -310,8 +310,10 @@
+   int FontSmlSize;
+   int FontFixSize;
+   int MaxVideoFileSize;
++  int MaxRecordingSize;
+   int SplitEditedFiles;
+   int DelTimeshiftRec;
++  int HardLinkCutter;
+   int MinEventTimeout, MinUserInactivity;
+   time_t NextWakeupTime;
+   int MultiSpeedMode;
+diff -Nru vdr-1.7.29/cutter.c vdr-1.7.29-hlcutter/cutter.c
+--- vdr-1.7.29/cutter.c	2012-06-10 17:33:36.000000000 +0300
++++ vdr-1.7.29-hlcutter/cutter.c	2012-07-16 17:47:24.626631359 +0300
+@@ -80,6 +80,7 @@
+      Mark = fromMarks.Next(Mark);
+      off_t FileSize = 0;
+      int CurrentFileNumber = 0;
++     bool SkipThisSourceFile = false;
+      int LastIFrame = 0;
+      toMarks.Add(0);
+      toMarks.Save();
+@@ -100,13 +101,93 @@
+ 
+            // Read one frame:
+ 
+-           if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
+-              if (FileNumber != CurrentFileNumber) {
+-                 fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+-                 if (fromFile)
+-                    fromFile->SetReadAhead(MEGABYTE(20));
+-                 CurrentFileNumber = FileNumber;
+-                 }
++           if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
++              // Error, unless we're past last cut-in and there's no cut-out
++              if (Mark || LastMark)
++                 error = "index";
++              break;
++              }
++
++           if (FileNumber != CurrentFileNumber) {
++              fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
++              if (fromFile)
++                 fromFile->SetReadAhead(MEGABYTE(20));
++              CurrentFileNumber = FileNumber;
++              if (SkipThisSourceFile) {
++                 // At end of fast forward: Always skip to next file
++                 toFile = toFileName->NextFile();
++                 if (!toFile) {
++                    error = "toFile 4";
++                    break;
++                    }
++                 FileSize = 0;
++                 SkipThisSourceFile = false;
++                 }                 
++              
++
++              if (Setup.HardLinkCutter && FileOffset == 0) {
++                 // We are at the beginning of a new source file.
++                 // Do we need to copy the whole file?
++
++                 // if !Mark && LastMark, then we're past the last cut-out and continue to next I-frame
++                 // if !Mark && !LastMark, then there's just a cut-in, but no cut-out
++                 // if Mark, then we're between a cut-in and a cut-out
++                 
++                 uint16_t MarkFileNumber;
++                 off_t MarkFileOffset;
++                 // Get file number of next cut mark
++                 if (!Mark && !LastMark
++                     || Mark
++                        && fromIndex->Get(Mark->Position(), &MarkFileNumber, &MarkFileOffset)
++                        && (MarkFileNumber != CurrentFileNumber)) {
++                    // The current source file will be copied completely.
++                    // Start new output file unless we did that already
++                    if (FileSize != 0) {
++                       toFile = toFileName->NextFile();
++                       if (!toFile) {
++                          error = "toFile 3";
++                          break;
++                          }
++                       FileSize = 0;
++                       }
++
++                    // Safety check that file has zero size
++                    struct stat buf;
++                    if (stat(toFileName->Name(), &buf) == 0) {
++                       if (buf.st_size != 0) {
++                          esyslog("cCuttingThread: File %s exists and has nonzero size", toFileName->Name());
++                          error = "nonzero file exist";
++                          break;
++                          }
++                       }
++                    else if (errno != ENOENT) {
++                       esyslog("cCuttingThread: stat failed on %s", toFileName->Name());
++                       error = "stat";
++                       break;
++                       }
++
++                    // Clean the existing 0-byte file
++                    toFileName->Close();
++                    cString ActualToFileName(ReadLink(toFileName->Name()), true);
++                    unlink(ActualToFileName);
++                    unlink(toFileName->Name());
++
++                    // Try to create a hard link
++                    if (HardLinkVideoFile(fromFileName->Name(), toFileName->Name())) {
++                       // Success. Skip all data transfer for this file
++                       SkipThisSourceFile = true;
++                       cutIn = false;
++                       toFile = NULL; // was deleted by toFileName->Close()
++                       } 
++                    else {
++                       // Fallback: Re-open the file if necessary
++                       toFile = toFileName->Open();
++                       }
++                    }
++                 } 
++              }
++
++           if (!SkipThisSourceFile) {
+               if (fromFile) {
+                  int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer));
+                  if (len < 0) {
+@@ -123,19 +204,12 @@
+                  break;
+                  }
+               }
+-           else {
+-              // Error, unless we're past the last cut-in and there's no cut-out
+-              if (Mark || LastMark)
+-                 error = "index";
+-              break;
+-              }
+-
+            // Write one frame:
+ 
+            if (Independent) { // every file shall start with an independent frame
+               if (LastMark) // edited version shall end before next I-frame
+                  break;
+-              if (FileSize > maxVideoFileSize) {
++              if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
+                  toFile = toFileName->NextFile();
+                  if (!toFile) {
+                     error = "toFile 1";
+@@ -159,7 +233,7 @@
+                     }
+                  CheckForSeamlessStream = false;
+                  }
+-              if (cutIn) {
++              if (!SkipThisSourceFile && cutIn) {
+                  if (isPesRecording)
+                     cRemux::SetBrokenLink(buffer, Length);
+                  else
+@@ -167,7 +241,7 @@
+                  cutIn = false;
+                  }
+               }
+-           if (toFile->Write(buffer, Length) < 0) {
++           if (!SkipThisSourceFile && toFile->Write(buffer, Length) < 0) {
+               error = "safe_write";
+               break;
+               }
+@@ -212,7 +286,7 @@
+                     }
+                  }
+               else
+-                 LastMark = true;
++                 LastMark = true; // After last cut-out: Write on until next I-frame, then exit
+               }
+            }
+      Recordings.TouchUpdate();
+diff -Nru vdr-1.7.29/menu.c vdr-1.7.29-hlcutter/menu.c
+--- vdr-1.7.29/menu.c	2012-06-17 14:12:25.000000000 +0300
++++ vdr-1.7.29-hlcutter/menu.c	2012-07-16 17:47:24.638631250 +0300
+@@ -3111,8 +3111,10 @@
+   Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"),     data.NameInstantRecord, sizeof(data.NameInstantRecord)));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"),   &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
++  Add(new cMenuEditIntItem( tr("Setup.Recording$Max. recording size (GB)"),  &data.MaxRecordingSize, MINRECORDINGSIZE, MAXRECORDINGSIZE));
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"),        &data.SplitEditedFiles));
+   Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Hard Link Cutter"),          &data.HardLinkCutter));
+ }
+ 
+ // --- cMenuSetupReplay ------------------------------------------------------
+diff -Nru vdr-1.7.29/po/de_DE.po vdr-1.7.29-hlcutter/po/de_DE.po
+--- vdr-1.7.29/po/de_DE.po	2012-06-17 15:00:25.000000000 +0300
++++ vdr-1.7.29-hlcutter/po/de_DE.po	2012-07-16 17:47:24.645631154 +0300
+@@ -1068,12 +1068,18 @@
+ msgid "Setup.Recording$Max. video file size (MB)"
+ msgstr "Max. Videodateigröße (MB)"
+ 
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Max. Aufnahmegröße (GB)"
++
+ msgid "Setup.Recording$Split edited files"
+ msgstr "Editierte Dateien aufteilen"
+ 
+ msgid "Setup.Recording$Delete timeshift recording"
+ msgstr "Zeitversetzte Aufnahme löschen"
+ 
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Hard Link Cutter"
++
+ msgid "Replay"
+ msgstr "Wiedergabe"
+ 
+diff -Nru vdr-1.7.29/po/fi_FI.po vdr-1.7.29-hlcutter/po/fi_FI.po
+--- vdr-1.7.29/po/fi_FI.po	2012-06-17 14:59:42.000000000 +0300
++++ vdr-1.7.29-hlcutter/po/fi_FI.po	2012-07-16 17:47:24.651631074 +0300
+@@ -1071,12 +1071,18 @@
+ msgid "Setup.Recording$Max. video file size (MB)"
+ msgstr "Suurin tiedostokoko (Mt)"
+ 
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Suurin tallennekoko (Gt)"
++
+ msgid "Setup.Recording$Split edited files"
+ msgstr "Jaottele muokatut tallenteet"
+ 
+ msgid "Setup.Recording$Delete timeshift recording"
+ msgstr "Poista ajansiirtotallenne"
+ 
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Käytä kovia linkkejä muokkauksessa"
++
+ msgid "Replay"
+ msgstr "Toisto"
+ 
+diff -Nru vdr-1.7.29/README-HLCUTTER vdr-1.7.29-hlcutter/README-HLCUTTER
+--- vdr-1.7.29/README-HLCUTTER	1970-01-01 02:00:00.000000000 +0200
++++ vdr-1.7.29-hlcutter/README-HLCUTTER	2012-07-16 17:46:54.286042815 +0300
+@@ -0,0 +1,128 @@
++
++                    VDR-HLCUTTER README
++
++
++Written by:           Udo Richter
++Available at:         http://www.udo-richter.de/vdr/patches.html#hlcutter
++                      http://www.udo-richter.de/vdr/patches.en.html#hlcutter
++Contact:              udo_richter at gmx.de
++
++
++
++About
++-----
++
++The hard link cutter patch changes the recording editing algorithms of VDR to
++use filesystem hard links to 'copy' recording files whenever possible to speed
++up editing recordings noticeably.
++
++The patch has matured to be quite stable, at least I'm using it without issues.
++Nevertheless the patch is still in development and should be used with caution. 
++The patch is EXPERIMENTAL for multiple /videoxx folders. The safety checks 
++should prevent data loss, but you should always carefully check the results.
++
++While editing a recording, the patch searches for any 00x.vdr files that don't
++contain editing marks and would normally be copied 1:1 unmodified to the edited
++recording. In this case the current target 00x.vdr file will be aborted, and 
++the cutter process attempts to duplicate the source file as a hard link, so 
++that both files share the same disk space. If this succeeds, the editing 
++process fast-forwards through the duplicated file and continues normally 
++beginning with the next source file. If hard linking fails, the cutter process
++continues with plain old copying. (but does not take up the aborted last file.)
++
++After editing, the un-edited recording can be deleted as usual, the hard linked
++copies will continue to exist as the only remaining copy.
++
++To be effective, the default 'Max. video file size (MB)' should be lowered. 
++The patch lowers the smallest possible file size to 1mb. Since VDR only 
++supports up to 255 files, this would limit the recording size to 255Mb or
++10 minutes, in other words: This setting is insane!
++
++To make sure that the 255 file limit will not be reached, the patch also 
++introduces "Max. recording size (GB)" with a default of 100Gb (66 hours), and 
++increases the file size to 2000Mb early enough, so that 100Gb-recordings will
++fit into the 255 files.
++
++Picking the right parameters can be tricky. The smaller the file size, the 
++faster the editing process works. However, with a small file size, long 
++recordings will fall back to 2000Mb files soon, that are slow on editing again.
++
++Here are some examples:
++
++Max file size:      100Gb   100Gb   100Gb   100Gb   100Gb   100Gb   100Gb
++Max recording size: 1Mb     10Mb    20Mb    30Mb    40Mb    50Mb    100Mb
++
++Small files:        1-203   1-204   1-205   1-206   1-207   1-209   1-214
++  GBytes:           0.2     2.0     4.0     6.0     8.1     10.2    20.9
++  Hours:            0.13    1.3     2.65    4       5.4     6.8     13.9
++
++Big (2000mb) files: 204-255 204-255 206-255 207-255 208-255 210-255 215-255
++  GBytes:           101.5   99.6    97.7    95.7    93.8    89.8    80.1
++  Hours:            67      66      65      63      62      60      53
++
++A recording limit of 100Gb keeps plenty of reserve without blocking too much
++file numbers. And with a file size of 30-40Mb, recordings of 4-5 hours fit into
++small files completely. (depends on bit rate of course)
++
++
++
++The patch must be enabled in Setup-> Recordings-> Hard Link Cutter. When 
++disabled, the cutter process behaves identical to VDR's default cutter.
++
++There's a //#define HARDLINK_TEST_ONLY in the videodir.c file that enables a
++test-mode that hard-links 00x.vdr_ files only, and continues the classic 
++editing. The resulting 00x.vdr and 00x.vdr_ files should be identical. If you 
++delete the un-edited recording, don't forget to delete the *.vdr_ files too, 
++they will now eat real disk space.
++
++Note: 'du' displays the disk space of hard links only on first appearance, and
++usually you will see a noticeably smaller size on the edited recording.
++
++
++History
++-------
++
++Version 0.2.3
++  Fix: Compatible to VDR-1.7.27+ thx to Ville Skyttä
++  New: Add German translation
++  New: Add Finnish translation, thx to Ville Skyttä
++
++Version 0.2.2
++  Fix: Adapt to GCC-4.4, thx to Ville Skyttä
++
++Version 0.2.1
++  New: Support for TS recordings with up to 65535 files and up to 1TB per file
++
++Version 0.2.0
++  New: Support for multiple /videoXX recording folders, using advanced searching
++       for matching file systems where a hard link can be created.
++       Also supports deep mounted file systems.
++  Fix: Do not fail if last mark is a cut-in. (Again.)
++
++Version 0.1.4
++  New: Dynamic increase of file size before running out of xxx.vdr files
++  Fix: Last edit mark is not a cut-out
++  Fix: Write error if link-copied file is smaller than allowed file size
++  Fix: Broken index/marks if cut-in is at the start of a new file
++  Fix: Clear dangling pointer to free'd cUnbufferedFile, 
++       thx to Matthias Schwarzott
++
++Version 0.1.0
++  Initial release
++
++
++
++
++Future plans
++------------
++
++Since original and edited copy share disk space, free space is wrong if one of
++them is moved to *.del. Free space should only count files with hard link 
++count = 1. This still goes wrong if all copies get deleted.
++
++
++For more safety, the hard-linked files may be made read-only, as modifications
++to one copy will affect the other copy too. (except deleting, of course)
++
++
++SetBrokenLink may get lost on rare cases, this needs some more thoughts.
+diff -Nru vdr-1.7.29/recorder.c vdr-1.7.29-hlcutter/recorder.c
+--- vdr-1.7.29/recorder.c	2011-09-04 12:26:44.000000000 +0300
++++ vdr-1.7.29-hlcutter/recorder.c	2012-07-16 17:47:24.654631032 +0300
+@@ -89,7 +89,7 @@
+ bool cRecorder::NextFile(void)
+ {
+   if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
+-     if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
++     if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
+         recordFile = fileName->NextFile();
+         fileSize = 0;
+         }
+diff -Nru vdr-1.7.29/recording.c vdr-1.7.29-hlcutter/recording.c
+--- vdr-1.7.29/recording.c	2012-06-09 16:57:30.000000000 +0300
++++ vdr-1.7.29-hlcutter/recording.c	2012-07-16 17:47:24.657630993 +0300
+@@ -2087,6 +2087,20 @@
+   return NULL;
+ }
+ 
++off_t cFileName::MaxFileSize() {
++  const int maxVideoFileSize = isPesRecording ? MAXVIDEOFILESIZEPES : MAXVIDEOFILESIZETS;
++  const int setupMaxVideoFileSize = min(maxVideoFileSize, Setup.MaxVideoFileSize);
++  const int maxFileNumber = isPesRecording ? 255 : 65535;
++
++  const off_t smallFiles = (maxFileNumber * off_t(maxVideoFileSize) - 1024 * Setup.MaxRecordingSize)
++                           / max(maxVideoFileSize - setupMaxVideoFileSize, 1);
++
++  if (fileNumber <= smallFiles)
++     return MEGABYTE(off_t(setupMaxVideoFileSize));
++  
++  return MEGABYTE(off_t(maxVideoFileSize));
++}
++
+ cUnbufferedFile *cFileName::NextFile(void)
+ {
+   return SetOffset(fileNumber + 1);
+diff -Nru vdr-1.7.29/recording.h vdr-1.7.29-hlcutter/recording.h
+--- vdr-1.7.29/recording.h	2012-06-09 16:55:22.000000000 +0300
++++ vdr-1.7.29-hlcutter/recording.h	2012-07-16 17:47:24.660630952 +0300
+@@ -259,9 +259,17 @@
+ // before the next independent frame, to have a complete Group Of Pictures):
+ #define MAXVIDEOFILESIZETS  1048570 // MB
+ #define MAXVIDEOFILESIZEPES    2000 // MB
+-#define MINVIDEOFILESIZE        100 // MB
++#define MINVIDEOFILESIZE          1 // MB
+ #define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
+ 
++#define MINRECORDINGSIZE      25 // GB
++#define MAXRECORDINGSIZE     500 // GB
++#define DEFAULTRECORDINGSIZE 100 // GB
++// Dynamic recording size:
++// Keep recording file size at Setup.MaxVideoFileSize for as long as possible,
++// but switch to MAXVIDEOFILESIZE early enough, so that Setup.MaxRecordingSize
++// will be reached, before recording to file 65535.vdr
++
+ struct tIndexTs;
+ class cIndexFileGenerator;
+ 
+@@ -314,6 +322,8 @@
+   cUnbufferedFile *Open(void);
+   void Close(void);
+   cUnbufferedFile *SetOffset(int Number, off_t Offset = 0); // yes, Number is int for easier internal calculating
++  off_t MaxFileSize();
++      // Dynamic file size for this file
+   cUnbufferedFile *NextFile(void);
+   };
+ 
+diff -Nru vdr-1.7.29/vdr-1.7.27-hlcutter-0.2.3.diff vdr-1.7.29-hlcutter/vdr-1.7.27-hlcutter-0.2.3.diff
+--- vdr-1.7.29/vdr-1.7.27-hlcutter-0.2.3.diff	1970-01-01 02:00:00.000000000 +0200
++++ vdr-1.7.29-hlcutter/vdr-1.7.27-hlcutter-0.2.3.diff	2012-04-02 21:42:11.000000000 +0300
+@@ -0,0 +1,612 @@
++diff -Naur vdr-1.7.27/config.c vdr-1.7.27-hlcutter/config.c
++--- vdr-1.7.27/config.c	2012-02-29 11:15:54.000000000 +0100
+++++ vdr-1.7.27-hlcutter/config.c	2012-03-31 13:25:56.000000000 +0200
++@@ -445,8 +445,10 @@
++   FontSmlSize = 18;
++   FontFixSize = 20;
++   MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
+++  MaxRecordingSize = DEFAULTRECORDINGSIZE;
++   SplitEditedFiles = 0;
++   DelTimeshiftRec = 0;
+++  HardLinkCutter = 0;
++   MinEventTimeout = 30;
++   MinUserInactivity = 300;
++   NextWakeupTime = 0;
++@@ -639,8 +641,10 @@
++   else if (!strcasecmp(Name, "FontSmlSize"))         FontSmlSize        = atoi(Value);
++   else if (!strcasecmp(Name, "FontFixSize"))         FontFixSize        = atoi(Value);
++   else if (!strcasecmp(Name, "MaxVideoFileSize"))    MaxVideoFileSize   = atoi(Value);
+++  else if (!strcasecmp(Name, "MaxRecordingSize"))    MaxRecordingSize   = atoi(Value);
++   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = atoi(Value);
++   else if (!strcasecmp(Name, "DelTimeshiftRec"))     DelTimeshiftRec    = atoi(Value);
+++  else if (!strcasecmp(Name, "HardLinkCutter"))      HardLinkCutter     = atoi(Value);
++   else if (!strcasecmp(Name, "MinEventTimeout"))     MinEventTimeout    = atoi(Value);
++   else if (!strcasecmp(Name, "MinUserInactivity"))   MinUserInactivity  = atoi(Value);
++   else if (!strcasecmp(Name, "NextWakeupTime"))      NextWakeupTime     = atoi(Value);
++@@ -736,8 +740,10 @@
++   Store("FontSmlSize",        FontSmlSize);
++   Store("FontFixSize",        FontFixSize);
++   Store("MaxVideoFileSize",   MaxVideoFileSize);
+++  Store("MaxRecordingSize",   MaxRecordingSize);
++   Store("SplitEditedFiles",   SplitEditedFiles);
++   Store("DelTimeshiftRec",    DelTimeshiftRec);
+++  Store("HardLinkCutter",     HardLinkCutter);
++   Store("MinEventTimeout",    MinEventTimeout);
++   Store("MinUserInactivity",  MinUserInactivity);
++   Store("NextWakeupTime",     NextWakeupTime);
++diff -Naur vdr-1.7.27/config.h vdr-1.7.27-hlcutter/config.h
++--- vdr-1.7.27/config.h	2012-03-11 11:41:44.000000000 +0100
+++++ vdr-1.7.27-hlcutter/config.h	2012-03-31 13:25:56.000000000 +0200
++@@ -299,8 +299,10 @@
++   int FontSmlSize;
++   int FontFixSize;
++   int MaxVideoFileSize;
+++  int MaxRecordingSize;
++   int SplitEditedFiles;
++   int DelTimeshiftRec;
+++  int HardLinkCutter;
++   int MinEventTimeout, MinUserInactivity;
++   time_t NextWakeupTime;
++   int MultiSpeedMode;
++diff -Naur vdr-1.7.27/cutter.c vdr-1.7.27-hlcutter/cutter.c
++--- vdr-1.7.27/cutter.c	2012-02-16 13:08:39.000000000 +0100
+++++ vdr-1.7.27-hlcutter/cutter.c	2012-03-31 13:26:51.000000000 +0200
++@@ -80,6 +80,7 @@
++      Mark = fromMarks.Next(Mark);
++      off_t FileSize = 0;
++      int CurrentFileNumber = 0;
+++     bool SkipThisSourceFile = false;
++      int LastIFrame = 0;
++      toMarks.Add(0);
++      toMarks.Save();
++@@ -98,13 +99,93 @@
++ 
++            // Read one frame:
++ 
++-           if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
++-              if (FileNumber != CurrentFileNumber) {
++-                 fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
++-                 if (fromFile)
++-                    fromFile->SetReadAhead(MEGABYTE(20));
++-                 CurrentFileNumber = FileNumber;
++-                 }
+++           if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
+++              // Error, unless we're past last cut-in and there's no cut-out
+++              if (Mark || LastMark)
+++                 error = "index";
+++              break;
+++              }
+++
+++           if (FileNumber != CurrentFileNumber) {
+++              fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+++              if (fromFile)
+++                 fromFile->SetReadAhead(MEGABYTE(20));
+++              CurrentFileNumber = FileNumber;
+++              if (SkipThisSourceFile) {
+++                 // At end of fast forward: Always skip to next file
+++                 toFile = toFileName->NextFile();
+++                 if (!toFile) {
+++                    error = "toFile 4";
+++                    break;
+++                    }
+++                 FileSize = 0;
+++                 SkipThisSourceFile = false;
+++                 }                 
+++              
+++
+++              if (Setup.HardLinkCutter && FileOffset == 0) {
+++                 // We are at the beginning of a new source file.
+++                 // Do we need to copy the whole file?
+++
+++                 // if !Mark && LastMark, then we're past the last cut-out and continue to next I-frame
+++                 // if !Mark && !LastMark, then there's just a cut-in, but no cut-out
+++                 // if Mark, then we're between a cut-in and a cut-out
+++                 
+++                 uint16_t MarkFileNumber;
+++                 off_t MarkFileOffset;
+++                 // Get file number of next cut mark
+++                 if (!Mark && !LastMark
+++                     || Mark
+++                        && fromIndex->Get(Mark->Position(), &MarkFileNumber, &MarkFileOffset)
+++                        && (MarkFileNumber != CurrentFileNumber)) {
+++                    // The current source file will be copied completely.
+++                    // Start new output file unless we did that already
+++                    if (FileSize != 0) {
+++                       toFile = toFileName->NextFile();
+++                       if (!toFile) {
+++                          error = "toFile 3";
+++                          break;
+++                          }
+++                       FileSize = 0;
+++                       }
+++
+++                    // Safety check that file has zero size
+++                    struct stat buf;
+++                    if (stat(toFileName->Name(), &buf) == 0) {
+++                       if (buf.st_size != 0) {
+++                          esyslog("cCuttingThread: File %s exists and has nonzero size", toFileName->Name());
+++                          error = "nonzero file exist";
+++                          break;
+++                          }
+++                       }
+++                    else if (errno != ENOENT) {
+++                       esyslog("cCuttingThread: stat failed on %s", toFileName->Name());
+++                       error = "stat";
+++                       break;
+++                       }
+++
+++                    // Clean the existing 0-byte file
+++                    toFileName->Close();
+++                    cString ActualToFileName(ReadLink(toFileName->Name()), true);
+++                    unlink(ActualToFileName);
+++                    unlink(toFileName->Name());
+++
+++                    // Try to create a hard link
+++                    if (HardLinkVideoFile(fromFileName->Name(), toFileName->Name())) {
+++                       // Success. Skip all data transfer for this file
+++                       SkipThisSourceFile = true;
+++                       cutIn = false;
+++                       toFile = NULL; // was deleted by toFileName->Close()
+++                       } 
+++                    else {
+++                       // Fallback: Re-open the file if necessary
+++                       toFile = toFileName->Open();
+++                       }
+++                    }
+++                 } 
+++              }
+++
+++           if (!SkipThisSourceFile) {
++               if (fromFile) {
++                  int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer));
++                  if (len < 0) {
++@@ -121,19 +202,12 @@
++                  break;
++                  }
++               }
++-           else {
++-              // Error, unless we're past the last cut-in and there's no cut-out
++-              if (Mark || LastMark)
++-                 error = "index";
++-              break;
++-              }
++-
++            // Write one frame:
++ 
++            if (Independent) { // every file shall start with an independent frame
++               if (LastMark) // edited version shall end before next I-frame
++                  break;
++-              if (FileSize > maxVideoFileSize) {
+++              if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
++                  toFile = toFileName->NextFile();
++                  if (!toFile) {
++                     error = "toFile 1";
++@@ -143,7 +217,7 @@
++                  }
++               LastIFrame = 0;
++ 
++-              if (cutIn) {
+++              if (!SkipThisSourceFile && cutIn) {
++                  if (isPesRecording)
++                     cRemux::SetBrokenLink(buffer, Length);
++                  else
++@@ -151,7 +225,7 @@
++                  cutIn = false;
++                  }
++               }
++-           if (toFile->Write(buffer, Length) < 0) {
+++           if (!SkipThisSourceFile && toFile->Write(buffer, Length) < 0) {
++               error = "safe_write";
++               break;
++               }
++@@ -186,7 +260,7 @@
++                     }
++                  }
++               else
++-                 LastMark = true;
+++                 LastMark = true; // After last cut-out: Write on until next I-frame, then exit
++               }
++            }
++      Recordings.TouchUpdate();
++diff -Naur vdr-1.7.27/menu.c vdr-1.7.27-hlcutter/menu.c
++--- vdr-1.7.27/menu.c	2012-03-13 14:14:38.000000000 +0100
+++++ vdr-1.7.27-hlcutter/menu.c	2012-03-31 13:25:56.000000000 +0200
++@@ -3116,8 +3116,10 @@
++   Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"),     data.NameInstantRecord, sizeof(data.NameInstantRecord)));
++   Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"),   &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
++   Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
+++  Add(new cMenuEditIntItem( tr("Setup.Recording$Max. recording size (GB)"),  &data.MaxRecordingSize, MINRECORDINGSIZE, MAXRECORDINGSIZE));
++   Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"),        &data.SplitEditedFiles));
++   Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
+++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Hard Link Cutter"),          &data.HardLinkCutter));
++ }
++ 
++ // --- cMenuSetupReplay ------------------------------------------------------
++diff -Naur vdr-1.7.27/po/de_DE.po vdr-1.7.27-hlcutter/po/de_DE.po
++--- vdr-1.7.27/po/de_DE.po	2012-03-11 11:44:44.000000000 +0100
+++++ vdr-1.7.27-hlcutter/po/de_DE.po	2012-03-31 13:36:31.000000000 +0200
++@@ -1071,12 +1071,18 @@
++ msgid "Setup.Recording$Max. video file size (MB)"
++ msgstr "Max. Videodateigröße (MB)"
++ 
+++msgid "Setup.Recording$Max. recording size (GB)"
+++msgstr "Max. Aufnahmegröße (GB)"
+++
++ msgid "Setup.Recording$Split edited files"
++ msgstr "Editierte Dateien aufteilen"
++ 
++ msgid "Setup.Recording$Delete timeshift recording"
++ msgstr "Zeitversetzte Aufnahme löschen"
++ 
+++msgid "Setup.Recording$Hard Link Cutter"
+++msgstr "Hard Link Cutter"
+++
++ msgid "Replay"
++ msgstr "Wiedergabe"
++ 
++diff -Naur vdr-1.7.27/po/fi_FI.po vdr-1.7.27-hlcutter/po/fi_FI.po
++--- vdr-1.7.27/po/fi_FI.po	2012-03-11 11:44:43.000000000 +0100
+++++ vdr-1.7.27-hlcutter/po/fi_FI.po	2012-03-31 13:39:33.000000000 +0200
++@@ -1074,12 +1074,18 @@
++ msgid "Setup.Recording$Max. video file size (MB)"
++ msgstr "Suurin tiedostokoko (Mt)"
++ 
+++msgid "Setup.Recording$Max. recording size (GB)"
+++msgstr "Suurin tallennekoko (Gt)"
+++
++ msgid "Setup.Recording$Split edited files"
++ msgstr "Jaottele muokatut tallenteet"
++ 
++ msgid "Setup.Recording$Delete timeshift recording"
++ msgstr "Poista ajansiirtotallenne"
++ 
+++msgid "Setup.Recording$Hard Link Cutter"
+++msgstr "Käytä kovia linkkejä muokkauksessa"
+++
++ msgid "Replay"
++ msgstr "Toisto"
++ 
++diff -Naur vdr-1.7.27/README-HLCUTTER vdr-1.7.27-hlcutter/README-HLCUTTER
++--- vdr-1.7.27/README-HLCUTTER	1970-01-01 01:00:00.000000000 +0100
+++++ vdr-1.7.27-hlcutter/README-HLCUTTER	2012-03-31 13:40:55.000000000 +0200
++@@ -0,0 +1,128 @@
+++
+++                    VDR-HLCUTTER README
+++
+++
+++Written by:           Udo Richter
+++Available at:         http://www.udo-richter.de/vdr/patches.html#hlcutter
+++                      http://www.udo-richter.de/vdr/patches.en.html#hlcutter
+++Contact:              udo_richter at gmx.de
+++
+++
+++
+++About
+++-----
+++
+++The hard link cutter patch changes the recording editing algorithms of VDR to
+++use filesystem hard links to 'copy' recording files whenever possible to speed
+++up editing recordings noticeably.
+++
+++The patch has matured to be quite stable, at least I'm using it without issues.
+++Nevertheless the patch is still in development and should be used with caution. 
+++The patch is EXPERIMENTAL for multiple /videoxx folders. The safety checks 
+++should prevent data loss, but you should always carefully check the results.
+++
+++While editing a recording, the patch searches for any 00x.vdr files that don't
+++contain editing marks and would normally be copied 1:1 unmodified to the edited
+++recording. In this case the current target 00x.vdr file will be aborted, and 
+++the cutter process attempts to duplicate the source file as a hard link, so 
+++that both files share the same disk space. If this succeeds, the editing 
+++process fast-forwards through the duplicated file and continues normally 
+++beginning with the next source file. If hard linking fails, the cutter process
+++continues with plain old copying. (but does not take up the aborted last file.)
+++
+++After editing, the un-edited recording can be deleted as usual, the hard linked
+++copies will continue to exist as the only remaining copy.
+++
+++To be effective, the default 'Max. video file size (MB)' should be lowered. 
+++The patch lowers the smallest possible file size to 1mb. Since VDR only 
+++supports up to 255 files, this would limit the recording size to 255Mb or
+++10 minutes, in other words: This setting is insane!
+++
+++To make sure that the 255 file limit will not be reached, the patch also 
+++introduces "Max. recording size (GB)" with a default of 100Gb (66 hours), and 
+++increases the file size to 2000Mb early enough, so that 100Gb-recordings will
+++fit into the 255 files.
+++
+++Picking the right parameters can be tricky. The smaller the file size, the 
+++faster the editing process works. However, with a small file size, long 
+++recordings will fall back to 2000Mb files soon, that are slow on editing again.
+++
+++Here are some examples:
+++
+++Max file size:      100Gb   100Gb   100Gb   100Gb   100Gb   100Gb   100Gb
+++Max recording size: 1Mb     10Mb    20Mb    30Mb    40Mb    50Mb    100Mb
+++
+++Small files:        1-203   1-204   1-205   1-206   1-207   1-209   1-214
+++  GBytes:           0.2     2.0     4.0     6.0     8.1     10.2    20.9
+++  Hours:            0.13    1.3     2.65    4       5.4     6.8     13.9
+++
+++Big (2000mb) files: 204-255 204-255 206-255 207-255 208-255 210-255 215-255
+++  GBytes:           101.5   99.6    97.7    95.7    93.8    89.8    80.1
+++  Hours:            67      66      65      63      62      60      53
+++
+++A recording limit of 100Gb keeps plenty of reserve without blocking too much
+++file numbers. And with a file size of 30-40Mb, recordings of 4-5 hours fit into
+++small files completely. (depends on bit rate of course)
+++
+++
+++
+++The patch must be enabled in Setup-> Recordings-> Hard Link Cutter. When 
+++disabled, the cutter process behaves identical to VDR's default cutter.
+++
+++There's a //#define HARDLINK_TEST_ONLY in the videodir.c file that enables a
+++test-mode that hard-links 00x.vdr_ files only, and continues the classic 
+++editing. The resulting 00x.vdr and 00x.vdr_ files should be identical. If you 
+++delete the un-edited recording, don't forget to delete the *.vdr_ files too, 
+++they will now eat real disk space.
+++
+++Note: 'du' displays the disk space of hard links only on first appearance, and
+++usually you will see a noticeably smaller size on the edited recording.
+++
+++
+++History
+++-------
+++
+++Version 0.2.3
+++  Fix: Compatible to VDR-1.7.27+ thx to Ville Skyttä
+++  New: Add German translation
+++  New: Add Finnish translation, thx to Ville Skyttä
+++
+++Version 0.2.2
+++  Fix: Adapt to GCC-4.4, thx to Ville Skyttä
+++
+++Version 0.2.1
+++  New: Support for TS recordings with up to 65535 files and up to 1TB per file
+++
+++Version 0.2.0
+++  New: Support for multiple /videoXX recording folders, using advanced searching
+++       for matching file systems where a hard link can be created.
+++       Also supports deep mounted file systems.
+++  Fix: Do not fail if last mark is a cut-in. (Again.)
+++
+++Version 0.1.4
+++  New: Dynamic increase of file size before running out of xxx.vdr files
+++  Fix: Last edit mark is not a cut-out
+++  Fix: Write error if link-copied file is smaller than allowed file size
+++  Fix: Broken index/marks if cut-in is at the start of a new file
+++  Fix: Clear dangling pointer to free'd cUnbufferedFile, 
+++       thx to Matthias Schwarzott
+++
+++Version 0.1.0
+++  Initial release
+++
+++
+++
+++
+++Future plans
+++------------
+++
+++Since original and edited copy share disk space, free space is wrong if one of
+++them is moved to *.del. Free space should only count files with hard link 
+++count = 1. This still goes wrong if all copies get deleted.
+++
+++
+++For more safety, the hard-linked files may be made read-only, as modifications
+++to one copy will affect the other copy too. (except deleting, of course)
+++
+++
+++SetBrokenLink may get lost on rare cases, this needs some more thoughts.
++diff -Naur vdr-1.7.27/recorder.c vdr-1.7.27-hlcutter/recorder.c
++--- vdr-1.7.27/recorder.c	2011-09-04 11:26:44.000000000 +0200
+++++ vdr-1.7.27-hlcutter/recorder.c	2012-03-31 13:25:56.000000000 +0200
++@@ -89,7 +89,7 @@
++ bool cRecorder::NextFile(void)
++ {
++   if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
++-     if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
+++     if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
++         recordFile = fileName->NextFile();
++         fileSize = 0;
++         }
++diff -Naur vdr-1.7.27/recording.c vdr-1.7.27-hlcutter/recording.c
++--- vdr-1.7.27/recording.c	2012-03-13 14:17:57.000000000 +0100
+++++ vdr-1.7.27-hlcutter/recording.c	2012-03-31 13:25:56.000000000 +0200
++@@ -2064,6 +2064,20 @@
++   return NULL;
++ }
++ 
+++off_t cFileName::MaxFileSize() {
+++  const int maxVideoFileSize = isPesRecording ? MAXVIDEOFILESIZEPES : MAXVIDEOFILESIZETS;
+++  const int setupMaxVideoFileSize = min(maxVideoFileSize, Setup.MaxVideoFileSize);
+++  const int maxFileNumber = isPesRecording ? 255 : 65535;
+++
+++  const off_t smallFiles = (maxFileNumber * off_t(maxVideoFileSize) - 1024 * Setup.MaxRecordingSize)
+++                           / max(maxVideoFileSize - setupMaxVideoFileSize, 1);
+++
+++  if (fileNumber <= smallFiles)
+++     return MEGABYTE(off_t(setupMaxVideoFileSize));
+++  
+++  return MEGABYTE(off_t(maxVideoFileSize));
+++}
+++
++ cUnbufferedFile *cFileName::NextFile(void)
++ {
++   return SetOffset(fileNumber + 1);
++diff -Naur vdr-1.7.27/recording.h vdr-1.7.27-hlcutter/recording.h
++--- vdr-1.7.27/recording.h	2012-03-13 13:41:05.000000000 +0100
+++++ vdr-1.7.27-hlcutter/recording.h	2012-03-31 13:25:56.000000000 +0200
++@@ -264,9 +264,17 @@
++ // before the next independent frame, to have a complete Group Of Pictures):
++ #define MAXVIDEOFILESIZETS  1048570 // MB
++ #define MAXVIDEOFILESIZEPES    2000 // MB
++-#define MINVIDEOFILESIZE        100 // MB
+++#define MINVIDEOFILESIZE          1 // MB
++ #define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
++ 
+++#define MINRECORDINGSIZE      25 // GB
+++#define MAXRECORDINGSIZE     500 // GB
+++#define DEFAULTRECORDINGSIZE 100 // GB
+++// Dynamic recording size:
+++// Keep recording file size at Setup.MaxVideoFileSize for as long as possible,
+++// but switch to MAXVIDEOFILESIZE early enough, so that Setup.MaxRecordingSize
+++// will be reached, before recording to file 65535.vdr
+++
++ struct tIndexTs;
++ class cIndexFileGenerator;
++ 
++@@ -319,6 +327,8 @@
++   cUnbufferedFile *Open(void);
++   void Close(void);
++   cUnbufferedFile *SetOffset(int Number, off_t Offset = 0); // yes, Number is int for easier internal calculating
+++  off_t MaxFileSize();
+++      // Dynamic file size for this file
++   cUnbufferedFile *NextFile(void);
++   };
++ 
++diff -Naur vdr-1.7.27/videodir.c vdr-1.7.27-hlcutter/videodir.c
++--- vdr-1.7.27/videodir.c	2008-02-16 14:00:03.000000000 +0100
+++++ vdr-1.7.27-hlcutter/videodir.c	2012-03-31 13:25:56.000000000 +0200
++@@ -19,6 +19,9 @@
++ #include "recording.h"
++ #include "tools.h"
++ 
+++
+++//#define HARDLINK_TEST_ONLY
+++
++ const char *VideoDirectory = VIDEODIR;
++ 
++ class cVideoDirectory {
++@@ -168,6 +171,120 @@
++   return RemoveFileOrDir(FileName, true);
++ }
++ 
+++static bool StatNearestDir(const char *FileName, struct stat *Stat)
+++{
+++  cString Name(FileName);
+++  char *p;
+++  while ((p = strrchr((char*)(const char*)Name + 1, '/')) != NULL) {
+++        *p = 0; // truncate at last '/'
+++        if (stat(Name, Stat) == 0) {
+++           isyslog("StatNearestDir: Stating %s", (const char*)Name);
+++           return true;
+++           }
+++        }
+++  return false;
+++}
+++
+++bool HardLinkVideoFile(const char *OldName, const char *NewName)
+++{
+++  // Incoming name must be in base video directory:
+++  if (strstr(OldName, VideoDirectory) != OldName) {
+++     esyslog("ERROR: %s not in %s", OldName, VideoDirectory);
+++     return false;
+++     }
+++  if (strstr(NewName, VideoDirectory) != NewName) {
+++     esyslog("ERROR: %s not in %s", NewName, VideoDirectory);
+++     return false;
+++     }
+++
+++  const char *ActualNewName = NewName;
+++  cString ActualOldName(ReadLink(OldName), true);
+++
+++  // Some safety checks:
+++  struct stat StatOldName;
+++  if (lstat(ActualOldName, &StatOldName) == 0) {
+++     if (S_ISLNK(StatOldName.st_mode)) {
+++        esyslog("HardLinkVideoFile: Failed to resolve symbolic link %s", (const char*)ActualOldName);
+++        return false;
+++        }
+++     }
+++  else {
+++     esyslog("HardLinkVideoFile: lstat failed on %s", (const char*)ActualOldName);
+++     return false;
+++     }
+++  isyslog("HardLinkVideoFile: %s is on %i", (const char*)ActualOldName, (int)StatOldName.st_dev);
+++
+++  // Find the video directory where ActualOldName is located
+++
+++  cVideoDirectory Dir;
+++  struct stat StatDir;
+++  if (!StatNearestDir(NewName, &StatDir)) {
+++     esyslog("HardLinkVideoFile: stat failed on %s", NewName);
+++     return false;
+++     }
+++  
+++  isyslog("HardLinkVideoFile: %s is on %i", NewName, (int)StatDir.st_dev);
+++  if (StatDir.st_dev != StatOldName.st_dev) {
+++     // Not yet found.
+++     
+++     if (!Dir.IsDistributed()) {
+++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
+++        return false;
+++        }
+++
+++     // Search in video01 and upwards
+++     bool found = false;
+++     while (Dir.Next()) {
+++           Dir.Store();
+++           const char *TmpNewName = Dir.Adjust(NewName);
+++           if (StatNearestDir(TmpNewName, &StatDir) && StatDir.st_dev == StatOldName.st_dev) {
+++              isyslog("HardLinkVideoFile: %s is on %i (match)", TmpNewName, (int)StatDir.st_dev);
+++              ActualNewName = TmpNewName;
+++              found = true;
+++              break;
+++              }
+++           isyslog("HardLinkVideoFile: %s is on %i", TmpNewName, (int)StatDir.st_dev);
+++           }
+++     if (ActualNewName == NewName) {
+++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
+++        return false;
+++        }
+++
+++     // Looking good, we have a match. Create necessary folders.
+++     if (!MakeDirs(ActualNewName, false))
+++        return false;
+++     // There's no guarantee that the directory of ActualNewName 
+++     // is on the same device as the dir that StatNearestDir found.
+++     // But worst case is that the link fails.
+++     }
+++
+++#ifdef HARDLINK_TEST_ONLY
+++  // Do the hard link to *.vdr_ for testing only
+++  char *name = NULL;
+++  asprintf(&name, "%s_",ActualNewName);
+++  link(ActualOldName, name); 
+++  free(name);
+++  return false;
+++#endif // HARDLINK_TEST_ONLY
+++  
+++  // Try creating the hard link
+++  if (link(ActualOldName, ActualNewName) != 0) {
+++     // Failed to hard link. Maybe not allowed on file system.
+++     LOG_ERROR_STR(ActualNewName);
+++     isyslog("HardLinkVideoFile: failed to hard link from %s to %s", (const char*)ActualOldName, ActualNewName);
+++     return false;
+++     }
+++  
+++  if (ActualNewName != NewName) {
+++     // video01 and up. Do the remaining symlink
+++     if (symlink(ActualNewName, NewName) < 0) {
+++        LOG_ERROR_STR(NewName);
+++        return false;
+++        }
+++     }
+++  return true;
+++}
+++
++ bool VideoFileSpaceAvailable(int SizeMB)
++ {
++   cVideoDirectory Dir;
++diff -Naur vdr-1.7.27/videodir.h vdr-1.7.27-hlcutter/videodir.h
++--- vdr-1.7.27/videodir.h	2008-02-16 13:53:11.000000000 +0100
+++++ vdr-1.7.27-hlcutter/videodir.h	2012-03-31 13:25:56.000000000 +0200
++@@ -19,6 +19,7 @@
++ int CloseVideoFile(cUnbufferedFile *File);
++ bool RenameVideoFile(const char *OldName, const char *NewName);
++ bool RemoveVideoFile(const char *FileName);
+++bool HardLinkVideoFile(const char *OldName, const char *NewName);
++ bool VideoFileSpaceAvailable(int SizeMB);
++ int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
++ cString PrefixVideoFileName(const char *FileName, char Prefix);
+diff -Nru vdr-1.7.29/videodir.c vdr-1.7.29-hlcutter/videodir.c
+--- vdr-1.7.29/videodir.c	2012-06-10 16:45:21.000000000 +0300
++++ vdr-1.7.29-hlcutter/videodir.c	2012-07-16 17:47:24.663630910 +0300
+@@ -19,6 +19,9 @@
+ #include "recording.h"
+ #include "tools.h"
+ 
++
++//#define HARDLINK_TEST_ONLY
++
+ const char *VideoDirectory = VIDEODIR;
+ 
+ class cVideoDirectory {
+@@ -168,6 +171,120 @@
+   return RemoveFileOrDir(FileName, true);
+ }
+ 
++static bool StatNearestDir(const char *FileName, struct stat *Stat)
++{
++  cString Name(FileName);
++  char *p;
++  while ((p = strrchr((char*)(const char*)Name + 1, '/')) != NULL) {
++        *p = 0; // truncate at last '/'
++        if (stat(Name, Stat) == 0) {
++           isyslog("StatNearestDir: Stating %s", (const char*)Name);
++           return true;
++           }
++        }
++  return false;
++}
++
++bool HardLinkVideoFile(const char *OldName, const char *NewName)
++{
++  // Incoming name must be in base video directory:
++  if (strstr(OldName, VideoDirectory) != OldName) {
++     esyslog("ERROR: %s not in %s", OldName, VideoDirectory);
++     return false;
++     }
++  if (strstr(NewName, VideoDirectory) != NewName) {
++     esyslog("ERROR: %s not in %s", NewName, VideoDirectory);
++     return false;
++     }
++
++  const char *ActualNewName = NewName;
++  cString ActualOldName(ReadLink(OldName), true);
++
++  // Some safety checks:
++  struct stat StatOldName;
++  if (lstat(ActualOldName, &StatOldName) == 0) {
++     if (S_ISLNK(StatOldName.st_mode)) {
++        esyslog("HardLinkVideoFile: Failed to resolve symbolic link %s", (const char*)ActualOldName);
++        return false;
++        }
++     }
++  else {
++     esyslog("HardLinkVideoFile: lstat failed on %s", (const char*)ActualOldName);
++     return false;
++     }
++  isyslog("HardLinkVideoFile: %s is on %i", (const char*)ActualOldName, (int)StatOldName.st_dev);
++
++  // Find the video directory where ActualOldName is located
++
++  cVideoDirectory Dir;
++  struct stat StatDir;
++  if (!StatNearestDir(NewName, &StatDir)) {
++     esyslog("HardLinkVideoFile: stat failed on %s", NewName);
++     return false;
++     }
++  
++  isyslog("HardLinkVideoFile: %s is on %i", NewName, (int)StatDir.st_dev);
++  if (StatDir.st_dev != StatOldName.st_dev) {
++     // Not yet found.
++     
++     if (!Dir.IsDistributed()) {
++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++        return false;
++        }
++
++     // Search in video01 and upwards
++     bool found = false;
++     while (Dir.Next()) {
++           Dir.Store();
++           const char *TmpNewName = Dir.Adjust(NewName);
++           if (StatNearestDir(TmpNewName, &StatDir) && StatDir.st_dev == StatOldName.st_dev) {
++              isyslog("HardLinkVideoFile: %s is on %i (match)", TmpNewName, (int)StatDir.st_dev);
++              ActualNewName = TmpNewName;
++              found = true;
++              break;
++              }
++           isyslog("HardLinkVideoFile: %s is on %i", TmpNewName, (int)StatDir.st_dev);
++           }
++     if (ActualNewName == NewName) {
++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++        return false;
++        }
++
++     // Looking good, we have a match. Create necessary folders.
++     if (!MakeDirs(ActualNewName, false))
++        return false;
++     // There's no guarantee that the directory of ActualNewName 
++     // is on the same device as the dir that StatNearestDir found.
++     // But worst case is that the link fails.
++     }
++
++#ifdef HARDLINK_TEST_ONLY
++  // Do the hard link to *.vdr_ for testing only
++  char *name = NULL;
++  asprintf(&name, "%s_",ActualNewName);
++  link(ActualOldName, name); 
++  free(name);
++  return false;
++#endif // HARDLINK_TEST_ONLY
++  
++  // Try creating the hard link
++  if (link(ActualOldName, ActualNewName) != 0) {
++     // Failed to hard link. Maybe not allowed on file system.
++     LOG_ERROR_STR(ActualNewName);
++     isyslog("HardLinkVideoFile: failed to hard link from %s to %s", (const char*)ActualOldName, ActualNewName);
++     return false;
++     }
++  
++  if (ActualNewName != NewName) {
++     // video01 and up. Do the remaining symlink
++     if (symlink(ActualNewName, NewName) < 0) {
++        LOG_ERROR_STR(NewName);
++        return false;
++        }
++     }
++  return true;
++}
++
+ bool VideoFileSpaceAvailable(int SizeMB)
+ {
+   cVideoDirectory Dir;
+diff -Nru vdr-1.7.29/videodir.h vdr-1.7.29-hlcutter/videodir.h
+--- vdr-1.7.29/videodir.h	2012-04-22 18:07:56.000000000 +0300
++++ vdr-1.7.29-hlcutter/videodir.h	2012-07-16 17:47:24.665630884 +0300
+@@ -19,6 +19,7 @@
+ int CloseVideoFile(cUnbufferedFile *File);
+ bool RenameVideoFile(const char *OldName, const char *NewName);
+ bool RemoveVideoFile(const char *FileName);
++bool HardLinkVideoFile(const char *OldName, const char *NewName);
+ bool VideoFileSpaceAvailable(int SizeMB);
+ int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
+ cString PrefixVideoFileName(const char *FileName, char Prefix);
diff --git a/vdr.spec b/vdr.spec
index 3d6a262..836011f 100644
--- a/vdr.spec
+++ b/vdr.spec
@@ -25,7 +25,7 @@
 %global migrfile %{_var}/run/systemd-migr_%{name}-%{version}-%{release}.%{_arch}
 
 Name:           vdr
-Version:        1.7.28
+Version:        1.7.29
 Release:        1%{?dist}
 Summary:        Video Disk Recorder
 
@@ -54,7 +54,7 @@ Source19:       %{name}-check-setup.sh
 Source20:       %{name}-rcu.conf
 Patch0:         %{name}-channel+epg.patch
 Patch1:         http://zap.tartarus.org/~ds/debian/dists/stable/main/source/vdr_1.4.5-2.ds.diff.gz
-Patch2:         http://www.saunalahti.fi/~rahrenbe/vdr/patches/vdr-1.7.28-vasarajanauloja.patch.gz
+Patch2:         http://www.saunalahti.fi/~rahrenbe/vdr/patches/vdr-1.7.29-vasarajanauloja.patch.gz
 # Extracted from http://copperhead.htpc-forum.de/downloads/extensionpatch/extpngvdr1.7.21v1.diff.gz
 Patch3:         %{name}-1.7.21-plugin-missing.patch
 Patch4:         %{name}-1.7.22-paths.patch
@@ -66,7 +66,7 @@ Patch7:         http://projects.vdr-developer.org/projects/plg-ttxtsubs/reposito
 # Original at http://toms-cafe.de/vdr/download/vdr-jumpplay-1.0-1.7.6.diff
 Patch8:         %{name}-1.7.28-vasarajanauloja-jumpplay.patch
 # http://www.udo-richter.de/vdr/patches.en.html#hlcutter
-Patch9:         http://www.udo-richter.de/vdr/files/vdr-1.7.27-hlcutter-0.2.3.diff
+Patch9:         %{name}-1.7.29-hlcutter-0.2.3.diff
 Patch10:        %{name}-1.7.27-libhdffcmd-cflags.patch
 # http://article.gmane.org/gmane.linux.vdr/43590
 Patch11:        %{name}-1.7.25-mainmenuhooks101.patch
@@ -74,15 +74,13 @@ Patch11:        %{name}-1.7.25-mainmenuhooks101.patch
 # Modified so that it applies over the timer-info patch
 Patch12:        %{name}-1.7.21-timercmd.patch
 Patch13:        http://projects.vdr-developer.org/git/vdr-plugin-epgsearch.git/plain/patches/vdr-1.5.17-progressbar-support-0.0.1.diff
-Patch14:        http://www.saunalahti.fi/~rahrenbe/vdr/patches/vdr-1.7.28-finnish.patch.gz
+Patch14:        http://www.saunalahti.fi/~rahrenbe/vdr/patches/vdr-1.7.29-finnish.patch.gz
 Patch15:        %{name}-1.7.21-fedora-pkgconfig.patch
 Patch16:        %{name}-1.7.21-jumpplay-finnish.patch
 Patch17:        http://projects.vdr-developer.org/git/vdr-plugin-epgsearch.git/plain/patches/vdr.epgsearch-exttimeredit-0.0.2.diff
 Patch18:        %{name}-timer-info-1.7.28.patch
 # http://projects.vdr-developer.org/issues/819
 Patch19:        %{name}-1.7.22-ttxtsubs-on.patch
-# http://www.linuxtv.org/pipermail/vdr/2012-June/026400.html
-Patch20:        %{name}-1.7.28-epghandledexternally.diff
 
 BuildRequires:  libjpeg-devel
 BuildRequires:  libcap-devel
@@ -201,7 +199,7 @@ sed \
 # TODO: does not apply
 #patch6 -p0
 %patch7 -p1 -F 2
-%patch8 -p1
+%patch8 -p1 -F 2
 %patch9 -p1
 %patch10 -p1
 %patch11 -p1
@@ -215,7 +213,6 @@ sed \
 #patch17 -p0 -F 3
 %patch18 -p1
 %patch19 -p1
-%patch20 -p1
 
 for f in CONTRIBUTORS HISTORY UPDATE-1.4.0 README.timer-info ; do
   iconv -f iso-8859-1 -t utf-8 -o $f.utf8 $f && mv $f.utf8 $f
@@ -552,6 +549,9 @@ rm -f %{migrfile} &>/dev/null || :
 
 
 %changelog
+* Wed Jul 18 2012 Ville Skyttä <ville.skytta at iki.fi> - 1.7.29-1
+- Update to 1.7.29.
+
 * Wed Jun 27 2012 Ville Skyttä <ville.skytta at iki.fi> - 1.7.28-1
 - Update to 1.7.28.
 - Add softhdddevice to sysconfig's VDR_PLUGIN_ORDER.


More information about the scm-commits mailing list