vdr 2.6.8
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 5.1 2022/11/06 11:25:13 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
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
690
692{
693 bool Interrupted = cuttingThread && cuttingThread->Active();
694 const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
695 delete cuttingThread;
696 cuttingThread = NULL;
698 if ((Interrupted || Error) && *editedVersionName) {
699 if (Interrupted)
700 isyslog("editing process has been interrupted");
701 if (Error)
702 esyslog("ERROR: '%s' during editing process", Error);
705 }
706}
707
709{
710 if (cuttingThread) {
711 if (cuttingThread->Active())
712 return true;
714 Stop();
715 if (!error)
717 }
718 return false;
719}
720
722{
723 return error;
724}
725
726#define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process
727
728bool CutRecording(const char *FileName)
729{
730 if (DirectoryOk(FileName)) {
731 cRecording Recording(FileName);
732 if (Recording.Name()) {
733 cMarks Marks;
734 if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
735 if (Marks.GetNumSequences()) {
736 cCutter Cutter(FileName);
737 if (Cutter.Start()) {
738 while (Cutter.Active())
740 if (!Cutter.Error())
741 return true;
742 fprintf(stderr, "error while cutting\n");
743 }
744 else
745 fprintf(stderr, "can't start editing process\n");
746 }
747 else
748 fprintf(stderr, "'%s' has no editing sequences\n", FileName);
749 }
750 else
751 fprintf(stderr, "'%s' has no editing marks\n", FileName);
752 }
753 else
754 fprintf(stderr, "'%s' is not a recording\n", FileName);
755 }
756 else
757 fprintf(stderr, "'%s' is not a directory\n", FileName);
758 return false;
759}
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
cCuttingThread * cuttingThread
Definition cutter.h:22
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:691
bool Error(void)
Returns true if an error occurred while cutting the recording.
Definition cutter.c:721
cString originalVersionName
Definition cutter.h:20
bool Active(void)
Returns true if the cutter is currently active.
Definition cutter.c:708
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
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
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
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:3236
uint16_t Number(void)
Definition recording.h:534
cUnbufferedFile * Open(void)
Definition recording.c:3160
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
Definition recording.c:3194
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:2900
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
Definition recording.c:2917
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
Definition recording.h:510
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition thread.c:926
int Count(void) const
Definition tools.h:640
int Position(void) const
Definition recording.h:387
int GetNumSequences(void) const
Returns the actual number of sequences to be cut from the recording.
Definition recording.c:2438
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:2371
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:2404
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
Definition recording.c:2295
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:2420
bool Save(void)
Definition recording.c:2338
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
void Append(uchar *Data, int Length)
Appends Length bytes of Data to this packet buffer.
Definition cutter.c:46
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...
Definition remux.h:409
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.
Definition remux.h:400
int Vpid(void) const
Returns the video pid as defined by the current PMT, or 0 if no video pid has been detected,...
Definition remux.h:403
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
Definition recording.c:2491
bool WriteInfo(const char *OtherFileName=NULL)
Writes in info file of this recording.
Definition recording.c:1275
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
Definition recording.c:1296
const char * Name(void) const
Returns the full name of the recording (without the video directory).
Definition recording.h:162
time_t Start(void) const
Definition recording.h:147
const char * PrefixFileName(char Prefix)
Definition recording.c:1222
double FramesPerSecond(void) const
Definition recording.h:173
bool IsPesRecording(void) const
Definition recording.h:194
static void SetBrokenLink(uchar *Data, int Length)
Definition remux.c:102
static const char * NowReplaying(void)
Definition menu.c:5875
int SplitEditedFiles
Definition config.h:347
int MaxVideoFileSize
Definition config.h:346
char SVDRPHostName[HOST_NAME_MAX]
Definition config.h:305
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1180
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.
Definition thread.h:101
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
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,...
Definition tools.h:507
void SetReadAhead(size_t ra)
Definition tools.c:1901
ssize_t Write(const void *Data, size_t Size)
Definition tools.c:1978
static bool RemoveVideoFile(const char *FileName)
Definition videodir.c:142
cSetup Setup
Definition config.c:372
#define CUTTINGCHECKINTERVAL
Definition cutter.c:726
bool CutRecording(const char *FileName)
Definition cutter.c:728
void AssertFreeDiskSpace(int Priority, bool Force)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
Definition recording.c:152
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition recording.c:3370
void SetRecordingTimerId(const char *Directory, const char *TimerId)
Definition recording.c:3423
#define MAXVIDEOFILESIZEPES
Definition recording.h:477
#define RUC_EDITINGRECORDING
Definition recording.h:452
#define RUC_EDITEDRECORDING
Definition recording.h:453
#define MAXFRAMESIZE
Definition recording.h:469
void TsSetPcr(uchar *p, int64_t Pcr)
Definition remux.c:131
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
Definition remux.c:234
void TsHidePayload(uchar *p)
Definition remux.c:121
#define MAXPID
Definition remux.c:464
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
int64_t TsGetPts(const uchar *p, int l)
Definition remux.c:160
int TsPid(const uchar *p)
Definition remux.h:82
bool TsHasPayload(const uchar *p)
Definition remux.h:62
#define MAX33BIT
Definition remux.h:59
#define PATPID
Definition remux.h:52
void TsSetContinuityCounter(uchar *p, uchar Counter)
Definition remux.h:103
uchar TsContinuityCounter(const uchar *p)
Definition remux.h:98
#define TS_SIZE
Definition remux.h:34
int64_t TsGetPcr(const uchar *p)
Definition remux.h:124
#define MAX27MHZ
Definition remux.h:60
#define PCRFACTOR
Definition remux.h:58
#define TS_SYNC_BYTE
Definition remux.h:33
#define PTSTICKS
Definition remux.h:57
#define TS_CONT_CNT_MASK
Definition remux.h:42
int64_t PtsAdd(int64_t Pts1, int64_t Pts2)
Adds the given PTS values, taking into account the 33bit wrap around.
Definition remux.h:216
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition tools.c:504
bool DirectoryOk(const char *DirName, bool LogErrors)
Definition tools.c:486
#define MEGABYTE(n)
Definition tools.h:45
unsigned char uchar
Definition tools.h:31
#define dsyslog(a...)
Definition tools.h:37
#define MALLOC(type, size)
Definition tools.h:47
#define esyslog(a...)
Definition tools.h:35
#define isyslog(a...)
Definition tools.h:36