summaryrefslogtreecommitdiff
path: root/cutter.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2012-11-18 12:19:51 +0100
committerKlaus Schmidinger <vdr@tvdr.de>2012-11-18 12:19:51 +0100
commitcca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6 (patch)
tree21b5f90cfe4143d1fc2188663d671b7f92f05670 /cutter.c
parent5b4e1fa793506405d0d8bac47a36640a06340c80 (diff)
downloadvdr-cca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6.tar.gz
vdr-cca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6.tar.bz2
Improved editing TS recordings
Diffstat (limited to 'cutter.c')
-rw-r--r--cutter.c650
1 files changed, 487 insertions, 163 deletions
diff --git a/cutter.c b/cutter.c
index a0e2b477..ace1893b 100644
--- a/cutter.c
+++ b/cutter.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: cutter.c 2.15 2012/10/04 12:19:37 kls Exp $
+ * $Id: cutter.c 2.16 2012/11/18 12:09:00 kls Exp $
*/
#include "cutter.h"
@@ -13,17 +13,261 @@
#include "remux.h"
#include "videodir.h"
+// --- cPacketBuffer ---------------------------------------------------------
+
+class cPacketBuffer {
+private:
+ uchar *data;
+ int size;
+ int length;
+public:
+ cPacketBuffer(void);
+ ~cPacketBuffer();
+ void Append(uchar *Data, int Length);
+ ///< Appends Length bytes of Data to this packet buffer.
+ void Flush(uchar *Data, int &Length, int MaxLength);
+ ///< Flushes the content of this packet buffer into the given Data, starting
+ ///< at position Length, and clears the buffer afterwards. Length will be
+ ///< incremented accordingly. If Length plus the total length of the stored
+ ///< packets would exceed MaxLength, nothing is copied.
+ };
+
+cPacketBuffer::cPacketBuffer(void)
+{
+ data = NULL;
+ size = length = 0;
+}
+
+cPacketBuffer::~cPacketBuffer()
+{
+ free(data);
+}
+
+void cPacketBuffer::Append(uchar *Data, int Length)
+{
+ if (length + Length >= size) {
+ int NewSize = (length + Length) * 3 / 2;
+ if (uchar *p = (uchar *)realloc(data, NewSize)) {
+ data = p;
+ size = NewSize;
+ }
+ else
+ return; // out of memory
+ }
+ memcpy(data + length, Data, Length);
+ length += Length;
+}
+
+void cPacketBuffer::Flush(uchar *Data, int &Length, int MaxLength)
+{
+ if (Data && length > 0 && Length + length <= MaxLength) {
+ memcpy(Data + Length, data, length);
+ Length += length;
+ }
+ length = 0;
+}
+
+// --- cPacketStorage --------------------------------------------------------
+
+class cPacketStorage {
+private:
+ cPacketBuffer *buffers[MAXPID];
+public:
+ cPacketStorage(void);
+ ~cPacketStorage();
+ void Append(int Pid, uchar *Data, int Length);
+ void Flush(int Pid, uchar *Data, int &Length, int MaxLength);
+ };
+
+cPacketStorage::cPacketStorage(void)
+{
+ for (int i = 0; i < MAXPID; i++)
+ buffers[i] = NULL;
+}
+
+cPacketStorage::~cPacketStorage()
+{
+ for (int i = 0; i < MAXPID; i++)
+ delete buffers[i];
+}
+
+void cPacketStorage::Append(int Pid, uchar *Data, int Length)
+{
+ if (!buffers[Pid])
+ buffers[Pid] = new cPacketBuffer;
+ buffers[Pid]->Append(Data, Length);
+}
+
+void cPacketStorage::Flush(int Pid, uchar *Data, int &Length, int MaxLength)
+{
+ if (buffers[Pid])
+ buffers[Pid]->Flush(Data, Length, MaxLength);
+}
+
+// --- cDanglingPacketStripper -----------------------------------------------
+
+class cDanglingPacketStripper {
+private:
+ bool processed[MAXPID];
+ cPatPmtParser patPmtParser;
+public:
+ cDanglingPacketStripper(void);
+ bool Process(uchar *Data, int Length, int64_t FirstPts);
+ ///< Scans the frame given in Data and hides the payloads of any TS packets
+ ///< that either didn't start within this frame, or have a PTS that is
+ ///< before FirstPts. The TS packets in question are not physically removed
+ ///< from Data in order to keep any frame counts and PCR timestamps intact.
+ ///< Returns true if any dangling packets have been found.
+ };
+
+cDanglingPacketStripper::cDanglingPacketStripper(void)
+{
+ memset(processed, 0x00, sizeof(processed));
+}
+
+bool cDanglingPacketStripper::Process(uchar *Data, int Length, int64_t FirstPts)
+{
+ bool Found = false;
+ while (Length >= TS_SIZE && *Data == TS_SYNC_BYTE) {
+ int Pid = TsPid(Data);
+ if (Pid == PATPID)
+ patPmtParser.ParsePat(Data, TS_SIZE);
+ else if (Pid == patPmtParser.PmtPid())
+ patPmtParser.ParsePmt(Data, TS_SIZE);
+ else {
+ int64_t Pts = TsGetPts(Data, TS_SIZE);
+ if (Pts >= 0)
+ processed[Pid] = PtsDiff(FirstPts, Pts) >= 0; // Pts is at or after FirstPts
+ if (!processed[Pid]) {
+ TsHidePayload(Data);
+ Found = true;
+ }
+ }
+ Length -= TS_SIZE;
+ Data += TS_SIZE;
+ }
+ return Found;
+}
+
+// --- cPtsFixer -------------------------------------------------------------
+
+class cPtsFixer {
+private:
+ int delta; // time between two frames
+ int64_t last; // the last (i.e. highest) video PTS value seen
+ int64_t offset; // offset to add to PTS values
+ bool fixCounters; // controls fixing the TS continuity counters (only from the second CutIn up)
+ uchar counter[MAXPID]; // the TS continuity counter for each PID
+ cPatPmtParser patPmtParser;
+public:
+ cPtsFixer(void);
+ void Setup(double FramesPerSecond);
+ void Fix(uchar *Data, int Length, bool CutIn);
+ };
+
+cPtsFixer::cPtsFixer(void)
+{
+ delta = 0;
+ last = -1;
+ offset = -1;
+ fixCounters = false;
+ memset(counter, 0x00, sizeof(counter));
+}
+
+void cPtsFixer::Setup(double FramesPerSecond)
+{
+ delta = int(round(PTSTICKS / FramesPerSecond));
+}
+
+void cPtsFixer::Fix(uchar *Data, int Length, bool CutIn)
+{
+ if (!patPmtParser.Vpid()) {
+ if (!patPmtParser.ParsePatPmt(Data, Length))
+ return;
+ }
+ // Determine the PTS offset at the beginning of each sequence (except the first one):
+ if (CutIn && last >= 0) {
+ int64_t Pts = TsGetPts(Data, Length);
+ if (Pts >= 0) {
+ // offset is calculated so that Pts + offset results in last + delta:
+ offset = Pts - PtsAdd(last, delta);
+ if (offset <= 0)
+ offset = -offset;
+ else
+ offset = MAX33BIT + 1 - offset;
+ }
+ fixCounters = true;
+ }
+ // Keep track of the highest video PTS:
+ uchar *p = Data;
+ int len = Length;
+ while (len >= TS_SIZE && *p == TS_SYNC_BYTE) {
+ int Pid = TsPid(p);
+ if (Pid == patPmtParser.Vpid()) {
+ int64_t Pts = PtsAdd(TsGetPts(p, TS_SIZE), offset); // offset is taken into account here, to make last have the "new" value already!
+ if (Pts >= 0 && (last < 0 || PtsDiff(last, Pts) > 0))
+ last = Pts;
+ }
+ // Adjust the TS continuity counter:
+ if (fixCounters) {
+ counter[Pid] = (counter[Pid] + 1) & TS_CONT_CNT_MASK;
+ TsSetContinuityCounter(p, counter[Pid]);
+ }
+ else
+ counter[Pid] = TsGetContinuityCounter(p); // collect initial counters
+ p += TS_SIZE;
+ len -= TS_SIZE;
+ }
+ // Apply the PTS offset:
+ if (offset > 0) {
+ uchar *p = Data;
+ int len = Length;
+ while (len >= TS_SIZE && *p == TS_SYNC_BYTE) {
+ // Adjust the various timestamps:
+ int64_t Pts = TsGetPts(p, TS_SIZE);
+ if (Pts >= 0)
+ TsSetPts(p, TS_SIZE, PtsAdd(Pts, offset));
+ int64_t Dts = TsGetDts(p, TS_SIZE);
+ if (Dts >= 0)
+ TsSetDts(p, TS_SIZE, PtsAdd(Dts, offset));
+ int64_t Pcr = TsGetPcr(p);
+ if (Pcr >= 0) {
+ int64_t NewPcr = Pcr + offset * PCRFACTOR;
+ if (NewPcr >= MAX27MHZ)
+ NewPcr -= MAX27MHZ + 1;
+ TsSetPcr(p, NewPcr);
+ }
+ p += TS_SIZE;
+ len -= TS_SIZE;
+ }
+ }
+}
+
// --- cCuttingThread --------------------------------------------------------
class cCuttingThread : public cThread {
private:
const char *error;
bool isPesRecording;
+ double framesPerSecond;
cUnbufferedFile *fromFile, *toFile;
cFileName *fromFileName, *toFileName;
cIndexFile *fromIndex, *toIndex;
cMarks fromMarks, toMarks;
+ int numSequences;
off_t maxVideoFileSize;
+ off_t fileSize;
+ cPtsFixer ptsFixer;
+ bool suspensionLogged;
+ bool Throttled(void);
+ bool SwitchFile(bool Force = false);
+ bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
+ bool FramesAreEqual(int Index1, int Index2);
+ void GetPendingPackets(uchar *Buffer, int &Length, int Index, int64_t LastPts);
+ // Gather all non-video TS packets from Index upward that either belong to
+ // payloads that started before Index, or have a PTS that is before LastPts,
+ // and add them to the end of the given Data.
+ bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
protected:
virtual void Action(void);
public:
@@ -41,16 +285,25 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
fromIndex = toIndex = NULL;
cRecording Recording(FromFileName);
isPesRecording = Recording.IsPesRecording();
- if (fromMarks.Load(FromFileName, Recording.FramesPerSecond(), isPesRecording) && fromMarks.Count()) {
- fromFileName = new cFileName(FromFileName, false, true, isPesRecording);
- toFileName = new cFileName(ToFileName, true, true, isPesRecording);
- fromIndex = new cIndexFile(FromFileName, false, isPesRecording);
- toIndex = new cIndexFile(ToFileName, true, isPesRecording);
- toMarks.Load(ToFileName, Recording.FramesPerSecond(), isPesRecording); // doesn't actually load marks, just sets the file name
- maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
- if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
- maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
- Start();
+ framesPerSecond = Recording.FramesPerSecond();
+ suspensionLogged = false;
+ fileSize = 0;
+ ptsFixer.Setup(framesPerSecond);
+ if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) {
+ numSequences = fromMarks.GetNumSequences();
+ if (numSequences > 0) {
+ fromFileName = new cFileName(FromFileName, false, true, isPesRecording);
+ toFileName = new cFileName(ToFileName, true, true, isPesRecording);
+ fromIndex = new cIndexFile(FromFileName, false, isPesRecording);
+ toIndex = new cIndexFile(ToFileName, true, isPesRecording);
+ toMarks.Load(ToFileName, framesPerSecond, isPesRecording); // doesn't actually load marks, just sets the file name
+ maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
+ if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
+ maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
+ Start();
+ }
+ else
+ esyslog("no editing sequences found for %s", FromFileName);
}
else
esyslog("no editing marks found for %s", FromFileName);
@@ -65,169 +318,236 @@ cCuttingThread::~cCuttingThread()
delete toIndex;
}
+bool cCuttingThread::Throttled(void)
+{
+ if (cIoThrottle::Engaged()) {
+ if (!suspensionLogged) {
+ dsyslog("suspending cutter thread");
+ suspensionLogged = true;
+ }
+ return true;
+ }
+ else if (suspensionLogged) {
+ dsyslog("resuming cutter thread");
+ suspensionLogged = false;
+ }
+ return false;
+}
+
+bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
+{
+ uint16_t FileNumber;
+ off_t FileOffset;
+ if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
+ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+ if (fromFile) {
+ fromFile->SetReadAhead(MEGABYTE(20));
+ int len = ReadFrame(fromFile, Buffer, Length, MAXFRAMESIZE);
+ if (len < 0)
+ error = "ReadFrame";
+ else if (len != Length)
+ Length = len;
+ return error == NULL;
+ }
+ else
+ error = "fromFile";
+ }
+ return false;
+}
+
+bool cCuttingThread::SwitchFile(bool Force)
+{
+ if (fileSize > maxVideoFileSize || Force) {
+ toFile = toFileName->NextFile();
+ if (!toFile) {
+ error = "toFile";
+ return false;
+ }
+ fileSize = 0;
+ }
+ return true;
+}
+
+bool cCuttingThread::FramesAreEqual(int Index1, int Index2)
+{
+ bool Independent;
+ uchar Buffer1[MAXFRAMESIZE];
+ uchar Buffer2[MAXFRAMESIZE];
+ int Length1;
+ int Length2;
+ if (LoadFrame(Index1, Buffer1, Independent, Length1) && LoadFrame(Index2, Buffer2, Independent, Length2)) {
+ if (Length1 == Length2) {
+ int Diffs = 0;
+ for (int i = 0; i < Length1; i++) {
+ if (Buffer1[i] != Buffer2[i]) {
+ if (Diffs++ > 10) // the continuity counters of the PAT/PMT packets may differ
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int64_t LastPts)
+{
+ bool Processed[MAXPID] = { false };
+ int NumIndependentFrames = 0;
+ cPatPmtParser PatPmtParser;
+ cPacketStorage PacketStorage;
+ for (; NumIndependentFrames < 2; Index++) {
+ uchar Buffer[MAXFRAMESIZE];
+ bool Independent;
+ int len;
+ if (LoadFrame(Index, Buffer, Independent, len)) {
+ if (Independent)
+ NumIndependentFrames++;
+ uchar *p = Buffer;
+ while (len >= TS_SIZE && *p == TS_SYNC_BYTE) {
+ int Pid = TsPid(p);
+ if (Pid == PATPID)
+ PatPmtParser.ParsePat(p, TS_SIZE);
+ else if (Pid == PatPmtParser.PmtPid())
+ PatPmtParser.ParsePmt(p, TS_SIZE);
+ else if (!Processed[Pid]) {
+ int64_t Pts = TsGetPts(p, TS_SIZE);
+ if (Pts >= 0) {
+ int64_t d = PtsDiff(LastPts, Pts);
+ if (d <= 0) // Pts is before or at LastPts
+ PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE);
+ if (d >= 0) { // Pts is at or after LastPts
+ NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets
+ Processed[Pid] = true;
+ }
+ }
+ if (!Processed[Pid])
+ PacketStorage.Append(Pid, p, TS_SIZE);
+ }
+ len -= TS_SIZE;
+ p += TS_SIZE;
+ }
+ }
+ else
+ break;
+ }
+}
+
+bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
+{
+ // Check for seamless connections:
+ bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex);
+ bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex);
+ // Process all frames from BeginIndex (included) to EndIndex (excluded):
+ cDanglingPacketStripper DanglingPacketStripper;
+ int NumIndependentFrames = 0;
+ int64_t FirstPts = -1;
+ int64_t LastPts = -1;
+ for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
+ uchar Buffer[MAXFRAMESIZE];
+ bool Independent;
+ int Length;
+ if (LoadFrame(Index, Buffer, Independent, Length)) {
+ if (!isPesRecording) {
+ int64_t Pts = TsGetPts(Buffer, Length);
+ if (FirstPts < 0)
+ FirstPts = Pts; // the PTS of the first frame in the sequence
+ else if (LastPts < 0 || PtsDiff(LastPts, Pts) > 0)
+ LastPts = Pts; // the PTS of the frame that is displayed as the very last one of the sequence
+ }
+ // Fixup data at the beginning of the sequence:
+ if (!SeamlessBegin) {
+ if (isPesRecording) {
+ if (Index == BeginIndex)
+ cRemux::SetBrokenLink(Buffer, Length);
+ }
+ else if (NumIndependentFrames < 2) {
+ if (DanglingPacketStripper.Process(Buffer, Length, FirstPts))
+ NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more dangling packets
+ }
+ }
+ // Fixup data at the end of the sequence:
+ if (!SeamlessEnd) {
+ if (Index == EndIndex - 1) {
+ if (!isPesRecording)
+ GetPendingPackets(Buffer, Length, EndIndex, LastPts + int(round(PTSTICKS / framesPerSecond))); // adding one frame length to fully cover the very last frame
+ }
+ }
+ // Fixup timestamps and continuity counters:
+ if (!isPesRecording) {
+ if (numSequences > 1)
+ ptsFixer.Fix(Buffer, Length, !SeamlessBegin && Index == BeginIndex);
+ }
+ // Every file shall start with an independent frame:
+ if (Independent) {
+ NumIndependentFrames++;
+ if (!SwitchFile())
+ return false;
+ }
+ // Write index:
+ if (!toIndex->Write(Independent, toFileName->Number(), fileSize)) {
+ error = "toIndex";
+ return false;
+ }
+ // Write data:
+ if (toFile->Write(Buffer, Length) < 0) {
+ error = "safe_write";
+ return false;
+ }
+ fileSize += Length;
+ // Generate marks at the editing points in the edited recording:
+ if (numSequences > 0 && Index == BeginIndex) {
+ if (toMarks.Count() > 0)
+ toMarks.Add(toIndex->Last());
+ toMarks.Add(toIndex->Last());
+ toMarks.Save();
+ }
+ }
+ else
+ return false;
+ }
+ return true;
+}
+
void cCuttingThread::Action(void)
{
- cMark *Mark = fromMarks.First();
- if (Mark) {
+ if (cMark *BeginMark = fromMarks.GetNextBegin()) {
fromFile = fromFileName->Open();
toFile = toFileName->Open();
if (!fromFile || !toFile)
return;
- fromFile->SetReadAhead(MEGABYTE(20));
- int Index = Mark->Position();
- Mark = fromMarks.Next(Mark);
- off_t FileSize = 0;
- int CurrentFileNumber = 0;
- int LastIFrame = 0;
- toMarks.Add(0);
- toMarks.Save();
- uchar buffer[MAXFRAMESIZE], buffer2[MAXFRAMESIZE];
- int Length2;
- bool CheckForSeamlessStream = false;
- bool LastMark = false;
- bool cutIn = true;
- bool suspensionLogged = false;
- while (Running()) {
- uint16_t FileNumber;
- off_t FileOffset;
- int Length;
- bool Independent;
-
+ int LastEndIndex = -1;
+ while (BeginMark && Running()) {
// Suspend cutting if we have severe throughput problems:
-
- if (cIoThrottle::Engaged()) {
- if (!suspensionLogged) {
- dsyslog("suspending cutter thread");
- suspensionLogged = true;
- }
+ if (Throttled()) {
cCondWait::SleepMs(100);
continue;
}
- else if (suspensionLogged) {
- dsyslog("resuming cutter thread");
- suspensionLogged = false;
- }
-
// Make sure there is enough disk space:
-
AssertFreeDiskSpace(-1);
-
- // 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 (fromFile) {
- 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;
- }
+ // Determine the actual begin and end marks, skipping any marks at the same position:
+ cMark *EndMark = fromMarks.GetNextEnd(BeginMark);
+ // Process the current sequence:
+ int EndIndex = EndMark ? EndMark->Position() : fromIndex->Last() + 1;
+ int NextBeginIndex = -1;
+ if (EndMark) {
+ if (cMark *NextBeginMark = fromMarks.GetNextBegin(EndMark))
+ NextBeginIndex = NextBeginMark->Position();
}
- else {
- // Error, unless we're past the last cut-in and there's no cut-out
- if (Mark || LastMark)
- error = "index";
+ if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
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) {
- toFile = toFileName->NextFile();
- if (!toFile) {
- error = "toFile 1";
+ if (!EndMark)
+ break; // reached EOF
+ LastEndIndex = EndIndex;
+ // Switch to the next sequence:
+ BeginMark = fromMarks.GetNextBegin(EndMark);
+ if (BeginMark) {
+ // Split edited files:
+ if (Setup.SplitEditedFiles) {
+ if (!SwitchFile(true))
break;
- }
- FileSize = 0;
- }
- LastIFrame = 0;
- // Compare the current frame with the previously stored one, to see if this is a seamlessly merged recording of the same stream:
- if (CheckForSeamlessStream) {
- if (Length == Length2) {
- int diffs = 0;
- for (int i = 0; i < Length; i++) {
- if (buffer[i] != buffer2[i]) {
- if (diffs++ > 10)
- break;
- }
- }
- if (diffs < 10) // the continuity counters of the PAT/PMT packets may differ
- cutIn = false; // it's apparently a seamless stream, so no need for "broken" handling
- }
- CheckForSeamlessStream = false;
- }
- if (cutIn) {
- if (isPesRecording)
- cRemux::SetBrokenLink(buffer, Length);
- else
- TsSetTeiOnBrokenPackets(buffer, Length);
- cutIn = false;
}
}
- if (toFile->Write(buffer, Length) < 0) {
- error = "safe_write";
- break;
- }
- if (!toIndex->Write(Independent, 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) {
- // Read the next frame, for later comparison with the first frame at this mark:
- if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length2)) {
- if (FileNumber != CurrentFileNumber)
- fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
- if (fromFile) {
- int len = ReadFrame(fromFile, buffer2, Length2, sizeof(buffer2));
- if (len >= 0 && len == Length2)
- CheckForSeamlessStream = true;
- }
- }
- Index = Mark->Position();
- Mark = fromMarks.Next(Mark);
- CurrentFileNumber = 0; // triggers SetOffset before reading next frame
- cutIn = true;
- if (Setup.SplitEditedFiles) {
- toFile = toFileName->NextFile();
- if (!toFile) {
- error = "toFile 2";
- break;
- }
- FileSize = 0;
- }
- }
- else
- LastMark = true;
- }
}
Recordings.TouchUpdate();
}
@@ -255,7 +575,7 @@ bool cCutter::Start(const char *FileName)
cMarks FromMarks;
FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
- if (cMark *First = FromMarks.First())
+ if (cMark *First = FromMarks.GetNextBegin())
Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);
const char *evn = Recording.PrefixFileName('%');
@@ -343,13 +663,17 @@ bool CutRecording(const char *FileName)
if (Recording.Name()) {
cMarks Marks;
if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
- if (cCutter::Start(FileName)) {
- while (cCutter::Active())
- cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
- return true;
+ if (Marks.GetNumSequences()) {
+ if (cCutter::Start(FileName)) {
+ while (cCutter::Active())
+ cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
+ return true;
+ }
+ else
+ fprintf(stderr, "can't start editing process\n");
}
else
- fprintf(stderr, "can't start editing process\n");
+ fprintf(stderr, "'%s' has no editing sequences\n", FileName);
}
else
fprintf(stderr, "'%s' has no editing marks\n", FileName);