15#define __STDC_FORMAT_MACROS
33#define SUMMARYFALLBACK
46#define DATAFORMATPES "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
47#define NAMEFORMATPES "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
48#define DATAFORMATTS "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
49#define NAMEFORMATTS "%s/%s/" DATAFORMATTS
51#define RESUMEFILESUFFIX "/resume%s%s"
53#define SUMMARYFILESUFFIX "/summary.vdr"
55#define INFOFILESUFFIX "/info"
56#define MARKSFILESUFFIX "/marks"
58#define SORTMODEFILE ".sort"
59#define TIMERRECFILE ".timer"
61#define MINDISKSPACE 1024
63#define REMOVECHECKDELTA 60
64#define DELETEDLIFETIME 300
65#define DISKCHECKDELTA 100
66#define REMOVELATENCY 10
67#define MARKSUPDATEDELTA 10
68#define MAXREMOVETIME 10
70#define MAX_LINK_LEVEL 6
72#define LIMIT_SECS_PER_MB_RADIO 5
83 virtual void Action(
void)
override;
89:
cThread(
"remove deleted recordings", true)
97 if (LockFile.
Lock()) {
98 time_t StartTime = time(NULL);
100 bool interrupted =
false;
102 for (
cRecording *r = DeletedRecordings->First(); r; ) {
111 if (r->RetentionExpired()) {
114 DeletedRecordings->Del(r);
119 r = DeletedRecordings->Next(r);
137 static time_t LastRemoveCheck = 0;
141 for (
const cRecording *r = DeletedRecordings->First(); r; r = DeletedRecordings->
Next(r)) {
142 if (r->RetentionExpired()) {
148 LastRemoveCheck = time(NULL);
159 static time_t LastFreeDiskCheck = 0;
160 int Factor = (Priority == -1) ? 10 : 1;
161 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
165 if (!LockFile.
Lock())
168 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
169 int NumDeletedRecordings = 0;
172 NumDeletedRecordings = DeletedRecordings->Count();
173 if (NumDeletedRecordings) {
181 r = DeletedRecordings->
Next(r);
186 DeletedRecordings->Del(r0);
191 if (NumDeletedRecordings == 0) {
196 if (DeletedRecordings->Count())
201 isyslog(
"...no deleted recording found, trying to delete an old recording...");
203 Recordings->SetExplicitModify();
204 if (Recordings->Count()) {
221 r = Recordings->
Next(r);
225 Recordings->SetModified();
230 isyslog(
"...no old recording found, giving up");
233 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
236 LastFreeDiskCheck = time(NULL);
242#define RESUME_NOT_INITIALIZED (-2)
268 esyslog(
"ERROR: can't allocate memory for resume file name");
293 if ((st.st_mode & S_IWUSR) == 0)
299 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
305 else if (errno != ENOENT)
314 while ((s = ReadLine.
Read(f)) != NULL) {
318 case 'I': resume = atoi(t);
325 else if (errno != ENOENT)
337 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
349 fprintf(f,
"I %d\n",
Index);
385 else if (errno != ENOENT)
416 for (
int i = 0; i <
MAXAPIDS; i++) {
417 const char *s = Channel->
Alang(i);
422 else if (strlen(s) > strlen(Component->
language))
429 for (
int i = 0; i <
MAXDPIDS; i++) {
430 const char *s = Channel->
Dlang(i);
434 Component =
Components->GetComponent(i, 2, 5);
437 else if (strlen(s) > strlen(Component->
language))
442 for (
int i = 0; i <
MAXSPIDS; i++) {
443 const char *s = Channel->
Slang(i);
448 else if (strlen(s) > strlen(Component->
language))
561 if (fstat(fileno(f), &st))
563 if (
modified == st.st_mtime && !Force)
573 while ((s = ReadLine.
Read(f)) != NULL) {
578 char *p = strchr(t,
' ');
589 unsigned int EventID;
592 unsigned int TableID = 0;
593 unsigned int Version = 0xFF;
594 int n = sscanf(t,
"%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
595 if (n >= 3 && n <= 5) {
609 int n = sscanf(t,
"%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &
frameWidth, &
frameHeight, &scanTypeCode, &arBuf);
639 case 'O':
errors = atoi(t);
640 if (t = strchr(t,
' '))
650 esyslog(
"ERROR: EPG data problem in line %d", line);
665 event->Dump(f, Prefix,
true);
670 fprintf(f,
"%sP %d\n", Prefix,
priority);
671 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
672 fprintf(f,
"%sO %d", Prefix,
errors);
677 fprintf(f,
"%s@ %s\n", Prefix,
aux);
693 else if (errno != ENOENT)
769 case ' ': *p =
'_';
break;
776 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
780 sprintf(buf,
"#%02X", (
unsigned char)*p);
781 memmove(p + 2, p, strlen(p) + 1);
786 esyslog(
"ERROR: out of memory");
793 case '_': *p =
' ';
break;
798 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
800 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
804 memmove(p + 1, p + 3, strlen(p) - 2);
810 case '\x01': *p =
'\'';
break;
811 case '\x02': *p =
'/';
break;
812 case '\x03': *p =
':';
break;
819 if (*p == (ToFileSystem ? ce->a : ce->b)) {
820 *p = ToFileSystem ? ce->b : ce->a;
842 int Length = strlen(s);
845 bool NameTooLong =
false;
849 for (
char *p = s; *p; p++) {
852 NameTooLong |= NameLength > NameMax;
873 NameTooLong |= NameLength > NameMax;
881 while (i-- > 0 && a[i] >= 0) {
886 if (NameLength > NameMax) {
889 while (i-- > 0 && a[i] >= 0) {
891 if (NameLength - l <= NameMax) {
892 memmove(s + i, s + n, Length - n + 1);
893 memmove(a + i, a + n, Length - n + 1);
906 while (PathLength > PathMax && n > 0) {
911 while (--i > 0 && a[i - 1] >= 0) {
915 if (PathLength - l <= PathMax)
921 memmove(s + b, s + n, Length - n + 1);
947 const char *
Title = Event ? Event->
Title() : NULL;
948 const char *Subtitle = Event ? Event->
ShortText() : NULL;
955 if (macroTITLE || macroEPISODE) {
960 int l = strlen(
name);
1002 const char *p = strrchr(
FileName,
'/');
1007 time_t now = time(NULL);
1009 struct tm t = *localtime_r(&now, &tm_r);
1014 || 7 == sscanf(p + 1,
DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
1031 FILE *f = fopen(InfoFileName,
"r");
1034 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
1036 info->SetPriority(priority);
1037 info->SetLifetime(lifetime);
1041 else if (errno != ENOENT)
1043#ifdef SUMMARYFALLBACK
1047 FILE *f = fopen(SummaryFileName,
"r");
1050 char *data[3] = { NULL };
1053 while ((s = ReadLine.
Read(f)) != NULL) {
1054 if (*s || line > 1) {
1056 int len = strlen(s);
1057 len += strlen(data[line]) + 1;
1058 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
1059 data[line] = NewBuffer;
1060 strcat(data[line],
"\n");
1061 strcat(data[line], s);
1064 esyslog(
"ERROR: out of memory");
1067 data[line] = strdup(s);
1077 else if (data[1] && data[2]) {
1081 int len = strlen(data[1]);
1083 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
1084 data[1] = NewBuffer;
1085 strcat(data[1],
"\n");
1086 strcat(data[1], data[2]);
1092 esyslog(
"ERROR: out of memory");
1095 info->SetTitle(data[0]);
1096 info->SetShortText(data[1]);
1097 info->SetDescription(data[2]);
1098 for (
int i = 0; i < 3; i ++)
1101 else if (errno != ENOENT)
1123 char *t = s, *s1 = NULL, *s2 = NULL;
1144 memmove(s1, s2, t - s2 + 1);
1157 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
1165 int l = strxfrm(NULL, s, 0) + 1;
1200 return time(NULL) -
Deleted() > Retention;
1223 int l = strlen(Path);
1243 struct tm *t = localtime_r(&
start, &tm_r);
1259 const char *New = NewIndicator &&
IsNew() ?
"*" :
"";
1260 const char *Err = NewIndicator && (
info->Errors() > 0) ?
"!" :
"";
1265 struct tm *t = localtime_r(&
start, &tm_r);
1300 const char *s =
name;
1333 const char *s =
name;
1373 if (!OtherFileName) {
1376 if (ExistingInfo.
Read())
1401 dsyslog(
"changing priority/lifetime of '%s' to %d/%d",
Name(), NewPriority, NewLifetime);
1402 info->SetPriority(NewPriority);
1403 info->SetLifetime(NewLifetime);
1411 info->SetFileName(NewFileName);
1412 resume->SetFileName(NewFileName);
1424 if (strcmp(NewName,
Name())) {
1425 dsyslog(
"changing name of '%s' to '%s'",
Name(), NewName);
1431 name = strdup(NewName);
1433 bool Exists = access(NewFileName, F_OK) == 0;
1435 esyslog(
"ERROR: recording '%s' already exists", NewName);
1438 name = strdup(OldName);
1443 info->SetFileName(NewFileName);
1444 resume->SetFileName(NewFileName);
1454 char *NewName = strdup(
FileName());
1455 char *ext = strrchr(NewName,
'.');
1456 if (ext && strcmp(ext,
RECEXT) == 0) {
1457 strncpy(ext,
DELEXT, strlen(ext));
1458 if (access(NewName, F_OK) == 0) {
1460 isyslog(
"removing recording '%s'", NewName);
1464 if (access(
FileName(), F_OK) == 0) {
1497 char *NewName = strdup(
FileName());
1498 char *ext = strrchr(NewName,
'.');
1499 if (ext && strcmp(ext,
DELEXT) == 0) {
1500 strncpy(ext,
RECEXT, strlen(ext));
1501 if (access(NewName, F_OK) == 0) {
1503 esyslog(
"ERROR: attempt to restore '%s', while recording '%s' exists",
FileName(), NewName);
1508 if (access(
FileName(), F_OK) == 0) {
1566 if (IndexLength > 0) {
1609 void ScanVideoDir(
const char *DirName,
int LinkLevel = 0,
int DirLevel = 0);
1611 virtual void Action(
void)
override;
1618:
cThread(
"video directory scanner", true)
1654 if (lstat(buffer, &st) == 0) {
1656 if (S_ISLNK(st.st_mode)) {
1658 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1662 if (stat(buffer, &st) != 0)
1665 if (S_ISDIR(st.st_mode)) {
1673 Recordings->
Lock(StateKey,
true);
1675 dsyslog(
"activated name checking for initial read of video directory");
1703 if (!
initial && DirLevel == 0) {
1710 if (access(r->
FileName(), F_OK) != 0) {
1721 if (access(r->
FileName(), F_OK) != 0) {
1769 if (lastModified > time(NULL))
1789 if (Recording->Id() == Id)
1799 if (strcmp(Recording->FileName(), FileName) == 0)
1826 esyslog(
"ERROR: cRecordings::DelByName() called with '%s' on a list other than Recordings - ignored", FileName);
1829 char *DelRecFileName = strdup(FileName);
1830 if (
char *ext = strrchr(DelRecFileName,
'.')) {
1831 if (strcmp(ext,
RECEXT)) {
1832 esyslog(
"ERROR: cRecordings::DelByName() called with '%s', using '.rec' instead", DelRecFileName);
1833 strncpy(ext,
RECEXT, strlen(ext));
1839 esyslog(
"ERROR: cRecordings::DelByName(): '%s' not found in Recordings - using dummy", DelRecFileName);
1840 Recording = dummy =
new cRecording(FileName);
1844 Del(Recording,
false);
1845 char *ext = strrchr(Recording->
fileName,
'.');
1847 strncpy(ext,
DELEXT, strlen(ext));
1848 if (access(Recording->
FileName(), F_OK) == 0) {
1850 DeletedRecordings->Add(Recording);
1855 free(DelRecFileName);
1862 Recording->numFrames = -1;
1863 Recording->ReadInfo(
true);
1871 int FileSizeMB = Recording->FileSizeMB();
1872 if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
1883 if (Recording->IsOnVideoDirectoryFileSystem()) {
1884 int FileSizeMB = Recording->FileSizeMB();
1885 if (FileSizeMB > 0) {
1886 int LengthInSeconds = Recording->LengthInSeconds();
1887 if (LengthInSeconds > 0) {
1890 length += LengthInSeconds;
1896 return (size && length) ? double(size) * 60 / length : -1;
1903 if (Recording->IsInPath(Path))
1904 Use |= Recording->IsInUse();
1913 if (Recording->IsInPath(Path))
1921 if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
1922 dsyslog(
"moving '%s' to '%s'", OldPath, NewPath);
1925 if (Recording->IsInPath(OldPath)) {
1926 const char *p = Recording->Name() + strlen(OldPath);
1928 if (!Recording->ChangeName(NewName))
1942 if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
1943 Recording->ResetResume();
1950 Recording->ClearSortName();
1962 virtual void Action(
void)
override;
1964 cDirCopier(
const char *DirNameSrc,
const char *DirNameDst);
1987 dsyslog(
"suspending copy thread");
1993 dsyslog(
"resuming copy thread");
2010 size_t BufferSize = BUFSIZ;
2011 uchar *Buffer = NULL;
2025 size_t Read =
safe_read(From, Buffer, BufferSize);
2027 size_t Written =
safe_write(To, Buffer, Read);
2028 if (Written != Read) {
2029 esyslog(
"ERROR: can't write to destination file '%s': %m", *FileNameDst);
2033 else if (Read == 0) {
2035 if (fsync(To) < 0) {
2036 esyslog(
"ERROR: can't sync destination file '%s': %m", *FileNameDst);
2039 if (close(From) < 0) {
2040 esyslog(
"ERROR: can't close source file '%s': %m", *FileNameSrc);
2043 if (close(To) < 0) {
2044 esyslog(
"ERROR: can't close destination file '%s': %m", *FileNameDst);
2048 off_t FileSizeSrc =
FileSize(FileNameSrc);
2049 off_t FileSizeDst =
FileSize(FileNameDst);
2050 if (FileSizeSrc != FileSizeDst) {
2051 esyslog(
"ERROR: file size discrepancy: %" PRId64
" != %" PRId64, FileSizeSrc, FileSizeDst);
2056 esyslog(
"ERROR: can't read from source file '%s': %m", *FileNameSrc);
2060 else if ((e = d.
Next()) != NULL) {
2065 if (stat(FileNameSrc, &st) < 0) {
2066 esyslog(
"ERROR: can't access source file '%s': %m", *FileNameSrc);
2069 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
2070 esyslog(
"ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc);
2073 dsyslog(
"copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
2075 BufferSize =
max(
size_t(st.st_blksize * 10),
size_t(BUFSIZ));
2078 esyslog(
"ERROR: out of memory");
2082 if (access(FileNameDst, F_OK) == 0) {
2083 esyslog(
"ERROR: destination file '%s' already exists", *FileNameDst);
2086 if ((From = open(FileNameSrc, O_RDONLY)) < 0) {
2087 esyslog(
"ERROR: can't open source file '%s': %m", *FileNameSrc);
2090 if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) {
2091 esyslog(
"ERROR: can't open destination file '%s': %m", *FileNameDst);
2130 int Usage(
const char *FileName = NULL)
const;
2158 if (FileName && *FileName) {
2191 Recordings->
Del(Recording);
2214 Recordings->
Del(Recording);
2234 Recordings->
Del(Recording);
2249 Recordings->
Del(Recording);
2279 Recordings->SetExplicitModify();
2282 if (!r->Active(Recordings)) {
2283 error |= r->Error();
2284 r->Cleanup(Recordings);
2300 if (FileName && *FileName) {
2304 if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
2313 dsyslog(
"recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2316 if (FileNameSrc && *FileNameSrc) {
2317 if (Usage ==
ruCut || FileNameDst && *FileNameDst) {
2319 if (Usage ==
ruCut && !FileNameDst)
2321 if (!
Get(FileNameSrc) && !
Get(FileNameDst)) {
2329 esyslog(
"ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2332 esyslog(
"ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2335 esyslog(
"ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2338 esyslog(
"ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst);
2360 return r->Usage(FileName);
2366 int RequiredDiskSpaceMB = 0;
2370 if ((r->Usage() &
ruCut) != 0) {
2376 RequiredDiskSpaceMB +=
DirSizeMB(r->FileNameSrc());
2379 return RequiredDiskSpaceMB;
2420 const char *p = strchr(s,
' ');
2431 return fprintf(f,
"%s\n", *
ToText()) > 0;
2444 if (errno != ENOENT) {
2452bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
2466 time_t t = time(NULL);
2470 lastChange = LastModified > 0 ? LastModified : t;
2509 if (m->Position() - p) {
2520 if (m2->Position() < m1->Position()) {
2521 swap(m1->position, m2->position);
2522 swap(m1->comment, m2->comment);
2537 if (mi->Position() == Position)
2546 if (mi->Position() < Position)
2555 if (mi->Position() > Position)
2564 if (BeginMark && EndMark && BeginMark->
Position() == EndMark->
Position()) {
2565 while (
const cMark *NextMark =
Next(BeginMark)) {
2566 if (BeginMark->
Position() == NextMark->Position()) {
2567 if (!(BeginMark =
Next(NextMark)))
2582 if (EndMark && BeginMark && BeginMark->
Position() == EndMark->
Position()) {
2583 while (
const cMark *NextMark =
Next(EndMark)) {
2584 if (EndMark->
Position() == NextMark->Position()) {
2585 if (!(EndMark =
Next(NextMark)))
2597 int NumSequences = 0;
2605 if (NumSequences == 1 && BeginMark->Position() == 0)
2609 return NumSequences;
2614 if (
Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
2616 int EditedFrame = 0;
2618 bool InEdit =
false;
2620 int p = mi->Position();
2622 EditedFrame += p - PrevPos;
2625 EditedFrame -= p - Frame;
2637 EditedFrame += LastFrame - PrevPos;
2638 if (Frame < LastFrame)
2639 EditedFrame -= LastFrame - Frame;
2656 isyslog(
"executing '%s'", *cmd);
2663#define IFG_BUFFER_SIZE KILOBYTE(100)
2670 virtual void Action(
void)
override;
2677:
cThread(
"index file generator")
2690 bool IndexFileComplete =
false;
2691 bool IndexFileWritten =
false;
2692 bool Rewind =
false;
2701 off_t FrameOffset = -1;
2702 bool pendIndependentFrame =
false;
2703 uint16_t pendNumber = 0;
2704 off_t pendFileSize = 0;
2705 bool pendMissing =
false;
2709 bool Stuffed =
false;
2721 if (FrameDetector.
Synced()) {
2724 int OldPatVersion, OldPmtVersion;
2725 PatPmtParser.
GetVersions(OldPatVersion, OldPmtVersion);
2727 int NewPatVersion, NewPmtVersion;
2728 if (PatPmtParser.
GetVersions(NewPatVersion, NewPmtVersion)) {
2729 if (NewPatVersion != OldPatVersion || NewPmtVersion != OldPmtVersion) {
2730 dsyslog(
"PAT/PMT version change while generating index");
2731 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2737 int Processed = FrameDetector.
Analyze(Data, Length);
2738 if (Processed > 0) {
2739 bool PreviousErrors =
false;
2740 bool MissingFrames =
false;
2741 if (FrameDetector.
NewFrame(PreviousErrors, MissingFrames)) {
2743 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing);
2745 pendNumber = FileName.
Number();
2746 pendFileSize = FrameOffset >= 0 ? FrameOffset :
FileSize;
2747 pendMissing = MissingFrames;
2749 IndexFileWritten =
true;
2750 Errors = FrameDetector.
Errors();
2753 Buffer.
Del(Processed);
2758 int Processed = FrameDetector.
Analyze(Data, Length,
false);
2759 if (Processed > 0) {
2760 if (FrameDetector.
Synced()) {
2764 Buffer.
Del(Processed);
2774 else if (PatPmtParser.
IsPmtPid(Pid))
2780 FrameDetector.
SetPid(PatPmtParser.
Vpid() ? PatPmtParser.
Vpid() : PatPmtParser.
Apid(0), PatPmtParser.
Vpid() ? PatPmtParser.
Vtype() : PatPmtParser.
Atype(0));
2786 Buffer.
Del(p - Data);
2790 else if (ReplayFile) {
2791 int Result = Buffer.
Read(ReplayFile, BufferChunks);
2793 if (Buffer.
Available() > 0 && !Stuffed) {
2802 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
2816 bool PreviousErrors =
false;
2817 bool MissingFrames =
false;
2818 Errors = FrameDetector.
Errors(&PreviousErrors, &MissingFrames);
2820 IndexFile.
Write(pendIndependentFrame, pendNumber, pendFileSize, PreviousErrors, pendMissing || MissingFrames);
2821 IndexFileComplete =
true;
2826 if (IndexFileComplete) {
2827 if (IndexFileWritten) {
2829 if (RecordingInfo.
Read()) {
2834 Errors != RecordingInfo.
Errors()) {
2838 RecordingInfo.
Write();
2843 Skins.QueueMessage(
mtInfo,
tr(
"Index file regeneration complete"));
2847 Skins.QueueMessage(
mtError,
tr(
"Index file regeneration failed!"));
2855#define INDEXFILESUFFIX "/index"
2858#define MAXINDEXCATCHUP 8
2859#define INDEXCATCHUPWAIT 100
2875 tIndexTs(off_t Offset,
bool Independent, uint16_t Number,
bool Errors,
bool Missing)
2881 independent = Independent;
2886#define MAXWAITFORINDEXFILE 10
2887#define INDEXFILECHECKINTERVAL 500
2888#define INDEXFILETESTINTERVAL 10
2902 if (!Record && PauseLive) {
2905 while (time(NULL) < tmax &&
FileSize(
fileName) < off_t(2 *
sizeof(tIndexTs)))
2918 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
2924 delta = int(buf.st_size %
sizeof(tIndexTs));
2926 delta =
sizeof(tIndexTs) - delta;
2927 esyslog(
"ERROR: invalid file size (%" PRId64
") in '%s'", buf.st_size, *
fileName);
2929 last = int((buf.st_size + delta) /
sizeof(tIndexTs) - 1);
2930 if (!Record &&
last >= 0) {
2955 esyslog(
"ERROR: can't allocate %zd bytes for index '%s'",
size *
sizeof(tIndexTs), *
fileName);
2967 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
2969 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
2996 while (Count-- > 0) {
2997 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
2998 IndexTs->offset = IndexPes.offset;
2999 IndexTs->independent = IndexPes.type == 1;
3000 IndexTs->number = IndexPes.number;
3008 while (Count-- > 0) {
3009 IndexPes.offset = uint32_t(IndexTs->offset);
3010 IndexPes.type =
uchar(IndexTs->independent ? 1 : 2);
3011 IndexPes.number =
uchar(IndexTs->number);
3012 IndexPes.reserved = 0;
3013 memcpy((
void *)IndexTs, &IndexPes,
sizeof(*IndexTs));
3027 if (fstat(
f, &buf) == 0) {
3028 int newLast = int(buf.st_size /
sizeof(tIndexTs) - 1);
3029 if (newLast >
last) {
3031 if (NewSize <= newLast) {
3033 if (NewSize <= newLast)
3034 NewSize = newLast + 1;
3036 if (tIndexTs *NewBuffer = (tIndexTs *)realloc(
index, NewSize *
sizeof(tIndexTs))) {
3039 int offset = (
last + 1) *
sizeof(tIndexTs);
3040 int delta = (newLast -
last) *
sizeof(tIndexTs);
3041 if (lseek(
f, offset, SEEK_SET) == offset) {
3043 esyslog(
"ERROR: can't read from index");
3058 esyslog(
"ERROR: can't realloc() index");
3071 return index != NULL;
3074bool cIndexFile::Write(
bool Independent, uint16_t FileNumber, off_t FileOffset,
bool Errors,
bool Missing)
3077 tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
3091bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length,
bool *Errors,
bool *Missing)
3094 if (Index >= 0 && Index <=
last) {
3095 *FileNumber =
index[Index].number;
3096 *FileOffset =
index[Index].offset;
3098 *Independent =
index[Index].independent;
3101 uint16_t fn =
index[Index + 1].number;
3102 off_t fo =
index[Index + 1].offset;
3103 if (fn == *FileNumber)
3104 *Length = int(fo - *FileOffset);
3112 *Errors =
index[Index].errors;
3114 *Missing =
index[Index].missing;
3124 tIndexTs *p = &
index[Index];
3125 if (p->errors || p->missing)
3135 int d = Forward ? 1 : -1;
3138 if (Index >= 0 && Index <=
last) {
3139 if (
index[Index].independent) {
3146 *FileNumber =
index[Index].number;
3147 *FileOffset =
index[Index].offset;
3150 uint16_t fn =
index[Index + 1].number;
3151 off_t fo =
index[Index + 1].offset;
3152 if (fn == *FileNumber)
3153 *Length = int(fo - *FileOffset);
3174 if (
index[Index].independent)
3180 if (
index[il].independent)
3187 if (
index[ih].independent)
3203 for (i = 0; i <=
last; i++) {
3204 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
3233 if (*s && stat(s, &buf) == 0)
3234 return buf.st_size / (IsPesRecording ?
sizeof(tIndexTs) :
sizeof(tIndexPes));
3242 if (Recording.
Name()) {
3245 unlink(IndexFileName);
3247 while (IndexFileGenerator->
Active())
3249 if (access(IndexFileName, R_OK) == 0)
3252 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
3255 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
3258 fprintf(stderr,
"'%s' is not a recording\n", FileName);
3261 fprintf(stderr,
"'%s' is not a directory\n", FileName);
3267#define MAXFILESPERRECORDINGPES 255
3268#define RECORDFILESUFFIXPES "/%03d.vdr"
3269#define MAXFILESPERRECORDINGTS 65535
3270#define RECORDFILESUFFIXTS "/%05d.ts"
3271#define RECORDFILESUFFIXLEN 20
3283 esyslog(
"ERROR: can't copy file name '%s'", FileName);
3313 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
3315 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
3319 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
3321 int Pid =
TsPid(buf);
3323 PatPmtParser.
ParsePat(buf,
sizeof(buf));
3324 else if (PatPmtParser.
IsPmtPid(Pid)) {
3325 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
3326 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
3337 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
3351 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
3365 else if (errno != ENOENT)
3375 if (
file->Close() < 0)
3395 if (buf.st_size != 0)
3399 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
3406 else if (errno != ENOENT) {
3413 if (!
record && Offset >= 0 &&
file->Seek(Offset, SEEK_SET) != Offset) {
3420 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
3442 while ((s = ReadLine.
Read(f)) != NULL)
3460 if (fputs(
doneRecordings[i], f) == EOF || fputc(
'\n', f) == EOF) {
3482 if (FILE *f = fopen(
fileName,
"a")) {
3488 esyslog(
"ERROR: can't open '%s' for appending '%s'", *
fileName, Title);
3505 const char *t = Title;
3511 if (toupper(
uchar(*s)) != toupper(
uchar(*t)))
3526 const char *Sign =
"";
3532 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond);
3533 int s = int(Seconds);
3534 int m = s / 60 % 60;
3537 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
3543 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
3547 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f;
3553 return int(round(Seconds * FramesPerSecond));
3562 else if (Length > Max) {
3563 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
3566 int r = f->
Read(b, Length);
3586 if (fgets(buf,
sizeof(buf), f))
3615 dsyslog(
"writing timer id '%s' to %s", TimerId, *FileName);
3616 if (FILE *f = fopen(FileName,
"w")) {
3617 fprintf(f,
"%s\n", TimerId);
3624 dsyslog(
"removing %s", *FileName);
3632 const char *Id = NULL;
3633 if (FILE *f = fopen(FileName,
"r")) {
3634 char buf[HOST_NAME_MAX + 10];
3635 if (fgets(buf,
sizeof(buf), f)) {
3649 if (FileSizeMB > 0) {
3652 if (NumFramesOrg > 0) {
3654 if (NumFramesEdit > 0)
3655 return max(1,
int(FileSizeMB * (
double(NumFramesEdit) / NumFramesOrg)));
3664 if (FileSizeMB > 0) {
3668 if (access(EditedFileName, F_OK)) {
3669 int ExistingEditedSizeMB =
DirSizeMB(EditedFileName);
3670 if (ExistingEditedSizeMB > 0)
3671 FreeDiskMB += ExistingEditedSizeMB;
3675 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
void SetTitle(const char *Title)
void SetDescription(const char *Description)
const char * AspectRatioText(void) const
const char * ShortText(void) const
eScanType ScanType(void) const
cRecordingInfo(const cChannel *Channel=NULL, const cEvent *Event=NULL)
void SetLifetime(int Lifetime)
bool Write(FILE *f, const char *Prefix="") const
const char * Title(void) const
cString FrameParams(void) const
const char * Aux(void) const
void SetFileName(const char *FileName)
void SetPriority(int Priority)
void SetParentalRating(int ParentalRating)
uint16_t FrameWidth(void) const
void SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio)
void SetShortText(const char *ShortText)
void SetAux(const char *Aux)
void SetData(const char *Title, const char *ShortText, const char *Description)
const char * Description(void) const
eAspectRatio AspectRatio(void) const
bool Read(FILE *f, bool Force=false)
void SetErrors(int Errors, int TmpErrors=0)
double FramesPerSecond(void) const
const cComponents * Components(void) const
static const char * command
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
int isOnVideoDirectoryFileSystem
virtual ~cRecording() override
bool ChangePriorityLifetime(int NewPriority, int NewLifetime)
Changes the priority and lifetime of this recording to the given values.
bool HasMarks(void) const
Returns true if this recording has any editing marks.
bool WriteInfo(const char *OtherFileName=NULL)
Writes the info file of this recording.
time_t GetLastReplayTime(void) const
Returns the time this recording was last replayed (which is actually the timestamp of the resume file...
int IsInUse(void) const
Checks whether this recording is currently in use and therefore shall not be tampered with.
bool ChangeName(const char *NewName)
Changes the name of this recording to the given value.
bool Undelete(void)
Changes the file name (both internally and on disk) to make this a "normal" recording.
void ResetResume(void) const
void ReadInfo(bool Force=false)
bool Delete(void)
Changes the file name (both internally and on disk) to make this a "deleted" recording.
cString Folder(void) const
Returns the name of the folder this recording is stored in (without the video directory).
void DeleteResume(void) const
int NumFrames(void) const
Returns the number of frames in this recording.
bool IsEdited(void) const
int GetResume(void) const
Returns the index of the frame where replay of this recording shall be resumed, or -1 in case of an e...
bool IsInPath(const char *Path) const
Returns true if this recording is stored anywhere under the given Path.
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
char * SortName(void) const
const char * Name(void) const
Returns the full name of the recording (without the video directory).
int NumFramesAfterEdit(void) const
Returns the number of frames in the edited version of this recording.
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
const char * PrefixFileName(char Prefix)
bool DeleteMarks(void)
Deletes the editing marks from this recording (if any).
bool IsOnVideoDirectoryFileSystem(void) const
int HierarchyLevels(void) const
int FileSizeMB(void) const
Returns the total file size of this recording (in MB), or -1 if the file size is unknown.
cString BaseName(void) const
Returns the base name of this recording (without the video directory and folder).
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
bool Remove(void)
Actually removes the file from the disk.
cRecording(const cRecording &)
int LengthInSecondsAfterEdit(void) const
Returns the length (in seconds) of the edited version of this recording, or -1 in case of error.
bool RetentionExpired(void) const
double FramesPerSecond(void) const
bool IsPesRecording(void) const
time_t Deleted(void) const
static char * StripEpisodeName(char *s, bool Strip)
int LengthInSeconds(void) const
Returns the length (in seconds) of this recording, or -1 in case of error.
const char * FileNameSrc(void) const
void Cleanup(cRecordings *Recordings)
~cRecordingsHandlerEntry()
int Usage(const char *FileName=NULL) const
bool Active(cRecordings *Recordings)
const char * FileNameDst(void) const
cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst)
void DelAll(void)
Deletes/terminates all operations.
virtual ~cRecordingsHandler() override
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)
void SetFileName(const char *FileName)
cResumeFile(const char *FileName, bool IsPesRecording)
void Del(int Count)
Deletes at most Count bytes from the ring buffer.
virtual void Clear(void) override
Immediately clears the ring buffer.
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the ring buffer.
uchar * Get(int &Count)
Gets data from the ring buffer.
int Read(int FileHandle, int Max=0)
Reads at most Max bytes from FileHandle and stores them in the ring buffer.
virtual int Available(void) override
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)