15#define __STDC_FORMAT_MACROS
33#define SUMMARYFALLBACK
46#define DATAFORMATPES "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
47#define NAMEFORMATPES "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
48#define DATAFORMATTS "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
49#define NAMEFORMATTS "%s/%s/" DATAFORMATTS
51#define RESUMEFILESUFFIX "/resume%s%s"
53#define SUMMARYFILESUFFIX "/summary.vdr"
55#define INFOFILESUFFIX "/info"
56#define MARKSFILESUFFIX "/marks"
58#define SORTMODEFILE ".sort"
59#define TIMERRECFILE ".timer"
61#define MINDISKSPACE 1024
63#define REMOVECHECKDELTA 60
64#define DELETEDLIFETIME 300
65#define DISKCHECKDELTA 100
66#define REMOVELATENCY 10
67#define MARKSUPDATEDELTA 10
68#define MAXREMOVETIME 10
70#define MAX_LINK_LEVEL 6
72#define LIMIT_SECS_PER_MB_RADIO 5
83 virtual void Action(
void)
override;
89:
cThread(
"remove deleted recordings", true)
97 if (LockFile.
Lock()) {
98 time_t StartTime = time(NULL);
100 bool interrupted =
false;
102 for (
cRecording *r = DeletedRecordings->First(); r; ) {
111 if (r->RetentionExpired()) {
114 DeletedRecordings->Del(r);
119 r = DeletedRecordings->Next(r);
137 static time_t LastRemoveCheck = 0;
141 for (
const cRecording *r = DeletedRecordings->First(); r; r = DeletedRecordings->
Next(r)) {
142 if (r->RetentionExpired()) {
148 LastRemoveCheck = time(NULL);
159 static time_t LastFreeDiskCheck = 0;
160 int Factor = (Priority == -1) ? 10 : 1;
161 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
165 if (!LockFile.
Lock())
168 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
169 int NumDeletedRecordings = 0;
172 NumDeletedRecordings = DeletedRecordings->Count();
173 if (NumDeletedRecordings) {
181 r = DeletedRecordings->
Next(r);
186 DeletedRecordings->Del(r0);
191 if (NumDeletedRecordings == 0) {
196 if (DeletedRecordings->Count())
201 isyslog(
"...no deleted recording found, trying to delete an old recording...");
203 Recordings->SetExplicitModify();
204 if (Recordings->Count()) {
221 r = Recordings->
Next(r);
225 Recordings->SetModified();
230 isyslog(
"...no old recording found, giving up");
233 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
236 LastFreeDiskCheck = time(NULL);
242#define RESUME_NOT_INITIALIZED (-2)
268 esyslog(
"ERROR: can't allocate memory for resume file name");
293 if ((st.st_mode & S_IWUSR) == 0)
299 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
305 else if (errno != ENOENT)
313 while ((s = ReadLine.
Read(f)) != NULL) {
316 case 'I': resume = atoi(t);
323 else if (errno != ENOENT)
335 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
347 fprintf(f,
"I %d\n",
Index);
383 else if (errno != ENOENT)
414 for (
int i = 0; i <
MAXAPIDS; i++) {
415 const char *s = Channel->
Alang(i);
420 else if (strlen(s) > strlen(Component->
language))
427 for (
int i = 0; i <
MAXDPIDS; i++) {
428 const char *s = Channel->
Dlang(i);
435 else if (strlen(s) > strlen(Component->
language))
440 for (
int i = 0; i <
MAXSPIDS; i++) {
441 const char *s = Channel->
Slang(i);
446 else if (strlen(s) > strlen(Component->
language))
559 if (fstat(fileno(f), &st))
561 if (
modified == st.st_mtime && !Force)
571 while ((s = ReadLine.
Read(f)) != NULL) {
576 char *p = strchr(t,
' ');
587 unsigned int EventID;
590 unsigned int TableID = 0;
591 unsigned int Version = 0xFF;
592 int n = sscanf(t,
"%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
593 if (n >= 3 && n <= 5) {
607 int n = sscanf(t,
"%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &
frameWidth, &
frameHeight, &scanTypeCode, &arBuf);
637 case 'O':
errors = atoi(t);
638 if (t = strchr(t,
' '))
648 esyslog(
"ERROR: EPG data problem in line %d", line);
663 event->Dump(f, Prefix,
true);
668 fprintf(f,
"%sP %d\n", Prefix,
priority);
669 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
670 fprintf(f,
"%sO %d", Prefix,
errors);
675 fprintf(f,
"%s@ %s\n", Prefix,
aux);
691 else if (errno != ENOENT)
767 case ' ': *p =
'_';
break;
774 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
778 sprintf(buf,
"#%02X", (
unsigned char)*p);
779 memmove(p + 2, p, strlen(p) + 1);
784 esyslog(
"ERROR: out of memory");
791 case '_': *p =
' ';
break;
796 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
798 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
802 memmove(p + 1, p + 3, strlen(p) - 2);
808 case '\x01': *p =
'\'';
break;
809 case '\x02': *p =
'/';
break;
810 case '\x03': *p =
':';
break;
817 if (*p == (ToFileSystem ? ce->a : ce->b)) {
818 *p = ToFileSystem ? ce->b : ce->a;
840 int Length = strlen(s);
843 bool NameTooLong =
false;
847 for (
char *p = s; *p; p++) {
850 NameTooLong |= NameLength > NameMax;
871 NameTooLong |= NameLength > NameMax;
879 while (i-- > 0 && a[i] >= 0) {
884 if (NameLength > NameMax) {
887 while (i-- > 0 && a[i] >= 0) {
889 if (NameLength - l <= NameMax) {
890 memmove(s + i, s + n, Length - n + 1);
891 memmove(a + i, a + n, Length - n);
904 while (PathLength > PathMax && n > 0) {
909 while (--i > 0 && a[i - 1] >= 0) {
913 if (PathLength - l <= PathMax)
919 memmove(s + b, s + n, Length - n + 1);
945 const char *
Title = Event ? Event->
Title() : NULL;
946 const char *Subtitle = Event ? Event->
ShortText() : NULL;
953 if (macroTITLE || macroEPISODE) {
958 int l = strlen(
name);
1000 const char *p = strrchr(
FileName,
'/');
1005 time_t now = time(NULL);
1007 struct tm t = *localtime_r(&now, &tm_r);
1012 || 7 == sscanf(p + 1,
DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
1029 FILE *f = fopen(InfoFileName,
"r");
1032 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
1039 else if (errno != ENOENT)
1041#ifdef SUMMARYFALLBACK
1045 FILE *f = fopen(SummaryFileName,
"r");
1048 char *data[3] = { NULL };
1051 while ((s = ReadLine.
Read(f)) != NULL) {
1052 if (*s || line > 1) {
1054 int len = strlen(s);
1055 len += strlen(data[line]) + 1;
1056 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
1057 data[line] = NewBuffer;
1058 strcat(data[line],
"\n");
1059 strcat(data[line], s);
1062 esyslog(
"ERROR: out of memory");
1065 data[line] = strdup(s);
1075 else if (data[1] && data[2]) {
1079 int len = strlen(data[1]);
1081 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
1082 data[1] = NewBuffer;
1083 strcat(data[1],
"\n");
1084 strcat(data[1], data[2]);
1090 esyslog(
"ERROR: out of memory");
1096 for (
int i = 0; i < 3; i ++)
1099 else if (errno != ENOENT)
1121 char *t = s, *s1 = NULL, *s2 = NULL;
1142 memmove(s1, s2, t - s2 + 1);
1155 strftime(buf,
sizeof(buf),
"%Y%m%d%H%M", localtime_r(&
start, &tm_r));
1163 int l = strxfrm(NULL, s, 0) + 1;
1198 return time(NULL) -
Deleted() > Retention;
1218 int l = strlen(Path);
1238 struct tm *t = localtime_r(&
start, &tm_r);
1254 const char *New = NewIndicator &&
IsNew() ?
"*" :
"";
1255 const char *Err = NewIndicator && (
info->
Errors() > 0) ?
"!" :
"";
1262 struct tm *t = localtime_r(&
start, &tm_r);
1299 const char *s =
name;
1332 const char *s =
name;
1372 if (!OtherFileName) {
1375 if (ExistingInfo.
Read())
1400 dsyslog(
"changing priority/lifetime of '%s' to %d/%d",
Name(), NewPriority, NewLifetime);
1423 if (strcmp(NewName,
Name())) {
1424 dsyslog(
"changing name of '%s' to '%s'",
Name(), NewName);
1430 name = strdup(NewName);
1432 bool Exists = access(NewFileName, F_OK) == 0;
1434 esyslog(
"ERROR: recording '%s' already exists", NewName);
1437 name = strdup(OldName);
1453 char *NewName = strdup(
FileName());
1454 char *ext = strrchr(NewName,
'.');
1455 if (ext && strcmp(ext,
RECEXT) == 0) {
1456 strncpy(ext,
DELEXT, strlen(ext));
1457 if (access(NewName, F_OK) == 0) {
1459 isyslog(
"removing recording '%s'", NewName);
1463 if (access(
FileName(), F_OK) == 0) {
1499 char *NewName = strdup(
FileName());
1500 char *ext = strrchr(NewName,
'.');
1501 if (ext && strcmp(ext,
DELEXT) == 0) {
1502 strncpy(ext,
RECEXT, strlen(ext));
1503 if (access(NewName, F_OK) == 0) {
1505 esyslog(
"ERROR: attempt to restore '%s', while recording '%s' exists",
FileName(), NewName);
1510 if (access(
FileName(), F_OK) == 0) {
1572 if (IndexLength > 0) {
1615 void ScanVideoDir(
const char *DirName,
int LinkLevel = 0,
int DirLevel = 0);
1617 virtual void Action(
void)
override;
1624:
cThread(
"video directory scanner", true)
1660 if (lstat(buffer, &st) == 0) {
1662 if (S_ISLNK(st.st_mode)) {
1664 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1668 if (stat(buffer, &st) != 0)
1671 if (S_ISDIR(st.st_mode)) {
1679 Recordings->
Lock(StateKey,
true);
1681 dsyslog(
"activated name checking for initial read of video directory");
1711 if (!
initial && DirLevel == 0) {
1718 if (access(r->
FileName(), F_OK) != 0) {
1729 if (access(r->
FileName(), F_OK) != 0) {
1777 if (lastModified > time(NULL))
1797 if (Recording->Id() == Id)
1807 if (strcmp(Recording->FileName(), FileName) == 0)
1834 esyslog(
"ERROR: cRecordings::DelByName() called with '%s' on a list other than Recordings - ignored", FileName);
1837 char *DelRecFileName = strdup(FileName);
1838 if (
char *ext = strrchr(DelRecFileName,
'.')) {
1839 if (strcmp(ext,
RECEXT)) {
1840 esyslog(
"ERROR: cRecordings::DelByName() called with '%s', using '.rec' instead", DelRecFileName);
1841 strncpy(ext,
RECEXT, strlen(ext));
1847 esyslog(
"ERROR: cRecordings::DelByName(): '%s' not found in Recordings - using dummy", DelRecFileName);
1848 Recording = dummy =
new cRecording(FileName);
1852 Del(Recording,
false);
1853 char *ext = strrchr(Recording->
fileName,
'.');
1855 strncpy(ext,
DELEXT, strlen(ext));
1856 if (access(Recording->
FileName(), F_OK) == 0) {
1858 DeletedRecordings->Add(Recording);
1863 free(DelRecFileName);
1870 Recording->numFrames = -1;
1871 Recording->ReadInfo(
true);
1879 int FileSizeMB = Recording->FileSizeMB();
1880 if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
1891 if (Recording->IsOnVideoDirectoryFileSystem()) {
1892 int FileSizeMB = Recording->FileSizeMB();
1893 if (FileSizeMB > 0) {
1894 int LengthInSeconds = Recording->LengthInSeconds();
1895 if (LengthInSeconds > 0) {
1898 length += LengthInSeconds;
1904 return (size && length) ? double(size) * 60 / length : -1;
1911 if (Recording->IsInPath(Path))
1912 Use |= Recording->IsInUse();
1921 if (Recording->IsInPath(Path))
1929 if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
1930 dsyslog(
"moving '%s' to '%s'", OldPath, NewPath);
1933 if (Recording->IsInPath(OldPath)) {
1934 const char *p = Recording->Name() + strlen(OldPath);
1936 if (!Recording->ChangeName(NewName))
1950 if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
1951 Recording->ResetResume();
1958 Recording->ClearSortName();
1970 virtual void Action(
void)
override;
1972 cDirCopier(
const char *DirNameSrc,
const char *DirNameDst);
1995 dsyslog(
"suspending copy thread");
2001 dsyslog(
"resuming copy thread");
2018 size_t BufferSize = BUFSIZ;
2019 uchar *Buffer = NULL;
2033 size_t Read =
safe_read(From, Buffer, BufferSize);
2035 size_t Written =
safe_write(To, Buffer, Read);
2036 if (Written != Read) {
2037 esyslog(
"ERROR: can't write to destination file '%s': %m", *FileNameDst);
2041 else if (Read == 0) {
2043 if (fsync(To) < 0) {
2044 esyslog(
"ERROR: can't sync destination file '%s': %m", *FileNameDst);
2047 if (close(From) < 0) {
2048 esyslog(
"ERROR: can't close source file '%s': %m", *FileNameSrc);
2051 if (close(To) < 0) {
2052 esyslog(
"ERROR: can't close destination file '%s': %m", *FileNameDst);
2056 off_t FileSizeSrc =
FileSize(FileNameSrc);
2057 off_t FileSizeDst =
FileSize(FileNameDst);
2058 if (FileSizeSrc != FileSizeDst) {
2059 esyslog(
"ERROR: file size discrepancy: %" PRId64
" != %" PRId64, FileSizeSrc, FileSizeDst);
2064 esyslog(
"ERROR: can't read from source file '%s': %m", *FileNameSrc);
2068 else if ((e = d.
Next()) != NULL) {
2073 if (stat(FileNameSrc, &st) < 0) {
2074 esyslog(
"ERROR: can't access source file '%s': %m", *FileNameSrc);
2077 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
2078 esyslog(
"ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
2081 dsyslog(
"copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
2083 BufferSize =
max(
size_t(st.st_blksize * 10),
size_t(BUFSIZ));
2086 esyslog(
"ERROR: out of memory");
2090 if (access(FileNameDst, F_OK) == 0) {
2091 esyslog(
"ERROR: destination file '%s' already exists", *FileNameDst);
2094 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
2095 esyslog(
"ERROR: can't open source file '%s': %m", *FileNameSrc);
2098 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
2099 esyslog(
"ERROR: can't open destination file '%s': %m", *FileNameDst);
2138 int Usage(
const char *FileName = NULL)
const;
2166 if (FileName && *FileName) {
2199 Recordings->
Del(Recording);
2222 Recordings->
Del(Recording);
2242 Recordings->
Del(Recording);
2257 Recordings->
Del(Recording);
2288 Recordings->SetExplicitModify();
2291 if (!r->Active(Recordings)) {
2292 error |= r->Error();
2293 r->Cleanup(Recordings);
2310 if (FileName && *FileName) {
2314 if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
2323 dsyslog(
"recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2326 if (FileNameSrc && *FileNameSrc) {
2327 if (Usage ==
ruCut || FileNameDst && *FileNameDst) {
2329 if (Usage ==
ruCut && !FileNameDst)
2331 if (!
Get(FileNameSrc) && !
Get(FileNameDst)) {
2340 esyslog(
"ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2343 esyslog(
"ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2346 esyslog(
"ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2349 esyslog(
"ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2375 return r->Usage(FileName);
2382 int RequiredDiskSpaceMB = 0;
2386 if ((r->Usage() &
ruCut) != 0) {
2392 RequiredDiskSpaceMB +=
DirSizeMB(r->FileNameSrc());
2395 return RequiredDiskSpaceMB;
2401 if (
state != OldState) {
2446 const char *p = strchr(s,
' ');
2457 return fprintf(f,
"%s\n", *
ToText()) > 0;
2470 if (errno != ENOENT) {
2478bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
2492 time_t t = time(NULL);
2496 lastChange = LastModified > 0 ? LastModified : t;
2535 if (m->Position() - p) {
2546 if (m2->Position() < m1->Position()) {
2547 swap(m1->position, m2->position);
2548 swap(m1->comment, m2->comment);
2563 if (mi->Position() == Position)
2572 if (mi->Position() < Position)
2581 if (mi->Position() > Position)
2590 if (BeginMark && EndMark && BeginMark->
Position() == EndMark->
Position()) {
2591 while (
const cMark *NextMark =
Next(BeginMark)) {
2592 if (BeginMark->
Position() == NextMark->Position()) {
2593 if (!(BeginMark =
Next(NextMark)))
2608 if (EndMark && BeginMark && BeginMark->
Position() == EndMark->
Position()) {
2609 while (
const cMark *NextMark =
Next(EndMark)) {
2610 if (EndMark->
Position() == NextMark->Position()) {
2611 if (!(EndMark =
Next(NextMark)))
2623 int NumSequences = 0;
2631 if (NumSequences == 1 && BeginMark->Position() == 0)
2635 return NumSequences;
2640 if (
Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
2642 int EditedFrame = 0;
2644 bool InEdit =
false;
2646 int p = mi->Position();
2648 EditedFrame += p - PrevPos;
2651 EditedFrame -= p - Frame;
2663 EditedFrame += LastFrame - PrevPos;
2664 if (Frame < LastFrame)
2665 EditedFrame -= LastFrame - Frame;
2682 isyslog(
"executing '%s'", *cmd);
2689#define IFG_BUFFER_SIZE KILOBYTE(100)
2696 virtual void Action(
void)
override;
2703:
cThread(
"index file generator")
2716 bool IndexFileComplete =
false;
2717 bool IndexFileWritten =
false;
2718 bool Rewind =
false;
2727 off_t FrameOffset = -1;
2728 bool pendIndependentFrame =
false;
2729 uint16_t pendNumber = 0;
2730 off_t pendFileSize = 0;
2731 bool pendMissing =
false;
2735 bool Stuffed =
false;
2747 if (FrameDetector.
Synced()) {
2750 int OldPatVersion, OldPmtVersion;
2751 PatPmtParser.
GetVersions(OldPatVersion, OldPmtVersion);
2753 int NewPatVersion, NewPmtVersion;
2754 if (PatPmtParser.
GetVersions(NewPatVersion, NewPmtVersion)) {
2755 if (NewPatVersion != OldPatVersion || NewPmtVersion != OldPmtVersion) {
2756 dsyslog(
"PAT/PMT version change while generating index");
2757 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2763 int Processed = FrameDetector.
Analyze(Data, Length);
2764 if (Processed > 0) {
2765 bool PreviousErrors =
false;
2766 bool MissingFrames =
false;
2767 if (FrameDetector.
NewFrame(PreviousErrors, MissingFrames)) {
2769 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing);
2771 pendNumber = FileName.
Number();
2772 pendFileSize = FrameOffset >= 0 ? FrameOffset :
FileSize;
2773 pendMissing = MissingFrames;
2775 IndexFileWritten =
true;
2776 Errors = FrameDetector.
Errors();
2779 Buffer.
Del(Processed);
2784 int Processed = FrameDetector.
Analyze(Data, Length,
false);
2785 if (Processed > 0) {
2786 if (FrameDetector.
Synced()) {
2790 Buffer.
Del(Processed);
2800 else if (PatPmtParser.
IsPmtPid(Pid))
2806 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2812 Buffer.
Del(p - Data);
2816 else if (ReplayFile) {
2817 int Result = Buffer.
Read(ReplayFile, BufferChunks);
2819 if (Buffer.
Available() > 0 && !Stuffed) {
2828 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
2842 bool PreviousErrors =
false;
2843 Errors = FrameDetector.
Errors(&PreviousErrors);
2845 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing);
2846 IndexFileComplete =
true;
2851 if (IndexFileComplete) {
2852 if (IndexFileWritten) {
2854 if (RecordingInfo.
Read()) {
2859 Errors != RecordingInfo.
Errors()) {
2863 RecordingInfo.
Write();
2880#define INDEXFILESUFFIX "/index"
2883#define MAXINDEXCATCHUP 8
2884#define INDEXCATCHUPWAIT 100
2900 tIndexTs(off_t Offset,
bool Independent, uint16_t Number,
bool Errors,
bool Missing)
2906 independent = Independent;
2911#define MAXWAITFORINDEXFILE 10
2912#define INDEXFILECHECKINTERVAL 500
2913#define INDEXFILETESTINTERVAL 10
2927 if (!Record && PauseLive) {
2930 while (time(NULL) < tmax &&
FileSize(
fileName) < off_t(2 *
sizeof(tIndexTs)))
2943 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
2949 delta = int(buf.st_size %
sizeof(tIndexTs));
2951 delta =
sizeof(tIndexTs) - delta;
2952 esyslog(
"ERROR: invalid file size (%" PRId64
") in '%s'", buf.st_size, *
fileName);
2954 last = int((buf.st_size + delta) /
sizeof(tIndexTs) - 1);
2955 if (!Record &&
last >= 0) {
2980 esyslog(
"ERROR: can't allocate %zd bytes for index '%s'",
size *
sizeof(tIndexTs), *
fileName);
2992 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
2994 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
3021 while (Count-- > 0) {
3022 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
3023 IndexTs->offset = IndexPes.offset;
3024 IndexTs->independent = IndexPes.type == 1;
3025 IndexTs->number = IndexPes.number;
3033 while (Count-- > 0) {
3034 IndexPes.offset = uint32_t(IndexTs->offset);
3035 IndexPes.type =
uchar(IndexTs->independent ? 1 : 2);
3036 IndexPes.number =
uchar(IndexTs->number);
3037 IndexPes.reserved = 0;
3038 memcpy((
void *)IndexTs, &IndexPes,
sizeof(*IndexTs));
3054 if (fstat(
f, &buf) == 0) {
3055 int newLast = int(buf.st_size /
sizeof(tIndexTs) - 1);
3056 if (newLast >
last) {
3058 if (NewSize <= newLast) {
3060 if (NewSize <= newLast)
3061 NewSize = newLast + 1;
3063 if (tIndexTs *NewBuffer = (tIndexTs *)realloc(
index, NewSize *
sizeof(tIndexTs))) {
3066 int offset = (
last + 1) *
sizeof(tIndexTs);
3067 int delta = (newLast -
last) *
sizeof(tIndexTs);
3068 if (lseek(
f, offset, SEEK_SET) == offset) {
3070 esyslog(
"ERROR: can't read from index");
3085 esyslog(
"ERROR: can't realloc() index");
3104 return index != NULL;
3107bool cIndexFile::Write(
bool Independent, uint16_t FileNumber, off_t FileOffset,
bool Errors,
bool Missing)
3110 tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
3124bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length,
bool *Errors,
bool *Missing)
3128 if (Index >= 0 && Index <=
last) {
3129 *FileNumber =
index[Index].number;
3130 *FileOffset =
index[Index].offset;
3132 *Independent =
index[Index].independent;
3135 uint16_t fn =
index[Index + 1].number;
3136 off_t fo =
index[Index + 1].offset;
3137 if (fn == *FileNumber)
3138 *Length = int(fo - *FileOffset);
3146 *Errors =
index[Index].errors;
3148 *Missing =
index[Index].missing;
3159 tIndexTs *p = &
index[Index];
3160 if (p->errors || p->missing)
3171 int d = Forward ? 1 : -1;
3174 if (Index >= 0 && Index <=
last) {
3175 if (
index[Index].independent) {
3182 *FileNumber =
index[Index].number;
3183 *FileOffset =
index[Index].offset;
3186 uint16_t fn =
index[Index + 1].number;
3187 off_t fo =
index[Index + 1].offset;
3188 if (fn == *FileNumber)
3189 *Length = int(fo - *FileOffset);
3211 if (
index[Index].independent)
3217 if (
index[il].independent)
3224 if (
index[ih].independent)
3241 for (i = 0; i <=
last; i++) {
3242 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
3272 if (*s && stat(s, &buf) == 0)
3273 return buf.st_size / (IsPesRecording ?
sizeof(tIndexTs) :
sizeof(tIndexPes));
3281 if (Recording.
Name()) {
3284 unlink(IndexFileName);
3286 while (IndexFileGenerator->
Active())
3288 if (access(IndexFileName, R_OK) == 0)
3291 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
3294 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
3297 fprintf(stderr,
"'%s' is not a recording\n", FileName);
3300 fprintf(stderr,
"'%s' is not a directory\n", FileName);
3306#define MAXFILESPERRECORDINGPES 255
3307#define RECORDFILESUFFIXPES "/%03d.vdr"
3308#define MAXFILESPERRECORDINGTS 65535
3309#define RECORDFILESUFFIXTS "/%05d.ts"
3310#define RECORDFILESUFFIXLEN 20
3322 esyslog(
"ERROR: can't copy file name '%s'", FileName);
3352 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
3354 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
3358 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
3360 int Pid =
TsPid(buf);
3362 PatPmtParser.
ParsePat(buf,
sizeof(buf));
3363 else if (PatPmtParser.
IsPmtPid(Pid)) {
3364 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
3365 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
3376 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
3390 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
3404 else if (errno != ENOENT)
3434 if (buf.st_size != 0)
3438 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
3445 else if (errno != ENOENT) {
3452 if (!
record && Offset >= 0 &&
file->
Seek(Offset, SEEK_SET) != Offset) {
3459 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
3481 while ((s = ReadLine.
Read(f)) != NULL)
3499 if (fputs(
doneRecordings[i], f) == EOF || fputc(
'\n', f) == EOF) {
3521 if (FILE *f = fopen(
fileName,
"a")) {
3527 esyslog(
"ERROR: can't open '%s' for appending '%s'", *
fileName, Title);
3544 const char *t = Title;
3550 if (toupper(
uchar(*s)) != toupper(
uchar(*t)))
3565 const char *Sign =
"";
3571 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
3572 int s = int(Seconds);
3573 int m = s / 60 % 60;
3576 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
3582 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
3586 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
3592 return int(round(Seconds * FramesPerSecond));
3601 else if (Length > Max) {
3602 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
3605 int r = f->
Read(b, Length);
3625 if (fgets(buf,
sizeof(buf), f))
3654 dsyslog(
"writing timer id '%s' to %s", TimerId, *FileName);
3655 if (FILE *f = fopen(FileName,
"w")) {
3656 fprintf(f,
"%s\n", TimerId);
3663 dsyslog(
"removing %s", *FileName);
3671 const char *Id = NULL;
3672 if (FILE *f = fopen(FileName,
"r")) {
3673 char buf[HOST_NAME_MAX + 10];
3674 if (fgets(buf,
sizeof(buf), f)) {
3687 if (
const char *p = strchr(TimerId,
'@')) {
3689 dsyslog(
"found stale recording timer id %s", *TimerId);
3701 if (FileSizeMB > 0) {
3704 if (NumFramesOrg > 0) {
3706 if (NumFramesEdit > 0)
3707 return max(1,
int(FileSizeMB * (
double(NumFramesEdit) / NumFramesOrg)));
3716 if (FileSizeMB > 0) {
3720 if (access(EditedFileName, F_OK)) {
3721 int ExistingEditedSizeMB =
DirSizeMB(EditedFileName);
3722 if (ExistingEditedSizeMB > 0)
3723 FreeDiskMB += ExistingEditedSizeMB;
3727 return FileSizeMB < FreeDiskMB;
const char * Slang(int i) const
const char * Name(void) const
tChannelID GetChannelID(void) const
const char * Dlang(int i) const
const char * Alang(int i) const
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
int NumComponents(void) const
void SetComponent(int Index, const char *s)
bool TimedWait(cMutex &Mutex, int TimeoutMs)
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
bool Load(const char *FileName=NULL, bool AllowComments=false, bool MustExist=false)
bool Start(void)
Starts the actual cutting process.
cCutter(const char *FileName)
Sets up a new cutter for the given FileName, which must be the full path name of an existing recordin...
bool Error(void)
Returns true if an error occurred while cutting the recording.
bool Active(void)
Returns true if the cutter is currently active.
static cString EditedFileName(const char *FileName)
Returns the full path name of the edited version of the recording with the given FileName.
virtual ~cDirCopier() override
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
cDirCopier(const char *DirNameSrc, const char *DirNameDst)
cStringList doneRecordings
void Add(const char *Title)
void Append(const char *Title)
bool Load(const char *FileName)
bool Contains(const char *Title) const
const char * ShortText(void) const
const cComponents * Components(void) const
const char * Title(void) const
void SetStartTime(time_t StartTime)
void SetComponents(cComponents *Components)
void SetEventID(tEventID EventID)
void SetVersion(uchar Version)
void SetDuration(int Duration)
void SetTitle(const char *Title)
void SetTableID(uchar TableID)
cUnbufferedFile * NextFile(void)
cUnbufferedFile * Open(void)
cFileName(const char *FileName, bool Record, bool Blocking=false, bool IsPesRecording=false)
bool GetLastPatPmtVersions(int &PatVersion, int &PmtVersion)
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
bool Synced(void)
Returns true if the frame detector has synced on the data stream.
bool IndependentFrame(void)
Returns true if a new frame was detected and this is an independent frame (i.e.
double FramesPerSecond(void)
Returns the number of frames per second, or 0 if this information is not available.
uint16_t FrameWidth(void)
Returns the frame width, or 0 if this information is not available.
int Errors(bool *PreviousErrors=NULL, bool *MissingFrames=NULL)
Returns the total number of errors so far.
eScanType ScanType(void)
Returns the scan type, or stUnknown if this information is not available.
bool NewFrame(int *PreviousErrors=NULL, int *MissingFrames=NULL)
int Analyze(const uchar *Data, int Length, bool ErrorCheck=true)
Analyzes the TS packets pointed to by Data.
uint16_t FrameHeight(void)
Returns the frame height, or 0 if this information is not available.
void SetPid(int Pid, int Type)
Sets the Pid and stream Type to detect frames for.
eAspectRatio AspectRatio(void)
Returns the aspect ratio, or arUnknown if this information is not available.
cIndexFileGenerator(const char *RecordingName)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber=NULL, off_t *FileOffset=NULL, int *Length=NULL)
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors=false, bool Missing=false)
bool IsStillRecording(void)
void ConvertFromPes(tIndexTs *IndexTs, int Count)
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false)
static int GetLength(const char *FileName, bool IsPesRecording=false)
Calculates the recording length (number of frames) without actually reading the index file.
bool CatchUp(int Index=-1)
const cErrors * GetErrors(void)
Returns the frame indexes of errors in the recording (if any).
void ConvertToPes(tIndexTs *IndexTs, int Count)
cIndexFileGenerator * indexFileGenerator
static cString IndexFileName(const char *FileName, bool IsPesRecording)
int GetClosestIFrame(int Index)
Returns the index of the I-frame that is closest to the given Index (or Index itself,...
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL, bool *Errors=NULL, bool *Missing=NULL)
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
void Del(cListObject *Object, bool DeleteObject=true)
void SetExplicitModify(void)
If you have obtained a write lock on this list, and you don't want it to be automatically marked as m...
void SetModified(void)
Unconditionally marks this list as modified.
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
void Add(cListObject *Object, cListObject *After=NULL)
cListObject(const cListObject &ListObject)
cListObject * Next(void) const
const cMark * Prev(const cMark *Object) const
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
cList(const char *NeedsLocking=NULL)
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
const cMark * Last(void) const
bool Lock(int WaitSeconds=0)
cMark(int Position=0, const char *Comment=NULL, double FramesPerSecond=DEFAULTFRAMESPERSECOND)
bool Parse(const char *s)
const char * Comment(void) const
virtual ~cMark() override
int GetNumSequences(void) const
Returns the actual number of sequences to be cut from the recording.
void Add(int Position)
If this cMarks object is used by multiple threads, the caller must Lock() it before calling Add() and...
const cMark * GetNextBegin(const cMark *EndMark=NULL) const
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark.
const cMark * GetNext(int Position) const
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
const cMark * GetNextEnd(const cMark *BeginMark) const
Returns the next "end" mark after BeginMark, skipping any marks at the same position as BeginMark.
const cMark * Get(int Position) const
cString recordingFileName
static bool DeleteMarksFile(const cRecording *Recording)
int GetFrameAfterEdit(int Frame, int LastFrame) const
Returns the number of the given Frame within the region covered by begin/end sequences.
static cString MarksFileName(const cRecording *Recording)
Returns the marks file name for the given Recording (regardless whether such a file actually exists).
const cMark * GetPrev(int Position) const
bool GetVersions(int &PatVersion, int &PmtVersion) const
Returns true if a valid PAT/PMT has been parsed and stores the current version numbers in the given v...
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...
void ParsePat(const uchar *Data, int Length)
Parses the PAT data from the single TS packet in Data.
bool ParsePatPmt(const uchar *Data, int Length)
Parses the given Data (which may consist of several TS packets, typically an entire frame) and extrac...
void ParsePmt(const uchar *Data, int Length)
Parses the PMT data from the single TS packet in Data.
bool Completed(void)
Returns true if the PMT has been completely parsed.
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,...
struct dirent * Next(void)
static cRecordControl * GetRecordControl(const char *FileName)
char ScanTypeChar(void) const
void SetFramesPerSecond(double FramesPerSecond)
int TmpErrors(void) const
uint16_t FrameHeight(void) const
void SetTitle(const char *Title)
void SetDescription(const char *Description)
const char * AspectRatioText(void) const
const char * ShortText(void) const
eScanType ScanType(void) const
cRecordingInfo(const cChannel *Channel=NULL, const cEvent *Event=NULL)
void SetLifetime(int Lifetime)
bool Write(FILE *f, const char *Prefix="") const
const char * Title(void) const
cString FrameParams(void) const
const char * Aux(void) const
void SetFileName(const char *FileName)
void SetPriority(int Priority)
void SetParentalRating(int ParentalRating)
uint16_t FrameWidth(void) const
void SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio)
void SetShortText(const char *ShortText)
void SetAux(const char *Aux)
void SetData(const char *Title, const char *ShortText, const char *Description)
const char * Description(void) const
eAspectRatio AspectRatio(void) const
bool Read(FILE *f, bool Force=false)
void SetErrors(int Errors, int TmpErrors=0)
double FramesPerSecond(void) const
const cComponents * Components(void) const
static const char * command
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
int isOnVideoDirectoryFileSystem
virtual ~cRecording() override
bool ChangePriorityLifetime(int NewPriority, int NewLifetime)
Changes the priority and lifetime of this recording to the given values.
bool HasMarks(void) const
Returns true if this recording has any editing marks.
bool WriteInfo(const char *OtherFileName=NULL)
Writes the info file of this recording.
time_t GetLastReplayTime(void) const
Returns the time this recording was last replayed (which is actually the timestamp of the resume file...
int IsInUse(void) const
Checks whether this recording is currently in use and therefore shall not be tampered with.
bool ChangeName(const char *NewName)
Changes the name of this recording to the given value.
bool Undelete(void)
Changes the file name (both internally and on disk) to make this a "normal" recording.
void ResetResume(void) const
void ReadInfo(bool Force=false)
bool Delete(void)
Changes the file name (both internally and on disk) to make this a "deleted" recording.
cString Folder(void) const
Returns the name of the folder this recording is stored in (without the video directory).
void DeleteResume(void) const
int NumFrames(void) const
Returns the number of frames in this recording.
bool IsEdited(void) const
int GetResume(void) const
Returns the index of the frame where replay of this recording shall be resumed, or -1 in case of an e...
bool IsInPath(const char *Path) const
Returns true if this recording is stored anywhere under the given Path.
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
char * SortName(void) const
const char * Name(void) const
Returns the full name of the recording (without the video directory).
int NumFramesAfterEdit(void) const
Returns the number of frames in the edited version of this recording.
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
const char * PrefixFileName(char Prefix)
bool DeleteMarks(void)
Deletes the editing marks from this recording (if any).
bool IsOnVideoDirectoryFileSystem(void) const
int HierarchyLevels(void) const
int FileSizeMB(void) const
Returns the total file size of this recording (in MB), or -1 if the file size is unknown.
cString BaseName(void) const
Returns the base name of this recording (without the video directory and folder).
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
bool Remove(void)
Actually removes the file from the disk.
cRecording(const cRecording &)
int LengthInSecondsAfterEdit(void) const
Returns the length (in seconds) of the edited version of this recording, or -1 in case of error.
bool RetentionExpired(void) const
double FramesPerSecond(void) const
bool IsPesRecording(void) const
time_t Deleted(void) const
static char * StripEpisodeName(char *s, bool Strip)
int LengthInSeconds(void) const
Returns the length (in seconds) of this recording, or -1 in case of error.
const char * FileNameSrc(void) const
void Cleanup(cRecordings *Recordings)
~cRecordingsHandlerEntry()
int Usage(const char *FileName=NULL) const
bool Active(cRecordings *Recordings)
const char * FileNameDst(void) const
cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst)
void DelAll(void)
Deletes/terminates all operations.
virtual ~cRecordingsHandler() override
bool StateChanged(int &OldState)
Returns true if the state if this recordings handler has changed since the last call with the given O...
cRecordingsHandlerEntry * Get(const char *FileName)
bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst=NULL)
Adds the given FileNameSrc to the recordings handler for (later) processing.
bool Finished(bool &Error)
Returns true if all operations in the list have been finished.
int GetUsage(const char *FileName)
Returns the usage type for the given FileName.
cList< cRecordingsHandlerEntry > operations
void Del(const char *FileName)
Deletes the given FileName from the list of operations.
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
int GetRequiredDiskSpaceMB(const char *FileName=NULL)
Returns the total disk space required to process all actions.
void ResetResume(const char *ResumeFileName=NULL)
void UpdateByName(const char *FileName)
static const char * UpdateFileName(void)
double MBperMinute(void) const
Returns the average data rate (in MB/min) of all recordings, or -1 if this value is unknown.
virtual ~cRecordings() override
cRecordings(bool Deleted=false)
int GetNumRecordingsInPath(const char *Path) const
Returns the total number of recordings in the given Path, including all sub-folders of Path.
const cRecording * GetById(int Id) const
static cRecordings deletedRecordings
void AddByName(const char *FileName, bool TriggerUpdate=true)
static cRecordings recordings
int TotalFileSizeMB(void) const
static void Update(bool Wait=false)
Triggers an update of the list of recordings, which will run as a separate thread if Wait is false.
static cRecordings * GetRecordingsWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for write access.
static void TouchUpdate(void)
Touches the '.update' file in the video directory, so that other instances of VDR that access the sam...
void Add(cRecording *Recording)
static cVideoDirectoryScannerThread * videoDirectoryScannerThread
void DelByName(const char *FileName)
bool MoveRecordings(const char *OldPath, const char *NewPath)
Moves all recordings in OldPath to NewPath.
static bool NeedsUpdate(void)
void ClearSortNames(void)
static int lastRecordingId
static std::atomic< time_t > lastUpdate
const cRecording * GetByName(const char *FileName) const
static char * updateFileName
int PathIsInUse(const char *Path) const
Checks whether any recording in the given Path is currently in use and therefore the whole Path shall...
static bool HasKeys(void)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
cRemoveDeletedRecordingsThread(void)
static const char * NowReplaying(void)
void SetFileName(const char *FileName)
cResumeFile(const char *FileName, bool IsPesRecording)
void Del(int Count)
Deletes at most Count bytes from the ring buffer.
virtual void Clear(void) override
Immediately clears the ring buffer.
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the ring buffer.
uchar * Get(int &Count)
Gets data from the ring buffer.
int Read(int FileHandle, int Max=0)
Reads at most Max bytes from FileHandle and stores them in the ring buffer.
virtual int Available(void) override
int AlwaysSortFoldersFirst
char SVDRPHostName[HOST_NAME_MAX]
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
cString(const char *S=NULL, bool TakePointer=false)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
cString & Append(const char *String)
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
bool Active(void)
Checks whether the thread is still alive.
const char * Aux(void) const
const char * File(void) const
bool IsSingleEvent(void) const
void SetFile(const char *File)
time_t StartTime(void) const
The start time of this timer, which is the time as given by the user (for normal timers) or the start...
const cChannel * Channel(void) const
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner,...
static cUnbufferedFile * Create(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
ssize_t Read(void *Data, size_t Size)
off_t Seek(off_t Offset, int Whence)
virtual void Append(T Data)
cRecordings * deletedRecordings
void ScanVideoDir(const char *DirName, int LinkLevel=0, int DirLevel=0)
~cVideoDirectoryScannerThread()
cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
static cString PrefixVideoFileName(const char *FileName, char Prefix)
static void RemoveEmptyVideoDirectories(const char *IgnoreFiles[]=NULL)
static bool IsOnVideoDirectoryFileSystem(const char *FileName)
static const char * Name(void)
static cUnbufferedFile * OpenVideoFile(const char *FileName, int Flags)
static bool VideoFileSpaceAvailable(int SizeMB)
static bool MoveVideoFile(const char *FromName, const char *ToName)
static int VideoDiskSpace(int *FreeMB=NULL, int *UsedMB=NULL)
static bool RenameVideoFile(const char *OldName, const char *NewName)
static bool RemoveVideoFile(const char *FileName)
#define TIMERMACRO_EPISODE
#define MAXFILESPERRECORDINGPES
struct __attribute__((packed))
#define RECORDFILESUFFIXPES
#define SUMMARYFILESUFFIX
#define MAXFILESPERRECORDINGTS
tCharExchange CharExchange[]
cString GetRecordingTimerId(const char *Directory)
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
static const char * SkipFuzzyChars(const char *s)
void AssertFreeDiskSpace(int Priority, bool Force)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
void GetRecordingsSortMode(const char *Directory)
bool GenerateIndex(const char *FileName)
Generates the index of the existing recording with the given FileName.
char * LimitNameLengths(char *s, int PathMax, int NameMax)
static const char * FuzzyChars
bool NeedsConversion(const char *p)
int SecondsToFrames(int Seconds, double FramesPerSecond)
eRecordingsSortMode RecordingsSortMode
bool HasRecordingsSortMode(const char *Directory)
#define INDEXFILETESTINTERVAL
#define MAXWAITFORINDEXFILE
bool EnoughFreeDiskSpaceForEdit(const char *FileName)
#define INDEXFILECHECKINTERVAL
char * ExchangeChars(char *s, bool ToFileSystem)
void IncRecordingsSortMode(const char *Directory)
int HMSFToIndex(const char *HMSF, double FramesPerSecond)
#define LIMIT_SECS_PER_MB_RADIO
void SetRecordingsSortMode(const char *Directory, eRecordingsSortMode SortMode)
cDoneRecordings DoneRecordingsPattern
static cRemoveDeletedRecordingsThread RemoveDeletedRecordingsThread
int FileSizeMBafterEdit(const char *FileName)
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
cRecordingsHandler RecordingsHandler
cMutex MutexMarkFramesPerSecond
static bool StillRecording(const char *Directory)
#define RESUME_NOT_INITIALIZED
#define RECORDFILESUFFIXLEN
void SetRecordingTimerId(const char *Directory, const char *TimerId)
void ClrRecordingTimerId(const char *Directory)
#define RECORDFILESUFFIXTS
double MarkFramesPerSecond
const char * InvalidChars
void RemoveDeletedRecordings(void)
#define DEFAULTFRAMESPERSECOND
int HMSFToIndex(const char *HMSF, double FramesPerSecond=DEFAULTFRAMESPERSECOND)
eRecordingsSortMode RecordingsSortMode
#define RUC_COPIEDRECORDING
#define LOCK_DELETEDRECORDINGS_WRITE
char * ExchangeChars(char *s, bool ToFileSystem)
#define RUC_DELETERECORDING
#define RUC_MOVEDRECORDING
int FileSizeMBafterEdit(const char *FileName)
cRecordingsHandler RecordingsHandler
#define RUC_COPYINGRECORDING
#define LOCK_DELETEDRECORDINGS_READ
#define LOCK_RECORDINGS_WRITE
cString IndexToHMSF(int Index, bool WithFrame=false, double FramesPerSecond=DEFAULTFRAMESPERSECOND)
const char * AspectRatioTexts[]
const char * ScanTypeChars
int TsPid(const uchar *p)
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
static const tChannelID InvalidID
static tChannelID FromString(const char *s)
cString ToString(void) const
char language[MAXLANGCODE2]
int SystemExec(const char *Command, bool Detached)