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 @@
}
CheckForSeamlessStream = false;
}
- 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@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);