15 #define __STDC_FORMAT_MACROS // Required for format specifiers
31 #define SUMMARYFALLBACK
44 #define DATAFORMATPES "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
45 #define NAMEFORMATPES "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
46 #define DATAFORMATTS "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
47 #define NAMEFORMATTS "%s/%s/" DATAFORMATTS
49 #define RESUMEFILESUFFIX "/resume%s%s"
50 #ifdef SUMMARYFALLBACK
51 #define SUMMARYFILESUFFIX "/summary.vdr"
53 #define INFOFILESUFFIX "/info"
54 #define MARKSFILESUFFIX "/marks"
56 #define SORTMODEFILE ".sort"
58 #define MINDISKSPACE 1024 // MB
60 #define REMOVECHECKDELTA 60 // seconds between checks for removing deleted files
61 #define DELETEDLIFETIME 300 // seconds after which a deleted recording will be actually removed
62 #define DISKCHECKDELTA 100 // seconds between checks for free disk space
63 #define REMOVELATENCY 10 // seconds to wait until next check after removing a file
64 #define MARKSUPDATEDELTA 10 // seconds between checks for updating editing marks
65 #define MININDEXAGE 3600 // seconds before an index file is considered no longer to be written
67 #define MAX_LINK_LEVEL 6
86 :
cThread(
"remove deleted recordings", true)
94 if (LockFile.
Lock()) {
123 static time_t LastRemoveCheck = 0;
125 if (!RemoveDeletedRecordingsThread.
Active()) {
129 RemoveDeletedRecordingsThread.
Start();
134 LastRemoveCheck = time(NULL);
145 static time_t LastFreeDiskCheck = 0;
146 int Factor = (Priority == -1) ? 10 : 1;
147 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
151 if (!LockFile.
Lock())
154 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
182 isyslog(
"...no deleted recording found, trying to delete an old recording...");
209 isyslog(
"...no old recording found, giving up");
212 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
215 LastFreeDiskCheck = time(NULL);
231 esyslog(
"ERROR: can't allocate memory for resume file name");
245 if ((st.st_mode & S_IWUSR) == 0)
251 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
257 else if (errno != ENOENT)
266 while ((s = ReadLine.
Read(f)) != NULL) {
270 case 'I': resume = atoi(t);
277 else if (errno != ENOENT)
288 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
300 fprintf(f,
"I %d\n", Index);
317 else if (errno != ENOENT)
329 event = ownEvent ? ownEvent : Event;
342 for (
int i = 0; i <
MAXAPIDS; i++) {
343 const char *s = Channel->
Alang(i);
348 else if (strlen(s) > strlen(Component->
language))
355 for (
int i = 0; i <
MAXDPIDS; i++) {
356 const char *s = Channel->
Dlang(i);
363 else if (strlen(s) > strlen(Component->
language))
368 for (
int i = 0; i <
MAXSPIDS; i++) {
369 const char *s = Channel->
Slang(i);
374 else if (strlen(s) > strlen(Component->
language))
409 ((
cEvent *)event)->SetShortText(ShortText);
411 ((
cEvent *)event)->SetDescription(Description);
417 aux = Aux ? strdup(Aux) : NULL;
431 while ((s = ReadLine.
Read(f)) != NULL) {
436 char *p = strchr(t,
' ');
447 unsigned int EventID;
450 unsigned int TableID = 0;
451 unsigned int Version = 0xFF;
452 int n = sscanf(t,
"%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
453 if (n >= 3 && n <= 5) {
473 esyslog(
"ERROR: EPG data problem in line %d", line);
488 event->Dump(f, Prefix,
true);
490 fprintf(f,
"%sP %d\n", Prefix,
priority);
491 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
493 fprintf(f,
"%s@ %s\n", Prefix,
aux);
509 else if (errno != ENOENT)
533 #define RESUME_NOT_INITIALIZED (-2)
566 case ' ': *p =
'_';
break;
573 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
577 sprintf(buf,
"#%02X", (
unsigned char)*p);
578 memmove(p + 2, p, strlen(p) + 1);
583 esyslog(
"ERROR: out of memory");
590 case '_': *p =
' ';
break;
595 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
597 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
601 memmove(p + 1, p + 3, strlen(p) - 2);
607 case '\x01': *p =
'\'';
break;
608 case '\x02': *p =
'/';
break;
609 case '\x03': *p =
':';
break;
615 for (
struct tCharExchange *ce = CharExchange; ce->
a && ce->b; ce++) {
616 if (*p == (ToFileSystem ? ce->a : ce->b)) {
617 *p = ToFileSystem ? ce->b : ce->a;
639 int Length = strlen(s);
642 bool NameTooLong =
false;
646 for (
char *p = s; *p; p++) {
649 NameTooLong |= NameLength > NameMax;
670 NameTooLong |= NameLength > NameMax;
678 while (i-- > 0 && a[i] >= 0) {
683 if (NameLength > NameMax) {
686 while (i-- > 0 && a[i] >= 0) {
688 if (NameLength - l <= NameMax) {
689 memmove(s + i, s + n, Length - n + 1);
690 memmove(a + i, a + n, Length - n + 1);
703 while (PathLength > PathMax && n > 0) {
708 while (--i > 0 && a[i - 1] >= 0) {
712 if (PathLength - l <= PathMax)
718 memmove(s + b, s + n, Length - n + 1);
744 const char *
Title = Event ? Event->
Title() : NULL;
745 const char *Subtitle = Event ? Event->
ShortText() : NULL;
752 if (macroTITLE || macroEPISODE) {
757 int l = strlen(name);
800 FileName =
fileName = strdup(FileName);
805 const char *p = strrchr(FileName,
'/');
810 time_t now = time(NULL);
812 struct tm t = *localtime_r(&now, &tm_r);
821 strncpy(
name, FileName, p - FileName);
831 FILE *f = fopen(InfoFileName,
"r");
834 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
842 else if (errno == ENOENT)
846 #ifdef SUMMARYFALLBACK
850 FILE *f = fopen(SummaryFileName,
"r");
853 char *data[3] = { NULL };
856 while ((s = ReadLine.
Read(f)) != NULL) {
857 if (*s || line > 1) {
860 len += strlen(data[line]) + 1;
861 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
862 data[line] = NewBuffer;
863 strcat(data[line],
"\n");
864 strcat(data[line], s);
867 esyslog(
"ERROR: out of memory");
870 data[line] = strdup(s);
880 else if (data[1] && data[2]) {
884 int len = strlen(data[1]);
886 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
888 strcat(data[1],
"\n");
889 strcat(data[1], data[2]);
895 esyslog(
"ERROR: out of memory");
899 for (
int i = 0; i < 3; i ++)
902 else if (errno != ENOENT)
921 char *t = s, *s1 = NULL, *s2 = NULL;
942 memmove(s1, s2, t - s2 + 1);
956 int l = strxfrm(NULL, s, 0) + 1;
989 struct tm *t = localtime_r(&
start, &tm_r);
994 if (strcmp(Name,
name) != 0)
995 dsyslog(
"recording file name '%s' truncated to '%s'",
name, Name);
1005 char New = NewIndicator &&
IsNew() ?
'*' :
' ';
1010 struct tm *t = localtime_r(&
start, &tm_r);
1044 const char *s =
name;
1077 if (FileName && *FileName) {
1087 const char *s =
name;
1099 s = !s ?
name : s + 1;
1121 FILE *f = fopen(InfoFileName,
"w");
1141 char *NewName = strdup(
FileName());
1142 char *ext = strrchr(NewName,
'.');
1143 if (ext && strcmp(ext,
RECEXT) == 0) {
1144 strncpy(ext,
DELEXT, strlen(ext));
1145 if (access(NewName, F_OK) == 0) {
1147 isyslog(
"removing recording '%s'", NewName);
1151 if (access(
FileName(), F_OK) == 0) {
1178 char *NewName = strdup(
FileName());
1179 char *ext = strrchr(NewName,
'.');
1180 if (ext && strcmp(ext,
DELEXT) == 0) {
1181 strncpy(ext,
RECEXT, strlen(ext));
1182 if (access(NewName, F_OK) == 0) {
1184 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1243 :
cThread(
"video directory scanner")
1281 while ((Foreground ||
Running()) && (e = d.
Next()) != NULL) {
1284 if (lstat(buffer, &st) == 0) {
1286 if (S_ISLNK(st.st_mode)) {
1288 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1292 if (stat(buffer, &st) != 0)
1295 if (S_ISDIR(st.st_mode)) {
1320 int NewState =
state;
1321 bool Result = State != NewState;
1337 if (lastModified > time(NULL))
1357 if (strcmp(recording->FileName(), FileName) == 0)
1383 Del(recording,
false);
1384 char *ext = strrchr(recording->
fileName,
'.');
1385 if (ext && RemoveRecording) {
1386 strncpy(ext,
DELEXT, strlen(ext));
1387 if (access(recording->
FileName(), F_OK) == 0) {
1388 recording->
deleted = time(NULL);
1412 int FileSizeMB = recording->FileSizeMB();
1413 if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
1425 if (recording->IsOnVideoDirectoryFileSystem()) {
1426 int FileSizeMB = recording->FileSizeMB();
1427 if (FileSizeMB > 0) {
1428 int LengthInSeconds = recording->LengthInSeconds();
1429 if (LengthInSeconds > 0) {
1431 length += LengthInSeconds;
1436 return (size && length) ? double(size) * 60 / length : -1;
1443 if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
1444 recording->ResetResume();
1453 recording->ClearSortName();
1482 const char *p = strchr(s,
' ');
1493 return fprintf(f,
"%s", *
ToText()) > 0;
1498 bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
1512 time_t t = time(NULL);
1516 lastChange = LastModified > 0 ? LastModified : t;
1529 cMutexLock MutexLock(&MutexMarkFramesPerSecond);
1555 if (
int d = m->Position() - p) {
1566 if (m2->Position() < m1->Position()) {
1567 swap(m1->position, m2->position);
1568 swap(m1->comment, m2->comment);
1583 if (mi->Position() == Position)
1592 if (mi->Position() < Position)
1601 if (mi->Position() > Position)
1611 while (
cMark *NextMark =
Next(BeginMark)) {
1612 if (BeginMark->
Position() == NextMark->Position()) {
1613 if (!(BeginMark =
Next(NextMark)))
1629 while (
cMark *NextMark =
Next(EndMark)) {
1630 if (EndMark->
Position() == NextMark->Position()) {
1631 if (!(EndMark =
Next(NextMark)))
1643 int NumSequences = 0;
1651 if (NumSequences == 1 && BeginMark->Position() == 0)
1655 return NumSequences;
1670 isyslog(
"executing '%s'", *cmd);
1677 #define IFG_BUFFER_SIZE KILOBYTE(100)
1683 virtual void Action(
void);
1690 :
cThread(
"index file generator")
1691 ,recordingName(RecordingName)
1703 bool IndexFileComplete =
false;
1704 bool IndexFileWritten =
false;
1705 bool Rewind =
false;
1714 off_t FrameOffset = -1;
1727 if (FrameDetector.
Synced()) {
1730 FrameOffset = FileSize;
1731 int Processed = FrameDetector.
Analyze(Data, Length);
1732 if (Processed > 0) {
1736 IndexFileWritten =
true;
1738 FileSize += Processed;
1739 Buffer.
Del(Processed);
1742 else if (PatPmtParser.
Vpid()) {
1744 int Processed = FrameDetector.
Analyze(Data, Length);
1745 if (Processed > 0) {
1746 if (FrameDetector.
Synced()) {
1750 Buffer.
Del(Processed);
1756 while (Length >= TS_SIZE) {
1760 else if (PatPmtParser.
IsPmtPid(Pid))
1764 if (PatPmtParser.
Vpid()) {
1772 Buffer.
Del(p - Data);
1776 else if (ReplayFile) {
1777 int Result = Buffer.
Read(ReplayFile, BufferChunks);
1787 IndexFileComplete =
true;
1791 if (IndexFileComplete) {
1792 if (IndexFileWritten) {
1794 if (RecordingInfo.
Read()) {
1797 RecordingInfo.
Write();
1813 #define INDEXFILESUFFIX "/index"
1816 #define MAXINDEXCATCHUP 8 // number of retries
1817 #define INDEXCATCHUPWAIT 100 // milliseconds
1831 tIndexTs(off_t Offset,
bool Independent, uint16_t Number)
1840 #define MAXWAITFORINDEXFILE 10 // max. time to wait for the regenerated index file (seconds)
1841 #define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
1842 #define INDEXFILETESTINTERVAL 10 // ms between tests for the size of the index file in case of pausing live video
1845 :resumeFile(FileName, IsPesRecording)
1855 if (!Record && PauseLive) {
1862 if (!Record && access(
fileName, R_OK) != 0) {
1871 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
1877 delta = int(buf.st_size %
sizeof(
tIndexTs));
1880 esyslog(
"ERROR: invalid file size (%"PRId64
") in '%s'", buf.st_size, *
fileName);
1882 last = int((buf.st_size + delta) /
sizeof(
tIndexTs) - 1);
1883 if (!Record &&
last >= 0) {
1915 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
1917 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
1944 while (Count-- > 0) {
1945 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
1956 while (Count-- > 0) {
1961 memcpy(IndexTs, &IndexPes,
sizeof(*IndexTs));
1973 for (
int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >=
last); i++) {
1975 if (fstat(
f, &buf) == 0) {
1976 int newLast = int(buf.st_size /
sizeof(
tIndexTs) - 1);
1977 if (newLast >
last) {
1979 if (NewSize <= newLast) {
1981 if (NewSize <= newLast)
1982 NewSize = newLast + 1;
1989 if (lseek(
f, offset, SEEK_SET) == offset) {
1991 esyslog(
"ERROR: can't read from index");
2006 esyslog(
"ERROR: can't realloc() index");
2019 return index != NULL;
2025 tIndexTs i(FileOffset, Independent, FileNumber);
2039 bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length)
2042 if (Index >= 0 && Index <=
last) {
2051 if (fn == *FileNumber)
2052 *Length = int(fo - *FileOffset);
2068 int d = Forward ? 1 : -1;
2071 if (Index >= 0 && Index <=
last) {
2072 if (
index[Index].independent) {
2085 if (fn == *FileNumber)
2086 *Length = int(fo - *FileOffset);
2107 if (
index[Index].independent)
2113 if (
index[il].independent)
2120 if (
index[ih].independent)
2136 for (i = 0; i <=
last; i++) {
2137 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
2166 if (*s && stat(s, &buf) == 0)
2175 if (Recording.
Name()) {
2178 unlink(IndexFileName);
2180 while (IndexFileGenerator->
Active())
2182 if (access(IndexFileName, R_OK) == 0)
2185 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
2188 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
2191 fprintf(stderr,
"'%s' is not a recording\n", FileName);
2194 fprintf(stderr,
"'%s' is not a directory\n", FileName);
2200 #define MAXFILESPERRECORDINGPES 255
2201 #define RECORDFILESUFFIXPES "/%03d.vdr"
2202 #define MAXFILESPERRECORDINGTS 65535
2203 #define RECORDFILESUFFIXTS "/%05d.ts"
2204 #define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
2242 for (; Number > 0; Number--) {
2246 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
2248 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
2252 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
2254 int Pid =
TsPid(buf);
2256 PatPmtParser.
ParsePat(buf,
sizeof(buf));
2257 else if (PatPmtParser.
IsPmtPid(Pid)) {
2258 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
2259 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
2270 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
2284 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
2298 else if (errno != ENOENT)
2319 if (0 < Number && Number <= MaxFilesPerRecording) {
2327 if (buf.st_size != 0)
2331 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
2338 else if (errno != ENOENT) {
2352 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
2365 const char *Sign =
"";
2371 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond + 1);
2372 int s = int(Seconds);
2373 int m = s / 60 % 60;
2376 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
2382 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
2386 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f - 1;
2392 return int(round(Seconds * FramesPerSecond));
2401 else if (Length > Max) {
2402 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
2405 int r = f->
Read(b, Length);
2424 if (fgets(buf,
sizeof(buf), f))
struct dirent * Next(void)
void ParsePat(const uchar *Data, int Length)
Parses the PAT data from the single TS packet in Data.
void SetFramesPerSecond(double FramesPerSecond)
virtual void Clear(void)
Immediately clears the ring buffer.
bool Update(bool Wait=false)
Triggers an update of the list of recordings, which will run as a separate thread if Wait is false...
int TotalFileSizeMB(void)
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
int NumFrames(void) const
Returns the number of frames in this recording.
static tChannelID FromString(const char *s)
bool RemoveVideoFile(const char *FileName)
void Refresh(bool Foreground=false)
static char * StripEpisodeName(char *s, bool Strip)
const char * UpdateFileName(const char *FileName)
void SetPid(int Pid, int Type)
Sets the Pid and stream Type to detect frames for.
void SetComponent(int Index, const char *s)
#define DEFAULTFRAMESPERSECOND
void ParsePmt(const uchar *Data, int Length)
Parses the PMT data from the single TS packet in Data.
const char * InvalidChars
void SetStartTime(time_t StartTime)
void SetDuration(int Duration)
cMark * GetPrev(int Position)
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
void SetRecordingsSortMode(const char *Directory, eRecordingsSortMode SortMode)
void ResetResume(const char *ResumeFileName=NULL)
void SetTableID(uchar TableID)
void Add(cListObject *Object, cListObject *After=NULL)
bool CatchUp(int Index=-1)
cResumeFile(const char *FileName, bool IsPesRecording)
bool IsEdited(void) const
void DelByName(const char *FileName, bool RemoveRecording=true)
char * LimitNameLengths(char *s, int PathMax, int NameMax)
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
double FramesPerSecond(void) const
eRecordingsSortMode RecordingsSortMode
ssize_t Read(void *Data, size_t Size)
char language[MAXLANGCODE2]
cMark * GetNextBegin(cMark *EndMark=NULL)
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark...
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
const char * Title(char Delimiter= ' ', bool NewIndicator=false, int Level=-1) const
bool VideoFileSpaceAvailable(int SizeMB)
#define TIMERMACRO_EPISODE
static cString sprintf(const char *fmt,...) __attribute__((format(printf
off_t Seek(off_t Offset, int Whence)
int Analyze(const uchar *Data, int Length)
Analyzes the TS packets pointed to by Data.
const char * VideoDirectory
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber=NULL, off_t *FileOffset=NULL, int *Length=NULL)
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
bool IsOnVideoDirectoryFileSystem(void) const
cUnbufferedFile * NextFile(void)
#define RECORDFILESUFFIXTS
int AlwaysSortFoldersFirst
double MarkFramesPerSecond
int Vpid(void) const
Returns the video pid as defined by the current PMT, or 0 if no video pid has been detected...
const cComponents * Components(void) const
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
cUnbufferedFile * OpenVideoFile(const char *FileName, int Flags)
double FramesPerSecond(void) const
#define MAXWAITFORINDEXFILE
void ResetResume(void) const
void RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
time_t StartTime(void) const
cRecording(const cRecording &)
const char * Dlang(int i) const
#define INDEXFILETESTINTERVAL
void SetAux(const char *Aux)
#define RECORDFILESUFFIXPES
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner...
static cString IndexFileName(const char *FileName, bool IsPesRecording)
bool GetLastPatPmtVersions(int &PatVersion, int &PmtVersion)
const char * Alang(int i) const
bool Synced(void)
Returns true if the frame detector has synced on the data stream.
static const char * command
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
const cChannel * Channel(void) const
int TsPid(const uchar *p)
char * SortName(void) const
#define MAXFILESPERRECORDINGPES
bool GenerateIndex(const char *FileName)
cMark * GetNextEnd(cMark *BeginMark)
Returns the next "end" mark after BeginMark, skipping any marks at the same position as BeginMark...
cMark(int Position=0, const char *Comment=NULL, double FramesPerSecond=DEFAULTFRAMESPERSECOND)
void SetTitle(const char *Title)
void ScanVideoDir(const char *DirName, bool Foreground=false, int LinkLevel=0)
tCharExchange CharExchange[]
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...
cRecording * GetByName(const char *FileName)
const char * Name(void) const
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false)
cMark * GetNext(int Position)
T * Next(const T *object) const
const char * Comment(void) const
void GetRecordingsSortMode(const char *Directory)
int Read(int FileHandle, int Max=0)
Reads at most Max bytes from FileHandle and stores them in the ring buffer.
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
bool Write(FILE *f, const char *Prefix="") const
void SetData(const char *Title, const char *ShortText, const char *Description)
int GetResume(void) const
int LengthInSeconds(void) const
Returns the length (in seconds) of this recording, or -1 in case of error.
void RemoveDeletedRecordings(void)
tIndexTs(off_t Offset, bool Independent, uint16_t Number)
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
void UpdateByName(const char *FileName)
int HMSFToIndex(const char *HMSF, double FramesPerSecond)
void bool Start(void)
Actually starts the thread.
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
bool NeedsConversion(const char *p)
int FileSizeMB(void) const
Returns the total file size of this recording (in MB), or -1 if the file size is unknown.
bool Delete(void)
Changes the file name so that it will no longer be visible in the "Recordings" menu Returns false in ...
void ConvertToPes(tIndexTs *IndexTs, int Count)
cUnbufferedFile * Open(void)
static int GetLength(const char *FileName, bool IsPesRecording=false)
Calculates the recording length (number of frames) without actually reading the index file...
static int Utf8CharLen(const char *s)
tChannelID GetChannelID(void) const
int isOnVideoDirectoryFileSystem
void ConvertFromPes(tIndexTs *IndexTs, int Count)
int GetClosestIFrame(int Index)
Returns the index of the I-frame that is closest to the given Index (or Index itself, if it already points to an I-frame).
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
static char * updateFileName
bool HasRecordingsSortMode(const char *Directory)
bool TimedWait(cMutex &Mutex, int TimeoutMs)
int SystemExec(const char *Command, bool Detached)
int HierarchyLevels(void) const
void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void TouchUpdate(void)
Touches the '.update' file in the video directory, so that other instances of VDR that access the sam...
void Del(int Count)
Deletes at most Count bytes from the ring buffer.
bool Parse(const char *s)
#define MAXFILESPERRECORDINGTS
const char * UpdateFileName(void)
const char * Title(void) const
bool Lock(int WaitSeconds=0)
bool Remove(void)
Actually removes the file from the disk Returns false in case of error.
cRecordings DeletedRecordings
cIndexFileGenerator * indexFileGenerator
cRecordings(bool Deleted=false)
#define RECORDFILESUFFIXLEN
int GetNumSequences(void)
Returns the actual number of sequences to be cut from the recording.
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
void Del(cListObject *Object, bool DeleteObject=true)
cString ToString(void) const
cString PrefixVideoFileName(const char *FileName, char Prefix)
cMark * Get(int Position)
bool Active(void)
Checks whether the thread is still alive.
cFileName(const char *FileName, bool Record, bool Blocking=false, bool IsPesRecording=false)
cRemoveDeletedRecordingsThread(void)
#define RESUME_NOT_INITIALIZED
bool NewFrame(void)
Returns true if the data given to the last call to Analyze() started a new frame. ...
bool IndependentFrame(void)
Returns true if a new frame was detected and this is an independent frame (i.e.
const char * File(void) 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...
bool IsSingleEvent(void) const
uchar * Get(int &Count)
Gets data from the ring buffer.
double MBperMinute(void)
Returns the average data rate (in MB/min) of all recordings, or -1 if this value is unknown...
void IncRecordingsSortMode(const char *Directory)
int NumComponents(void) const
const char * Name(void) const
void AssertFreeDiskSpace(int Priority, bool Force)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
double FramesPerSecond(void)
Returns the number of frames per second, or 0 if this information is not available.
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
const char * Title(void) const
void SetVersion(uchar Version)
void ClearSortNames(void)
int SecondsToFrames(int Seconds, double FramesPerSecond)
bool StateChanged(int &State)
const char * Slang(int i) const
cMutex MutexMarkFramesPerSecond
const cComponents * Components(void) const
cIndexFileGenerator(const char *RecordingName)
static const tChannelID InvalidID
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
cRecordingInfo(const cChannel *Channel=NULL, const cEvent *Event=NULL)
int CloseVideoFile(cUnbufferedFile *File)
static cUnbufferedFile * Create(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
bool IsStillRecording(void)
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void SetEventID(tEventID EventID)
#define INDEXFILECHECKINTERVAL
char * ExchangeChars(char *s, bool ToFileSystem)
cString recordingFileName
const char * ShortText(void) const
const char * FileName(void) const
bool RenameVideoFile(const char *OldName, const char *NewName)
virtual int Compare(const cListObject &ListObject) const
Must return 0 if this object is equal to ListObject, a positive value if it is "greater", and a negative value if it is "smaller".
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
const char * PrefixFileName(char Prefix)
const char * Aux(void) const
cMark * Prev(const cMark *object) const
static cRemoveDeletedRecordingsThread RemoveDeletedRecordingsThread
void SetFile(const char *File)
void AddByName(const char *FileName, bool TriggerUpdate=true)
bool IsPesRecording(void) const
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
#define RUC_DELETERECORDING
#define SUMMARYFILESUFFIX
bool Undelete(void)
Changes the file name so that it will be visible in the "Recordings" menu again and not processed by ...