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
66 #define MAXREMOVETIME 10 // seconds after which to return from removing deleted recordings
68 #define MAX_LINK_LEVEL 6
88 :
cThread(
"remove deleted recordings", true)
96 if (LockFile.
Lock()) {
97 time_t StartTime = time(NULL);
130 static time_t LastRemoveCheck = 0;
132 if (!RemoveDeletedRecordingsThread.
Active()) {
136 RemoveDeletedRecordingsThread.
Start();
141 LastRemoveCheck = time(NULL);
152 static time_t LastFreeDiskCheck = 0;
153 int Factor = (Priority == -1) ? 10 : 1;
154 if (Force || time(NULL) - LastFreeDiskCheck >
DISKCHECKDELTA / Factor) {
158 if (!LockFile.
Lock())
161 isyslog(
"low disk space while recording, trying to remove a deleted recording...");
189 isyslog(
"...no deleted recording found, trying to delete an old recording...");
216 isyslog(
"...no old recording found, giving up");
219 isyslog(
"...no deleted recording found, priority %d too low to trigger deleting an old recording", Priority);
222 LastFreeDiskCheck = time(NULL);
231 VanishedRecordings.
Clear();
246 esyslog(
"ERROR: can't allocate memory for resume file name");
260 if ((st.st_mode & S_IWUSR) == 0)
266 if (
safe_read(f, &resume,
sizeof(resume)) !=
sizeof(resume)) {
272 else if (errno != ENOENT)
281 while ((s = ReadLine.
Read(f)) != NULL) {
285 case 'I': resume = atoi(t);
292 else if (errno != ENOENT)
303 int f = open(
fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
315 fprintf(f,
"I %d\n", Index);
332 else if (errno != ENOENT)
344 event = ownEvent ? ownEvent : Event;
357 for (
int i = 0; i <
MAXAPIDS; i++) {
358 const char *s = Channel->
Alang(i);
363 else if (strlen(s) > strlen(Component->
language))
370 for (
int i = 0; i <
MAXDPIDS; i++) {
371 const char *s = Channel->
Dlang(i);
378 else if (strlen(s) > strlen(Component->
language))
383 for (
int i = 0; i <
MAXSPIDS; i++) {
384 const char *s = Channel->
Slang(i);
389 else if (strlen(s) > strlen(Component->
language))
424 ((
cEvent *)event)->SetShortText(ShortText);
426 ((
cEvent *)event)->SetDescription(Description);
432 aux = Aux ? strdup(Aux) : NULL;
446 while ((s = ReadLine.
Read(f)) != NULL) {
451 char *p = strchr(t,
' ');
462 unsigned int EventID;
465 unsigned int TableID = 0;
466 unsigned int Version = 0xFF;
467 int n = sscanf(t,
"%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
468 if (n >= 3 && n <= 5) {
488 esyslog(
"ERROR: EPG data problem in line %d", line);
503 event->Dump(f, Prefix,
true);
505 fprintf(f,
"%sP %d\n", Prefix,
priority);
506 fprintf(f,
"%sL %d\n", Prefix,
lifetime);
508 fprintf(f,
"%s@ %s\n", Prefix,
aux);
524 else if (errno != ENOENT)
548 #define RESUME_NOT_INITIALIZED (-2)
581 case ' ': *p =
'_';
break;
588 if (
char *NewBuffer = (
char *)realloc(s, strlen(s) + 10)) {
592 sprintf(buf,
"#%02X", (
unsigned char)*p);
593 memmove(p + 2, p, strlen(p) + 1);
598 esyslog(
"ERROR: out of memory");
605 case '_': *p =
' ';
break;
610 if (strlen(p) > 2 && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) {
612 sprintf(buf,
"%c%c", *(p + 1), *(p + 2));
616 memmove(p + 1, p + 3, strlen(p) - 2);
622 case '\x01': *p =
'\'';
break;
623 case '\x02': *p =
'/';
break;
624 case '\x03': *p =
':';
break;
630 for (
struct tCharExchange *ce = CharExchange; ce->
a && ce->b; ce++) {
631 if (*p == (ToFileSystem ? ce->a : ce->b)) {
632 *p = ToFileSystem ? ce->b : ce->a;
654 int Length = strlen(s);
657 bool NameTooLong =
false;
661 for (
char *p = s; *p; p++) {
664 NameTooLong |= NameLength > NameMax;
685 NameTooLong |= NameLength > NameMax;
693 while (i-- > 0 && a[i] >= 0) {
698 if (NameLength > NameMax) {
701 while (i-- > 0 && a[i] >= 0) {
703 if (NameLength - l <= NameMax) {
704 memmove(s + i, s + n, Length - n + 1);
705 memmove(a + i, a + n, Length - n + 1);
718 while (PathLength > PathMax && n > 0) {
723 while (--i > 0 && a[i - 1] >= 0) {
727 if (PathLength - l <= PathMax)
733 memmove(s + b, s + n, Length - n + 1);
759 const char *
Title = Event ? Event->
Title() : NULL;
760 const char *Subtitle = Event ? Event->
ShortText() : NULL;
767 if (macroTITLE || macroEPISODE) {
772 int l = strlen(name);
815 FileName =
fileName = strdup(FileName);
820 const char *p = strrchr(FileName,
'/');
825 time_t now = time(NULL);
827 struct tm t = *localtime_r(&now, &tm_r);
836 strncpy(
name, FileName, p - FileName);
846 FILE *f = fopen(InfoFileName,
"r");
849 esyslog(
"ERROR: EPG data problem in file %s", *InfoFileName);
857 else if (errno == ENOENT)
861 #ifdef SUMMARYFALLBACK
865 FILE *f = fopen(SummaryFileName,
"r");
868 char *data[3] = { NULL };
871 while ((s = ReadLine.
Read(f)) != NULL) {
872 if (*s || line > 1) {
875 len += strlen(data[line]) + 1;
876 if (
char *NewBuffer = (
char *)realloc(data[line], len + 1)) {
877 data[line] = NewBuffer;
878 strcat(data[line],
"\n");
879 strcat(data[line], s);
882 esyslog(
"ERROR: out of memory");
885 data[line] = strdup(s);
895 else if (data[1] && data[2]) {
899 int len = strlen(data[1]);
901 if (
char *NewBuffer = (
char *)realloc(data[1], len + 1 + strlen(data[2]) + 1)) {
903 strcat(data[1],
"\n");
904 strcat(data[1], data[2]);
910 esyslog(
"ERROR: out of memory");
914 for (
int i = 0; i < 3; i ++)
917 else if (errno != ENOENT)
936 char *t = s, *s1 = NULL, *s2 = NULL;
957 memmove(s1, s2, t - s2 + 1);
970 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
978 int l = strxfrm(NULL, s, 0) + 1;
1012 struct tm *t = localtime_r(&
start, &tm_r);
1017 if (strcmp(Name,
name) != 0)
1018 dsyslog(
"recording file name '%s' truncated to '%s'",
name, Name);
1028 char New = NewIndicator &&
IsNew() ?
'*' :
' ';
1033 struct tm *t = localtime_r(&
start, &tm_r);
1067 const char *s =
name;
1100 if (FileName && *FileName) {
1110 const char *s =
name;
1122 s = !s ?
name : s + 1;
1144 FILE *f = fopen(InfoFileName,
"w");
1164 char *NewName = strdup(
FileName());
1165 char *ext = strrchr(NewName,
'.');
1166 if (ext && strcmp(ext,
RECEXT) == 0) {
1167 strncpy(ext,
DELEXT, strlen(ext));
1168 if (access(NewName, F_OK) == 0) {
1170 isyslog(
"removing recording '%s'", NewName);
1174 if (access(
FileName(), F_OK) == 0) {
1201 char *NewName = strdup(
FileName());
1202 char *ext = strrchr(NewName,
'.');
1203 if (ext && strcmp(ext,
DELEXT) == 0) {
1204 strncpy(ext,
RECEXT, strlen(ext));
1205 if (access(NewName, F_OK) == 0) {
1207 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1266 :
cThread(
"video directory scanner")
1306 while ((Foreground ||
Running()) && (e = d.
Next()) != NULL) {
1309 if (lstat(buffer, &st) == 0) {
1311 if (S_ISLNK(st.st_mode)) {
1313 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1317 if (stat(buffer, &st) != 0)
1320 if (S_ISDIR(st.st_mode)) {
1340 ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
1348 recording =
Next(recording);
1349 if (access(r->
FileName(), F_OK) != 0) {
1352 VanishedRecordings.
Add(r);
1362 int NewState =
state;
1363 bool Result = State != NewState;
1379 if (lastModified > time(NULL))
1400 if (strcmp(recording->FileName(), FileName) == 0)
1426 Del(recording,
false);
1427 char *ext = strrchr(recording->
fileName,
'.');
1428 if (ext && RemoveRecording) {
1429 strncpy(ext,
DELEXT, strlen(ext));
1430 if (access(recording->
FileName(), F_OK) == 0) {
1431 recording->
deleted = time(NULL);
1455 int FileSizeMB = recording->FileSizeMB();
1456 if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
1468 if (recording->IsOnVideoDirectoryFileSystem()) {
1469 int FileSizeMB = recording->FileSizeMB();
1470 if (FileSizeMB > 0) {
1471 int LengthInSeconds = recording->LengthInSeconds();
1472 if (LengthInSeconds > 0) {
1474 length += LengthInSeconds;
1479 return (size && length) ? double(size) * 60 / length : -1;
1486 if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
1487 recording->ResetResume();
1496 recording->ClearSortName();
1525 const char *p = strchr(s,
' ');
1536 return fprintf(f,
"%s", *
ToText()) > 0;
1541 bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
1555 time_t t = time(NULL);
1559 lastChange = LastModified > 0 ? LastModified : t;
1572 cMutexLock MutexLock(&MutexMarkFramesPerSecond);
1598 if (
int d = m->Position() - p) {
1609 if (m2->Position() < m1->Position()) {
1610 swap(m1->position, m2->position);
1611 swap(m1->comment, m2->comment);
1626 if (mi->Position() == Position)
1635 if (mi->Position() < Position)
1644 if (mi->Position() > Position)
1654 while (
cMark *NextMark =
Next(BeginMark)) {
1655 if (BeginMark->
Position() == NextMark->Position()) {
1656 if (!(BeginMark =
Next(NextMark)))
1672 while (
cMark *NextMark =
Next(EndMark)) {
1673 if (EndMark->
Position() == NextMark->Position()) {
1674 if (!(EndMark =
Next(NextMark)))
1686 int NumSequences = 0;
1694 if (NumSequences == 1 && BeginMark->Position() == 0)
1698 return NumSequences;
1713 isyslog(
"executing '%s'", *cmd);
1720 #define IFG_BUFFER_SIZE KILOBYTE(100)
1726 virtual void Action(
void);
1733 :
cThread(
"index file generator")
1734 ,recordingName(RecordingName)
1746 bool IndexFileComplete =
false;
1747 bool IndexFileWritten =
false;
1748 bool Rewind =
false;
1757 off_t FrameOffset = -1;
1759 bool Stuffed =
false;
1771 if (FrameDetector.
Synced()) {
1774 FrameOffset = FileSize;
1775 int Processed = FrameDetector.
Analyze(Data, Length);
1776 if (Processed > 0) {
1780 IndexFileWritten =
true;
1782 FileSize += Processed;
1783 Buffer.
Del(Processed);
1786 else if (PatPmtParser.
Vpid()) {
1788 int Processed = FrameDetector.
Analyze(Data, Length);
1789 if (Processed > 0) {
1790 if (FrameDetector.
Synced()) {
1794 Buffer.
Del(Processed);
1800 while (Length >= TS_SIZE) {
1804 else if (PatPmtParser.
IsPmtPid(Pid))
1808 if (PatPmtParser.
Vpid()) {
1816 Buffer.
Del(p - Data);
1820 else if (ReplayFile) {
1821 int Result = Buffer.
Read(ReplayFile, BufferChunks);
1823 if (Buffer.
Available() > 0 && !Stuffed) {
1832 Buffer.
Put(StuffingPacket,
sizeof(StuffingPacket));
1846 IndexFileComplete =
true;
1850 if (IndexFileComplete) {
1851 if (IndexFileWritten) {
1853 if (RecordingInfo.
Read()) {
1856 RecordingInfo.
Write();
1872 #define INDEXFILESUFFIX "/index"
1875 #define MAXINDEXCATCHUP 8 // number of retries
1876 #define INDEXCATCHUPWAIT 100 // milliseconds
1890 tIndexTs(off_t Offset,
bool Independent, uint16_t Number)
1899 #define MAXWAITFORINDEXFILE 10 // max. time to wait for the regenerated index file (seconds)
1900 #define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
1901 #define INDEXFILETESTINTERVAL 10 // ms between tests for the size of the index file in case of pausing live video
1904 :resumeFile(FileName, IsPesRecording)
1914 if (!Record && PauseLive) {
1921 if (!Record && access(
fileName, R_OK) != 0) {
1930 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
1936 delta = int(buf.st_size %
sizeof(
tIndexTs));
1939 esyslog(
"ERROR: invalid file size (%"PRId64
") in '%s'", buf.st_size, *
fileName);
1941 last = int((buf.st_size + delta) /
sizeof(
tIndexTs) - 1);
1942 if (!Record &&
last >= 0) {
1974 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
1976 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
2003 while (Count-- > 0) {
2004 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
2015 while (Count-- > 0) {
2020 memcpy(IndexTs, &IndexPes,
sizeof(*IndexTs));
2032 for (
int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >=
last); i++) {
2034 if (fstat(
f, &buf) == 0) {
2035 int newLast = int(buf.st_size /
sizeof(
tIndexTs) - 1);
2036 if (newLast >
last) {
2038 if (NewSize <= newLast) {
2040 if (NewSize <= newLast)
2041 NewSize = newLast + 1;
2048 if (lseek(
f, offset, SEEK_SET) == offset) {
2050 esyslog(
"ERROR: can't read from index");
2065 esyslog(
"ERROR: can't realloc() index");
2078 return index != NULL;
2084 tIndexTs i(FileOffset, Independent, FileNumber);
2098 bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length)
2101 if (Index >= 0 && Index <=
last) {
2110 if (fn == *FileNumber)
2111 *Length = int(fo - *FileOffset);
2127 int d = Forward ? 1 : -1;
2130 if (Index >= 0 && Index <=
last) {
2131 if (
index[Index].independent) {
2144 if (fn == *FileNumber)
2145 *Length = int(fo - *FileOffset);
2166 if (
index[Index].independent)
2172 if (
index[il].independent)
2179 if (
index[ih].independent)
2195 for (i = 0; i <=
last; i++) {
2196 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
2225 if (*s && stat(s, &buf) == 0)
2234 if (Recording.
Name()) {
2237 unlink(IndexFileName);
2239 while (IndexFileGenerator->
Active())
2241 if (access(IndexFileName, R_OK) == 0)
2244 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
2247 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
2250 fprintf(stderr,
"'%s' is not a recording\n", FileName);
2253 fprintf(stderr,
"'%s' is not a directory\n", FileName);
2259 #define MAXFILESPERRECORDINGPES 255
2260 #define RECORDFILESUFFIXPES "/%03d.vdr"
2261 #define MAXFILESPERRECORDINGTS 65535
2262 #define RECORDFILESUFFIXTS "/%05d.ts"
2263 #define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
2301 for (; Number > 0; Number--) {
2305 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
2307 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
2311 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
2313 int Pid =
TsPid(buf);
2315 PatPmtParser.
ParsePat(buf,
sizeof(buf));
2316 else if (PatPmtParser.
IsPmtPid(Pid)) {
2317 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
2318 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
2329 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
2343 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
2357 else if (errno != ENOENT)
2378 if (0 < Number && Number <= MaxFilesPerRecording) {
2386 if (buf.st_size != 0)
2390 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
2397 else if (errno != ENOENT) {
2411 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
2424 const char *Sign =
"";
2430 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond + 1);
2431 int s = int(Seconds);
2432 int m = s / 60 % 60;
2435 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
2441 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
2445 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f - 1;
2451 return int(round(Seconds * FramesPerSecond));
2460 else if (Length > Max) {
2461 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
2464 int r = f->
Read(b, Length);
2483 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
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the ring buffer.
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).
static bool HasKeys(void)
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 ...
virtual int Available(void)