vdr 2.6.1
cutter.c
Go to the documentation of this file.
1/*
2 * cutter.c: The video cutting facilities
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: cutter.c 4.6 2018/01/18 12:19:31 kls Exp $
8 */
9
10#include "cutter.h"
11#include "menu.h"
12#include "recording.h"
13#include "remux.h"
14#include "videodir.h"
15
16// --- cPacketBuffer ---------------------------------------------------------
17
19private:
21 int size;
22 int length;
23public:
24 cPacketBuffer(void);
26 void Append(uchar *Data, int Length);
28 void Flush(uchar *Data, int &Length, int MaxLength);
33 };
34
36{
37 data = NULL;
38 size = length = 0;
39}
40
42{
43 free(data);
44}
45
46void cPacketBuffer::Append(uchar *Data, int Length)
47{
48 if (length + Length >= size) {
49 int NewSize = (length + Length) * 3 / 2;
50 if (uchar *p = (uchar *)realloc(data, NewSize)) {
51 data = p;
52 size = NewSize;
53 }
54 else
55 return; // out of memory
56 }
57 memcpy(data + length, Data, Length);
58 length += Length;
59}
60
61void cPacketBuffer::Flush(uchar *Data, int &Length, int MaxLength)
62{
63 if (Data && length > 0 && Length + length <= MaxLength) {
64 memcpy(Data + Length, data, length);
65 Length += length;
66 }
67 length = 0;
68}
69
70// --- cPacketStorage --------------------------------------------------------
71
73private:
75public:
76 cPacketStorage(void);
78 void Append(int Pid, uchar *Data, int Length);
79 void Flush(int Pid, uchar *Data, int &Length, int MaxLength);
80 };
81
83{
84 for (int i = 0; i < MAXPID; i++)
85 buffers[i] = NULL;
86}
87
89{
90 for (int i = 0; i < MAXPID; i++)
91 delete buffers[i];
92}
93
94void cPacketStorage::Append(int Pid, uchar *Data, int Length)
95{
96 if (!buffers[Pid])
97 buffers[Pid] = new cPacketBuffer;
98 buffers[Pid]->Append(Data, Length);
99}
100
101void cPacketStorage::Flush(int Pid, uchar *Data, int &Length, int MaxLength)
102{
103 if (buffers[Pid])
104 buffers[Pid]->Flush(Data, Length, MaxLength);
105}
106
107// --- cMpeg2Fixer -----------------------------------------------------------
108
109class cMpeg2Fixer : private cTsPayload {
110private:
111 bool FindHeader(uint32_t Code, const char *Header);
112public:
113 cMpeg2Fixer(uchar *Data, int Length, int Vpid);
114 void SetBrokenLink(void);
115 void SetClosedGop(void);
116 int GetTref(void);
117 void AdjGopTime(int Offset, int FramesPerSecond);
118 void AdjTref(int TrefOffset);
119 };
120
121cMpeg2Fixer::cMpeg2Fixer(uchar *Data, int Length, int Vpid)
122{
123 // Go to first video packet:
124 for (; Length > 0; Data += TS_SIZE, Length -= TS_SIZE) {
125 if (TsPid(Data) == Vpid) {
126 Setup(Data, Length, Vpid);
127 break;
128 }
129 }
130}
131
132bool cMpeg2Fixer::FindHeader(uint32_t Code, const char *Header)
133{
134 Reset();
135 if (Find(Code))
136 return true;
137 esyslog("ERROR: %s header not found!", Header);
138 return false;
139}
140
142{
143 if (!FindHeader(0x000001B8, "GOP"))
144 return;
145 SkipBytes(3);
146 uchar b = GetByte();
147 if (!(b & 0x40)) { // GOP not closed
148 b |= 0x20;
149 SetByte(b, GetLastIndex());
150 }
151}
152
154{
155 if (!FindHeader(0x000001B8, "GOP"))
156 return;
157 SkipBytes(3);
158 uchar b = GetByte();
159 b |= 0x40;
160 SetByte(b, GetLastIndex());
161}
162
164{
165 if (!FindHeader(0x00000100, "picture"))
166 return 0;
167 int Tref = GetByte() << 2;
168 Tref |= GetByte() >> 6;
169 return Tref;
170}
171
172void cMpeg2Fixer::AdjGopTime(int Offset, int FramesPerSecond)
173{
174 if (!FindHeader(0x000001B8, "GOP"))
175 return;
176 uchar Byte1 = GetByte();
177 int Index1 = GetLastIndex();
178 uchar Byte2 = GetByte();
179 int Index2 = GetLastIndex();
180 uchar Byte3 = GetByte();
181 int Index3 = GetLastIndex();
182 uchar Byte4 = GetByte();
183 int Index4 = GetLastIndex();
184 uchar Frame = ((Byte3 & 0x1F) << 1) | (Byte4 >> 7);
185 uchar Sec = ((Byte2 & 0x07) << 3) | (Byte3 >> 5);
186 uchar Min = ((Byte1 & 0x03) << 4) | (Byte2 >> 4);
187 uchar Hour = ((Byte1 & 0x7C) >> 2);
188 int GopTime = ((Hour * 60 + Min) * 60 + Sec) * FramesPerSecond + Frame;
189 if (GopTime) { // do not fix when zero
190 GopTime += Offset;
191 if (GopTime < 0)
192 GopTime += 24 * 60 * 60 * FramesPerSecond;
193 Frame = GopTime % FramesPerSecond;
194 GopTime = GopTime / FramesPerSecond;
195 Sec = GopTime % 60;
196 GopTime = GopTime / 60;
197 Min = GopTime % 60;
198 GopTime = GopTime / 60;
199 Hour = GopTime % 24;
200 SetByte((Byte1 & 0x80) | (Hour << 2) | (Min >> 4), Index1);
201 SetByte(((Min & 0x0F) << 4) | 0x08 | (Sec >> 3), Index2);
202 SetByte(((Sec & 0x07) << 3) | (Frame >> 1), Index3);
203 SetByte((Byte4 & 0x7F) | ((Frame & 0x01) << 7), Index4);
204 }
205}
206
207void cMpeg2Fixer::AdjTref(int TrefOffset)
208{
209 if (!FindHeader(0x00000100, "picture"))
210 return;
211 int Tref = GetByte() << 2;
212 int Index1 = GetLastIndex();
213 uchar Byte2 = GetByte();
214 int Index2 = GetLastIndex();
215 Tref |= Byte2 >> 6;
216 Tref -= TrefOffset;
217 SetByte(Tref >> 2, Index1);
218 SetByte((Tref << 6) | (Byte2 & 0x3F), Index2);
219}
220
221// --- cCuttingThread --------------------------------------------------------
222
223class cCuttingThread : public cThread {
224private:
225 const char *error;
234 off_t fileSize;
236 int sequence; // cutting sequence
237 int delta; // time between two frames (PTS ticks)
238 int64_t lastVidPts; // the video PTS of the last frame (in display order)
239 bool multiFramePkt; // multiple frames within one PES packet
240 int64_t firstPts; // first valid PTS, for dangling packet stripping
241 int64_t offset; // offset to add to all timestamps
242 int tRefOffset; // number of stripped frames in GOP
243 uchar counter[MAXPID]; // the TS continuity counter for each PID
244 bool keepPkt[MAXPID]; // flag for each PID to keep packets, for dangling packet stripping
245 int numIFrames; // number of I-frames without pending packets
247 bool Throttled(void);
248 bool SwitchFile(bool Force = false);
249 bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
250 bool FramesAreEqual(int Index1, int Index2);
251 void GetPendingPackets(uchar *Buffer, int &Length, int Index);
252 // Gather all non-video TS packets from Index upward that either belong to
253 // payloads that started before Index, or have a PTS that is before lastVidPts,
254 // and add them to the end of the given Data.
255 bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
256 bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
257protected:
258 virtual void Action(void);
259public:
260 cCuttingThread(const char *FromFileName, const char *ToFileName);
261 virtual ~cCuttingThread();
262 const char *Error(void) { return error; }
263 };
264
265cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
266:cThread("video cutting", true)
267{
268 error = NULL;
269 fromFile = toFile = NULL;
270 fromFileName = toFileName = NULL;
271 fromIndex = toIndex = NULL;
272 cRecording Recording(FromFileName);
273 isPesRecording = Recording.IsPesRecording();
274 framesPerSecond = Recording.FramesPerSecond();
275 suspensionLogged = false;
276 fileSize = 0;
277 sequence = 0;
278 delta = int(round(PTSTICKS / framesPerSecond));
279 lastVidPts = -1;
280 multiFramePkt = false;
281 firstPts = -1;
282 offset = 0;
283 tRefOffset = 0;
284 memset(counter, 0x00, sizeof(counter));
285 numIFrames = 0;
286 if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) {
288 if (numSequences > 0) {
289 fromFileName = new cFileName(FromFileName, false, true, isPesRecording);
290 toFileName = new cFileName(ToFileName, true, true, isPesRecording);
291 fromIndex = new cIndexFile(FromFileName, false, isPesRecording);
292 toIndex = new cIndexFile(ToFileName, true, isPesRecording);
293 toMarks.Load(ToFileName, framesPerSecond, isPesRecording); // doesn't actually load marks, just sets the file name
297 Start();
298 }
299 else
300 esyslog("no editing sequences found for %s", FromFileName);
301 }
302 else
303 esyslog("no editing marks found for %s", FromFileName);
304}
305
307{
308 Cancel(3);
309 delete fromFileName;
310 delete toFileName;
311 delete fromIndex;
312 delete toIndex;
313}
314
316{
317 if (cIoThrottle::Engaged()) {
318 if (!suspensionLogged) {
319 dsyslog("suspending cutter thread");
320 suspensionLogged = true;
321 }
322 return true;
323 }
324 else if (suspensionLogged) {
325 dsyslog("resuming cutter thread");
326 suspensionLogged = false;
327 }
328 return false;
329}
330
331bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
332{
333 uint16_t FileNumber;
334 off_t FileOffset;
335 if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
336 fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
337 if (fromFile) {
339 int len = ReadFrame(fromFile, Buffer, Length, MAXFRAMESIZE);
340 if (len < 0)
341 error = "ReadFrame";
342 else if (len != Length)
343 Length = len;
344 return error == NULL;
345 }
346 else
347 error = "fromFile";
348 }
349 return false;
350}
351
353{
354 if (fileSize > maxVideoFileSize || Force) {
356 if (!toFile) {
357 error = "toFile";
358 return false;
359 }
360 fileSize = 0;
361 }
362 return true;
363}
364
366private:
368public:
369 cHeapBuffer(int Size) { buffer = MALLOC(uchar, Size); }
370 ~cHeapBuffer() { free(buffer); }
371 operator uchar * () { return buffer; }
372 };
373
374bool cCuttingThread::FramesAreEqual(int Index1, int Index2)
375{
376 cHeapBuffer Buffer1(MAXFRAMESIZE);
377 cHeapBuffer Buffer2(MAXFRAMESIZE);
378 if (!Buffer1 || !Buffer2)
379 return false;
380 bool Independent;
381 int Length1;
382 int Length2;
383 if (LoadFrame(Index1, Buffer1, Independent, Length1) && LoadFrame(Index2, Buffer2, Independent, Length2)) {
384 if (Length1 == Length2) {
385 int Diffs = 0;
386 for (int i = 0; i < Length1; i++) {
387 if (Buffer1[i] != Buffer2[i]) {
388 if (Diffs++ > 10) // the continuity counters of the PAT/PMT packets may differ
389 return false;
390 }
391 }
392 return true;
393 }
394 }
395 return false;
396}
397
398void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index)
399{
401 if (!Buffer)
402 return;
403 bool Processed[MAXPID] = { false };
404 cPacketStorage PacketStorage;
405 int64_t LastPts = lastVidPts + delta;// adding one frame length to fully cover the very last frame
406 Processed[patPmtParser.Vpid()] = true; // we only want non-video packets
407 for (int NumIndependentFrames = 0; NumIndependentFrames < 2; Index++) {
408 bool Independent;
409 int len;
410 if (LoadFrame(Index, Buffer, Independent, len)) {
411 if (Independent)
412 NumIndependentFrames++;
413 for (uchar *p = Buffer; len >= TS_SIZE && *p == TS_SYNC_BYTE; len -= TS_SIZE, p += TS_SIZE) {
414 int Pid = TsPid(p);
415 if (Pid != PATPID && !patPmtParser.IsPmtPid(Pid) && !Processed[Pid]) {
416 int64_t Pts = TsGetPts(p, TS_SIZE);
417 if (Pts >= 0) {
418 int64_t d = PtsDiff(LastPts, Pts);
419 if (d < 0) // Pts is before LastPts
420 PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE);
421 else { // Pts is at or after LastPts
422 NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets
423 Processed[Pid] = true;
424 }
425 }
426 if (!Processed[Pid])
427 PacketStorage.Append(Pid, p, TS_SIZE);
428 }
429 }
430 }
431 else
432 break;
433 }
434}
435
436bool cCuttingThread::FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut)
437{
438 if (!patPmtParser.Vpid()) {
439 if (!patPmtParser.ParsePatPmt(Data, Length))
440 return false;
441 }
442 if (CutIn) {
443 sequence++;
444 memset(keepPkt, false, sizeof(keepPkt));
445 numIFrames = 0;
446 firstPts = TsGetPts(Data, Length);
447 // Determine the PTS offset at the beginning of each sequence (except the first one):
448 if (sequence != 1) {
449 if (firstPts >= 0)
451 }
452 }
453 if (CutOut)
454 GetPendingPackets(Data, Length, Index + 1);
455 if (Independent) {
456 numIFrames++;
457 tRefOffset = 0;
458 }
459 // Fix MPEG-2:
460 if (patPmtParser.Vtype() == 2) {
461 cMpeg2Fixer Mpeg2fixer(Data, Length, patPmtParser.Vpid());
462 if (CutIn) {
463 if (sequence == 1 || multiFramePkt)
464 Mpeg2fixer.SetBrokenLink();
465 else {
466 Mpeg2fixer.SetClosedGop();
467 tRefOffset = Mpeg2fixer.GetTref();
468 }
469 }
470 if (tRefOffset)
471 Mpeg2fixer.AdjTref(tRefOffset);
472 if (sequence > 1 && Independent)
473 Mpeg2fixer.AdjGopTime((offset - MAX33BIT) / delta, round(framesPerSecond));
474 }
475 bool DeletedFrame = false;
476 bool GotVidPts = false;
477 bool StripVideo = sequence > 1 && tRefOffset;
478 uchar *p;
479 int len;
480 for (p = Data, len = Length; len >= TS_SIZE && *p == TS_SYNC_BYTE; p += TS_SIZE, len -= TS_SIZE) {
481 int Pid = TsPid(p);
482 // Strip dangling packets:
483 if (numIFrames < 2 && Pid != PATPID && !patPmtParser.IsPmtPid(Pid)) {
484 if (Pid != patPmtParser.Vpid() || StripVideo) {
485 int64_t Pts = TsGetPts(p, TS_SIZE);
486 if (Pts >= 0)
487 keepPkt[Pid] = PtsDiff(firstPts, Pts) >= 0; // Pts is at or after FirstPts
488 if (!keepPkt[Pid]) {
489 TsHidePayload(p);
490 numIFrames = 0; // we search until we find two consecutive I-frames without any more dangling packets
491 if (Pid == patPmtParser.Vpid())
492 DeletedFrame = true;
493 }
494 }
495 }
496 // Adjust the TS continuity counter:
497 if (sequence > 1) {
498 if (TsHasPayload(p))
499 counter[Pid] = (counter[Pid] + 1) & TS_CONT_CNT_MASK;
501 }
502 else
503 counter[Pid] = TsContinuityCounter(p); // collect initial counters
504 // Adjust PTS:
505 int64_t Pts = TsGetPts(p, TS_SIZE);
506 if (Pts >= 0) {
507 if (sequence > 1) {
508 Pts = PtsAdd(Pts, offset);
509 TsSetPts(p, TS_SIZE, Pts);
510 }
511 // Keep track of the highest video PTS - in case of multiple fields per frame, take the first one
512 if (!GotVidPts && Pid == patPmtParser.Vpid()) {
513 GotVidPts = true;
514 if (lastVidPts < 0 || PtsDiff(lastVidPts, Pts) > 0)
515 lastVidPts = Pts;
516 }
517 }
518 // Adjust DTS:
519 if (sequence > 1) {
520 int64_t Dts = TsGetDts(p, TS_SIZE);
521 if (Dts >= 0) {
522 Dts = PtsAdd(Dts, offset);
523 if (CutIn)
524 Dts = PtsAdd(Dts, tRefOffset * delta);
525 TsSetDts(p, TS_SIZE, Dts);
526 }
527 int64_t Pcr = TsGetPcr(p);
528 if (Pcr >= 0) {
529 Pcr = Pcr + offset * PCRFACTOR;
530 if (Pcr > MAX27MHZ)
531 Pcr -= MAX27MHZ + 1;
532 TsSetPcr(p, Pcr);
533 }
534 }
535 }
536 if (!DeletedFrame && !GotVidPts) {
537 // Adjust PTS for multiple frames within a single PES packet:
539 multiFramePkt = true;
540 }
541 return DeletedFrame;
542}
543
544bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
545{
546 // Check for seamless connections:
547 bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex);
548 bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex);
549 // Process all frames from BeginIndex (included) to EndIndex (excluded):
551 if (!Buffer) {
552 error = "malloc";
553 return false;
554 }
555 for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
556 bool Independent;
557 int Length;
558 if (LoadFrame(Index, Buffer, Independent, Length)) {
559 // Make sure there is enough disk space:
561 bool CutIn = !SeamlessBegin && Index == BeginIndex;
562 bool CutOut = !SeamlessEnd && Index == EndIndex - 1;
563 bool DeletedFrame = false;
564 if (!isPesRecording) {
565 DeletedFrame = FixFrame(Buffer, Length, Independent, Index, CutIn, CutOut);
566 }
567 else if (CutIn)
568 cRemux::SetBrokenLink(Buffer, Length);
569 // Every file shall start with an independent frame:
570 if (Independent) {
571 if (!SwitchFile())
572 return false;
573 }
574 // Write index:
575 if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
576 error = "toIndex";
577 return false;
578 }
579 // Write data:
580 if (toFile->Write(Buffer, Length) < 0) {
581 error = "safe_write";
582 return false;
583 }
584 fileSize += Length;
585 // Generate marks at the editing points in the edited recording:
586 if (numSequences > 1 && Index == BeginIndex) {
587 if (toMarks.Count() > 0)
590 toMarks.Save();
591 }
592 }
593 else
594 return false;
595 }
596 return true;
597}
598
600{
601 if (cMark *BeginMark = fromMarks.GetNextBegin()) {
604 if (!fromFile || !toFile)
605 return;
606 int LastEndIndex = -1;
607 while (BeginMark && Running()) {
608 // Suspend cutting if we have severe throughput problems:
609 if (Throttled()) {
611 continue;
612 }
613 // Determine the actual begin and end marks, skipping any marks at the same position:
614 cMark *EndMark = fromMarks.GetNextEnd(BeginMark);
615 // Process the current sequence:
616 int EndIndex = EndMark ? EndMark->Position() : fromIndex->Last() + 1;
617 int NextBeginIndex = -1;
618 if (EndMark) {
619 if (cMark *NextBeginMark = fromMarks.GetNextBegin(EndMark))
620 NextBeginIndex = NextBeginMark->Position();
621 }
622 if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
623 break;
624 if (!EndMark)
625 break; // reached EOF
626 LastEndIndex = EndIndex;
627 // Switch to the next sequence:
628 BeginMark = fromMarks.GetNextBegin(EndMark);
629 if (BeginMark) {
630 // Split edited files:
632 if (!SwitchFile(true))
633 break;
634 }
635 }
636 }
637 }
638 else
639 esyslog("no editing marks found!");
640}
641
642// --- cCutter ---------------------------------------------------------------
643
644cCutter::cCutter(const char *FileName)
645{
646 cuttingThread = NULL;
647 error = false;
648 originalVersionName = FileName;
649}
650
652{
653 Stop();
654}
655
656cString cCutter::EditedFileName(const char *FileName)
657{
658 cRecording Recording(FileName);
659 cMarks Marks;
660 if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording())) {
661 if (cMark *First = Marks.GetNextBegin())
662 Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);
663 return Recording.PrefixFileName('%');
664 }
665 return NULL;
666}
667
669{
670 if (!cuttingThread) {
671 error = false;
672 if (*originalVersionName) {
675 if (*editedVersionName) {
676 if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
679 Recording.WriteInfo(editedVersionName);
681 return true;
682 }
683 }
684 }
685 }
686 }
687 return false;
688}
689
691{
692 bool Interrupted = cuttingThread && cuttingThread->Active();
693 const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
694 delete cuttingThread;
695 cuttingThread = NULL;
696 if ((Interrupted || Error) && *editedVersionName) {
697 if (Interrupted)
698 isyslog("editing process has been interrupted");
699 if (Error)
700 esyslog("ERROR: '%s' during editing process", Error);
703 }
704}
705
707{
708 if (cuttingThread) {
709 if (cuttingThread->Active())
710 return true;
712 Stop();
713 if (!error)
715 }
716 return false;
717}
718
720{
721 return error;
722}
723
724#define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process
725
726bool CutRecording(const char *FileName)
727{
728 if (DirectoryOk(FileName)) {
729 cRecording Recording(FileName);
730 if (Recording.Name()) {
731 cMarks Marks;
732 if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
733 if (Marks.GetNumSequences()) {
734 cCutter Cutter(FileName);
735 if (Cutter.Start()) {
736 while (Cutter.Active())
738 if (!Cutter.Error())
739 return true;
740 fprintf(stderr, "error while cutting\n");
741 }
742 else
743 fprintf(stderr, "can't start editing process\n");
744 }
745 else
746 fprintf(stderr, "'%s' has no editing sequences\n", FileName);
747 }
748 else
749 fprintf(stderr, "'%s' has no editing marks\n", FileName);
750 }
751 else
752 fprintf(stderr, "'%s' is not a recording\n", FileName);
753 }
754 else
755 fprintf(stderr, "'%s' is not a directory\n", FileName);
756 return false;
757}
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:72
static void Shutdown(void)
Definition: player.c:108
Definition: cutter.h:18
bool Start(void)
Starts the actual cutting process.
Definition: cutter.c:668
cString editedVersionName
Definition: cutter.h:21
cCutter(const char *FileName)
Sets up a new cutter for the given FileName, which must be the full path name of an existing recordin...
Definition: cutter.c:644
~cCutter()
Definition: cutter.c:651
bool error
Definition: cutter.h:23
void Stop(void)
Stops an ongoing cutting process.
Definition: cutter.c:690
bool Error(void)
Returns true if an error occurred while cutting the recording.
Definition: cutter.c:719
cString originalVersionName
Definition: cutter.h:20
bool Active(void)
Returns true if the cutter is currently active.
Definition: cutter.c:706
cCuttingThread * cuttingThread
Definition: cutter.h:22
static cString EditedFileName(const char *FileName)
Returns the full path name of the edited version of the recording with the given FileName.
Definition: cutter.c:656
int tRefOffset
Definition: cutter.c:242
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
Definition: cutter.c:331
bool Throttled(void)
Definition: cutter.c:315
cPatPmtParser patPmtParser
Definition: cutter.c:246
uchar counter[MAXPID]
Definition: cutter.c:243
int64_t lastVidPts
Definition: cutter.c:238
virtual ~cCuttingThread()
Definition: cutter.c:306
cMarks toMarks
Definition: cutter.c:231
int numIFrames
Definition: cutter.c:245
const char * Error(void)
Definition: cutter.c:262
bool suspensionLogged
Definition: cutter.c:235
cUnbufferedFile * fromFile
Definition: cutter.c:228
int64_t offset
Definition: cutter.c:241
cIndexFile * toIndex
Definition: cutter.c:230
cIndexFile * fromIndex
Definition: cutter.c:230
cFileName * fromFileName
Definition: cutter.c:229
bool SwitchFile(bool Force=false)
Definition: cutter.c:352
int64_t firstPts
Definition: cutter.c:240
cMarks fromMarks
Definition: cutter.c:231
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut)
Definition: cutter.c:436
const char * error
Definition: cutter.c:225
bool keepPkt[MAXPID]
Definition: cutter.c:244
cFileName * toFileName
Definition: cutter.c:229
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: cutter.c:599
int sequence
Definition: cutter.c:236
bool isPesRecording
Definition: cutter.c:226
int numSequences
Definition: cutter.c:232
cUnbufferedFile * toFile
Definition: cutter.c:228
bool FramesAreEqual(int Index1, int Index2)
Definition: cutter.c:374
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
Definition: cutter.c:544
bool multiFramePkt
Definition: cutter.c:239
double framesPerSecond
Definition: cutter.c:227
cCuttingThread(const char *FromFileName, const char *ToFileName)
Definition: cutter.c:265
off_t maxVideoFileSize
Definition: cutter.c:233
off_t fileSize
Definition: cutter.c:234
void GetPendingPackets(uchar *Buffer, int &Length, int Index)
Definition: cutter.c:398
cUnbufferedFile * NextFile(void)
Definition: recording.c:3078
uint16_t Number(void)
cUnbufferedFile * Open(void)
Definition: recording.c:3002
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
Definition: recording.c:3036
uchar * buffer
Definition: cutter.c:367
~cHeapBuffer()
Definition: cutter.c:370
cHeapBuffer(int Size)
Definition: cutter.c:369
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
Definition: recording.c:2742
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
Definition: recording.c:2759
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition: thread.c:918
int Count(void) const
int Position(void) const
int GetNumSequences(void) const
Returns the actual number of sequences to be cut from the recording.
Definition: recording.c:2318
void Add(int Position)
If this cMarks object is used by multiple threads, the caller must Lock() it before calling Add() and...
Definition: recording.c:2251
const cMark * GetNextBegin(const cMark *EndMark=NULL) const
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark.
Definition: recording.c:2284
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
Definition: recording.c:2175
const cMark * GetNextEnd(const cMark *BeginMark) const
Returns the next "end" mark after BeginMark, skipping any marks at the same position as BeginMark.
Definition: recording.c:2300
bool Save(void)
Definition: recording.c:2218
void SetClosedGop(void)
Definition: cutter.c:153
int GetTref(void)
Definition: cutter.c:163
bool FindHeader(uint32_t Code, const char *Header)
Definition: cutter.c:132
void AdjTref(int TrefOffset)
Definition: cutter.c:207
void SetBrokenLink(void)
Definition: cutter.c:141
void AdjGopTime(int Offset, int FramesPerSecond)
Definition: cutter.c:172
cMpeg2Fixer(uchar *Data, int Length, int Vpid)
Definition: cutter.c:121
int length
Definition: cutter.c:22
void Append(uchar *Data, int Length)
Appends Length bytes of Data to this packet buffer.
Definition: cutter.c:46
int size
Definition: cutter.c:21
void Flush(uchar *Data, int &Length, int MaxLength)
Flushes the content of this packet buffer into the given Data, starting at position Length,...
Definition: cutter.c:61
uchar * data
Definition: cutter.c:20
~cPacketBuffer()
Definition: cutter.c:41
cPacketBuffer(void)
Definition: cutter.c:35
~cPacketStorage()
Definition: cutter.c:88
cPacketBuffer * buffers[MAXPID]
Definition: cutter.c:74
void Append(int Pid, uchar *Data, int Length)
Definition: cutter.c:94
cPacketStorage(void)
Definition: cutter.c:82
void Flush(int Pid, uchar *Data, int &Length, int MaxLength)
Definition: cutter.c:101
int Vtype(void) const
Returns the video stream type as defined by the current PMT, or 0 if no video stream type has been de...
bool ParsePatPmt(const uchar *Data, int Length)
Parses the given Data (which may consist of several TS packets, typically an entire frame) and extrac...
Definition: remux.c:919
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
int Vpid(void) const
Returns the video pid as defined by the current PMT, or 0 if no video pid has been detected,...
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
Definition: recording.c:2339
bool WriteInfo(const char *OtherFileName=NULL)
Writes in info file of this recording.
Definition: recording.c:1200
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
Definition: recording.c:1221
const char * Name(void) const
Returns the full name of the recording (without the video directory).
time_t Start(void) const
const char * PrefixFileName(char Prefix)
Definition: recording.c:1147
double FramesPerSecond(void) const
bool IsPesRecording(void) const
static void SetBrokenLink(uchar *Data, int Length)
Definition: remux.c:102
static const char * NowReplaying(void)
Definition: menu.c:5869
int SplitEditedFiles
Definition: config.h:345
int MaxVideoFileSize
Definition: config.h:344
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
bool Active(void)
Checks whether the thread is still alive.
Definition: thread.c:329
void SetByte(uchar Byte, int Index)
Sets the TS data byte at the given Index to the value Byte.
Definition: remux.c:328
uchar GetByte(void)
Gets the next byte of the TS payload, skipping any intermediate TS header data.
Definition: remux.c:280
int GetLastIndex(void)
Returns the index into the TS data of the payload byte that has most recently been read.
Definition: remux.c:323
void Setup(uchar *Data, int Length, int Pid=-1)
Sets up this TS payload handler with the given Data, which points to a sequence of Length bytes of co...
Definition: remux.c:272
bool Find(uint32_t Code)
Searches for the four byte sequence given in Code and returns true if it was found within the payload...
Definition: remux.c:334
void Reset(void)
Definition: remux.c:265
bool SkipBytes(int Bytes)
Skips the given number of bytes in the payload and returns true if there is still data left to read.
Definition: remux.c:311
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner,...
void SetReadAhead(size_t ra)
Definition: tools.c:1870
ssize_t Write(const void *Data, size_t Size)
Definition: tools.c:1947
static bool RemoveVideoFile(const char *FileName)
Definition: videodir.c:142
cSetup Setup
Definition: config.c:372
#define CUTTINGCHECKINTERVAL
Definition: cutter.c:724
bool CutRecording(const char *FileName)
Definition: cutter.c:726
#define MAXVIDEOFILESIZEPES
#define RUC_EDITINGRECORDING
#define RUC_EDITEDRECORDING
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition: recording.c:3212
void AssertFreeDiskSpace(int Priority=0, bool Force=false)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
Definition: recording.c:154
#define MAXFRAMESIZE
void TsSetPcr(uchar *p, int64_t Pcr)
Definition: remux.c:131
int TsPid(const uchar *p)
bool TsHasPayload(const uchar *p)
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
Definition: remux.c:234
#define MAX33BIT
#define PATPID
void TsHidePayload(uchar *p)
Definition: remux.c:121
void TsSetContinuityCounter(uchar *p, uchar Counter)
uchar TsContinuityCounter(const uchar *p)
#define TS_SIZE
#define MAXPID
int64_t TsGetPcr(const uchar *p)
#define MAX27MHZ
#define PCRFACTOR
#define TS_SYNC_BYTE
int64_t TsGetDts(const uchar *p, int l)
Definition: remux.c:173
void TsSetDts(uchar *p, int l, int64_t Dts)
Definition: remux.c:200
void TsSetPts(uchar *p, int l, int64_t Pts)
Definition: remux.c:186
#define PTSTICKS
int64_t TsGetPts(const uchar *p, int l)
Definition: remux.c:160
#define TS_CONT_CNT_MASK
int64_t PtsAdd(int64_t Pts1, int64_t Pts2)
Adds the given PTS values, taking into account the 33bit wrap around.
#define MEGABYTE(n)
bool MakeDirs(const char *FileName, bool IsDirectory=false)
Definition: tools.c:499
unsigned char uchar
#define dsyslog(a...)
#define MALLOC(type, size)
bool DirectoryOk(const char *DirName, bool LogErrors=false)
Definition: tools.c:481
#define esyslog(a...)
#define isyslog(a...)