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)
256 esyslog(
"ERROR: can't allocate memory for resume file name");
285 if ((st.st_mode & S_IWUSR) == 0)
291 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
297 else if (errno != ENOENT)
306 while ((s = ReadLine.
Read(f)) != NULL) {
310 case 'I': resume = atoi(t);
317 else if (errno != ENOENT)
329 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
341 fprintf(f,
"I %d\n",
Index);
377 else if (errno != ENOENT)
408 for (
int i = 0; i <
MAXAPIDS; i++) {
409 const char *s = Channel->
Alang(i);
414 else if (strlen(s) > strlen(Component->
language))
421 for (
int i = 0; i <
MAXDPIDS; i++) {
422 const char *s = Channel->
Dlang(i);
426 Component =
Components->GetComponent(i, 2, 5);
429 else if (strlen(s) > strlen(Component->
language))
434 for (
int i = 0; i <
MAXSPIDS; i++) {
435 const char *s = Channel->
Slang(i);
440 else if (strlen(s) > strlen(Component->
language))
533 if (fstat(fileno(f), &st))
535 if (
modified == st.st_mtime && !Force)
545 while ((s = ReadLine.
Read(f)) != NULL) {
550 char *p = strchr(t,
' ');
561 unsigned int EventID;
564 unsigned int TableID = 0;
565 unsigned int Version = 0xFF;
566 int n = sscanf(t,
"%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
567 if (n >= 3 && n <= 5) {
581 int n = sscanf(t,
"%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &
frameWidth, &
frameHeight, &scanTypeCode, &arBuf);
611 case 'O':
errors = atoi(t);
612 if (t = strchr(t,
' '))
622 esyslog(
"ERROR: EPG data problem in line %d", line);
637 event->Dump(f, Prefix,
true);
642 fprintf(f,
"%sP %d\n", Prefix,
priority);
643 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
644 fprintf(f,
"%sO %d", Prefix,
errors);
649 fprintf(f,
"%s@ %s\n", Prefix,
aux);
665 else if (errno != ENOENT)
741 case ' ': *p =
'_';
break;
748 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
752 sprintf(buf,
"#%02X", (
unsigned char)*p);
753 memmove(p + 2, p, strlen(p) + 1);
758 esyslog(
"ERROR: out of memory");
765 case '_': *p =
' ';
break;
770 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
772 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
776 memmove(p + 1, p + 3, strlen(p) - 2);
782 case '\x01': *p =
'\'';
break;
783 case '\x02': *p =
'/';
break;
784 case '\x03': *p =
':';
break;
791 if (*p == (ToFileSystem ? ce->a : ce->b)) {
792 *p = ToFileSystem ? ce->b : ce->a;
814 int Length = strlen(s);
817 bool NameTooLong =
false;
821 for (
char *p = s; *p; p++) {
824 NameTooLong |= NameLength > NameMax;
845 NameTooLong |= NameLength > NameMax;
853 while (i-- > 0 && a[i] >= 0) {
858 if (NameLength > NameMax) {
861 while (i-- > 0 && a[i] >= 0) {
863 if (NameLength - l <= NameMax) {
864 memmove(s + i, s + n, Length - n + 1);
865 memmove(a + i, a + n, Length - n + 1);
878 while (PathLength > PathMax && n > 0) {
883 while (--i > 0 && a[i - 1] >= 0) {
887 if (PathLength - l <= PathMax)
893 memmove(s + b, s + n, Length - n + 1);
919 const char *
Title = Event ? Event->
Title() : NULL;
920 const char *Subtitle = Event ? Event->
ShortText() : NULL;
927 if (macroTITLE || macroEPISODE) {
932 int l = strlen(
name);
974 const char *p = strrchr(
FileName,
'/');
979 time_t now = time(NULL);
981 struct tm t = *localtime_r(&now, &tm_r);
986 || 7 == sscanf(p + 1,
DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
1003 FILE *f = fopen(InfoFileName,
"r");
1006 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
1008 info->SetPriority(priority);
1009 info->SetLifetime(lifetime);
1013 else if (errno != ENOENT)
1015#ifdef SUMMARYFALLBACK
1019 FILE *f = fopen(SummaryFileName,
"r");
1022 char *data[3] = { NULL };
1025 while ((s = ReadLine.
Read(f)) != NULL) {
1026 if (*s || line > 1) {
1028 int len = strlen(s);
1029 len += strlen(data[line]) + 1;
1030 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
1031 data[line] = NewBuffer;
1032 strcat(data[line],
"\n");
1033 strcat(data[line], s);
1036 esyslog(
"ERROR: out of memory");
1039 data[line] = strdup(s);
1049 else if (data[1] && data[2]) {
1053 int len = strlen(data[1]);
1055 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
1056 data[1] = NewBuffer;
1057 strcat(data[1],
"\n");
1058 strcat(data[1], data[2]);
1064 esyslog(
"ERROR: out of memory");
1067 info->SetData(data[0], data[1], data[2]);
1068 for (
int i = 0; i < 3; i ++)
1071 else if (errno != ENOENT)
1093 char *t = s, *s1 = NULL, *s2 = NULL;
1114 memmove(s1, s2, t - s2 + 1);
1127 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
1135 int l = strxfrm(NULL, s, 0) + 1;
1170 return time(NULL) -
Deleted() > Retention;
1193 int l = strlen(Path);
1213 struct tm *t = localtime_r(&
start, &tm_r);
1229 const char *New = NewIndicator &&
IsNew() ?
"*" :
"";
1230 const char *Err = NewIndicator && (
info->Errors() > 0) ?
"!" :
"";
1235 struct tm *t = localtime_r(&
start, &tm_r);
1270 const char *s =
name;
1303 const char *s =
name;
1343 if (!OtherFileName) {
1346 if (ExistingInfo.
Read())
1371 dsyslog(
"changing priority/lifetime of '%s' to %d/%d",
Name(), NewPriority, NewLifetime);
1372 info->SetPriority(NewPriority);
1373 info->SetLifetime(NewLifetime);
1381 info->SetFileName(NewFileName);
1393 if (strcmp(NewName,
Name())) {
1394 dsyslog(
"changing name of '%s' to '%s'",
Name(), NewName);
1400 name = strdup(NewName);
1402 bool Exists = access(NewFileName, F_OK) == 0;
1404 esyslog(
"ERROR: recording '%s' already exists", NewName);
1407 name = strdup(OldName);
1412 info->SetFileName(NewFileName);
1422 char *NewName = strdup(
FileName());
1423 char *ext = strrchr(NewName,
'.');
1424 if (ext && strcmp(ext,
RECEXT) == 0) {
1425 strncpy(ext,
DELEXT, strlen(ext));
1426 if (access(NewName, F_OK) == 0) {
1428 isyslog(
"removing recording '%s'", NewName);
1432 if (access(
FileName(), F_OK) == 0) {
1465 char *NewName = strdup(
FileName());
1466 char *ext = strrchr(NewName,
'.');
1467 if (ext && strcmp(ext,
DELEXT) == 0) {
1468 strncpy(ext,
RECEXT, strlen(ext));
1469 if (access(NewName, F_OK) == 0) {
1471 esyslog(
"ERROR: attempt to restore '%s', while recording '%s' exists",
FileName(), NewName);
1476 if (access(
FileName(), F_OK) == 0) {
1534 if (IndexLength > 0) {
1577 void ScanVideoDir(
const char *DirName,
int LinkLevel = 0,
int DirLevel = 0);
1579 virtual void Action(
void)
override;
1586:
cThread(
"video directory scanner", true)
1622 if (lstat(buffer, &st) == 0) {
1624 if (S_ISLNK(st.st_mode)) {
1626 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1630 if (stat(buffer, &st) != 0)
1633 if (S_ISDIR(st.st_mode)) {
1641 Recordings->
Lock(StateKey,
true);
1643 dsyslog(
"activated name checking for initial read of video directory");
1671 if (!
initial && DirLevel == 0) {
1678 if (access(r->
FileName(), F_OK) != 0) {
1689 if (access(r->
FileName(), F_OK) != 0) {
1737 if (lastModified > time(NULL))
1757 if (Recording->Id() == Id)
1767 if (strcmp(Recording->FileName(), FileName) == 0)
1794 esyslog(
"ERROR: cRecordings::DelByName() called with '%s' on a list other than Recordings - ignored", FileName);
1797 char *DelRecFileName = strdup(FileName);
1798 if (
char *ext = strrchr(DelRecFileName,
'.')) {
1799 if (strcmp(ext,
DELEXT)) {
1800 esyslog(
"ERROR: cRecordings::DelByName() called with '%s', using '.rec' instead", DelRecFileName);
1801 strncpy(ext,
RECEXT, strlen(ext));
1807 esyslog(
"ERROR: cRecordings::DelByName(): '%s' not found in Recordings - using dummy", DelRecFileName);
1808 Recording = dummy =
new cRecording(FileName);
1812 Del(Recording,
false);
1813 char *ext = strrchr(Recording->
fileName,
'.');
1815 strncpy(ext,
DELEXT, strlen(ext));
1816 if (access(Recording->
FileName(), F_OK) == 0) {
1818 DeletedRecordings->Add(Recording);
1823 free(DelRecFileName);
1830 Recording->numFrames = -1;
1831 Recording->ReadInfo(
true);
1839 int FileSizeMB = Recording->FileSizeMB();
1840 if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
1851 if (Recording->IsOnVideoDirectoryFileSystem()) {
1852 int FileSizeMB = Recording->FileSizeMB();
1853 if (FileSizeMB > 0) {
1854 int LengthInSeconds = Recording->LengthInSeconds();
1855 if (LengthInSeconds > 0) {
1858 length += LengthInSeconds;
1864 return (size && length) ? double(size) * 60 / length : -1;
1871 if (Recording->IsInPath(Path))
1872 Use |= Recording->IsInUse();
1881 if (Recording->IsInPath(Path))
1889 if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
1890 dsyslog(
"moving '%s' to '%s'", OldPath, NewPath);
1893 if (Recording->IsInPath(OldPath)) {
1894 const char *p = Recording->Name() + strlen(OldPath);
1896 if (!Recording->ChangeName(NewName))
1910 if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
1911 Recording->ResetResume();
1918 Recording->ClearSortName();
1930 virtual void Action(
void)
override;
1932 cDirCopier(
const char *DirNameSrc,
const char *DirNameDst);
1955 dsyslog(
"suspending copy thread");
1961 dsyslog(
"resuming copy thread");
1978 size_t BufferSize = BUFSIZ;
1979 uchar *Buffer = NULL;
1993 size_t Read =
safe_read(From, Buffer, BufferSize);
1995 size_t Written =
safe_write(To, Buffer, Read);
1996 if (Written != Read) {
1997 esyslog(
"ERROR: can't write to destination file '%s': %m", *FileNameDst);
2001 else if (Read == 0) {
2003 if (fsync(To) < 0) {
2004 esyslog(
"ERROR: can't sync destination file '%s': %m", *FileNameDst);
2007 if (close(From) < 0) {
2008 esyslog(
"ERROR: can't close source file '%s': %m", *FileNameSrc);
2011 if (close(To) < 0) {
2012 esyslog(
"ERROR: can't close destination file '%s': %m", *FileNameDst);
2016 off_t FileSizeSrc =
FileSize(FileNameSrc);
2017 off_t FileSizeDst =
FileSize(FileNameDst);
2018 if (FileSizeSrc != FileSizeDst) {
2019 esyslog(
"ERROR: file size discrepancy: %" PRId64
" != %" PRId64, FileSizeSrc, FileSizeDst);
2024 esyslog(
"ERROR: can't read from source file '%s': %m", *FileNameSrc);
2028 else if ((e = d.
Next()) != NULL) {
2033 if (stat(FileNameSrc, &st) < 0) {
2034 esyslog(
"ERROR: can't access source file '%s': %m", *FileNameSrc);
2037 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
2038 esyslog(
"ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
2041 dsyslog(
"copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
2043 BufferSize =
max(
size_t(st.st_blksize * 10),
size_t(BUFSIZ));
2046 esyslog(
"ERROR: out of memory");
2050 if (access(FileNameDst, F_OK) == 0) {
2051 esyslog(
"ERROR: destination file '%s' already exists", *FileNameDst);
2054 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
2055 esyslog(
"ERROR: can't open source file '%s': %m", *FileNameSrc);
2058 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
2059 esyslog(
"ERROR: can't open destination file '%s': %m", *FileNameDst);
2098 int Usage(
const char *FileName = NULL)
const;
2126 if (FileName && *FileName) {
2159 Recordings->
Del(Recording);
2182 Recordings->
Del(Recording);
2202 Recordings->
Del(Recording);
2217 Recordings->
Del(Recording);
2247 Recordings->SetExplicitModify();
2250 if (!r->Active(Recordings)) {
2251 error |= r->Error();
2252 r->Cleanup(Recordings);
2268 if (FileName && *FileName) {
2272 if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
2281 dsyslog(
"recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2284 if (FileNameSrc && *FileNameSrc) {
2285 if (Usage ==
ruCut || FileNameDst && *FileNameDst) {
2287 if (Usage ==
ruCut && !FileNameDst)
2289 if (!
Get(FileNameSrc) && !
Get(FileNameDst)) {
2297 esyslog(
"ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2300 esyslog(
"ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2303 esyslog(
"ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2306 esyslog(
"ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2328 return r->Usage(FileName);
2334 int RequiredDiskSpaceMB = 0;
2338 if ((r->Usage() &
ruCut) != 0) {
2344 RequiredDiskSpaceMB +=
DirSizeMB(r->FileNameSrc());
2347 return RequiredDiskSpaceMB;
2388 const char *p = strchr(s,
' ');
2399 return fprintf(f,
"%s\n", *
ToText()) > 0;
2412 if (errno != ENOENT) {
2420bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
2434 time_t t = time(NULL);
2438 lastChange = LastModified > 0 ? LastModified : t;
2477 if (m->Position() - p) {
2488 if (m2->Position() < m1->Position()) {
2489 swap(m1->position, m2->position);
2490 swap(m1->comment, m2->comment);
2505 if (mi->Position() == Position)
2514 if (mi->Position() < Position)
2523 if (mi->Position() > Position)
2532 if (BeginMark && EndMark && BeginMark->
Position() == EndMark->
Position()) {
2533 while (
const cMark *NextMark =
Next(BeginMark)) {
2534 if (BeginMark->
Position() == NextMark->Position()) {
2535 if (!(BeginMark =
Next(NextMark)))
2550 if (EndMark && BeginMark && BeginMark->
Position() == EndMark->
Position()) {
2551 while (
const cMark *NextMark =
Next(EndMark)) {
2552 if (EndMark->
Position() == NextMark->Position()) {
2553 if (!(EndMark =
Next(NextMark)))
2565 int NumSequences = 0;
2573 if (NumSequences == 1 && BeginMark->Position() == 0)
2577 return NumSequences;
2582 if (
Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
2584 int EditedFrame = 0;
2586 bool InEdit =
false;
2588 int p = mi->Position();
2590 EditedFrame += p - PrevPos;
2593 EditedFrame -= p - Frame;
2605 EditedFrame += LastFrame - PrevPos;
2606 if (Frame < LastFrame)
2607 EditedFrame -= LastFrame - Frame;
2624 isyslog(
"executing '%s'", *cmd);
2631#define IFG_BUFFER_SIZE KILOBYTE(100)
2638 virtual void Action(
void)
override;
2645:
cThread(
"index file generator")
2658 bool IndexFileComplete =
false;
2659 bool IndexFileWritten =
false;
2660 bool Rewind =
false;
2669 off_t FrameOffset = -1;
2670 bool pendIndependentFrame =
false;
2671 uint16_t pendNumber = 0;
2672 off_t pendFileSize = 0;
2673 bool pendMissing =
false;
2677 bool Stuffed =
false;
2689 if (FrameDetector.
Synced()) {
2692 int OldPatVersion, OldPmtVersion;
2693 PatPmtParser.
GetVersions(OldPatVersion, OldPmtVersion);
2695 int NewPatVersion, NewPmtVersion;
2696 if (PatPmtParser.
GetVersions(NewPatVersion, NewPmtVersion)) {
2697 if (NewPatVersion != OldPatVersion || NewPmtVersion != OldPmtVersion) {
2698 dsyslog(
"PAT/PMT version change while generating index");
2699 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2705 int Processed = FrameDetector.
Analyze(Data, Length);
2706 if (Processed > 0) {
2707 bool PreviousErrors =
false;
2708 bool MissingFrames =
false;
2709 if (FrameDetector.
NewFrame(PreviousErrors, MissingFrames)) {
2711 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing);
2713 pendNumber = FileName.
Number();
2714 pendFileSize = FrameOffset >= 0 ? FrameOffset :
FileSize;
2715 pendMissing = MissingFrames;
2717 IndexFileWritten =
true;
2718 Errors = FrameDetector.
Errors();
2721 Buffer.
Del(Processed);
2726 int Processed = FrameDetector.
Analyze(Data, Length,
false);
2727 if (Processed > 0) {
2728 if (FrameDetector.
Synced()) {
2732 Buffer.
Del(Processed);
2742 else if (PatPmtParser.
IsPmtPid(Pid))
2748 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2754 Buffer.
Del(p - Data);
2758 else if (ReplayFile) {
2759 int Result = Buffer.
Read(ReplayFile, BufferChunks);
2761 if (Buffer.
Available() > 0 && !Stuffed) {
2770 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
2784 bool PreviousErrors =
false;
2785 bool MissingFrames =
false;
2786 Errors = FrameDetector.
Errors(&PreviousErrors, &MissingFrames);
2788 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing || MissingFrames);
2789 IndexFileComplete =
true;
2794 if (IndexFileComplete) {
2795 if (IndexFileWritten) {
2797 if (RecordingInfo.
Read()) {
2802 Errors != RecordingInfo.
Errors()) {
2806 RecordingInfo.
Write();
2811 Skins.QueueMessage(
mtInfo,
tr(
"Index file regeneration complete"));
2815 Skins.QueueMessage(
mtError,
tr(
"Index file regeneration failed!"));
2823#define INDEXFILESUFFIX "/index"
2826#define MAXINDEXCATCHUP 8
2827#define INDEXCATCHUPWAIT 100
2843 tIndexTs(off_t Offset,
bool Independent, uint16_t Number,
bool Errors,
bool Missing)
2849 independent = Independent;
2854#define MAXWAITFORINDEXFILE 10
2855#define INDEXFILECHECKINTERVAL 500
2856#define INDEXFILETESTINTERVAL 10
2870 if (!Record && PauseLive) {
2873 while (time(NULL) < tmax &&
FileSize(
fileName) < off_t(2 *
sizeof(tIndexTs)))
2886 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
2892 delta = int(buf.st_size %
sizeof(tIndexTs));
2894 delta =
sizeof(tIndexTs) - delta;
2895 esyslog(
"ERROR: invalid file size (%" PRId64
") in '%s'", buf.st_size, *
fileName);
2897 last = int((buf.st_size + delta) /
sizeof(tIndexTs) - 1);
2898 if (!Record &&
last >= 0) {
2923 esyslog(
"ERROR: can't allocate %zd bytes for index '%s'",
size *
sizeof(tIndexTs), *
fileName);
2935 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
2937 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
2964 while (Count-- > 0) {
2965 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
2966 IndexTs->offset = IndexPes.offset;
2967 IndexTs->independent = IndexPes.type == 1;
2968 IndexTs->number = IndexPes.number;
2976 while (Count-- > 0) {
2977 IndexPes.offset = uint32_t(IndexTs->offset);
2978 IndexPes.type =
uchar(IndexTs->independent ? 1 : 2);
2979 IndexPes.number =
uchar(IndexTs->number);
2980 IndexPes.reserved = 0;
2981 memcpy((
void *)IndexTs, &IndexPes,
sizeof(*IndexTs));
2995 if (fstat(
f, &buf) == 0) {
2996 int newLast = int(buf.st_size /
sizeof(tIndexTs) - 1);
2997 if (newLast >
last) {
2999 if (NewSize <= newLast) {
3001 if (NewSize <= newLast)
3002 NewSize = newLast + 1;
3004 if (tIndexTs *NewBuffer = (tIndexTs *)realloc(
index, NewSize *
sizeof(tIndexTs))) {
3007 int offset = (
last + 1) *
sizeof(tIndexTs);
3008 int delta = (newLast -
last) *
sizeof(tIndexTs);
3009 if (lseek(
f, offset, SEEK_SET) == offset) {
3011 esyslog(
"ERROR: can't read from index");
3026 esyslog(
"ERROR: can't realloc() index");
3039 return index != NULL;
3042bool cIndexFile::Write(
bool Independent, uint16_t FileNumber, off_t FileOffset,
bool Errors,
bool Missing)
3045 tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
3059bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length,
bool *Errors,
bool *Missing)
3062 if (Index >= 0 && Index <=
last) {
3063 *FileNumber =
index[Index].number;
3064 *FileOffset =
index[Index].offset;
3066 *Independent =
index[Index].independent;
3069 uint16_t fn =
index[Index + 1].number;
3070 off_t fo =
index[Index + 1].offset;
3071 if (fn == *FileNumber)
3072 *Length = int(fo - *FileOffset);
3080 *Errors =
index[Index].errors;
3082 *Missing =
index[Index].missing;
3092 tIndexTs *p = &
index[Index];
3093 if (p->errors || p->missing)
3103 int d = Forward ? 1 : -1;
3106 if (Index >= 0 && Index <=
last) {
3107 if (
index[Index].independent) {
3114 *FileNumber =
index[Index].number;
3115 *FileOffset =
index[Index].offset;
3118 uint16_t fn =
index[Index + 1].number;
3119 off_t fo =
index[Index + 1].offset;
3120 if (fn == *FileNumber)
3121 *Length = int(fo - *FileOffset);
3142 if (
index[Index].independent)
3148 if (
index[il].independent)
3155 if (
index[ih].independent)
3171 for (i = 0; i <=
last; i++) {
3172 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
3201 if (*s && stat(s, &buf) == 0)
3202 return buf.st_size / (IsPesRecording ?
sizeof(tIndexTs) :
sizeof(tIndexPes));
3210 if (Recording.
Name()) {
3213 unlink(IndexFileName);
3215 while (IndexFileGenerator->
Active())
3217 if (access(IndexFileName, R_OK) == 0)
3220 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
3223 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
3226 fprintf(stderr,
"'%s' is not a recording\n", FileName);
3229 fprintf(stderr,
"'%s' is not a directory\n", FileName);
3235#define MAXFILESPERRECORDINGPES 255
3236#define RECORDFILESUFFIXPES "/%03d.vdr"
3237#define MAXFILESPERRECORDINGTS 65535
3238#define RECORDFILESUFFIXTS "/%05d.ts"
3239#define RECORDFILESUFFIXLEN 20
3251 esyslog(
"ERROR: can't copy file name '%s'", FileName);
3281 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
3283 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
3287 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
3289 int Pid =
TsPid(buf);
3291 PatPmtParser.
ParsePat(buf,
sizeof(buf));
3292 else if (PatPmtParser.
IsPmtPid(Pid)) {
3293 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
3294 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
3305 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
3319 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
3333 else if (errno != ENOENT)
3343 if (
file->Close() < 0)
3363 if (buf.st_size != 0)
3367 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
3374 else if (errno != ENOENT) {
3381 if (!
record && Offset >= 0 &&
file->Seek(Offset, SEEK_SET) != Offset) {
3388 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
3410 while ((s = ReadLine.
Read(f)) != NULL)
3428 if (fputs(
doneRecordings[i], f) == EOF || fputc(
'\n', f) == EOF) {
3450 if (FILE *f = fopen(
fileName,
"a")) {
3456 esyslog(
"ERROR: can't open '%s' for appending '%s'", *
fileName, Title);
3473 const char *t = Title;
3479 if (toupper(
uchar(*s)) != toupper(
uchar(*t)))
3494 const char *Sign =
"";
3500 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
3501 int s = int(Seconds);
3502 int m = s / 60 % 60;
3505 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
3511 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
3515 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
3521 return int(round(Seconds * FramesPerSecond));
3530 else if (Length > Max) {
3531 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
3534 int r = f->
Read(b, Length);
3554 if (fgets(buf,
sizeof(buf), f))
3583 dsyslog(
"writing timer id '%s' to %s", TimerId, *FileName);
3584 if (FILE *f = fopen(FileName,
"w")) {
3585 fprintf(f,
"%s\n", TimerId);
3592 dsyslog(
"removing %s", *FileName);
3600 const char *Id = NULL;
3601 if (FILE *f = fopen(FileName,
"r")) {
3602 char buf[HOST_NAME_MAX + 10];
3603 if (fgets(buf,
sizeof(buf), f)) {
3617 if (FileSizeMB > 0) {
3620 if (NumFramesOrg > 0) {
3622 if (NumFramesEdit > 0)
3623 return max(1,
int(FileSizeMB * (
double(NumFramesEdit) / NumFramesOrg)));
3632 if (FileSizeMB > 0) {
3636 if (access(EditedFileName, F_OK)) {
3637 int ExistingEditedSizeMB =
DirSizeMB(EditedFileName);
3638 if (ExistingEditedSizeMB > 0)
3639 FreeDiskMB += ExistingEditedSizeMB;
3643 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.
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 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)
int TmpErrors(void) const
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 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 in 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
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)
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 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)