From 7ade39597ac83a03395e86a8276fbb6dec6a6a2f Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sat, 22 Jun 2002 10:11:59 +0200 Subject: Activated cutting --- cutter.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 cutter.c (limited to 'cutter.c') 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; +} -- cgit v1.2.3