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
87 :
cThread(
"remove deleted recordings", true)
95 if (LockFile.
Lock()) {
124 static time_t LastRemoveCheck = 0;
126 if (!RemoveDeletedRecordingsThread.
Active()) {
130 RemoveDeletedRecordingsThread.
Start();
135 LastRemoveCheck = time(NULL);
146 static time_t LastFreeDiskCheck = 0;
147 int Factor = (Priority == -1) ? 10 : 1;
148 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
152 if (!LockFile.
Lock())
155 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
183 isyslog(
"...no deleted recording found, trying to delete an old recording...");
210 isyslog(
"...no old recording found, giving up");
213 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
216 LastFreeDiskCheck = time(NULL);
225 VanishedRecordings.
Clear();
240 esyslog(
"ERROR: can't allocate memory for resume file name");
254 if ((st.st_mode & S_IWUSR) == 0)
260 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
266 else if (errno != ENOENT)
275 while ((s = ReadLine.
Read(f)) != NULL) {
279 case 'I': resume = atoi(t);
286 else if (errno != ENOENT)
297 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
309 fprintf(f,
"I %d\n", Index);
326 else if (errno != ENOENT)
338 event = ownEvent ? ownEvent : Event;
351 for (
int i = 0; i <
MAXAPIDS; i++) {
352 const char *s = Channel->
Alang(i);
357 else if (strlen(s) > strlen(Component->
language))
364 for (
int i = 0; i <
MAXDPIDS; i++) {
365 const char *s = Channel->
Dlang(i);
372 else if (strlen(s) > strlen(Component->
language))
377 for (
int i = 0; i <
MAXSPIDS; i++) {
378 const char *s = Channel->
Slang(i);
383 else if (strlen(s) > strlen(Component->
language))
418 ((
cEvent *)event)->SetShortText(ShortText);
420 ((
cEvent *)event)->SetDescription(Description);
426 aux = Aux ? strdup(Aux) : NULL;
440 while ((s = ReadLine.
Read(f)) != NULL) {
445 char *p = strchr(t,
' ');
456 unsigned int EventID;
459 unsigned int TableID = 0;
460 unsigned int Version = 0xFF;
461 int n = sscanf(t,
"%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
462 if (n >= 3 && n <= 5) {
482 esyslog(
"ERROR: EPG data problem in line %d", line);
497 event->Dump(f, Prefix,
true);
499 fprintf(f,
"%sP %d\n", Prefix,
priority);
500 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
502 fprintf(f,
"%s@ %s\n", Prefix,
aux);
518 else if (errno != ENOENT)
542 #define RESUME_NOT_INITIALIZED (-2)
575 case ' ': *p =
'_';
break;
582 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
586 sprintf(buf,
"#%02X", (
unsigned char)*p);
587 memmove(p + 2, p, strlen(p) + 1);
592 esyslog(
"ERROR: out of memory");
599 case '_': *p =
' ';
break;
604 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
606 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
610 memmove(p + 1, p + 3, strlen(p) - 2);
616 case '\x01': *p =
'\'';
break;
617 case '\x02': *p =
'/';
break;
618 case '\x03': *p =
':';
break;
624 for (
struct tCharExchange *ce = CharExchange; ce->
a && ce->b; ce++) {
625 if (*p == (ToFileSystem ? ce->a : ce->b)) {
626 *p = ToFileSystem ? ce->b : ce->a;
648 int Length = strlen(s);
651 bool NameTooLong =
false;
655 for (
char *p = s; *p; p++) {
658 NameTooLong |= NameLength > NameMax;
679 NameTooLong |= NameLength > NameMax;
687 while (i-- > 0 && a[i] >= 0) {
692 if (NameLength > NameMax) {
695 while (i-- > 0 && a[i] >= 0) {
697 if (NameLength - l <= NameMax) {
698 memmove(s + i, s + n, Length - n + 1);
699 memmove(a + i, a + n, Length - n + 1);
712 while (PathLength > PathMax && n > 0) {
717 while (--i > 0 && a[i - 1] >= 0) {
721 if (PathLength - l <= PathMax)
727 memmove(s + b, s + n, Length - n + 1);
753 const char *
Title = Event ? Event->
Title() : NULL;
754 const char *Subtitle = Event ? Event->
ShortText() : NULL;
761 if (macroTITLE || macroEPISODE) {
766 int l = strlen(name);
809 FileName =
fileName = strdup(FileName);
814 const char *p = strrchr(FileName,
'/');
819 time_t now = time(NULL);
821 struct tm t = *localtime_r(&now, &tm_r);
830 strncpy(
name, FileName, p - FileName);
840 FILE *f = fopen(InfoFileName,
"r");
843 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
851 else if (errno == ENOENT)
855 #ifdef SUMMARYFALLBACK
859 FILE *f = fopen(SummaryFileName,
"r");
862 char *data[3] = { NULL };
865 while ((s = ReadLine.
Read(f)) != NULL) {
866 if (*s || line > 1) {
869 len += strlen(data[line]) + 1;
870 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
871 data[line] = NewBuffer;
872 strcat(data[line],
"\n");
873 strcat(data[line], s);
876 esyslog(
"ERROR: out of memory");
879 data[line] = strdup(s);
889 else if (data[1] && data[2]) {
893 int len = strlen(data[1]);
895 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
897 strcat(data[1],
"\n");
898 strcat(data[1], data[2]);
904 esyslog(
"ERROR: out of memory");
908 for (
int i = 0; i < 3; i ++)
911 else if (errno != ENOENT)
930 char *t = s, *s1 = NULL, *s2 = NULL;
951 memmove(s1, s2, t - s2 + 1);
965 int l = strxfrm(NULL, s, 0) + 1;
998 struct tm *t = localtime_r(&
start, &tm_r);
1003 if (strcmp(Name,
name) != 0)
1004 dsyslog(
"recording file name '%s' truncated to '%s'",
name, Name);
1014 char New = NewIndicator &&
IsNew() ?
'*' :
' ';
1019 struct tm *t = localtime_r(&
start, &tm_r);
1053 const char *s =
name;
1086 if (FileName && *FileName) {
1096 const char *s =
name;
1108 s = !s ?
name : s + 1;
1130 FILE *f = fopen(InfoFileName,
"w");
1150 char *NewName = strdup(
FileName());
1151 char *ext = strrchr(NewName,
'.');
1152 if (ext && strcmp(ext,
RECEXT) == 0) {
1153 strncpy(ext,
DELEXT, strlen(ext));
1154 if (access(NewName, F_OK) == 0) {
1156 isyslog(
"removing recording '%s'", NewName);
1160 if (access(
FileName(), F_OK) == 0) {
1187 char *NewName = strdup(
FileName());
1188 char *ext = strrchr(NewName,
'.');
1189 if (ext && strcmp(ext,
DELEXT) == 0) {
1190 strncpy(ext,
RECEXT, strlen(ext));
1191 if (access(NewName, F_OK) == 0) {
1193 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1252 :
cThread(
"video directory scanner")
1292 while ((Foreground ||
Running()) && (e = d.
Next()) != NULL) {
1295 if (lstat(buffer, &st) == 0) {
1297 if (S_ISLNK(st.st_mode)) {
1299 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1303 if (stat(buffer, &st) != 0)
1306 if (S_ISDIR(st.st_mode)) {
1325 ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
1333 recording =
Next(recording);
1334 if (access(r->
FileName(), F_OK) != 0) {
1337 VanishedRecordings.
Add(r);
1347 int NewState =
state;
1348 bool Result = State != NewState;
1364 if (lastModified > time(NULL))
1385 if (strcmp(recording->FileName(), FileName) == 0)
1411 Del(recording,
false);
1412 char *ext = strrchr(recording->
fileName,
'.');
1413 if (ext && RemoveRecording) {
1414 strncpy(ext,
DELEXT, strlen(ext));
1415 if (access(recording->
FileName(), F_OK) == 0) {
1416 recording->
deleted = time(NULL);
1440 int FileSizeMB = recording->FileSizeMB();
1441 if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
1453 if (recording->IsOnVideoDirectoryFileSystem()) {
1454 int FileSizeMB = recording->FileSizeMB();
1455 if (FileSizeMB > 0) {
1456 int LengthInSeconds = recording->LengthInSeconds();
1457 if (LengthInSeconds > 0) {
1459 length += LengthInSeconds;
1464 return (size && length) ? double(size) * 60 / length : -1;
1471 if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
1472 recording->ResetResume();
1481 recording->ClearSortName();
1510 const char *p = strchr(s,
' ');
1521 return fprintf(f,
"%s", *
ToText()) > 0;
1526 bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
1540 time_t t = time(NULL);
1544 lastChange = LastModified > 0 ? LastModified : t;
1557 cMutexLock MutexLock(&MutexMarkFramesPerSecond);
1583 if (
int d = m->Position() - p) {
1594 if (m2->Position() < m1->Position()) {
1595 swap(m1->position, m2->position);
1596 swap(m1->comment, m2->comment);
1611 if (mi->Position() == Position)
1620 if (mi->Position() < Position)
1629 if (mi->Position() > Position)
1639 while (
cMark *NextMark =
Next(BeginMark)) {
1640 if (BeginMark->
Position() == NextMark->Position()) {
1641 if (!(BeginMark =
Next(NextMark)))
1657 while (
cMark *NextMark =
Next(EndMark)) {
1658 if (EndMark->
Position() == NextMark->Position()) {
1659 if (!(EndMark =
Next(NextMark)))
1671 int NumSequences = 0;
1679 if (NumSequences == 1 && BeginMark->Position() == 0)
1683 return NumSequences;
1698 isyslog(
"executing '%s'", *cmd);
1705 #define IFG_BUFFER_SIZE KILOBYTE(100)
1711 virtual void Action(
void);
1718 :
cThread(
"index file generator")
1719 ,recordingName(RecordingName)
1731 bool IndexFileComplete =
false;
1732 bool IndexFileWritten =
false;
1733 bool Rewind =
false;
1742 off_t FrameOffset = -1;
1755 if (FrameDetector.
Synced()) {
1758 FrameOffset = FileSize;
1759 int Processed = FrameDetector.
Analyze(Data, Length);
1760 if (Processed > 0) {
1764 IndexFileWritten =
true;
1766 FileSize += Processed;
1767 Buffer.
Del(Processed);
1770 else if (PatPmtParser.
Vpid()) {
1772 int Processed = FrameDetector.
Analyze(Data, Length);
1773 if (Processed > 0) {
1774 if (FrameDetector.
Synced()) {
1778 Buffer.
Del(Processed);
1784 while (Length >= TS_SIZE) {
1788 else if (PatPmtParser.
IsPmtPid(Pid))
1792 if (PatPmtParser.
Vpid()) {
1800 Buffer.
Del(p - Data);
1804 else if (ReplayFile) {
1805 int Result = Buffer.
Read(ReplayFile, BufferChunks);
1815 IndexFileComplete =
true;
1819 if (IndexFileComplete) {
1820 if (IndexFileWritten) {
1822 if (RecordingInfo.
Read()) {
1825 RecordingInfo.
Write();
1841 #define INDEXFILESUFFIX "/index"
1844 #define MAXINDEXCATCHUP 8 // number of retries
1845 #define INDEXCATCHUPWAIT 100 // milliseconds
1859 tIndexTs(off_t Offset,
bool Independent, uint16_t Number)
1868 #define MAXWAITFORINDEXFILE 10 // max. time to wait for the regenerated index file (seconds)
1869 #define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
1870 #define INDEXFILETESTINTERVAL 10 // ms between tests for the size of the index file in case of pausing live video
1873 :resumeFile(FileName, IsPesRecording)
1883 if (!Record && PauseLive) {
1890 if (!Record && access(
fileName, R_OK) != 0) {
1899 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
1905 delta = int(buf.st_size %
sizeof(
tIndexTs));
1908 esyslog(
"ERROR: invalid file size (%"PRId64
") in '%s'", buf.st_size, *
fileName);
1910 last = int((buf.st_size + delta) /
sizeof(
tIndexTs) - 1);
1911 if (!Record &&
last >= 0) {
1943 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
1945 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
1972 while (Count-- > 0) {
1973 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
1984 while (Count-- > 0) {
1989 memcpy(IndexTs, &IndexPes,
sizeof(*IndexTs));
2001 for (
int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >=
last); i++) {
2003 if (fstat(
f, &buf) == 0) {
2004 int newLast = int(buf.st_size /
sizeof(
tIndexTs) - 1);
2005 if (newLast >
last) {
2007 if (NewSize <= newLast) {
2009 if (NewSize <= newLast)
2010 NewSize = newLast + 1;
2017 if (lseek(
f, offset, SEEK_SET) == offset) {
2019 esyslog(
"ERROR: can't read from index");
2034 esyslog(
"ERROR: can't realloc() index");
2047 return index != NULL;
2053 tIndexTs i(FileOffset, Independent, FileNumber);
2067 bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length)
2070 if (Index >= 0 && Index <=
last) {
2079 if (fn == *FileNumber)
2080 *Length = int(fo - *FileOffset);
2096 int d = Forward ? 1 : -1;
2099 if (Index >= 0 && Index <=
last) {
2100 if (
index[Index].independent) {
2113 if (fn == *FileNumber)
2114 *Length = int(fo - *FileOffset);
2135 if (
index[Index].independent)
2141 if (
index[il].independent)
2148 if (
index[ih].independent)
2164 for (i = 0; i <=
last; i++) {
2165 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
2194 if (*s && stat(s, &buf) == 0)
2203 if (Recording.
Name()) {
2206 unlink(IndexFileName);
2208 while (IndexFileGenerator->
Active())
2210 if (access(IndexFileName, R_OK) == 0)
2213 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
2216 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
2219 fprintf(stderr,
"'%s' is not a recording\n", FileName);
2222 fprintf(stderr,
"'%s' is not a directory\n", FileName);
2228 #define MAXFILESPERRECORDINGPES 255
2229 #define RECORDFILESUFFIXPES "/%03d.vdr"
2230 #define MAXFILESPERRECORDINGTS 65535
2231 #define RECORDFILESUFFIXTS "/%05d.ts"
2232 #define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
2270 for (; Number > 0; Number--) {
2274 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
2276 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
2280 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
2282 int Pid =
TsPid(buf);
2284 PatPmtParser.
ParsePat(buf,
sizeof(buf));
2285 else if (PatPmtParser.
IsPmtPid(Pid)) {
2286 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
2287 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
2298 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
2312 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
2326 else if (errno != ENOENT)
2347 if (0 < Number && Number <= MaxFilesPerRecording) {
2355 if (buf.st_size != 0)
2359 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
2366 else if (errno != ENOENT) {
2380 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
2393 const char *Sign =
"";
2399 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond + 1);
2400 int s = int(Seconds);
2401 int m = s / 60 % 60;
2404 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
2410 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
2414 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f - 1;
2420 return int(round(Seconds * FramesPerSecond));
2429 else if (Length > Max) {
2430 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
2433 int r = f->
Read(b, Length);
2452 if (fgets(buf,
sizeof(buf), f))
struct dirent * Next(void)
void ClearVanishedRecordings(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)
static cRecordings VanishedRecordings
#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)
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.
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
void ScanVideoDir(const char *DirName, bool Foreground=false, int LinkLevel=0, int DirLevel=0)
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
cRecordings Recordings
Any access to Recordings that loops through the list of recordings needs to hold a thread lock on thi...
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
cRecordings DeletedRecordings(true)
#define SUMMARYFILESUFFIX
bool Undelete(void)
Changes the file name so that it will be visible in the "Recordings" menu again and not processed by ...