From 7ade39597ac83a03395e86a8276fbb6dec6a6a2f Mon Sep 17 00:00:00 2001
From: Klaus Schmidinger <vdr@tvdr.de>
Date: Sat, 22 Jun 2002 10:11:59 +0200
Subject: Activated cutting

---
 HISTORY     |   1 +
 Makefile    |   4 +-
 cutter.c    | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 cutter.h    |  29 +++++++
 dvbplayer.c |  21 +----
 menu.c      |  15 ++--
 recording.c |  22 +++++-
 recording.h |   7 +-
 vdr.c       |  13 ++--
 9 files changed, 325 insertions(+), 40 deletions(-)
 create mode 100644 cutter.c
 create mode 100644 cutter.h

diff --git a/HISTORY b/HISTORY
index 0df6b8a1..00a64394 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1351,3 +1351,4 @@ Video Disk Recorder Revision History
 2002-06-22: Version 1.1.4
 
 - Added Hungarian language texts (thanks to Istvan Koenigsberger and Guido Josten).
+- Activated cutting.
diff --git a/Makefile b/Makefile
index 09654b21..f3b37504 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Makefile 1.40 2002/06/10 16:31:34 kls Exp $
+# $Id: Makefile 1.41 2002/06/22 09:49:29 kls Exp $
 
 .DELETE_ON_ERROR:
 
@@ -21,7 +21,7 @@ INCLUDES = -I$(DVBDIR)/ost/include
 
 DTVLIB   = $(DTVDIR)/libdtv.a
 
-OBJS = audio.o config.o device.o dvbplayer.o dvbosd.o eit.o eitscan.o font.o i18n.o\
+OBJS = audio.o config.o cutter.o device.o dvbplayer.o dvbosd.o eit.o eitscan.o font.o i18n.o\
        interface.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o receiver.o\
        recorder.o recording.o remote.o remux.o ringbuffer.o status.o svdrp.o thread.o\
        tools.o vdr.o videodir.o
diff --git a/cutter.c b/cutter.c
new file mode 100644
index 00000000..2b623ed8
--- /dev/null
+++ b/cutter.c
@@ -0,0 +1,253 @@
+/*
+ * cutter.c: The video cutting facilities
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: cutter.c 1.1 2002/06/22 10:09:34 kls Exp $
+ */
+
+#include "cutter.h"
+#include "recording.h"
+#include "remux.h"
+#include "thread.h"
+#include "videodir.h"
+
+// --- cCuttingThread --------------------------------------------------------
+
+class cCuttingThread : public cThread {
+private:
+  const char *error;
+  bool active;
+  int fromFile, toFile;
+  cFileName *fromFileName, *toFileName;
+  cIndexFile *fromIndex, *toIndex;
+  cMarks fromMarks, toMarks;
+protected:
+  virtual void Action(void);
+public:
+  cCuttingThread(const char *FromFileName, const char *ToFileName);
+  virtual ~cCuttingThread();
+  const char *Error(void) { return error; }
+  };
+
+cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
+{
+  error = NULL;
+  active = false;
+  fromFile = toFile = -1;
+  fromFileName = toFileName = NULL;
+  fromIndex = toIndex = NULL;
+  if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
+     fromFileName = new cFileName(FromFileName, false, true);
+     toFileName = new cFileName(ToFileName, true, true);
+     fromIndex = new cIndexFile(FromFileName, false);
+     toIndex = new cIndexFile(ToFileName, true);
+     toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
+     Start();
+     }
+  else
+     esyslog("no editing marks found for %s", FromFileName);
+}
+
+cCuttingThread::~cCuttingThread()
+{
+  active = false;
+  Cancel(3);
+  delete fromFileName;
+  delete toFileName;
+  delete fromIndex;
+  delete toIndex;
+}
+
+void cCuttingThread::Action(void)
+{
+  dsyslog("video cutting thread started (pid=%d)", getpid());
+
+  cMark *Mark = fromMarks.First();
+  if (Mark) {
+     fromFile = fromFileName->Open();
+     toFile = toFileName->Open();
+     active = fromFile >= 0 && toFile >= 0;
+     int Index = Mark->position;
+     Mark = fromMarks.Next(Mark);
+     int FileSize = 0;
+     int CurrentFileNumber = 0;
+     int LastIFrame = 0;
+     toMarks.Add(0);
+     toMarks.Save();
+     uchar buffer[MAXFRAMESIZE];
+     while (active) {
+           uchar FileNumber;
+           int FileOffset, Length;
+           uchar PictureType;
+
+           // Make sure there is enough disk space:
+
+           AssertFreeDiskSpace();
+
+           // Read one frame:
+
+           if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
+              if (FileNumber != CurrentFileNumber) {
+                 fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+                 CurrentFileNumber = FileNumber;
+                 }
+              if (fromFile >= 0) {
+                 int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer));
+                 if (len < 0) {
+                    error = "ReadFrame";
+                    break;
+                    }
+                 if (len != Length) {
+                    CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer
+                    Length = len;
+                    }
+                 }
+              else {
+                 error = "fromFile";
+                 break;
+                 }
+              }
+           else
+              break;
+
+           // Write one frame:
+
+           if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
+              if (!Mark) // edited version shall end before next I-frame
+                 break;
+              if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
+                 toFile = toFileName->NextFile();
+                 if (toFile < 0) {
+                    error = "toFile 1";
+                    break;
+                    }
+                 FileSize = 0;
+                 }
+              LastIFrame = 0;
+              }
+           if (safe_write(toFile, buffer, Length) < 0) {
+              error = "safe_write";
+              break;
+              }
+           if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) {
+              error = "toIndex";
+              break;
+              }
+           FileSize += Length;
+           if (!LastIFrame)
+              LastIFrame = toIndex->Last();
+
+           // Check editing marks:
+
+           if (Mark && Index >= Mark->position) {
+              Mark = fromMarks.Next(Mark);
+              toMarks.Add(LastIFrame);
+              if (Mark)
+                 toMarks.Add(toIndex->Last() + 1);
+              toMarks.Save();
+              if (Mark) {
+                 Index = Mark->position;
+                 Mark = fromMarks.Next(Mark);
+                 CurrentFileNumber = 0; // triggers SetOffset before reading next frame
+                 if (Setup.SplitEditedFiles) {
+                    toFile = toFileName->NextFile();
+                    if (toFile < 0) {
+                       error = "toFile 2";
+                       break;
+                       }
+                    FileSize = 0;
+                    }
+                 }
+              // the 'else' case (i.e. 'final end mark reached') is handled above
+              // in 'Write one frame', so that the edited version will end right
+              // before the next I-frame.
+              }
+           }
+     }
+  else
+     esyslog("no editing marks found!");
+  dsyslog("end video cutting thread");
+}
+
+// --- cCutter ---------------------------------------------------------------
+
+char *cCutter::editedVersionName = NULL;
+cCuttingThread *cCutter::cuttingThread = NULL;
+bool cCutter::error = false;
+bool cCutter::ended = false;
+
+bool cCutter::Start(const char *FileName)
+{
+  if (!cuttingThread) {
+     error = false;
+     ended = false;
+     cRecording Recording(FileName);
+     const char *evn = Recording.PrefixFileName('%');
+     if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
+        // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
+        // remove a possible deleted recording with the same name to avoid symlink mixups:
+        char *s = strdup(evn);
+        char *e = strrchr(s, '.');
+        if (e) {
+           if (strcmp(e, ".rec") == 0) {
+              strcpy(e, ".del");
+              RemoveVideoFile(s);
+              }
+           }
+        delete s;
+        // XXX
+        editedVersionName = strdup(evn);
+        Recording.WriteSummary();
+        cuttingThread = new cCuttingThread(FileName, editedVersionName);
+        return true;
+        }
+     }
+  return false;
+}
+
+void cCutter::Stop(void)
+{
+  bool Interrupted = cuttingThread && cuttingThread->Active();
+  const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
+  delete cuttingThread;
+  cuttingThread = NULL;
+  if ((Interrupted || Error) && editedVersionName) {
+     if (Interrupted)
+        isyslog("editing process has been interrupted");
+     if (Error)
+        esyslog("ERROR: '%s' during editing process", Error);
+     RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
+     }
+}
+
+bool cCutter::Active(void)
+{
+  if (cuttingThread) {
+     if (cuttingThread->Active())
+        return true;
+     error = cuttingThread->Error();
+     Stop();
+     if (!error)
+        cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName);
+     delete editedVersionName;
+     editedVersionName = NULL;
+     ended = true;
+     }
+  return false;
+}
+
+bool cCutter::Error(void)
+{
+  bool result = error;
+  error = false;
+  return result;
+}
+
+bool cCutter::Ended(void)
+{
+  bool result = ended;
+  ended = false;
+  return result;
+}
diff --git a/cutter.h b/cutter.h
new file mode 100644
index 00000000..8275281f
--- /dev/null
+++ b/cutter.h
@@ -0,0 +1,29 @@
+/*
+ * cutter.h: The video cutting facilities
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: cutter.h 1.1 2002/06/22 10:03:15 kls Exp $
+ */
+
+#ifndef __CUTTER_H
+#define __CUTTER_H
+
+class cCuttingThread;
+
+class cCutter {
+private:
+  static char *editedVersionName;
+  static cCuttingThread *cuttingThread;
+  static bool error;
+  static bool ended;
+public:
+  static bool Start(const char *FileName);
+  static void Stop(void);
+  static bool Active(void);
+  static bool Error(void);
+  static bool Ended(void);
+  };
+
+#endif //__CUTTER_H
diff --git a/dvbplayer.c b/dvbplayer.c
index 10fdf4b5..f18eb84e 100644
--- a/dvbplayer.c
+++ b/dvbplayer.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbplayer.c 1.1 2002/06/16 10:59:45 kls Exp $
+ * $Id: dvbplayer.c 1.2 2002/06/22 10:11:59 kls Exp $
  */
 
 #include "dvbplayer.h"
@@ -13,22 +13,6 @@
 #include "ringbuffer.h"
 #include "thread.h"
 
-// --- ReadFrame -------------------------------------------------------------
-
-int ReadFrame(int f, uchar *b, int Length, int Max)
-{
-  if (Length == -1)
-     Length = Max; // this means we read up to EOF (see cIndex)
-  else if (Length > Max) {
-     esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
-     Length = Max;
-     }
-  int r = safe_read(f, b, Length);
-  if (r < 0)
-     LOG_ERROR;
-  return r;
-}
-
 // --- cBackTrace ----------------------------------------------------------
 
 #define AVG_FRAME_SIZE 15000         // an assumption about the average frame size
@@ -91,9 +75,6 @@ int cBackTrace::Get(bool Forward)
 // (must be larger than MINVIDEODATA - see remux.h)
 #define VIDEOBUFSIZE  MEGABYTE(1)
 
-// The maximum size of a single frame:
-#define MAXFRAMESIZE  KILOBYTE(192)
-
 // The number of frames to back up when resuming an interrupted replay session:
 #define RESUMEBACKUP (10 * FRAMESPERSEC)
 
diff --git a/menu.c b/menu.c
index ff63fbcd..29703da1 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.c 1.197 2002/06/16 13:23:51 kls Exp $
+ * $Id: menu.c 1.198 2002/06/22 09:55:58 kls Exp $
  */
 
 #include "menu.h"
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "config.h"
+#include "cutter.h"
 #include "eit.h"
 #include "i18n.h"
 #include "menuitems.h"
@@ -2026,10 +2027,8 @@ void cMenuMain::Set(void)
 
   // Editing control:
 
-  /*XXX+
-  if (cVideoCutter::Active())
+  if (cCutter::Active())
      Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
-     XXX*/
 
   // Color buttons:
 
@@ -2063,7 +2062,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
                           }
                        break;
     case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
-                          //XXX+cVideoCutter::Stop();
+                          cCutter::Stop();
                           return osEnd;
                           }
                        break;
@@ -2973,11 +2972,10 @@ void cReplayControl::MarkMove(bool Forward)
 
 void cReplayControl::EditCut(void)
 {
-  /*XXX+
   if (fileName) {
      Hide();
-     if (!cVideoCutter::Active()) {
-        if (!cVideoCutter::Start(fileName))
+     if (!cCutter::Active()) {
+        if (!cCutter::Start(fileName))
            Interface->Error(tr("Can't start editing process!"));
         else
            Interface->Info(tr("Editing process started"));
@@ -2986,7 +2984,6 @@ void cReplayControl::EditCut(void)
         Interface->Error(tr("Editing process already active!"));
      ShowMode();
      }
-     XXX*/
 }
 
 void cReplayControl::EditTest(void)
diff --git a/recording.c b/recording.c
index 7bc896c5..7ebec5c9 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.c 1.63 2002/06/16 11:29:27 kls Exp $
+ * $Id: recording.c 1.64 2002/06/22 10:11:49 kls Exp $
  */
 
 #include "recording.h"
@@ -1056,6 +1056,8 @@ int cFileName::NextFile(void)
   return SetOffset(fileNumber + 1);
 }
 
+// --- Index stuff -----------------------------------------------------------
+
 const char *IndexToHMSF(int Index, bool WithFrame)
 {
   static char buffer[16];
@@ -1080,3 +1082,21 @@ int SecondsToFrames(int Seconds)
 {
   return Seconds * FRAMESPERSEC;
 }
+
+// --- ReadFrame -------------------------------------------------------------
+
+int ReadFrame(int f, uchar *b, int Length, int Max)
+{
+  if (Length == -1)
+     Length = Max; // this means we read up to EOF (see cIndex)
+  else if (Length > Max) {
+     esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
+     Length = Max;
+     }
+  int r = safe_read(f, b, Length);
+  if (r < 0)
+     LOG_ERROR;
+  return r;
+}
+
+
diff --git a/recording.h b/recording.h
index 16c6506f..2fae49ee 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: recording.h 1.23 2002/06/16 11:29:47 kls Exp $
+ * $Id: recording.h 1.24 2002/06/22 10:09:27 kls Exp $
  */
 
 #ifndef __RECORDING_H
@@ -107,6 +107,9 @@ public:
 //XXX+
 #define FRAMESPERSEC 25
 
+// The maximum size of a single frame:
+#define MAXFRAMESIZE  KILOBYTE(192)
+
 // The maximum file size is limited by the range that can be covered
 // with 'int'. 4GB might be possible (if the range is considered
 // 'unsigned'), 2GB should be possible (even if the range is considered
@@ -163,4 +166,6 @@ int HMSFToIndex(const char *HMSF);
 int SecondsToFrames(int Seconds); //XXX+ ->player???
       // Returns the number of frames corresponding to the given number of seconds.
 
+int ReadFrame(int f, uchar *b, int Length, int Max);
+
 #endif //__RECORDING_H
diff --git a/vdr.c b/vdr.c
index bef3919f..9423219f 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
  *
  * The project's page is at http://www.cadsoft.de/people/kls/vdr
  *
- * $Id: vdr.c 1.114 2002/06/16 11:30:28 kls Exp $
+ * $Id: vdr.c 1.115 2002/06/22 09:56:12 kls Exp $
  */
 
 #include <getopt.h>
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "config.h"
+#include "cutter.h"
 #include "device.h"
 #include "eitscan.h"
 #include "i18n.h"
@@ -529,16 +530,14 @@ int main(int argc, char *argv[])
           }
         if (!Menu) {
            EITScanner.Process();
-           /*XXX+
-           if (!cVideoCutter::Active() && cVideoCutter::Ended()) {
-              if (cVideoCutter::Error())
+           if (!cCutter::Active() && cCutter::Ended()) {
+              if (cCutter::Error())
                  Interface->Error(tr("Editing process failed!"));
               else
                  Interface->Info(tr("Editing process finished"));
               }
-              XXX*/
            }
-        if (!*Interact && ((!cRecordControls::Active() /*XXX+&& !cVideoCutter::Active()XXX*/) || ForceShutdown)) {
+        if (!*Interact && ((!cRecordControls::Active() && !cCutter::Active()) || ForceShutdown)) {
            time_t Now = time(NULL);
            if (Now - LastActivity > ACTIVITYTIMEOUT) {
               // Shutdown:
@@ -598,7 +597,7 @@ int main(int argc, char *argv[])
   if (Interrupted)
      isyslog("caught signal %d", Interrupted);
   cRecordControls::Shutdown();
-  //XXX+cVideoCutter::Stop();
+  cCutter::Stop();
   delete Menu;
   delete ReplayControl;
   delete Interface;
-- 
cgit v1.2.3