scop pushed to vdr (f22). "Move uncompressed patches to git per http://fedoraproject.org/wiki/Packaging:Guidelines#Applying_patches"

notifications at fedoraproject.org notifications at fedoraproject.org
Mon Apr 6 11:56:43 UTC 2015


>From a68bbe1835ba3dd7576901a8f465e185eb20ea38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ville=20Skytt=C3=A4?= <ville.skytta at iki.fi>
Date: Thu, 19 Feb 2015 17:41:54 +0200
Subject: Move uncompressed patches to git per
 http://fedoraproject.org/wiki/Packaging:Guidelines#Applying_patches


diff --git a/.gitignore b/.gitignore
index 12b7572..b3aaae8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,2 @@
 /*.gz
 /*.bz2
-/vdr-1.5.17-progressbar-support-0.0.1.diff
-/vdr-timer-info-0.5-1.7.13.diff
-/vdr.epgsearch-exttimeredit-0.0.2.diff
-/vdr-1.7.29-hlcutter-0.2.3.diff
-/vdr-2.0.6-ttxtsubs.patch
-/opt-24_jumpplay.patch
-/vdr-2.1.5-naludump-0.1.diff
-/epghandler-segment-transfer.patch
diff --git a/epghandler-segment-transfer.patch b/epghandler-segment-transfer.patch
new file mode 100644
index 0000000..8374a66
--- /dev/null
+++ b/epghandler-segment-transfer.patch
@@ -0,0 +1,65 @@
+--- ../vdr-2.0.2.plain//eit.c	2012-12-04 12:10:10.000000000 +0100
++++ eit.c	2013-05-22 16:49:37.635027462 +0200
+@@ -46,6 +46,8 @@
+      return;
+      }
+ 
++  EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus);
++
+   bool handledExternally = EpgHandlers.HandledExternally(channel);
+   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
+ 
+@@ -310,6 +312,7 @@
+      Schedules->SetModified(pSchedule);
+      }
+   Channels.Unlock();
++  EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus);
+ }
+ 
+ // --- cTDT ------------------------------------------------------------------
+--- ../vdr-2.0.2.plain//epg.c	2013-02-17 15:12:07.000000000 +0100
++++ epg.c	2013-05-22 16:50:29.043029281 +0200
+@@ -1537,3 +1537,19 @@
+       }
+   Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
+ }
++
++void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus)
++{
++  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
++      if (eh->BeginSegmentTransfer(Channel, OnlyRunningStatus))
++         return;
++      }
++}
++
++void cEpgHandlers::EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
++{
++  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
++      if (eh->EndSegmentTransfer(Modified, OnlyRunningStatus))
++         return;
++      }
++}
+--- ../vdr-2.0.2.plain//epg.h	2012-09-24 14:53:53.000000000 +0200
++++ epg.h	2013-05-22 16:50:16.867028850 +0200
+@@ -273,6 +273,12 @@
+   virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; }
+           ///< Takes a look at all EPG events between SegmentStart and SegmentEnd and
+           ///< drops outdated events.
++  virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) { return false; }
++          ///< called directly after IgnoreChannel before any other handler method called
++          ///< designed to give handlers the ossibility to prepare a transaction 
++  virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) { return false; }
++          ///< called at last after the segment data is processed
++          ///< at this oint handlers should close/commt/rollback their transactions
+   };
+ 
+ class cEpgHandlers : public cList<cEpgHandler> {
+@@ -295,6 +301,8 @@
+   void HandleEvent(cEvent *Event);
+   void SortSchedule(cSchedule *Schedule);
+   void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
++  void BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus);
++  void EndSegmentTransfer(bool Modified, bool OnlyRunningStatus);
+   };
+ 
+ extern cEpgHandlers EpgHandlers;
diff --git a/sources b/sources
index 4ab426c..17dedbe 100644
--- a/sources
+++ b/sources
@@ -1,10 +1,4 @@
 3e9287f726df5a667054a15078235791  vdr_1.4.5-2.ds.diff.gz
-49db9691fe6023270d443a62bab0411b  vdr-1.5.17-progressbar-support-0.0.1.diff
-04511ae02243eb1bab94f3f45b59e574  vdr-timer-info-0.5-1.7.13.diff
-71f7281d55eba1957f4267f596b11e29  vdr.epgsearch-exttimeredit-0.0.2.diff
-da3a3ab8c2a42a320171b1648c3b3258  vdr-1.7.29-hlcutter-0.2.3.diff
 d76b06a7bf1f458a02f2d1169776fb7f  vdr_2.0.3-1.debian.tar.bz2
-94dbf03ad78b10668c2aef129e254b98  vdr-2.1.5-naludump-0.1.diff
-73ff9846be32894e399b821a9da17dac  epghandler-segment-transfer.patch
 b01a5d964af6379af5e7aa0f5203e1d3  vdr-2.1.9-menuselection.patch.gz
 8853f64c0fc3d41ffd3b4bfc6f0a14b7  vdr-2.2.0.tar.bz2
diff --git a/vdr-1.5.17-progressbar-support-0.0.1.diff b/vdr-1.5.17-progressbar-support-0.0.1.diff
new file mode 100644
index 0000000..98b887d
--- /dev/null
+++ b/vdr-1.5.17-progressbar-support-0.0.1.diff
@@ -0,0 +1,106 @@
+diff -Nru vdr-1.5.17-orig/skinclassic.c vdr-1.5.17-progressbar/skinclassic.c
+--- vdr-1.5.17-orig/skinclassic.c
++++ vdr-1.5.17-progressbar/skinclassic.c
+@@ -314,8 +314,47 @@
+   for (int i = 0; i < MaxTabs; i++) {
+       const char *s = GetTabbedText(Text, i);
+       if (s) {
+-         int xt = x0 + Tab(i);
+-         osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
++         bool isprogressbar = false;
++         int now = 0, total = 0;
++         // check if progress bar: "[|||||||   ]"
++         if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++            const char *p = s + 1;
++            // update status
++            isprogressbar = true;
++            for (; *p != ']'; ++p) {
++                // check if progressbar characters
++                if (*p == ' ' || *p == '|') {
++                   // update counters
++                   ++total;
++                   if (*p == '|')
++                      ++now;
++                   }
++                else {
++                   // wrong character detected; not a progressbar
++                   isprogressbar = false;
++                   break;
++                   }
++                }
++            }
++         int xt = x0 + Tab(i);
++         if (isprogressbar) {
++            // define x coordinates of progressbar
++            int px0 = xt;
++            int px1 = (Tab(i + 1)?Tab(i+1):x1) - 5;
++            int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++            // define y coordinates of progressbar
++            int py0 = y + 4;
++            int py1 = y + lineHeight - 4;
++            // draw background
++            osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i+1):x1) - 1, y + lineHeight - 1, ColorBg);
++            // draw progressbar
++            osd->DrawRectangle(px0,    py0, px,  py1, ColorFg);
++            osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++            osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++            osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++            }
++         else
++            osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
+          }
+       if (!Tab(i + 1))
+          break;
+diff -Nru vdr-1.5.17-orig/skinsttng.c vdr-1.5.17-progressbar/skinsttng.c
+--- vdr-1.5.17-orig/skinsttng.c
++++ vdr-1.5.17-progressbar/skinsttng.c
+@@ -558,8 +558,47 @@
+   for (int i = 0; i < MaxTabs; i++) {
+       const char *s = GetTabbedText(Text, i);
+       if (s) {
+-         int xt = x3 + 5 + Tab(i);
+-         osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
++         bool isprogressbar = false;
++         int now = 0, total = 0;
++         // check if progress bar: "[|||||||   ]"
++         if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++            const char *p = s + 1;
++            // update status
++            isprogressbar = true;
++            for (; *p != ']'; ++p) {
++                // check if progressbar characters
++                if (*p == ' ' || *p == '|') {
++                   // update counters
++                   ++total;
++                   if (*p == '|')
++                      ++now;
++                   }
++                else {
++                   // wrong character detected; not a progressbar
++                   isprogressbar = false;
++                   break;
++                   }
++                }
++            }
++         int xt = x3 + 5 + Tab(i);
++         if (isprogressbar) {
++            // define x coordinates of progressbar
++            int px0 = xt;
++            int px1 = x3 + (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1;
++            int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++            // define y coordinates of progressbar
++            int py0 = y + 4;
++            int py1 = y + lineHeight - 4;
++            // draw background
++            osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1, y + lineHeight - 1, ColorBg);
++            // draw progressbar
++            osd->DrawRectangle(px0,    py0, px,  py1, ColorFg);
++            osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++            osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++            osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++            }
++         else
++            osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
+          }
+       if (!Tab(i + 1))
+          break;
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..776a278
--- /dev/null
+++ b/vdr-1.7.29-hlcutter-0.2.3.diff
@@ -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 @@
+                     }
+                  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 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 --git a/vdr-2.1.5-naludump-0.1.diff b/vdr-2.1.5-naludump-0.1.diff
new file mode 100644
index 0000000..4b338cd
--- /dev/null
+++ b/vdr-2.1.5-naludump-0.1.diff
@@ -0,0 +1,621 @@
+diff -Naur vdr-2.1.6/config.c vdr-2.1.6-naludump-0.1/config.c
+--- vdr-2.1.6/config.c	2013-08-31 14:41:28.000000000 +0200
++++ vdr-2.1.6-naludump-0.1/config.c	2014-03-30 17:47:25.000000000 +0200
+@@ -462,6 +462,7 @@
+   MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
+   SplitEditedFiles = 0;
+   DelTimeshiftRec = 0;
++  DumpNaluFill = 0;
+   MinEventTimeout = 30;
+   MinUserInactivity = 300;
+   NextWakeupTime = 0;
+@@ -673,6 +674,7 @@
+   else if (!strcasecmp(Name, "MaxVideoFileSize"))    MaxVideoFileSize   = atoi(Value);
+   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = atoi(Value);
+   else if (!strcasecmp(Name, "DelTimeshiftRec"))     DelTimeshiftRec    = atoi(Value);
++  else if (!strcasecmp(Name, "DumpNaluFill"))        DumpNaluFill       = 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);
+@@ -788,6 +790,7 @@
+   Store("MaxVideoFileSize",   MaxVideoFileSize);
+   Store("SplitEditedFiles",   SplitEditedFiles);
+   Store("DelTimeshiftRec",    DelTimeshiftRec);
++  Store("DumpNaluFill",       DumpNaluFill);
+   Store("MinEventTimeout",    MinEventTimeout);
+   Store("MinUserInactivity",  MinUserInactivity);
+   Store("NextWakeupTime",     NextWakeupTime);
+diff -Naur vdr-2.1.6/config.h vdr-2.1.6-naludump-0.1/config.h
+--- vdr-2.1.6/config.h	2014-02-25 11:00:23.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/config.h	2014-03-30 17:47:25.000000000 +0200
+@@ -326,6 +326,7 @@
+   int MaxVideoFileSize;
+   int SplitEditedFiles;
+   int DelTimeshiftRec;
++  int DumpNaluFill;
+   int MinEventTimeout, MinUserInactivity;
+   time_t NextWakeupTime;
+   int MultiSpeedMode;
+diff -Naur vdr-2.1.6/menu.c vdr-2.1.6-naludump-0.1/menu.c
+--- vdr-2.1.6/menu.c	2014-03-16 11:38:31.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/menu.c	2014-03-30 17:47:25.000000000 +0200
+@@ -3547,6 +3547,7 @@
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
+   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$Dump NALU Fill data"),       &data.DumpNaluFill));
+ }
+ 
+ // --- cMenuSetupReplay ------------------------------------------------------
+diff -Naur vdr-2.1.6/recorder.c vdr-2.1.6-naludump-0.1/recorder.c
+--- vdr-2.1.6/recorder.c	2014-02-21 10:19:52.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/recorder.c	2014-03-30 17:47:25.000000000 +0200
+@@ -46,6 +46,14 @@
+      Type = 0x06;
+      }
+   frameDetector = new cFrameDetector(Pid, Type);
++  if (   Type == 0x1B // MPEG4 video
++      && (Setup.DumpNaluFill ? (strstr(FileName, "NALUKEEP") == NULL) : (strstr(FileName, "NALUDUMP") != NULL))) { // MPEG4
++     isyslog("Starting NALU fill dumper");
++     naluStreamProcessor = new cNaluStreamProcessor();
++     naluStreamProcessor->SetPid(Pid);
++     }
++  else
++     naluStreamProcessor = NULL;
+   index = NULL;
+   fileSize = 0;
+   lastDiskSpaceCheck = time(NULL);
+@@ -67,6 +75,12 @@
+ cRecorder::~cRecorder()
+ {
+   Detach();
++  if (naluStreamProcessor) {
++     long long int TotalPackets = naluStreamProcessor->GetTotalPackets();
++     long long int DroppedPackets = naluStreamProcessor->GetDroppedPackets();
++     isyslog("NALU fill dumper: %lld of %lld packets dropped, %lli%%", DroppedPackets, TotalPackets, TotalPackets ? DroppedPackets*100/TotalPackets : 0);
++     delete naluStreamProcessor;
++     }
+   delete index;
+   delete fileName;
+   delete frameDetector;
+@@ -157,11 +171,32 @@
+                              }
+                        t.Set(MAXBROKENTIMEOUT);
+                        }
+-                    if (recordFile->Write(b, Count) < 0) {
+-                       LOG_ERROR_STR(fileName->Name());
+-                       break;
++                    if (naluStreamProcessor) {
++                       naluStreamProcessor->PutBuffer(b, Count);
++                       bool Fail = false;
++                       while (true) {
++                             int OutLength = 0;
++                             uchar *OutData = naluStreamProcessor->GetBuffer(OutLength);
++                             if (!OutData || OutLength <= 0)
++                                break;
++                             if (recordFile->Write(OutData, OutLength) < 0) {
++                                LOG_ERROR_STR(fileName->Name());
++                                Fail = true;
++                                break;
++                                }
++                             fileSize += OutLength;
++                             }
++                       if (Fail)
++                          break;
++                       }
++                    else {
++                       if (recordFile->Write(b, Count) < 0) {
++                          LOG_ERROR_STR(fileName->Name());
++                          break;
++                          }
++                       fileSize += Count;
+                        }
+-                    fileSize += Count;
++
+                     }
+                  }
+               ringBuffer->Del(Count);
+diff -Naur vdr-2.1.6/recorder.h vdr-2.1.6-naludump-0.1/recorder.h
+--- vdr-2.1.6/recorder.h	2010-12-27 12:17:04.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/recorder.h	2014-03-30 17:47:25.000000000 +0200
+@@ -21,6 +21,7 @@
+   cRingBufferLinear *ringBuffer;
+   cFrameDetector *frameDetector;
+   cPatPmtGenerator patPmtGenerator;
++  cNaluStreamProcessor *naluStreamProcessor;
+   cFileName *fileName;
+   cIndexFile *index;
+   cUnbufferedFile *recordFile;
+diff -Naur vdr-2.1.6/remux.c vdr-2.1.6-naludump-0.1/remux.c
+--- vdr-2.1.6/remux.c	2014-03-08 16:05:35.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/remux.c	2014-03-30 17:47:25.000000000 +0200
+@@ -343,6 +343,42 @@
+      dsyslog("WARNING: required %d video TS packets to determine frame type", numPacketsPid);
+ }
+ 
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
++{
++    // Hint: ExtenAdaptionField(p, TsPayloadOffset(p) - 4) is a null operation
++
++    int Offset = TsPayloadOffset(Packet); // First byte after existing adaption field
++
++    if (ToLength <= 0)
++    {
++        // Remove adaption field
++        Packet[3] = Packet[3] & ~TS_ADAPT_FIELD_EXISTS;
++        return;
++    }
++
++    // Set adaption field present
++    Packet[3] = Packet[3] | TS_ADAPT_FIELD_EXISTS;
++
++    // Set new length of adaption field:
++    Packet[4] = ToLength <= TS_SIZE-4 ? ToLength-1 : TS_SIZE-4-1;
++
++    if (Packet[4] == TS_SIZE-4-1)
++    {
++        // No more payload, remove payload flag
++        Packet[3] = Packet[3] & ~TS_PAYLOAD_EXISTS;
++    }
++
++    int NewPayload = TsPayloadOffset(Packet); // First byte after new adaption field
++
++    // Fill new adaption field
++    if (Offset == 4 && Offset < NewPayload)
++        Offset++; // skip adaptation_field_length
++    if (Offset == 5 && Offset < NewPayload)
++        Packet[Offset++] = 0; // various flags set to 0
++    while (Offset < NewPayload)
++        Packet[Offset++] = 0xff; // stuffing byte
++}
++
+ // --- cPatPmtGenerator ------------------------------------------------------
+ 
+ cPatPmtGenerator::cPatPmtGenerator(const cChannel *Channel)
+@@ -1547,3 +1583,344 @@
+         }
+   return Processed;
+ }
++
++// --- cNaluDumper ---------------------------------------------------------
++
++cNaluDumper::cNaluDumper()
++{
++    LastContinuityOutput = -1;
++    reset();
++}
++
++void cNaluDumper::reset()
++{
++    LastContinuityInput = -1;
++    ContinuityOffset = 0;
++    PesId = -1;
++    PesOffset = 0;
++    NaluFillState = NALU_NONE;
++    NaluOffset = 0;
++    History = 0xffffffff;
++    DropAllPayload = false;
++}
++
++void cNaluDumper::ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info)
++{
++    Info.DropPayloadStartBytes = 0;
++    Info.DropPayloadEndBytes = 0;
++    int LastKeepByte = -1;
++
++    if (PayloadStart)
++    {
++        History = 0xffffffff;
++        PesId = -1;
++        NaluFillState = NALU_NONE;
++    }
++
++    for (int i=0; i<size; i++) {
++        History = (History << 8) | Payload[i];
++
++        PesOffset++;
++        NaluOffset++;
++
++        bool DropByte = false;
++
++        if (History >= 0x00000180 && History <= 0x000001FF)
++        {
++            // Start of PES packet
++            PesId = History & 0xff;
++            PesOffset = 0;
++            NaluFillState = NALU_NONE;
++        }
++        else if (PesId >= 0xe0 && PesId <= 0xef // video stream
++                 && History >= 0x00000100 && History <= 0x0000017F) // NALU start code
++        {
++            int NaluId = History & 0xff;
++            NaluOffset = 0;
++            NaluFillState = ((NaluId & 0x1f) == 0x0c) ? NALU_FILL : NALU_NONE;
++        }
++
++        if (PesId >= 0xe0 && PesId <= 0xef // video stream
++            && PesOffset >= 1 && PesOffset <= 2)
++        {
++            Payload[i] = 0; // Zero out PES length field
++        }
++
++        if (NaluFillState == NALU_FILL && NaluOffset > 0) // Within NALU fill data
++        {
++            // We expect a series of 0xff bytes terminated by a single 0x80 byte.
++
++            if (Payload[i] == 0xFF)
++            {
++                DropByte = true;
++            }
++            else if (Payload[i] == 0x80)
++            {
++                NaluFillState = NALU_TERM; // Last byte of NALU fill, next byte sets NaluFillEnd=true
++                DropByte = true;
++            }
++            else // Invalid NALU fill
++            {
++                dsyslog("cNaluDumper: Unexpected NALU fill data: %02x", Payload[i]);
++                NaluFillState = NALU_END;
++                if (LastKeepByte == -1)
++                {
++                    // Nalu fill from beginning of packet until last byte
++                    // packet start needs to be dropped
++                    Info.DropPayloadStartBytes = i;
++                }
++            }
++        }
++        else if (NaluFillState == NALU_TERM) // Within NALU fill data
++        {
++            // We are after the terminating 0x80 byte
++            NaluFillState = NALU_END;
++            if (LastKeepByte == -1)
++            {
++                // Nalu fill from beginning of packet until last byte
++                // packet start needs to be dropped
++                Info.DropPayloadStartBytes = i;
++            }
++        }
++
++        if (!DropByte)
++            LastKeepByte = i; // Last useful byte
++    }
++
++    Info.DropAllPayloadBytes = (LastKeepByte == -1);
++    Info.DropPayloadEndBytes = size-1-LastKeepByte;
++}
++
++bool cNaluDumper::ProcessTSPacket(unsigned char *Packet)
++{
++    bool HasAdaption = TsHasAdaptationField(Packet);
++    bool HasPayload = TsHasPayload(Packet);
++
++    // Check continuity:
++    int ContinuityInput = TsContinuityCounter(Packet);
++    if (LastContinuityInput >= 0)
++    {
++        int NewContinuityInput = HasPayload ? (LastContinuityInput + 1) & TS_CONT_CNT_MASK : LastContinuityInput;
++        int Offset = (NewContinuityInput - ContinuityInput) & TS_CONT_CNT_MASK;
++        if (Offset > 0)
++            dsyslog("cNaluDumper: TS continuity offset %i", Offset);
++        if (Offset > ContinuityOffset)
++            ContinuityOffset = Offset; // max if packets get dropped, otherwise always the current one.
++    }
++    LastContinuityInput = ContinuityInput;
++
++    if (HasPayload) {
++        sPayloadInfo Info;
++        int Offset = TsPayloadOffset(Packet);
++        ProcessPayload(Packet + Offset, TS_SIZE - Offset, TsPayloadStart(Packet), Info);
++
++        if (DropAllPayload && !Info.DropAllPayloadBytes)
++        {
++            // Return from drop packet mode to normal mode
++            DropAllPayload = false;
++
++            // Does the packet start with some remaining NALU fill data?
++            if (Info.DropPayloadStartBytes > 0)
++            {
++                // Add these bytes as stuffing to the adaption field.
++
++                // Sample payload layout:
++                // FF FF FF FF FF 80 00 00 01 xx xx xx xx
++                //                   ^DropPayloadStartBytes
++
++                TsExtendAdaptionField(Packet, Offset - 4 + Info.DropPayloadStartBytes);
++            }
++        }
++
++        bool DropThisPayload = DropAllPayload;
++
++        if (!DropAllPayload && Info.DropPayloadEndBytes > 0) // Payload ends with 0xff NALU Fill
++        {
++            // Last packet of useful data
++            // Do early termination of NALU fill data
++            Packet[TS_SIZE-1] = 0x80;
++            DropAllPayload = true;
++            // Drop all packets AFTER this one
++
++            // Since we already wrote the 0x80, we have to make sure that
++            // as soon as we stop dropping packets, any beginning NALU fill of next
++            // packet gets dumped. (see DropPayloadStartBytes above)
++        }
++
++        if (DropThisPayload && HasAdaption)
++        {
++            // Drop payload data, but keep adaption field data
++            TsExtendAdaptionField(Packet, TS_SIZE-4);
++            DropThisPayload = false;
++        }
++
++        if (DropThisPayload)
++        {
++            return true; // Drop packet
++        }
++    }
++
++    // Fix Continuity Counter and reproduce incoming offsets:
++    int NewContinuityOutput = TsHasPayload(Packet) ? (LastContinuityOutput + 1) & TS_CONT_CNT_MASK : LastContinuityOutput;
++    NewContinuityOutput = (NewContinuityOutput + ContinuityOffset) & TS_CONT_CNT_MASK;
++    TsSetContinuityCounter(Packet, NewContinuityOutput);
++    LastContinuityOutput = NewContinuityOutput;
++    ContinuityOffset = 0;
++
++    return false; // Keep packet
++}
++
++// --- cNaluStreamProcessor ---------------------------------------------------------
++
++cNaluStreamProcessor::cNaluStreamProcessor()
++{
++    pPatPmtParser = NULL;
++    vpid = -1;
++    data = NULL;
++    length = 0;
++    tempLength = 0;
++    tempLengthAtEnd = false;
++    TotalPackets = 0;
++    DroppedPackets = 0;
++}
++
++void cNaluStreamProcessor::PutBuffer(uchar *Data, int Length)
++{
++    if (length > 0)
++        esyslog("cNaluStreamProcessor::PutBuffer: New data before old data was processed!");
++
++    data = Data;
++    length = Length;
++}
++
++uchar* cNaluStreamProcessor::GetBuffer(int &OutLength)
++{
++    if (length <= 0)
++    {
++        // Need more data - quick exit
++        OutLength = 0;
++        return NULL;
++    }
++    if (tempLength > 0) // Data in temp buffer?
++    {
++        if (tempLengthAtEnd) // Data is at end, copy to beginning
++        {
++            // Overlapping src and dst!
++            for (int i=0; i<tempLength; i++)
++                tempBuffer[i] = tempBuffer[TS_SIZE-tempLength+i];
++        }
++        // Normalize TempBuffer fill
++        if (tempLength < TS_SIZE && length > 0)
++        {
++            int Size = min(TS_SIZE-tempLength, length);
++            memcpy(tempBuffer+tempLength, data, Size);
++            data += Size;
++            length -= Size;
++            tempLength += Size;
++        }
++        if (tempLength < TS_SIZE)
++        {
++            // All incoming data buffered, but need more data
++            tempLengthAtEnd = false;
++            OutLength = 0;
++            return NULL;
++        }
++        // Now: TempLength==TS_SIZE
++        if (tempBuffer[0] != TS_SYNC_BYTE)
++        {
++            // Need to sync on TS within temp buffer
++            int Skipped = 1;
++            while (Skipped < TS_SIZE && (tempBuffer[Skipped] != TS_SYNC_BYTE || (Skipped < length && data[Skipped] != TS_SYNC_BYTE)))
++                Skipped++;
++            esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
++            // Pass through skipped bytes
++            tempLengthAtEnd = true;
++            tempLength = TS_SIZE - Skipped; // may be 0, thats ok
++            OutLength = Skipped;
++            return tempBuffer;
++        }
++        // Now: TempBuffer is a TS packet
++        int Pid = TsPid(tempBuffer);
++        if (pPatPmtParser)
++        {
++            if (Pid == 0)
++                pPatPmtParser->ParsePat(tempBuffer, TS_SIZE);
++            else if (pPatPmtParser->IsPmtPid(Pid))
++                pPatPmtParser->ParsePmt(tempBuffer, TS_SIZE);
++        }
++
++        TotalPackets++;
++        bool Drop = false;
++        if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
++            Drop = NaluDumper.ProcessTSPacket(tempBuffer);
++        if (!Drop)
++        {
++            // Keep this packet, then continue with new data
++            tempLength = 0;
++            OutLength = TS_SIZE;
++            return tempBuffer;
++        }
++        // Drop TempBuffer
++        DroppedPackets++;
++        tempLength = 0;
++    }
++    // Now: TempLength==0, just process data/length
++
++    // Pointer to processed data / length:
++    uchar *Out = data;
++    uchar *OutEnd = Out;
++
++    while (length >= TS_SIZE)
++    {
++        if (data[0] != TS_SYNC_BYTE) {
++            int Skipped = 1;
++            while (Skipped < length && (data[Skipped] != TS_SYNC_BYTE || (length - Skipped > TS_SIZE && data[Skipped + TS_SIZE] != TS_SYNC_BYTE)))
++                Skipped++;
++            esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
++
++            // Pass through skipped bytes
++            if (OutEnd != data)
++                memcpy(OutEnd, data, Skipped);
++            OutEnd += Skipped;
++            continue;
++        }
++        // Now: Data starts with complete TS packet
++
++        int Pid = TsPid(data);
++        if (pPatPmtParser)
++        {
++            if (Pid == 0)
++                pPatPmtParser->ParsePat(data, TS_SIZE);
++            else if (pPatPmtParser->IsPmtPid(Pid))
++                pPatPmtParser->ParsePmt(data, TS_SIZE);
++        }
++
++        TotalPackets++;
++        bool Drop = false;
++        if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
++            Drop = NaluDumper.ProcessTSPacket(data);
++        if (!Drop)
++        {
++            if (OutEnd != data)
++                memcpy(OutEnd, data, TS_SIZE);
++            OutEnd += TS_SIZE;
++        }
++        else
++        {
++            DroppedPackets++;
++        }
++        data += TS_SIZE;
++        length -= TS_SIZE;
++    }
++    // Now: Less than a packet remains.
++    if (length > 0)
++    {
++        // copy remains into temp buffer
++        memcpy(tempBuffer, data, length);
++        tempLength = length;
++        tempLengthAtEnd = false;
++        length = 0;
++    }
++    OutLength = (OutEnd - Out);
++    return OutLength > 0 ? Out : NULL;
++}
+diff -Naur vdr-2.1.6/remux.h vdr-2.1.6-naludump-0.1/remux.h
+--- vdr-2.1.6/remux.h	2014-02-08 13:41:50.000000000 +0100
++++ vdr-2.1.6-naludump-0.1/remux.h	2014-03-30 17:47:25.000000000 +0200
+@@ -62,6 +62,11 @@
+   return p[3] & TS_PAYLOAD_EXISTS;
+ }
+ 
++inline bool TsSetPayload(const uchar *p)
++{
++  return p[3] & TS_PAYLOAD_EXISTS;
++}
++
+ inline bool TsHasAdaptationField(const uchar *p)
+ {
+   return p[3] & TS_ADAPT_FIELD_EXISTS;
+@@ -143,6 +148,7 @@
+ int64_t TsGetDts(const uchar *p, int l);
+ void TsSetPts(uchar *p, int l, int64_t Pts);
+ void TsSetDts(uchar *p, int l, int64_t Dts);
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength);
+ 
+ // Some PES handling tools:
+ // The following functions that take a pointer to PES data all assume that
+@@ -518,4 +524,78 @@
+       ///< available.
+   };
+ 
++
++#define PATCH_NALUDUMP 100
++
++class cNaluDumper {
++    unsigned int History;
++
++    int LastContinuityInput;
++    int LastContinuityOutput;
++    int ContinuityOffset;
++
++    bool DropAllPayload;
++
++    int PesId;
++    int PesOffset;
++
++    int NaluOffset;
++
++    enum eNaluFillState {
++        NALU_NONE=0,    // currently not NALU fill stream
++        NALU_FILL,      // Within NALU fill stream, 0xff bytes and NALU start code in byte 0
++        NALU_TERM,      // Within NALU fill stream, read 0x80 terminating byte
++        NALU_END        // Beyond end of NALU fill stream, expecting 0x00 0x00 0x01 now
++        };
++
++    eNaluFillState NaluFillState;
++
++    struct sPayloadInfo {
++        int DropPayloadStartBytes;
++        int DropPayloadEndBytes;
++        bool DropAllPayloadBytes;
++    };
++
++public:
++    cNaluDumper();
++
++    void reset();
++
++    // Single packet interface:
++    bool ProcessTSPacket(unsigned char *Packet);
++
++private:
++    void ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info);
++};
++
++class cNaluStreamProcessor {
++    //Buffer stream interface:
++    int vpid;
++    uchar *data;
++    int length;
++    uchar tempBuffer[TS_SIZE];
++    int tempLength;
++    bool tempLengthAtEnd;
++    cPatPmtParser *pPatPmtParser;
++    cNaluDumper NaluDumper;
++
++    long long int TotalPackets;
++    long long int DroppedPackets;
++public:
++    cNaluStreamProcessor();
++
++    void SetPid(int VPid) { vpid = VPid; }
++    void SetPatPmtParser(cPatPmtParser *_pPatPmtParser) { pPatPmtParser = _pPatPmtParser; }
++    // Set either a PID or set a pointer to an PatPmtParser that will detect _one_ PID
++
++    void PutBuffer(uchar *Data, int Length);
++    // Add new data to be processed. Data must be valid until Get() returns NULL.
++    uchar* GetBuffer(int &OutLength);
++    // Returns filtered data, or NULL/0 to indicate that all data from Put() was processed
++    // or buffered.
++
++    long long int GetTotalPackets() { return TotalPackets; }
++    long long int GetDroppedPackets() { return DroppedPackets; }
++};
++
+ #endif // __REMUX_H
diff --git a/vdr-timer-info-0.5-1.7.13.diff b/vdr-timer-info-0.5-1.7.13.diff
new file mode 100644
index 0000000..a0e8adb
--- /dev/null
+++ b/vdr-timer-info-0.5-1.7.13.diff
@@ -0,0 +1,306 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## opt-41-x_timer-info.dpatch by Andreas Brugger <brougs78 at gmx.net>, Thomas G�nther <tom at toms-cafe.de>
+## http://toms-cafe.de/vdr/download/vdr-timer-info-0.5-1.7.13.diff
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Shows info, if it is possible to record an event in the timer-info of
+## DP: vdr - see README.timer-info for details.
+
+ at DPATCH@
+diff -Naurp vdr-1.7.13/README.timer-info vdr-1.7.13-timer-info-0.5/README.timer-info
+--- vdr-1.7.13/README.timer-info	1970-01-01 00:00:00.000000000 +0000
++++ vdr-1.7.13-timer-info-0.5/README.timer-info	2010-02-28 18:26:31.000000000 +0000
+@@ -0,0 +1,69 @@
+++------------------------------------------------------------------------------+
++|               Info about the timer-info-patch by Brougs78                    |
++|                brougs78 at gmx.net / home.pages.at/brougs78                     |
+++------------------------------------------------------------------------------+
++
++
++README timer-info:
++------------------
++
++Features:
++ - Shows info, if it is possible to record an event in the timer menu of vdr.
++   For calculations the free space incl. the deleted recordings is used,
++   considering an average consumtion of 25.75 MB/min (also used by vdr itself).
++   The first column in the timer-list shows:
++      ( + ) recording will be most probably possible (enough space)
++      (+/-) recording may be possible
++      ( - ) recording will most probably fail (to less space)
++   The calculations also consider repeating timers.
++ - It is possible to deactivate the patch in the OSD-menu of VDR.
++
++
++HISTORY timer-info:
++-------------------
++
++25.11.2004: v0.1
++ - Initial release
++
++11.01.2005: v0.1b
++ - Bugfixes for vdr-1.3.18
++ - In the menu the free recording-time no longer includes the space of the
++   deleted recordings, because this slowed the vdr down to much.
++
++08.07.2005: v0.1c
++ - Made the patch configurable
++
++29.01.2006: v0.2 - Thomas G�nther <tom at toms-cafe.de>
++ - Rewritten great parts for vdr-1.3.38+
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.2-1.3.38+.diff
++
++05.02.2006: v0.3 - Thomas G�nther <tom at toms-cafe.de>
++ - Fixed refresh of timer menu in cMenuTimers::OnOff
++ - Fixed check of repeating timers
++ - Syslog debug messages can be enabled with Define DEBUG_TIMER_INFO
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.3-1.3.38+.diff
++
++03.03.2006: v0.4 - Thomas G�nther <tom at toms-cafe.de>
++ - Adapted to vdr-1.3.44
++ - Removed setup parameter "Show timer-info"
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.44.diff
++
++26.03.2006:      - Tobias Grimm <tg at e-tobi.net>
++ - Adapted to vdr-1.3.45
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.45.diff
++
++14.01.2008:      - Thomas G�nther <tom at toms-cafe.de>
++ - Adapted to vdr-1.5.13
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.5.13.diff
++
++17.02.2008:      - Tobias Grimm <tg at e-tobi.net>
++ - Adapted to vdr-1.5.15
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.5.15.diff
++
++12.04.2008: v0.5 - Thomas G�nther <tom at toms-cafe.de>
++ - Fixed display of +/- sign with UTF-8
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.5-1.5.15.diff
++
++28.02.2010:      - Thomas G�nther <tom at toms-cafe.de>
++ - Adapted to vdr-1.7.13
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.5-1.7.13.diff
+diff -Naurp vdr-1.7.13/menu.c vdr-1.7.13-timer-info-0.5/menu.c
+--- vdr-1.7.13/menu.c	2010-02-21 14:09:19.000000000 +0000
++++ vdr-1.7.13-timer-info-0.5/menu.c	2010-02-28 18:24:26.000000000 +0000
+@@ -1010,8 +1010,10 @@ eOSState cMenuEditTimer::ProcessKey(eKey
+ class cMenuTimerItem : public cOsdItem {
+ private:
+   cTimer *timer;
++  char diskStatus;
+ public:
+   cMenuTimerItem(cTimer *Timer);
++  void SetDiskStatus(char DiskStatus);
+   virtual int Compare(const cListObject &ListObject) const;
+   virtual void Set(void);
+   cTimer *Timer(void) { return timer; }
+@@ -1020,6 +1022,7 @@ public:
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+   timer = Timer;
++  diskStatus = ' ';
+   Set();
+ }
+ 
+@@ -1050,7 +1053,10 @@ void cMenuTimerItem::Set(void)
+      File++;
+   else
+      File = timer->File();
+-  SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++  cCharSetConv csc("ISO-8859-1", cCharSetConv::SystemCharacterTable());
++  char diskStatusString[2] = { diskStatus, 0 };
++  SetText(cString::sprintf("%s%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++                    csc.Convert(diskStatusString),
+                     !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
+                     timer->Channel()->Number(),
+                     *name,
+@@ -1063,6 +1069,57 @@ void cMenuTimerItem::Set(void)
+                     File));
+ }
+ 
++void cMenuTimerItem::SetDiskStatus(char DiskStatus)
++{
++  diskStatus = DiskStatus;
++  Set();
++}
++
++// --- cTimerEntry -----------------------------------------------------------
++
++class cTimerEntry : public cListObject {
++private:
++  cMenuTimerItem *item;
++  const cTimer *timer;
++  time_t start;
++public:
++  cTimerEntry(cMenuTimerItem *item) : item(item), timer(item->Timer()), start(timer->StartTime()) {}
++  cTimerEntry(const cTimer *timer, time_t start) : item(NULL), timer(timer), start(start) {}
++  virtual int Compare(const cListObject &ListObject) const;
++  bool active(void) const { return timer->HasFlags(tfActive); }
++  time_t startTime(void) const { return start; }
++  int priority(void) const { return timer->Priority(); }
++  int duration(void) const;
++  bool repTimer(void) const { return !timer->IsSingleEvent(); }
++  bool isDummy(void) const { return item == NULL; }
++  const cTimer *Timer(void) const { return timer; }
++  void SetDiskStatus(char DiskStatus);
++  };
++
++int cTimerEntry::Compare(const cListObject &ListObject) const
++{
++  cTimerEntry *entry = (cTimerEntry *)&ListObject;
++  int r = startTime() - entry->startTime();
++  if (r == 0)
++     r = entry->priority() - priority();
++  return r;
++}
++
++int cTimerEntry::duration(void) const
++{
++  int dur = (timer->Stop()  / 100 * 60 + timer->Stop()  % 100) -
++            (timer->Start() / 100 * 60 + timer->Start() % 100);
++  if (dur < 0)
++     dur += 24 * 60;
++  return dur;
++}
++
++void cTimerEntry::SetDiskStatus(char DiskStatus)
++{
++  if (item)
++     item->SetDiskStatus(DiskStatus);
++}
++
+ // --- cMenuTimers -----------------------------------------------------------
+ 
+ class cMenuTimers : public cOsdMenu {
+@@ -1075,14 +1132,17 @@ private:
+   eOSState Info(void);
+   cTimer *CurrentTimer(void);
+   void SetHelpKeys(void);
++  void ActualiseDiskStatus(void);
++  bool actualiseDiskStatus;
+ public:
+   cMenuTimers(void);
+   virtual ~cMenuTimers();
++  virtual void Display(void);
+   virtual eOSState ProcessKey(eKeys Key);
+   };
+ 
+ cMenuTimers::cMenuTimers(void)
+-:cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
++:cOsdMenu(tr("Timers"), 3, CHNUMWIDTH, 10, 6, 6)
+ {
+   helpKeys = -1;
+   for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+@@ -1093,6 +1153,7 @@ cMenuTimers::cMenuTimers(void)
+   SetCurrent(First());
+   SetHelpKeys();
+   Timers.IncBeingEdited();
++  actualiseDiskStatus = true;
+ }
+ 
+ cMenuTimers::~cMenuTimers()
+@@ -1131,7 +1192,7 @@ eOSState cMenuTimers::OnOff(void)
+      timer->OnOff();
+      timer->SetEventFromSchedule();
+      RefreshCurrent();
+-     DisplayCurrent(true);
++     Display();
+      if (timer->FirstDay())
+         isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay());
+      else
+@@ -1190,6 +1251,67 @@ eOSState cMenuTimers::Info(void)
+   return osContinue;
+ }
+ 
++void cMenuTimers::ActualiseDiskStatus(void)
++{
++  if (!actualiseDiskStatus || !Count())
++     return;
++
++  // compute free disk space
++  int freeMB, freeMinutes, runshortMinutes;
++  VideoDiskSpace(&freeMB);
++  freeMinutes = int(double(freeMB) * 1.1 / MB_PER_MINUTE); // overestimate by 10 percent
++  runshortMinutes = freeMinutes / 5; // 20 Percent
++
++  // fill entries list
++  cTimerEntry *entry;
++  cList<cTimerEntry> entries;
++  for (cOsdItem *item = First(); item; item = Next(item))
++     entries.Add(new cTimerEntry((cMenuTimerItem *)item));
++
++  // search last start time
++  time_t last = 0;
++  for (entry = entries.First(); entry; entry = entries.Next(entry))
++     last = max(entry->startTime(), last);
++
++  // add entries for repeating timers
++  for (entry = entries.First(); entry; entry = entries.Next(entry))
++     if (entry->repTimer() && !entry->isDummy())
++        for (time_t start = cTimer::IncDay(entry->startTime(), 1);
++             start <= last;
++             start = cTimer::IncDay(start, 1))
++           if (entry->Timer()->DayMatches(start))
++              entries.Add(new cTimerEntry(entry->Timer(), start));
++
++  // set the disk-status
++  entries.Sort();
++  for (entry = entries.First(); entry; entry = entries.Next(entry)) {
++     char status = ' ';
++     if (entry->active()) {
++        freeMinutes -= entry->duration();
++        status = freeMinutes > runshortMinutes ? '+' : freeMinutes > 0 ? 177 /* +/- */ : '-';
++        }
++     entry->SetDiskStatus(status);
++#ifdef DEBUG_TIMER_INFO
++     dsyslog("timer-info: %c | %d | %s | %s | %3d | %+5d -> %+5d",
++             status,
++             entry->startTime(),
++             entry->active() ? "aktiv " : "n.akt.",
++             entry->repTimer() ? entry->isDummy() ? "  dummy  " : "mehrmalig" : "einmalig ",
++             entry->duration(),
++             entry->active() ? freeMinutes + entry->duration() : freeMinutes,
++             freeMinutes);
++#endif
++     }
++
++  actualiseDiskStatus = false;
++}
++
++void cMenuTimers::Display(void)
++{
++  ActualiseDiskStatus();
++  cOsdMenu::Display();
++}
++
+ eOSState cMenuTimers::ProcessKey(eKeys Key)
+ {
+   int TimerNumber = HasSubMenu() ? Count() : -1;
+@@ -1198,18 +1320,22 @@ eOSState cMenuTimers::ProcessKey(eKeys K
+   if (state == osUnknown) {
+      switch (Key) {
+        case kOk:     return Edit();
+-       case kRed:    state = OnOff(); break; // must go through SetHelpKeys()!
++       case kRed:    actualiseDiskStatus = true;
++                     state = OnOff(); break; // must go through SetHelpKeys()!
+        case kGreen:  return New();
+-       case kYellow: state = Delete(); break;
++       case kYellow: actualiseDiskStatus = true;
++                     state = Delete(); break;
+        case kInfo:
+        case kBlue:   return Info();
+                      break;
+        default: break;
+        }
+      }
+-  if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) {
+-     // a newly created timer was confirmed with Ok
+-     Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++  if (TimerNumber >= 0 && !HasSubMenu()) {
++     if (Timers.Get(TimerNumber)) // a newly created timer was confirmed with Ok
++        Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++     Sort();
++     actualiseDiskStatus = true;
+      Display();
+      }
+   if (Key != kNone)
diff --git a/vdr.epgsearch-exttimeredit-0.0.2.diff b/vdr.epgsearch-exttimeredit-0.0.2.diff
new file mode 100644
index 0000000..c92ff8c
--- /dev/null
+++ b/vdr.epgsearch-exttimeredit-0.0.2.diff
@@ -0,0 +1,110 @@
+--- menu.c.orig	2009-04-11 14:47:08.000000000 +0200
++++ menu.c	2009-04-17 13:53:05.000000000 +0200
+@@ -853,6 +853,7 @@ eOSState cMenuEditTimer::ProcessKey(eKey
+ class cMenuTimerItem : public cOsdItem {
+ private:
+   cTimer *timer;
++  void DoSet(void);
+ public:
+   cMenuTimerItem(cTimer *Timer);
+   virtual int Compare(const cListObject &ListObject) const;
+@@ -863,7 +864,7 @@ public:
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+   timer = Timer;
+-  Set();
++  DoSet();
+ }
+ 
+ int cMenuTimerItem::Compare(const cListObject &ListObject) const
+@@ -873,6 +874,18 @@ int cMenuTimerItem::Compare(const cListO
+ 
+ void cMenuTimerItem::Set(void)
+ {
++  // check for deleted timer
++  for (cTimer *t = Timers.First(); ; t = Timers.Next(t)) {
++     if (t == timer)
++       break;  // timer still there
++     if (t == NULL)
++       return; // no matching timer found
++     }
++  DoSet();
++}
++
++void cMenuTimerItem::DoSet(void)
++{
+   cString day, name("");
+   if (timer->WeekDays())
+      day = timer->PrintDay(0, timer->WeekDays(), false);
+@@ -906,8 +919,7 @@ void cMenuTimerItem::Set(void)
+ class cMenuTimers : public cOsdMenu {
+ private:
+   int helpKeys;
+-  eOSState Edit(void);
+-  eOSState New(void);
++  eOSState Edit(bool New = false);
+   eOSState Delete(void);
+   eOSState OnOff(void);
+   eOSState Info(void);
+@@ -980,19 +992,30 @@ eOSState cMenuTimers::OnOff(void)
+   return osContinue;
+ }
+ 
+-eOSState cMenuTimers::Edit(void)
++eOSState cMenuTimers::Edit(bool New)
+ {
+-  if (HasSubMenu() || Count() == 0)
++  if (HasSubMenu() || (Count() == 0 && !New))
+      return osContinue;
+-  isyslog("editing timer %s", *CurrentTimer()->ToDescr());
+-  return AddSubMenu(new cMenuEditTimer(CurrentTimer()));
+-}
++  if (!New)
++     isyslog("editing timer %s", *CurrentTimer()->ToDescr());
+ 
+-eOSState cMenuTimers::New(void)
+-{
+-  if (HasSubMenu())
+-     return osContinue;
+-  return AddSubMenu(new cMenuEditTimer(new cTimer, true));
++  // Data structure for service "Epgsearch-exttimeredit-v1.0"
++  struct Epgsearch_exttimeredit_v1_0
++  {
++    // in
++    cTimer* timer;          // pointer to the timer to edit
++    bool bNew;              // flag that indicates, if this is a new timer or an existing one
++    const cEvent* event;    // pointer to the event corresponding to this timer (may be NULL)
++    // out
++    cOsdMenu* pTimerMenu;   // pointer to the menu of results
++  } exttimeredit;
++  exttimeredit.timer = New ? (new cTimer) : CurrentTimer();
++  exttimeredit.bNew = New;
++  exttimeredit.event = exttimeredit.timer->Event();
++  if (cPluginManager::CallFirstService("Epgsearch-exttimeredit-v1.0", &exttimeredit))
++    return AddSubMenu(exttimeredit.pTimerMenu);
++
++  return AddSubMenu(new cMenuEditTimer(exttimeredit.timer, New));
+ }
+ 
+ eOSState cMenuTimers::Delete(void)
+@@ -1038,7 +1061,7 @@ eOSState cMenuTimers::ProcessKey(eKeys K
+      switch (Key) {
+        case kOk:     return Edit();
+        case kRed:    state = OnOff(); break; // must go through SetHelpKeys()!
+-       case kGreen:  return New();
++       case kGreen:  return Edit(true);
+        case kYellow: state = Delete(); break;
+        case kInfo:
+        case kBlue:   return Info();
+@@ -1051,6 +1074,11 @@ eOSState cMenuTimers::ProcessKey(eKeys K
+      Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
+      Display();
+      }
++  if (!HasSubMenu() && Timers.Count()<Count()) {
++     // timer was deleted
++     cOsdMenu::Del(Current());
++     Display();
++     }
+   if (Key != kNone)
+      SetHelpKeys();
+   return state;
-- 
cgit v0.10.2


	http://pkgs.fedoraproject.org/cgit/vdr.git/commit/?h=f22&id=a68bbe1835ba3dd7576901a8f465e185eb20ea38


More information about the scm-commits mailing list