Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

cutter.c

Go to the documentation of this file.
00001 /*
00002  * cutter.c: The video cutting facilities
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: cutter.c 1.2 2002/08/11 11:09:23 kls Exp $
00008  */
00009 
00010 #include "cutter.h"
00011 #include "recording.h"
00012 #include "remux.h"
00013 #include "thread.h"
00014 #include "videodir.h"
00015 
00016 // --- cCuttingThread --------------------------------------------------------
00017 
00018 class cCuttingThread : public cThread {
00019 private:
00020   const char *error;
00021   bool active;
00022   int fromFile, toFile;
00023   cFileName *fromFileName, *toFileName;
00024   cIndexFile *fromIndex, *toIndex;
00025   cMarks fromMarks, toMarks;
00026 protected:
00027   virtual void Action(void);
00028 public:
00029   cCuttingThread(const char *FromFileName, const char *ToFileName);
00030   virtual ~cCuttingThread();
00031   const char *Error(void) { return error; }
00032   };
00033 
00034 cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
00035 {
00036   error = NULL;
00037   active = false;
00038   fromFile = toFile = -1;
00039   fromFileName = toFileName = NULL;
00040   fromIndex = toIndex = NULL;
00041   if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
00042      fromFileName = new cFileName(FromFileName, false, true);
00043      toFileName = new cFileName(ToFileName, true, true);
00044      fromIndex = new cIndexFile(FromFileName, false);
00045      toIndex = new cIndexFile(ToFileName, true);
00046      toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
00047      Start();
00048      }
00049   else
00050      esyslog("no editing marks found for %s", FromFileName);
00051 }
00052 
00053 cCuttingThread::~cCuttingThread()
00054 {
00055   active = false;
00056   Cancel(3);
00057   delete fromFileName;
00058   delete toFileName;
00059   delete fromIndex;
00060   delete toIndex;
00061 }
00062 
00063 void cCuttingThread::Action(void)
00064 {
00065   dsyslog("video cutting thread started (pid=%d)", getpid());
00066 
00067   cMark *Mark = fromMarks.First();
00068   if (Mark) {
00069      fromFile = fromFileName->Open();
00070      toFile = toFileName->Open();
00071      active = fromFile >= 0 && toFile >= 0;
00072      int Index = Mark->position;
00073      Mark = fromMarks.Next(Mark);
00074      int FileSize = 0;
00075      int CurrentFileNumber = 0;
00076      int LastIFrame = 0;
00077      toMarks.Add(0);
00078      toMarks.Save();
00079      uchar buffer[MAXFRAMESIZE];
00080      while (active) {
00081            uchar FileNumber;
00082            int FileOffset, Length;
00083            uchar PictureType;
00084 
00085            // Make sure there is enough disk space:
00086 
00087            AssertFreeDiskSpace();
00088 
00089            // Read one frame:
00090 
00091            if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
00092               if (FileNumber != CurrentFileNumber) {
00093                  fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
00094                  CurrentFileNumber = FileNumber;
00095                  }
00096               if (fromFile >= 0) {
00097                  int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer));
00098                  if (len < 0) {
00099                     error = "ReadFrame";
00100                     break;
00101                     }
00102                  if (len != Length) {
00103                     CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer
00104                     Length = len;
00105                     }
00106                  }
00107               else {
00108                  error = "fromFile";
00109                  break;
00110                  }
00111               }
00112            else
00113               break;
00114 
00115            // Write one frame:
00116 
00117            if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
00118               if (!Mark) // edited version shall end before next I-frame
00119                  break;
00120               if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
00121                  toFile = toFileName->NextFile();
00122                  if (toFile < 0) {
00123                     error = "toFile 1";
00124                     break;
00125                     }
00126                  FileSize = 0;
00127                  }
00128               LastIFrame = 0;
00129               }
00130            if (safe_write(toFile, buffer, Length) < 0) {
00131               error = "safe_write";
00132               break;
00133               }
00134            if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) {
00135               error = "toIndex";
00136               break;
00137               }
00138            FileSize += Length;
00139            if (!LastIFrame)
00140               LastIFrame = toIndex->Last();
00141 
00142            // Check editing marks:
00143 
00144            if (Mark && Index >= Mark->position) {
00145               Mark = fromMarks.Next(Mark);
00146               toMarks.Add(LastIFrame);
00147               if (Mark)
00148                  toMarks.Add(toIndex->Last() + 1);
00149               toMarks.Save();
00150               if (Mark) {
00151                  Index = Mark->position;
00152                  Mark = fromMarks.Next(Mark);
00153                  CurrentFileNumber = 0; // triggers SetOffset before reading next frame
00154                  if (Setup.SplitEditedFiles) {
00155                     toFile = toFileName->NextFile();
00156                     if (toFile < 0) {
00157                        error = "toFile 2";
00158                        break;
00159                        }
00160                     FileSize = 0;
00161                     }
00162                  }
00163               // the 'else' case (i.e. 'final end mark reached') is handled above
00164               // in 'Write one frame', so that the edited version will end right
00165               // before the next I-frame.
00166               }
00167            }
00168      }
00169   else
00170      esyslog("no editing marks found!");
00171   dsyslog("end video cutting thread");
00172 }
00173 
00174 // --- cCutter ---------------------------------------------------------------
00175 
00176 char *cCutter::editedVersionName = NULL;
00177 cCuttingThread *cCutter::cuttingThread = NULL;
00178 bool cCutter::error = false;
00179 bool cCutter::ended = false;
00180 
00181 bool cCutter::Start(const char *FileName)
00182 {
00183   if (!cuttingThread) {
00184      error = false;
00185      ended = false;
00186      cRecording Recording(FileName);
00187      const char *evn = Recording.PrefixFileName('%');
00188      if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
00189         // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
00190         // remove a possible deleted recording with the same name to avoid symlink mixups:
00191         char *s = strdup(evn);
00192         char *e = strrchr(s, '.');
00193         if (e) {
00194            if (strcmp(e, ".rec") == 0) {
00195               strcpy(e, ".del");
00196               RemoveVideoFile(s);
00197               }
00198            }
00199         free(s);
00200         // XXX
00201         editedVersionName = strdup(evn);
00202         Recording.WriteSummary();
00203         cuttingThread = new cCuttingThread(FileName, editedVersionName);
00204         return true;
00205         }
00206      }
00207   return false;
00208 }
00209 
00210 void cCutter::Stop(void)
00211 {
00212   bool Interrupted = cuttingThread && cuttingThread->Active();
00213   const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
00214   delete cuttingThread;
00215   cuttingThread = NULL;
00216   if ((Interrupted || Error) && editedVersionName) {
00217      if (Interrupted)
00218         isyslog("editing process has been interrupted");
00219      if (Error)
00220         esyslog("ERROR: '%s' during editing process", Error);
00221      RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
00222      }
00223 }
00224 
00225 bool cCutter::Active(void)
00226 {
00227   if (cuttingThread) {
00228      if (cuttingThread->Active())
00229         return true;
00230      error = cuttingThread->Error();
00231      Stop();
00232      if (!error)
00233         cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName);
00234      free(editedVersionName);
00235      editedVersionName = NULL;
00236      ended = true;
00237      }
00238   return false;
00239 }
00240 
00241 bool cCutter::Error(void)
00242 {
00243   bool result = error;
00244   error = false;
00245   return result;
00246 }
00247 
00248 bool cCutter::Ended(void)
00249 {
00250   bool result = ended;
00251   ended = false;
00252   return result;
00253 }

Generated on Wed Feb 5 23:30:07 2003 for VDR by doxygen1.3-rc2