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; ) {
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)) {
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);
252 esyslog(
"ERROR: can't allocate memory for resume file name");
266 if ((st.st_mode & S_IWUSR) == 0)
272 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
278 else if (errno != ENOENT)
287 while ((s = ReadLine.
Read(f)) != NULL) {
291 case 'I': resume = atoi(t);
298 else if (errno != ENOENT)
309 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
321 fprintf(f,
"I %d\n", Index);
350 else if (errno != ENOENT)
381 for (
int i = 0; i <
MAXAPIDS; i++) {
382 const char *s = Channel->
Alang(i);
387 else if (strlen(s) > strlen(Component->
language))
394 for (
int i = 0; i <
MAXDPIDS; i++) {
395 const char *s = Channel->
Dlang(i);
399 Component =
Components->GetComponent(i, 2, 5);
402 else if (strlen(s) > strlen(Component->
language))
407 for (
int i = 0; i <
MAXSPIDS; i++) {
408 const char *s = Channel->
Slang(i);
413 else if (strlen(s) > strlen(Component->
language))
504 if (fstat(fileno(f), &st))
506 if (
modified == st.st_mtime && !Force)
516 while ((s = ReadLine.
Read(f)) != NULL) {
521 char *p = strchr(t,
' ');
532 unsigned int EventID;
535 unsigned int TableID = 0;
536 unsigned int Version = 0xFF;
537 int n = sscanf(t,
"%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
538 if (n >= 3 && n <= 5) {
552 int n = sscanf(t,
"%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &
frameWidth, &
frameHeight, &scanTypeCode, &arBuf);
582 case 'O':
errors = atoi(t);
589 esyslog(
"ERROR: EPG data problem in line %d", line);
604 event->Dump(f, Prefix,
true);
609 fprintf(f,
"%sP %d\n", Prefix,
priority);
610 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
611 fprintf(f,
"%sO %d\n", Prefix,
errors);
613 fprintf(f,
"%s@ %s\n", Prefix,
aux);
629 else if (errno != ENOENT)
674#define RESUME_NOT_INITIALIZED (-2)
707 case ' ': *p =
'_';
break;
714 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
718 sprintf(buf,
"#%02X", (
unsigned char)*p);
719 memmove(p + 2, p, strlen(p) + 1);
724 esyslog(
"ERROR: out of memory");
731 case '_': *p =
' ';
break;
736 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
738 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
742 memmove(p + 1, p + 3, strlen(p) - 2);
748 case '\x01': *p =
'\'';
break;
749 case '\x02': *p =
'/';
break;
750 case '\x03': *p =
':';
break;
757 if (*p == (ToFileSystem ? ce->a : ce->b)) {
758 *p = ToFileSystem ? ce->b : ce->a;
780 int Length = strlen(s);
783 bool NameTooLong =
false;
787 for (
char *p = s; *p; p++) {
790 NameTooLong |= NameLength > NameMax;
811 NameTooLong |= NameLength > NameMax;
819 while (i-- > 0 && a[i] >= 0) {
824 if (NameLength > NameMax) {
827 while (i-- > 0 && a[i] >= 0) {
829 if (NameLength - l <= NameMax) {
830 memmove(s + i, s + n, Length - n + 1);
831 memmove(a + i, a + n, Length - n + 1);
844 while (PathLength > PathMax && n > 0) {
849 while (--i > 0 && a[i - 1] >= 0) {
853 if (PathLength - l <= PathMax)
859 memmove(s + b, s + n, Length - n + 1);
885 const char *
Title = Event ? Event->
Title() : NULL;
886 const char *Subtitle = Event ? Event->
ShortText() : NULL;
893 if (macroTITLE || macroEPISODE) {
898 int l = strlen(
name);
940 const char *p = strrchr(
FileName,
'/');
945 time_t now = time(NULL);
947 struct tm t = *localtime_r(&now, &tm_r);
952 || 7 == sscanf(p + 1,
DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
968 FILE *f = fopen(InfoFileName,
"r");
971 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
973 info->SetPriority(priority);
974 info->SetLifetime(lifetime);
978 else if (errno != ENOENT)
980#ifdef SUMMARYFALLBACK
984 FILE *f = fopen(SummaryFileName,
"r");
987 char *data[3] = { NULL };
990 while ((s = ReadLine.
Read(f)) != NULL) {
991 if (*s || line > 1) {
994 len += strlen(data[line]) + 1;
995 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
996 data[line] = NewBuffer;
997 strcat(data[line],
"\n");
998 strcat(data[line], s);
1001 esyslog(
"ERROR: out of memory");
1004 data[line] = strdup(s);
1014 else if (data[1] && data[2]) {
1018 int len = strlen(data[1]);
1020 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
1021 data[1] = NewBuffer;
1022 strcat(data[1],
"\n");
1023 strcat(data[1], data[2]);
1029 esyslog(
"ERROR: out of memory");
1032 info->SetData(data[0], data[1], data[2]);
1033 for (
int i = 0; i < 3; i ++)
1036 else if (errno != ENOENT)
1057 char *t = s, *s1 = NULL, *s2 = NULL;
1078 memmove(s1, s2, t - s2 + 1);
1091 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
1099 int l = strxfrm(NULL, s, 0) + 1;
1142 int l = strlen(Path);
1162 struct tm *t = localtime_r(&
start, &tm_r);
1178 const char *New = NewIndicator &&
IsNew() ?
"*" :
"";
1179 const char *Err = NewIndicator && (
info->Errors() > 0) ?
"!" :
"";
1184 struct tm *t = localtime_r(&
start, &tm_r);
1219 const char *s =
name;
1252 const char *s =
name;
1292 if (!OtherFileName) {
1295 if (ExistingInfo.
Read())
1320 dsyslog(
"changing priority/lifetime of '%s' to %d/%d",
Name(), NewPriority, NewLifetime);
1321 info->SetPriority(NewPriority);
1322 info->SetLifetime(NewLifetime);
1330 info->SetFileName(NewFileName);
1342 if (strcmp(NewName,
Name())) {
1343 dsyslog(
"changing name of '%s' to '%s'",
Name(), NewName);
1349 name = strdup(NewName);
1351 bool Exists = access(NewFileName, F_OK) == 0;
1353 esyslog(
"ERROR: recording '%s' already exists", NewName);
1356 name = strdup(OldName);
1361 info->SetFileName(NewFileName);
1371 char *NewName = strdup(
FileName());
1372 char *ext = strrchr(NewName,
'.');
1373 if (ext && strcmp(ext,
RECEXT) == 0) {
1374 strncpy(ext,
DELEXT, strlen(ext));
1375 if (access(NewName, F_OK) == 0) {
1377 isyslog(
"removing recording '%s'", NewName);
1381 if (access(
FileName(), F_OK) == 0) {
1408 char *NewName = strdup(
FileName());
1409 char *ext = strrchr(NewName,
'.');
1410 if (ext && strcmp(ext,
DELEXT) == 0) {
1411 strncpy(ext,
RECEXT, strlen(ext));
1412 if (access(NewName, F_OK) == 0) {
1414 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1466 if (IndexLength > 0) {
1509 void ScanVideoDir(
const char *DirName,
int LinkLevel = 0,
int DirLevel = 0);
1511 virtual void Action(
void)
override;
1518:
cThread(
"video directory scanner", true)
1554 if (lstat(buffer, &st) == 0) {
1556 if (S_ISLNK(st.st_mode)) {
1558 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1562 if (stat(buffer, &st) != 0)
1565 if (S_ISDIR(st.st_mode)) {
1573 Recordings->
Lock(StateKey,
true);
1575 dsyslog(
"activated name checking for initial read of video directory");
1603 if (!
initial && DirLevel == 0) {
1609 if (access(r->
FileName(), F_OK) != 0)
1655 if (lastModified > time(NULL))
1675 if (Recording->Id() == Id)
1685 if (strcmp(Recording->FileName(), FileName) == 0)
1714 Recording = dummy =
new cRecording(FileName);
1717 Del(Recording,
false);
1718 char *ext = strrchr(Recording->
fileName,
'.');
1720 strncpy(ext,
DELEXT, strlen(ext));
1721 if (access(Recording->
FileName(), F_OK) == 0) {
1723 DeletedRecordings->Add(Recording);
1734 Recording->numFrames = -1;
1735 Recording->ReadInfo(
true);
1743 int FileSizeMB = Recording->FileSizeMB();
1744 if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
1755 if (Recording->IsOnVideoDirectoryFileSystem()) {
1756 int FileSizeMB = Recording->FileSizeMB();
1757 if (FileSizeMB > 0) {
1758 int LengthInSeconds = Recording->LengthInSeconds();
1759 if (LengthInSeconds > 0) {
1762 length += LengthInSeconds;
1768 return (size && length) ? double(size) * 60 / length : -1;
1775 if (Recording->IsInPath(Path))
1776 Use |= Recording->IsInUse();
1785 if (Recording->IsInPath(Path))
1793 if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
1794 dsyslog(
"moving '%s' to '%s'", OldPath, NewPath);
1797 if (Recording->IsInPath(OldPath)) {
1798 const char *p = Recording->Name() + strlen(OldPath);
1800 if (!Recording->ChangeName(NewName))
1814 if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
1815 Recording->ResetResume();
1822 Recording->ClearSortName();
1834 virtual void Action(
void)
override;
1836 cDirCopier(
const char *DirNameSrc,
const char *DirNameDst);
1859 dsyslog(
"suspending copy thread");
1865 dsyslog(
"resuming copy thread");
1882 size_t BufferSize = BUFSIZ;
1883 uchar *Buffer = NULL;
1897 size_t Read =
safe_read(From, Buffer, BufferSize);
1899 size_t Written =
safe_write(To, Buffer, Read);
1900 if (Written != Read) {
1901 esyslog(
"ERROR: can't write to destination file '%s': %m", *FileNameDst);
1905 else if (Read == 0) {
1907 if (fsync(To) < 0) {
1908 esyslog(
"ERROR: can't sync destination file '%s': %m", *FileNameDst);
1911 if (close(From) < 0) {
1912 esyslog(
"ERROR: can't close source file '%s': %m", *FileNameSrc);
1915 if (close(To) < 0) {
1916 esyslog(
"ERROR: can't close destination file '%s': %m", *FileNameDst);
1920 off_t FileSizeSrc =
FileSize(FileNameSrc);
1921 off_t FileSizeDst =
FileSize(FileNameDst);
1922 if (FileSizeSrc != FileSizeDst) {
1923 esyslog(
"ERROR: file size discrepancy: %" PRId64
" != %" PRId64, FileSizeSrc, FileSizeDst);
1928 esyslog(
"ERROR: can't read from source file '%s': %m", *FileNameSrc);
1932 else if ((e = d.
Next()) != NULL) {
1937 if (stat(FileNameSrc, &st) < 0) {
1938 esyslog(
"ERROR: can't access source file '%s': %m", *FileNameSrc);
1941 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
1942 esyslog(
"ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
1945 dsyslog(
"copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
1947 BufferSize =
max(
size_t(st.st_blksize * 10),
size_t(BUFSIZ));
1950 esyslog(
"ERROR: out of memory");
1954 if (access(FileNameDst, F_OK) == 0) {
1955 esyslog(
"ERROR: destination file '%s' already exists", *FileNameDst);
1958 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
1959 esyslog(
"ERROR: can't open source file '%s': %m", *FileNameSrc);
1962 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
1963 esyslog(
"ERROR: can't open destination file '%s': %m", *FileNameDst);
2002 int Usage(
const char *FileName = NULL)
const;
2030 if (FileName && *FileName) {
2079 if (Recording.
Delete()) {
2099 Recording->Delete();
2113 Recording->Delete();
2143 Recordings->SetExplicitModify();
2146 if (!r->Active(Recordings)) {
2147 error |= r->Error();
2148 r->Cleanup(Recordings);
2164 if (FileName && *FileName) {
2168 if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
2177 dsyslog(
"recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2180 if (FileNameSrc && *FileNameSrc) {
2181 if (Usage ==
ruCut || FileNameDst && *FileNameDst) {
2183 if (Usage ==
ruCut && !FileNameDst)
2185 if (!
Get(FileNameSrc) && !
Get(FileNameDst)) {
2193 esyslog(
"ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2196 esyslog(
"ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2199 esyslog(
"ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2202 esyslog(
"ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2224 return r->Usage(FileName);
2230 int RequiredDiskSpaceMB = 0;
2234 if ((r->Usage() &
ruCut) != 0) {
2240 RequiredDiskSpaceMB +=
DirSizeMB(r->FileNameSrc());
2243 return RequiredDiskSpaceMB;
2284 const char *p = strchr(s,
' ');
2295 return fprintf(f,
"%s\n", *
ToText()) > 0;
2308 if (errno != ENOENT) {
2316bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
2330 time_t t = time(NULL);
2334 lastChange = LastModified > 0 ? LastModified : t;
2373 if (m->Position() - p) {
2384 if (m2->Position() < m1->Position()) {
2385 swap(m1->position, m2->position);
2386 swap(m1->comment, m2->comment);
2401 if (mi->Position() == Position)
2410 if (mi->Position() < Position)
2419 if (mi->Position() > Position)
2428 if (BeginMark && EndMark && BeginMark->
Position() == EndMark->
Position()) {
2429 while (
const cMark *NextMark =
Next(BeginMark)) {
2430 if (BeginMark->
Position() == NextMark->Position()) {
2431 if (!(BeginMark =
Next(NextMark)))
2446 if (EndMark && BeginMark && BeginMark->
Position() == EndMark->
Position()) {
2447 while (
const cMark *NextMark =
Next(EndMark)) {
2448 if (EndMark->
Position() == NextMark->Position()) {
2449 if (!(EndMark =
Next(NextMark)))
2461 int NumSequences = 0;
2469 if (NumSequences == 1 && BeginMark->Position() == 0)
2473 return NumSequences;
2478 if (
Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
2480 int EditedFrame = 0;
2482 bool InEdit =
false;
2484 int p = mi->Position();
2486 EditedFrame += p - PrevPos;
2489 EditedFrame -= p - Frame;
2501 EditedFrame += LastFrame - PrevPos;
2502 if (Frame < LastFrame)
2503 EditedFrame -= LastFrame - Frame;
2520 isyslog(
"executing '%s'", *cmd);
2527#define IFG_BUFFER_SIZE KILOBYTE(100)
2534 virtual void Action(
void)
override;
2541:
cThread(
"index file generator")
2555 bool IndexFileComplete =
false;
2556 bool IndexFileWritten =
false;
2557 bool Rewind =
false;
2566 off_t FrameOffset = -1;
2567 uint16_t FileNumber = 1;
2568 off_t FileOffset = 0;
2570 bool pendIndependentFrame =
false;
2571 uint16_t pendNumber = 0;
2572 off_t pendFileSize = 0;
2573 bool pendErrors =
false;
2574 bool pendMissing =
false;
2580 Last = IndexFile.
Last();
2581 if (Last >= 0 && !IndexFile.
Get(Last, &FileNumber, &FileOffset, &Independent, &Length))
2585 isyslog(
"updating index file");
2588 isyslog(
"generating index file");
2592 bool Stuffed =
false;
2596 ReplayFile = FileName.
SetOffset(FileNumber, FileOffset);
2605 if (FrameDetector.
Synced()) {
2608 int OldPatVersion, OldPmtVersion;
2609 PatPmtParser.
GetVersions(OldPatVersion, OldPmtVersion);
2611 int NewPatVersion, NewPmtVersion;
2612 if (PatPmtParser.
GetVersions(NewPatVersion, NewPmtVersion)) {
2613 if (NewPatVersion != OldPatVersion || NewPmtVersion != OldPmtVersion) {
2614 dsyslog(
"PAT/PMT version change while generating index");
2615 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2621 int Processed = FrameDetector.
Analyze(Data, Length);
2622 if (Processed > 0) {
2623 int PreviousErrors = 0;
2624 int MissingFrames = 0;
2625 if (FrameDetector.
NewFrame(&PreviousErrors, &MissingFrames)) {
2626 if (IndexFileWritten || Last < 0) {
2628 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
2630 pendNumber = FileName.
Number();
2631 pendFileSize = FrameOffset >= 0 ? FrameOffset :
FileSize;
2632 pendErrors = PreviousErrors;
2633 pendMissing = MissingFrames;
2636 IndexFileWritten =
true;
2643 Buffer.
Del(Processed);
2648 int Processed = FrameDetector.
Analyze(Data, Length,
false);
2649 if (Processed > 0) {
2650 if (FrameDetector.
Synced()) {
2654 Buffer.
Del(Processed);
2664 else if (PatPmtParser.
IsPmtPid(Pid))
2670 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2676 Buffer.
Del(p - Data);
2680 else if (ReplayFile) {
2681 int Result = Buffer.
Read(ReplayFile, BufferChunks);
2683 if (Buffer.
Available() > 0 && !Stuffed) {
2692 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
2707 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
2708 IndexFileComplete =
true;
2713 if (IndexFileComplete) {
2714 if (IndexFileWritten) {
2716 if (RecordingInfo.
Read()) {
2721 Errors != RecordingInfo.
Errors()) {
2725 RecordingInfo.
Write();
2730 Skins.QueueMessage(
mtInfo,
tr(
"Index file regeneration complete"));
2734 Skins.QueueMessage(
mtError,
tr(
"Index file regeneration failed!"));
2742#define INDEXFILESUFFIX "/index"
2745#define MAXINDEXCATCHUP 8
2746#define INDEXCATCHUPWAIT 100
2762 tIndexTs(off_t Offset,
bool Independent, uint16_t Number,
bool Errors,
bool Missing)
2768 independent = Independent;
2773#define MAXWAITFORINDEXFILE 10
2774#define INDEXFILECHECKINTERVAL 500
2775#define INDEXFILETESTINTERVAL 10
2789 if (!Record && PauseLive) {
2792 while (time(NULL) < tmax &&
FileSize(
fileName) < off_t(2 *
sizeof(tIndexTs)))
2805 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
2811 delta = int(buf.st_size %
sizeof(tIndexTs));
2813 delta =
sizeof(tIndexTs) - delta;
2814 esyslog(
"ERROR: invalid file size (%" PRId64
") in '%s'", buf.st_size, *
fileName);
2816 last = int((buf.st_size + delta) /
sizeof(tIndexTs) - 1);
2817 if ((!Record || Update) &&
last >= 0) {
2842 esyslog(
"ERROR: can't allocate %zd bytes for index '%s'",
size *
sizeof(tIndexTs), *
fileName);
2854 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
2856 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
2883 while (Count-- > 0) {
2884 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
2885 IndexTs->offset = IndexPes.offset;
2886 IndexTs->independent = IndexPes.type == 1;
2887 IndexTs->number = IndexPes.number;
2895 while (Count-- > 0) {
2896 IndexPes.offset = uint32_t(IndexTs->offset);
2897 IndexPes.type =
uchar(IndexTs->independent ? 1 : 2);
2898 IndexPes.number =
uchar(IndexTs->number);
2899 IndexPes.reserved = 0;
2900 memcpy((
void *)IndexTs, &IndexPes,
sizeof(*IndexTs));
2914 if (fstat(
f, &buf) == 0) {
2915 int newLast = int(buf.st_size /
sizeof(tIndexTs) - 1);
2916 if (newLast >
last) {
2918 if (NewSize <= newLast) {
2920 if (NewSize <= newLast)
2921 NewSize = newLast + 1;
2923 if (tIndexTs *NewBuffer = (tIndexTs *)realloc(
index, NewSize *
sizeof(tIndexTs))) {
2926 int offset = (
last + 1) *
sizeof(tIndexTs);
2927 int delta = (newLast -
last) *
sizeof(tIndexTs);
2928 if (lseek(
f, offset, SEEK_SET) == offset) {
2930 esyslog(
"ERROR: can't read from index");
2945 esyslog(
"ERROR: can't realloc() index");
2958 return index != NULL;
2961bool cIndexFile::Write(
bool Independent, uint16_t FileNumber, off_t FileOffset,
bool Errors,
bool Missing)
2964 tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
2978bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length,
bool *Errors,
bool *Missing)
2981 if (Index >= 0 && Index <=
last) {
2982 *FileNumber =
index[Index].number;
2983 *FileOffset =
index[Index].offset;
2985 *Independent =
index[Index].independent;
2988 uint16_t fn =
index[Index + 1].number;
2989 off_t fo =
index[Index + 1].offset;
2990 if (fn == *FileNumber)
2991 *Length = int(fo - *FileOffset);
2999 *Errors =
index[Index].errors;
3001 *Missing =
index[Index].missing;
3011 tIndexTs *p = &
index[Index];
3012 if (p->errors || p->missing)
3022 int d = Forward ? 1 : -1;
3025 if (Index >= 0 && Index <=
last) {
3026 if (
index[Index].independent) {
3033 *FileNumber =
index[Index].number;
3034 *FileOffset =
index[Index].offset;
3037 uint16_t fn =
index[Index + 1].number;
3038 off_t fo =
index[Index + 1].offset;
3039 if (fn == *FileNumber)
3040 *Length = int(fo - *FileOffset);
3061 if (
index[Index].independent)
3067 if (
index[il].independent)
3074 if (
index[ih].independent)
3090 for (i = 0; i <=
last; i++) {
3091 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
3120 if (*s && stat(s, &buf) == 0)
3121 return buf.st_size / (IsPesRecording ?
sizeof(tIndexTs) :
sizeof(tIndexPes));
3129 if (Recording.
Name()) {
3133 unlink(IndexFileName);
3135 while (IndexFileGenerator->
Active())
3137 if (access(IndexFileName, R_OK) == 0)
3140 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
3143 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
3146 fprintf(stderr,
"'%s' is not a recording\n", FileName);
3149 fprintf(stderr,
"'%s' is not a directory\n", FileName);
3155#define MAXFILESPERRECORDINGPES 255
3156#define RECORDFILESUFFIXPES "/%03d.vdr"
3157#define MAXFILESPERRECORDINGTS 65535
3158#define RECORDFILESUFFIXTS "/%05d.ts"
3159#define RECORDFILESUFFIXLEN 20
3171 esyslog(
"ERROR: can't copy file name '%s'", FileName);
3201 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
3203 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
3207 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
3209 int Pid =
TsPid(buf);
3211 PatPmtParser.
ParsePat(buf,
sizeof(buf));
3212 else if (PatPmtParser.
IsPmtPid(Pid)) {
3213 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
3214 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
3225 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
3239 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
3253 else if (errno != ENOENT)
3263 if (
file->Close() < 0)
3283 if (buf.st_size != 0)
3287 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
3294 else if (errno != ENOENT) {
3301 if (!
record && Offset >= 0 &&
file->Seek(Offset, SEEK_SET) != Offset) {
3308 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
3330 while ((s = ReadLine.
Read(f)) != NULL)
3348 if (fputs(
doneRecordings[i], f) == EOF || fputc(
'\n', f) == EOF) {
3370 if (FILE *f = fopen(
fileName,
"a")) {
3376 esyslog(
"ERROR: can't open '%s' for appending '%s'", *
fileName, Title);
3393 const char *t = Title;
3399 if (toupper(
uchar(*s)) != toupper(
uchar(*t)))
3414 const char *Sign =
"";
3420 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
3421 int s = int(Seconds);
3422 int m = s / 60 % 60;
3425 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
3431 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
3435 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
3441 return int(round(Seconds * FramesPerSecond));
3450 else if (Length > Max) {
3451 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
3454 int r = f->
Read(b, Length);
3474 if (fgets(buf,
sizeof(buf), f))
3503 dsyslog(
"writing timer id '%s' to %s", TimerId, *FileName);
3504 if (FILE *f = fopen(FileName,
"w")) {
3505 fprintf(f,
"%s\n", TimerId);
3512 dsyslog(
"removing %s", *FileName);
3520 const char *Id = NULL;
3521 if (FILE *f = fopen(FileName,
"r")) {
3522 char buf[HOST_NAME_MAX + 10];
3523 if (fgets(buf,
sizeof(buf), f)) {
3537 if (FileSizeMB > 0) {
3540 if (NumFramesOrg > 0) {
3542 if (NumFramesEdit > 0)
3543 return max(1,
int(FileSizeMB * (
double(NumFramesEdit) / NumFramesOrg)));
3552 if (FileSizeMB > 0) {
3556 if (access(EditedFileName, F_OK)) {
3557 int ExistingEditedSizeMB =
DirSizeMB(EditedFileName);
3558 if (ExistingEditedSizeMB > 0)
3559 FreeDiskMB += ExistingEditedSizeMB;
3563 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
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)
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 char * Title(void) const
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.
eScanType ScanType(void)
Returns the scan type, or stUnknown if this information is not available.
bool NewFrame(int *PreviousErrors=NULL, int *MissingFrames=NULL)
Returns true if the data given to the last call to Analyze() started a new frame.
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, bool Update=false)
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)
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)
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false, bool Update=false)
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)
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.
void Del(cListObject *Object, bool DeleteObject=true)
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 cRecording * First(void) const
cList(const char *NeedsLocking=NULL)
const cRecording * Next(const cRecording *Object) const
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)
uint16_t FrameHeight(void) const
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)
uint16_t FrameWidth(void) const
void SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio)
void SetErrors(int Errors)
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)
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 in info file of this recording.
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 so that it will be visible in the "Recordings" menu again and not processed by ...
void ResetResume(void) const
void ReadInfo(bool Force=false)
bool Delete(void)
Changes the file name so that it will no longer be visible in the "Recordings" menu Returns false in ...
cString Folder(void) const
Returns the name of the folder this recording is stored in (without the video directory).
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 Returns false in case of error.
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.
double FramesPerSecond(void) const
bool IsPesRecording(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
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
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)
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
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
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)
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 MAXFILESPERRECORDINGTS
tCharExchange CharExchange[]
cString GetRecordingTimerId(const char *Directory)
bool GenerateIndex(const char *FileName, bool Update)
Generates the index of the existing recording with the given FileName.
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)
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 MAXFILESPERRECORDINGPES
#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)
struct __attribute__((packed))
#define RESUME_NOT_INITIALIZED
#define RECORDFILESUFFIXLEN
#define RECORDFILESUFFIXPES
void SetRecordingTimerId(const char *Directory, const char *TimerId)
#define RECORDFILESUFFIXTS
double MarkFramesPerSecond
const char * InvalidChars
void RemoveDeletedRecordings(void)
#define SUMMARYFILESUFFIX
#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)
char language[MAXLANGCODE2]
int SystemExec(const char *Command, bool Detached)