16 #define __STDC_FORMAT_MACROS // Required for format specifiers
32 #define SUMMARYFALLBACK
45 #define DATAFORMATPES "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
46 #define NAMEFORMATPES "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
47 #define DATAFORMATTS "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
48 #define NAMEFORMATTS "%s/%s/" DATAFORMATTS
50 #define RESUMEFILESUFFIX "/resume%s%s"
51 #ifdef SUMMARYFALLBACK
52 #define SUMMARYFILESUFFIX "/summary.vdr"
54 #define INFOFILESUFFIX "/info"
55 #define MARKSFILESUFFIX "/marks"
57 #define SORTMODEFILE ".sort"
59 #define MINDISKSPACE 1024 // MB
61 #define REMOVECHECKDELTA 60 // seconds between checks for removing deleted files
62 #define DELETEDLIFETIME 300 // seconds after which a deleted recording will be actually removed
63 #define DISKCHECKDELTA 100 // seconds between checks for free disk space
64 #define REMOVELATENCY 10 // seconds to wait until next check after removing a file
65 #define MARKSUPDATEDELTA 10 // seconds between checks for updating editing marks
66 #define MININDEXAGE 3600 // seconds before an index file is considered no longer to be written
68 #define MAX_LINK_LEVEL 6
88 :
cThread(
"remove deleted recordings", true)
96 if (LockFile.
Lock()) {
125 static time_t LastRemoveCheck = 0;
127 if (!RemoveDeletedRecordingsThread.
Active()) {
131 RemoveDeletedRecordingsThread.
Start();
136 LastRemoveCheck = time(NULL);
147 static time_t LastFreeDiskCheck = 0;
148 int Factor = (Priority == -1) ? 10 : 1;
149 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
153 if (!LockFile.
Lock())
156 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
184 isyslog(
"...no deleted recording found, trying to delete an old recording...");
211 isyslog(
"...no old recording found, giving up");
214 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
217 LastFreeDiskCheck = time(NULL);
226 VanishedRecordings.
Clear();
241 esyslog(
"ERROR: can't allocate memory for resume file name");
255 if ((st.st_mode & S_IWUSR) == 0)
261 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
267 else if (errno != ENOENT)
276 while ((s = ReadLine.
Read(f)) != NULL) {
280 case 'I': resume = atoi(t);
287 else if (errno != ENOENT)
298 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
310 fprintf(f,
"I %d\n", Index);
327 else if (errno != ENOENT)
339 event = ownEvent ? ownEvent : Event;
352 for (
int i = 0; i <
MAXAPIDS; i++) {
353 const char *s = Channel->
Alang(i);
358 else if (strlen(s) > strlen(Component->
language))
365 for (
int i = 0; i <
MAXDPIDS; i++) {
366 const char *s = Channel->
Dlang(i);
373 else if (strlen(s) > strlen(Component->
language))
378 for (
int i = 0; i <
MAXSPIDS; i++) {
379 const char *s = Channel->
Slang(i);
384 else if (strlen(s) > strlen(Component->
language))
419 ((
cEvent *)event)->SetShortText(ShortText);
421 ((
cEvent *)event)->SetDescription(Description);
427 aux = Aux ? strdup(Aux) : NULL;
441 while ((s = ReadLine.
Read(f)) != NULL) {
446 char *p = strchr(t,
' ');
457 unsigned int EventID;
460 unsigned int TableID = 0;
461 unsigned int Version = 0xFF;
462 int n = sscanf(t,
"%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
463 if (n >= 3 && n <= 5) {
483 esyslog(
"ERROR: EPG data problem in line %d", line);
498 event->Dump(f, Prefix,
true);
500 fprintf(f,
"%sP %d\n", Prefix,
priority);
501 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
503 fprintf(f,
"%s@ %s\n", Prefix,
aux);
519 else if (errno != ENOENT)
543 #define RESUME_NOT_INITIALIZED (-2)
576 case ' ': *p =
'_';
break;
583 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
587 sprintf(buf,
"#%02X", (
unsigned char)*p);
588 memmove(p + 2, p, strlen(p) + 1);
593 esyslog(
"ERROR: out of memory");
600 case '_': *p =
' ';
break;
605 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
607 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
611 memmove(p + 1, p + 3, strlen(p) - 2);
617 case '\x01': *p =
'\'';
break;
618 case '\x02': *p =
'/';
break;
619 case '\x03': *p =
':';
break;
625 for (
struct tCharExchange *ce = CharExchange; ce->
a && ce->b; ce++) {
626 if (*p == (ToFileSystem ? ce->a : ce->b)) {
627 *p = ToFileSystem ? ce->b : ce->a;
649 int Length = strlen(s);
652 bool NameTooLong =
false;
656 for (
char *p = s; *p; p++) {
659 NameTooLong |= NameLength > NameMax;
680 NameTooLong |= NameLength > NameMax;
688 while (i-- > 0 && a[i] >= 0) {
693 if (NameLength > NameMax) {
696 while (i-- > 0 && a[i] >= 0) {
698 if (NameLength - l <= NameMax) {
699 memmove(s + i, s + n, Length - n + 1);
700 memmove(a + i, a + n, Length - n + 1);
713 while (PathLength > PathMax && n > 0) {
718 while (--i > 0 && a[i - 1] >= 0) {
722 if (PathLength - l <= PathMax)
728 memmove(s + b, s + n, Length - n + 1);
754 const char *
Title = Event ? Event->
Title() : NULL;
755 const char *Subtitle = Event ? Event->
ShortText() : NULL;
762 if (macroTITLE || macroEPISODE) {
767 int l = strlen(name);
810 FileName =
fileName = strdup(FileName);
815 const char *p = strrchr(FileName,
'/');
820 time_t now = time(NULL);
822 struct tm t = *localtime_r(&now, &tm_r);
831 strncpy(
name, FileName, p - FileName);
841 FILE *f = fopen(InfoFileName,
"r");
844 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
852 else if (errno == ENOENT)
856 #ifdef SUMMARYFALLBACK
860 FILE *f = fopen(SummaryFileName,
"r");
863 char *data[3] = { NULL };
866 while ((s = ReadLine.
Read(f)) != NULL) {
867 if (*s || line > 1) {
870 len += strlen(data[line]) + 1;
871 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
872 data[line] = NewBuffer;
873 strcat(data[line],
"\n");
874 strcat(data[line], s);
877 esyslog(
"ERROR: out of memory");
880 data[line] = strdup(s);
890 else if (data[1] && data[2]) {
894 int len = strlen(data[1]);
896 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
898 strcat(data[1],
"\n");
899 strcat(data[1], data[2]);
905 esyslog(
"ERROR: out of memory");
909 for (
int i = 0; i < 3; i ++)
912 else if (errno != ENOENT)
931 char *t = s, *s1 = NULL, *s2 = NULL;
952 memmove(s1, s2, t - s2 + 1);
965 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
973 int l = strxfrm(NULL, s, 0) + 1;
1007 struct tm *t = localtime_r(&
start, &tm_r);
1012 if (strcmp(Name,
name) != 0)
1013 dsyslog(
"recording file name '%s' truncated to '%s'",
name, Name);
1028 struct tm *t = localtime_r(&
start, &tm_r);
1062 const char *s =
name;
1095 const char *s =
name;
1107 s = !s ?
name : s + 1;
1129 FILE *f = fopen(InfoFileName,
"w");
1149 char *NewName = strdup(
FileName());
1150 char *ext = strrchr(NewName,
'.');
1151 if (ext && strcmp(ext,
RECEXT) == 0) {
1152 strncpy(ext,
DELEXT, strlen(ext));
1153 if (access(NewName, F_OK) == 0) {
1155 isyslog(
"removing recording '%s'", NewName);
1159 if (access(
FileName(), F_OK) == 0) {
1186 char *NewName = strdup(
FileName());
1187 char *ext = strrchr(NewName,
'.');
1188 if (ext && strcmp(ext,
DELEXT) == 0) {
1189 strncpy(ext,
RECEXT, strlen(ext));
1190 if (access(NewName, F_OK) == 0) {
1192 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1251 :
cThread(
"video directory scanner")
1291 while ((Foreground ||
Running()) && (e = d.
Next()) != NULL) {
1294 if (lstat(buffer, &st) == 0) {
1296 if (S_ISLNK(st.st_mode)) {
1298 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1302 if (stat(buffer, &st) != 0)
1305 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,
'.');
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)
static unsigned char buf(long p)
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)
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
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.
void DelByName(const char *FileName)
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 ...