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);
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))
506 if (fstat(fileno(f), &st))
508 if (
modified == st.st_mtime && !Force)
518 while ((s = ReadLine.
Read(f)) != NULL) {
523 char *p = strchr(t,
' ');
534 unsigned int EventID;
537 unsigned int TableID = 0;
538 unsigned int Version = 0xFF;
539 int n = sscanf(t,
"%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
540 if (n >= 3 && n <= 5) {
554 int n = sscanf(t,
"%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &
frameWidth, &
frameHeight, &scanTypeCode, &arBuf);
584 case 'O':
errors = atoi(t);
585 if (t = strchr(t,
' '))
595 esyslog(
"ERROR: EPG data problem in line %d", line);
610 event->Dump(f, Prefix,
true);
615 fprintf(f,
"%sP %d\n", Prefix,
priority);
616 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
617 fprintf(f,
"%sO %d", Prefix,
errors);
622 fprintf(f,
"%s@ %s\n", Prefix,
aux);
638 else if (errno != ENOENT)
683#define RESUME_NOT_INITIALIZED (-2)
716 case ' ': *p =
'_';
break;
723 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
727 sprintf(buf,
"#%02X", (
unsigned char)*p);
728 memmove(p + 2, p, strlen(p) + 1);
733 esyslog(
"ERROR: out of memory");
740 case '_': *p =
' ';
break;
745 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
747 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
751 memmove(p + 1, p + 3, strlen(p) - 2);
757 case '\x01': *p =
'\'';
break;
758 case '\x02': *p =
'/';
break;
759 case '\x03': *p =
':';
break;
766 if (*p == (ToFileSystem ? ce->a : ce->b)) {
767 *p = ToFileSystem ? ce->b : ce->a;
789 int Length = strlen(s);
792 bool NameTooLong =
false;
796 for (
char *p = s; *p; p++) {
799 NameTooLong |= NameLength > NameMax;
820 NameTooLong |= NameLength > NameMax;
828 while (i-- > 0 && a[i] >= 0) {
833 if (NameLength > NameMax) {
836 while (i-- > 0 && a[i] >= 0) {
838 if (NameLength - l <= NameMax) {
839 memmove(s + i, s + n, Length - n + 1);
840 memmove(a + i, a + n, Length - n + 1);
853 while (PathLength > PathMax && n > 0) {
858 while (--i > 0 && a[i - 1] >= 0) {
862 if (PathLength - l <= PathMax)
868 memmove(s + b, s + n, Length - n + 1);
894 const char *
Title = Event ? Event->
Title() : NULL;
895 const char *Subtitle = Event ? Event->
ShortText() : NULL;
902 if (macroTITLE || macroEPISODE) {
907 int l = strlen(
name);
949 const char *p = strrchr(
FileName,
'/');
954 time_t now = time(NULL);
956 struct tm t = *localtime_r(&now, &tm_r);
961 || 7 == sscanf(p + 1,
DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
977 FILE *f = fopen(InfoFileName,
"r");
980 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
982 info->SetPriority(priority);
983 info->SetLifetime(lifetime);
987 else if (errno != ENOENT)
989#ifdef SUMMARYFALLBACK
993 FILE *f = fopen(SummaryFileName,
"r");
996 char *data[3] = { NULL };
999 while ((s = ReadLine.
Read(f)) != NULL) {
1000 if (*s || line > 1) {
1002 int len = strlen(s);
1003 len += strlen(data[line]) + 1;
1004 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
1005 data[line] = NewBuffer;
1006 strcat(data[line],
"\n");
1007 strcat(data[line], s);
1010 esyslog(
"ERROR: out of memory");
1013 data[line] = strdup(s);
1023 else if (data[1] && data[2]) {
1027 int len = strlen(data[1]);
1029 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
1030 data[1] = NewBuffer;
1031 strcat(data[1],
"\n");
1032 strcat(data[1], data[2]);
1038 esyslog(
"ERROR: out of memory");
1041 info->SetData(data[0], data[1], data[2]);
1042 for (
int i = 0; i < 3; i ++)
1045 else if (errno != ENOENT)
1066 char *t = s, *s1 = NULL, *s2 = NULL;
1087 memmove(s1, s2, t - s2 + 1);
1100 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
1108 int l = strxfrm(NULL, s, 0) + 1;
1142 return time(NULL) -
Deleted() > Retention;
1165 int l = strlen(Path);
1185 struct tm *t = localtime_r(&
start, &tm_r);
1201 const char *New = NewIndicator &&
IsNew() ?
"*" :
"";
1202 const char *Err = NewIndicator && (
info->Errors() > 0) ?
"!" :
"";
1207 struct tm *t = localtime_r(&
start, &tm_r);
1242 const char *s =
name;
1275 const char *s =
name;
1315 if (!OtherFileName) {
1318 if (ExistingInfo.
Read())
1343 dsyslog(
"changing priority/lifetime of '%s' to %d/%d",
Name(), NewPriority, NewLifetime);
1344 info->SetPriority(NewPriority);
1345 info->SetLifetime(NewLifetime);
1353 info->SetFileName(NewFileName);
1365 if (strcmp(NewName,
Name())) {
1366 dsyslog(
"changing name of '%s' to '%s'",
Name(), NewName);
1372 name = strdup(NewName);
1374 bool Exists = access(NewFileName, F_OK) == 0;
1376 esyslog(
"ERROR: recording '%s' already exists", NewName);
1379 name = strdup(OldName);
1384 info->SetFileName(NewFileName);
1394 char *NewName = strdup(
FileName());
1395 char *ext = strrchr(NewName,
'.');
1396 if (ext && strcmp(ext,
RECEXT) == 0) {
1397 strncpy(ext,
DELEXT, strlen(ext));
1398 if (access(NewName, F_OK) == 0) {
1400 isyslog(
"removing recording '%s'", NewName);
1404 if (access(
FileName(), F_OK) == 0) {
1437 char *NewName = strdup(
FileName());
1438 char *ext = strrchr(NewName,
'.');
1439 if (ext && strcmp(ext,
DELEXT) == 0) {
1440 strncpy(ext,
RECEXT, strlen(ext));
1441 if (access(NewName, F_OK) == 0) {
1443 esyslog(
"ERROR: attempt to restore '%s', while recording '%s' exists",
FileName(), NewName);
1448 if (access(
FileName(), F_OK) == 0) {
1499 if (IndexLength > 0) {
1542 void ScanVideoDir(
const char *DirName,
int LinkLevel = 0,
int DirLevel = 0);
1544 virtual void Action(
void)
override;
1551:
cThread(
"video directory scanner", true)
1587 if (lstat(buffer, &st) == 0) {
1589 if (S_ISLNK(st.st_mode)) {
1591 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1595 if (stat(buffer, &st) != 0)
1598 if (S_ISDIR(st.st_mode)) {
1606 Recordings->
Lock(StateKey,
true);
1608 dsyslog(
"activated name checking for initial read of video directory");
1636 if (!
initial && DirLevel == 0) {
1643 if (access(r->
FileName(), F_OK) != 0) {
1654 if (access(r->
FileName(), F_OK) != 0) {
1702 if (lastModified > time(NULL))
1722 if (Recording->Id() == Id)
1732 if (strcmp(Recording->FileName(), FileName) == 0)
1759 esyslog(
"ERROR: cRecordings::DelByName() called with '%s' on a list other than Recordings - ignored", FileName);
1762 char *DelRecFileName = strdup(FileName);
1763 if (
char *ext = strrchr(DelRecFileName,
'.')) {
1764 if (strcmp(ext,
DELEXT)) {
1765 esyslog(
"ERROR: cRecordings::DelByName() called with '%s', using '.rec' instead", DelRecFileName);
1766 strncpy(ext,
RECEXT, strlen(ext));
1772 esyslog(
"ERROR: cRecordings::DelByName(): '%s' not found in Recordings - using dummy", DelRecFileName);
1773 Recording = dummy =
new cRecording(FileName);
1777 Del(Recording,
false);
1778 char *ext = strrchr(Recording->
fileName,
'.');
1780 strncpy(ext,
DELEXT, strlen(ext));
1781 if (access(Recording->
FileName(), F_OK) == 0) {
1783 DeletedRecordings->Add(Recording);
1788 free(DelRecFileName);
1795 Recording->numFrames = -1;
1796 Recording->ReadInfo(
true);
1804 int FileSizeMB = Recording->FileSizeMB();
1805 if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
1816 if (Recording->IsOnVideoDirectoryFileSystem()) {
1817 int FileSizeMB = Recording->FileSizeMB();
1818 if (FileSizeMB > 0) {
1819 int LengthInSeconds = Recording->LengthInSeconds();
1820 if (LengthInSeconds > 0) {
1823 length += LengthInSeconds;
1829 return (size && length) ? double(size) * 60 / length : -1;
1836 if (Recording->IsInPath(Path))
1837 Use |= Recording->IsInUse();
1846 if (Recording->IsInPath(Path))
1854 if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
1855 dsyslog(
"moving '%s' to '%s'", OldPath, NewPath);
1858 if (Recording->IsInPath(OldPath)) {
1859 const char *p = Recording->Name() + strlen(OldPath);
1861 if (!Recording->ChangeName(NewName))
1875 if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
1876 Recording->ResetResume();
1883 Recording->ClearSortName();
1895 virtual void Action(
void)
override;
1897 cDirCopier(
const char *DirNameSrc,
const char *DirNameDst);
1920 dsyslog(
"suspending copy thread");
1926 dsyslog(
"resuming copy thread");
1943 size_t BufferSize = BUFSIZ;
1944 uchar *Buffer = NULL;
1958 size_t Read =
safe_read(From, Buffer, BufferSize);
1960 size_t Written =
safe_write(To, Buffer, Read);
1961 if (Written != Read) {
1962 esyslog(
"ERROR: can't write to destination file '%s': %m", *FileNameDst);
1966 else if (Read == 0) {
1968 if (fsync(To) < 0) {
1969 esyslog(
"ERROR: can't sync destination file '%s': %m", *FileNameDst);
1972 if (close(From) < 0) {
1973 esyslog(
"ERROR: can't close source file '%s': %m", *FileNameSrc);
1976 if (close(To) < 0) {
1977 esyslog(
"ERROR: can't close destination file '%s': %m", *FileNameDst);
1981 off_t FileSizeSrc =
FileSize(FileNameSrc);
1982 off_t FileSizeDst =
FileSize(FileNameDst);
1983 if (FileSizeSrc != FileSizeDst) {
1984 esyslog(
"ERROR: file size discrepancy: %" PRId64
" != %" PRId64, FileSizeSrc, FileSizeDst);
1989 esyslog(
"ERROR: can't read from source file '%s': %m", *FileNameSrc);
1993 else if ((e = d.
Next()) != NULL) {
1998 if (stat(FileNameSrc, &st) < 0) {
1999 esyslog(
"ERROR: can't access source file '%s': %m", *FileNameSrc);
2002 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
2003 esyslog(
"ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
2006 dsyslog(
"copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
2008 BufferSize =
max(
size_t(st.st_blksize * 10),
size_t(BUFSIZ));
2011 esyslog(
"ERROR: out of memory");
2015 if (access(FileNameDst, F_OK) == 0) {
2016 esyslog(
"ERROR: destination file '%s' already exists", *FileNameDst);
2019 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
2020 esyslog(
"ERROR: can't open source file '%s': %m", *FileNameSrc);
2023 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
2024 esyslog(
"ERROR: can't open destination file '%s': %m", *FileNameDst);
2063 int Usage(
const char *FileName = NULL)
const;
2091 if (FileName && *FileName) {
2124 Recordings->
Del(Recording);
2147 Recordings->
Del(Recording);
2167 Recordings->
Del(Recording);
2182 Recordings->
Del(Recording);
2212 Recordings->SetExplicitModify();
2215 if (!r->Active(Recordings)) {
2216 error |= r->Error();
2217 r->Cleanup(Recordings);
2233 if (FileName && *FileName) {
2237 if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
2246 dsyslog(
"recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2249 if (FileNameSrc && *FileNameSrc) {
2250 if (Usage ==
ruCut || FileNameDst && *FileNameDst) {
2252 if (Usage ==
ruCut && !FileNameDst)
2254 if (!
Get(FileNameSrc) && !
Get(FileNameDst)) {
2262 esyslog(
"ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2265 esyslog(
"ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2268 esyslog(
"ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2271 esyslog(
"ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2293 return r->Usage(FileName);
2299 int RequiredDiskSpaceMB = 0;
2303 if ((r->Usage() &
ruCut) != 0) {
2309 RequiredDiskSpaceMB +=
DirSizeMB(r->FileNameSrc());
2312 return RequiredDiskSpaceMB;
2353 const char *p = strchr(s,
' ');
2364 return fprintf(f,
"%s\n", *
ToText()) > 0;
2377 if (errno != ENOENT) {
2385bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
2399 time_t t = time(NULL);
2403 lastChange = LastModified > 0 ? LastModified : t;
2442 if (m->Position() - p) {
2453 if (m2->Position() < m1->Position()) {
2454 swap(m1->position, m2->position);
2455 swap(m1->comment, m2->comment);
2470 if (mi->Position() == Position)
2479 if (mi->Position() < Position)
2488 if (mi->Position() > Position)
2497 if (BeginMark && EndMark && BeginMark->
Position() == EndMark->
Position()) {
2498 while (
const cMark *NextMark =
Next(BeginMark)) {
2499 if (BeginMark->
Position() == NextMark->Position()) {
2500 if (!(BeginMark =
Next(NextMark)))
2515 if (EndMark && BeginMark && BeginMark->
Position() == EndMark->
Position()) {
2516 while (
const cMark *NextMark =
Next(EndMark)) {
2517 if (EndMark->
Position() == NextMark->Position()) {
2518 if (!(EndMark =
Next(NextMark)))
2530 int NumSequences = 0;
2538 if (NumSequences == 1 && BeginMark->Position() == 0)
2542 return NumSequences;
2547 if (
Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
2549 int EditedFrame = 0;
2551 bool InEdit =
false;
2553 int p = mi->Position();
2555 EditedFrame += p - PrevPos;
2558 EditedFrame -= p - Frame;
2570 EditedFrame += LastFrame - PrevPos;
2571 if (Frame < LastFrame)
2572 EditedFrame -= LastFrame - Frame;
2589 isyslog(
"executing '%s'", *cmd);
2596#define IFG_BUFFER_SIZE KILOBYTE(100)
2603 virtual void Action(
void)
override;
2610:
cThread(
"index file generator")
2623 bool IndexFileComplete =
false;
2624 bool IndexFileWritten =
false;
2625 bool Rewind =
false;
2634 off_t FrameOffset = -1;
2635 bool pendIndependentFrame =
false;
2636 uint16_t pendNumber = 0;
2637 off_t pendFileSize = 0;
2638 bool pendMissing =
false;
2642 bool Stuffed =
false;
2654 if (FrameDetector.
Synced()) {
2657 int OldPatVersion, OldPmtVersion;
2658 PatPmtParser.
GetVersions(OldPatVersion, OldPmtVersion);
2660 int NewPatVersion, NewPmtVersion;
2661 if (PatPmtParser.
GetVersions(NewPatVersion, NewPmtVersion)) {
2662 if (NewPatVersion != OldPatVersion || NewPmtVersion != OldPmtVersion) {
2663 dsyslog(
"PAT/PMT version change while generating index");
2664 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2670 int Processed = FrameDetector.
Analyze(Data, Length);
2671 if (Processed > 0) {
2672 bool PreviousErrors =
false;
2673 bool MissingFrames =
false;
2674 if (FrameDetector.
NewFrame(PreviousErrors, MissingFrames)) {
2676 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing);
2678 pendNumber = FileName.
Number();
2679 pendFileSize = FrameOffset >= 0 ? FrameOffset :
FileSize;
2680 pendMissing = MissingFrames;
2682 IndexFileWritten =
true;
2683 Errors = FrameDetector.
Errors();
2686 Buffer.
Del(Processed);
2691 int Processed = FrameDetector.
Analyze(Data, Length,
false);
2692 if (Processed > 0) {
2693 if (FrameDetector.
Synced()) {
2697 Buffer.
Del(Processed);
2707 else if (PatPmtParser.
IsPmtPid(Pid))
2713 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2719 Buffer.
Del(p - Data);
2723 else if (ReplayFile) {
2724 int Result = Buffer.
Read(ReplayFile, BufferChunks);
2726 if (Buffer.
Available() > 0 && !Stuffed) {
2735 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
2749 bool PreviousErrors =
false;
2750 bool MissingFrames =
false;
2751 Errors = FrameDetector.
Errors(&PreviousErrors, &MissingFrames);
2753 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing || MissingFrames);
2754 IndexFileComplete =
true;
2759 if (IndexFileComplete) {
2760 if (IndexFileWritten) {
2762 if (RecordingInfo.
Read()) {
2767 Errors != RecordingInfo.
Errors()) {
2771 RecordingInfo.
Write();
2776 Skins.QueueMessage(
mtInfo,
tr(
"Index file regeneration complete"));
2780 Skins.QueueMessage(
mtError,
tr(
"Index file regeneration failed!"));
2788#define INDEXFILESUFFIX "/index"
2791#define MAXINDEXCATCHUP 8
2792#define INDEXCATCHUPWAIT 100
2808 tIndexTs(off_t Offset,
bool Independent, uint16_t Number,
bool Errors,
bool Missing)
2814 independent = Independent;
2819#define MAXWAITFORINDEXFILE 10
2820#define INDEXFILECHECKINTERVAL 500
2821#define INDEXFILETESTINTERVAL 10
2835 if (!Record && PauseLive) {
2838 while (time(NULL) < tmax &&
FileSize(
fileName) < off_t(2 *
sizeof(tIndexTs)))
2851 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
2857 delta = int(buf.st_size %
sizeof(tIndexTs));
2859 delta =
sizeof(tIndexTs) - delta;
2860 esyslog(
"ERROR: invalid file size (%" PRId64
") in '%s'", buf.st_size, *
fileName);
2862 last = int((buf.st_size + delta) /
sizeof(tIndexTs) - 1);
2863 if (!Record &&
last >= 0) {
2888 esyslog(
"ERROR: can't allocate %zd bytes for index '%s'",
size *
sizeof(tIndexTs), *
fileName);
2900 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
2902 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
2929 while (Count-- > 0) {
2930 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
2931 IndexTs->offset = IndexPes.offset;
2932 IndexTs->independent = IndexPes.type == 1;
2933 IndexTs->number = IndexPes.number;
2941 while (Count-- > 0) {
2942 IndexPes.offset = uint32_t(IndexTs->offset);
2943 IndexPes.type =
uchar(IndexTs->independent ? 1 : 2);
2944 IndexPes.number =
uchar(IndexTs->number);
2945 IndexPes.reserved = 0;
2946 memcpy((
void *)IndexTs, &IndexPes,
sizeof(*IndexTs));
2960 if (fstat(
f, &buf) == 0) {
2961 int newLast = int(buf.st_size /
sizeof(tIndexTs) - 1);
2962 if (newLast >
last) {
2964 if (NewSize <= newLast) {
2966 if (NewSize <= newLast)
2967 NewSize = newLast + 1;
2969 if (tIndexTs *NewBuffer = (tIndexTs *)realloc(
index, NewSize *
sizeof(tIndexTs))) {
2972 int offset = (
last + 1) *
sizeof(tIndexTs);
2973 int delta = (newLast -
last) *
sizeof(tIndexTs);
2974 if (lseek(
f, offset, SEEK_SET) == offset) {
2976 esyslog(
"ERROR: can't read from index");
2991 esyslog(
"ERROR: can't realloc() index");
3004 return index != NULL;
3007bool cIndexFile::Write(
bool Independent, uint16_t FileNumber, off_t FileOffset,
bool Errors,
bool Missing)
3010 tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
3024bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length,
bool *Errors,
bool *Missing)
3027 if (Index >= 0 && Index <=
last) {
3028 *FileNumber =
index[Index].number;
3029 *FileOffset =
index[Index].offset;
3031 *Independent =
index[Index].independent;
3034 uint16_t fn =
index[Index + 1].number;
3035 off_t fo =
index[Index + 1].offset;
3036 if (fn == *FileNumber)
3037 *Length = int(fo - *FileOffset);
3045 *Errors =
index[Index].errors;
3047 *Missing =
index[Index].missing;
3057 tIndexTs *p = &
index[Index];
3058 if (p->errors || p->missing)
3068 int d = Forward ? 1 : -1;
3071 if (Index >= 0 && Index <=
last) {
3072 if (
index[Index].independent) {
3079 *FileNumber =
index[Index].number;
3080 *FileOffset =
index[Index].offset;
3083 uint16_t fn =
index[Index + 1].number;
3084 off_t fo =
index[Index + 1].offset;
3085 if (fn == *FileNumber)
3086 *Length = int(fo - *FileOffset);
3107 if (
index[Index].independent)
3113 if (
index[il].independent)
3120 if (
index[ih].independent)
3136 for (i = 0; i <=
last; i++) {
3137 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
3166 if (*s && stat(s, &buf) == 0)
3167 return buf.st_size / (IsPesRecording ?
sizeof(tIndexTs) :
sizeof(tIndexPes));
3175 if (Recording.
Name()) {
3178 unlink(IndexFileName);
3180 while (IndexFileGenerator->
Active())
3182 if (access(IndexFileName, R_OK) == 0)
3185 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
3188 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
3191 fprintf(stderr,
"'%s' is not a recording\n", FileName);
3194 fprintf(stderr,
"'%s' is not a directory\n", FileName);
3200#define MAXFILESPERRECORDINGPES 255
3201#define RECORDFILESUFFIXPES "/%03d.vdr"
3202#define MAXFILESPERRECORDINGTS 65535
3203#define RECORDFILESUFFIXTS "/%05d.ts"
3204#define RECORDFILESUFFIXLEN 20
3216 esyslog(
"ERROR: can't copy file name '%s'", FileName);
3246 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
3248 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
3252 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
3254 int Pid =
TsPid(buf);
3256 PatPmtParser.
ParsePat(buf,
sizeof(buf));
3257 else if (PatPmtParser.
IsPmtPid(Pid)) {
3258 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
3259 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
3270 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
3284 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
3298 else if (errno != ENOENT)
3308 if (
file->Close() < 0)
3328 if (buf.st_size != 0)
3332 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
3339 else if (errno != ENOENT) {
3346 if (!
record && Offset >= 0 &&
file->Seek(Offset, SEEK_SET) != Offset) {
3353 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
3375 while ((s = ReadLine.
Read(f)) != NULL)
3393 if (fputs(
doneRecordings[i], f) == EOF || fputc(
'\n', f) == EOF) {
3415 if (FILE *f = fopen(
fileName,
"a")) {
3421 esyslog(
"ERROR: can't open '%s' for appending '%s'", *
fileName, Title);
3438 const char *t = Title;
3444 if (toupper(
uchar(*s)) != toupper(
uchar(*t)))
3459 const char *Sign =
"";
3465 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
3466 int s = int(Seconds);
3467 int m = s / 60 % 60;
3470 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
3476 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
3480 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
3486 return int(round(Seconds * FramesPerSecond));
3495 else if (Length > Max) {
3496 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
3499 int r = f->
Read(b, Length);
3519 if (fgets(buf,
sizeof(buf), f))
3548 dsyslog(
"writing timer id '%s' to %s", TimerId, *FileName);
3549 if (FILE *f = fopen(FileName,
"w")) {
3550 fprintf(f,
"%s\n", TimerId);
3557 dsyslog(
"removing %s", *FileName);
3565 const char *Id = NULL;
3566 if (FILE *f = fopen(FileName,
"r")) {
3567 char buf[HOST_NAME_MAX + 10];
3568 if (fgets(buf,
sizeof(buf), f)) {
3582 if (FileSizeMB > 0) {
3585 if (NumFramesOrg > 0) {
3587 if (NumFramesEdit > 0)
3588 return max(1,
int(FileSizeMB * (
double(NumFramesEdit) / NumFramesOrg)));
3597 if (FileSizeMB > 0) {
3601 if (access(EditedFileName, F_OK)) {
3602 int ExistingEditedSizeMB =
DirSizeMB(EditedFileName);
3603 if (ExistingEditedSizeMB > 0)
3604 FreeDiskMB += ExistingEditedSizeMB;
3608 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.
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).
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)