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);
964 strftime(buf,
sizeof(buf),
"%Y%m%d%H%I", localtime_r(&
start, &tm_r));
972 int l = strxfrm(NULL, s, 0) + 1;
1006 struct tm *t = localtime_r(&
start, &tm_r);
1011 if (strcmp(Name,
name) != 0)
1012 dsyslog(
"recording file name '%s' truncated to '%s'",
name, Name);
1022 char New = NewIndicator &&
IsNew() ?
'*' :
' ';
1027 struct tm *t = localtime_r(&
start, &tm_r);
1061 const char *s =
name;
1094 if (FileName && *FileName) {
1104 const char *s =
name;
1116 s = !s ?
name : s + 1;
1138 FILE *f = fopen(InfoFileName,
"w");
1158 char *NewName = strdup(
FileName());
1159 char *ext = strrchr(NewName,
'.');
1160 if (ext && strcmp(ext,
RECEXT) == 0) {
1161 strncpy(ext,
DELEXT, strlen(ext));
1162 if (access(NewName, F_OK) == 0) {
1164 isyslog(
"removing recording '%s'", NewName);
1168 if (access(
FileName(), F_OK) == 0) {
1195 char *NewName = strdup(
FileName());
1196 char *ext = strrchr(NewName,
'.');
1197 if (ext && strcmp(ext,
DELEXT) == 0) {
1198 strncpy(ext,
RECEXT, strlen(ext));
1199 if (access(NewName, F_OK) == 0) {
1201 esyslog(
"ERROR: attempt to undelete '%s', while recording '%s' exists",
FileName(), NewName);
1260 :
cThread(
"video directory scanner")
1300 while ((Foreground ||
Running()) && (e = d.
Next()) != NULL) {
1303 if (lstat(buffer, &st) == 0) {
1305 if (S_ISLNK(st.st_mode)) {
1307 isyslog(
"max link level exceeded - not scanning %s", *buffer);
1311 if (stat(buffer, &st) != 0)
1314 if (S_ISDIR(st.st_mode)) {
1334 ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
1342 recording =
Next(recording);
1343 if (access(r->
FileName(), F_OK) != 0) {
1346 VanishedRecordings.
Add(r);
1356 int NewState =
state;
1357 bool Result = State != NewState;
1373 if (lastModified > time(NULL))
1394 if (strcmp(recording->FileName(), FileName) == 0)
1420 Del(recording,
false);
1421 char *ext = strrchr(recording->
fileName,
'.');
1422 if (ext && RemoveRecording) {
1423 strncpy(ext,
DELEXT, strlen(ext));
1424 if (access(recording->
FileName(), F_OK) == 0) {
1425 recording->
deleted = time(NULL);
1449 int FileSizeMB = recording->FileSizeMB();
1450 if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
1462 if (recording->IsOnVideoDirectoryFileSystem()) {
1463 int FileSizeMB = recording->FileSizeMB();
1464 if (FileSizeMB > 0) {
1465 int LengthInSeconds = recording->LengthInSeconds();
1466 if (LengthInSeconds > 0) {
1468 length += LengthInSeconds;
1473 return (size && length) ? double(size) * 60 / length : -1;
1480 if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
1481 recording->ResetResume();
1490 recording->ClearSortName();
1519 const char *p = strchr(s,
' ');
1530 return fprintf(f,
"%s", *
ToText()) > 0;
1535 bool cMarks::Load(
const char *RecordingFileName,
double FramesPerSecond,
bool IsPesRecording)
1549 time_t t = time(NULL);
1553 lastChange = LastModified > 0 ? LastModified : t;
1566 cMutexLock MutexLock(&MutexMarkFramesPerSecond);
1592 if (
int d = m->Position() - p) {
1603 if (m2->Position() < m1->Position()) {
1604 swap(m1->position, m2->position);
1605 swap(m1->comment, m2->comment);
1620 if (mi->Position() == Position)
1629 if (mi->Position() < Position)
1638 if (mi->Position() > Position)
1648 while (
cMark *NextMark =
Next(BeginMark)) {
1649 if (BeginMark->
Position() == NextMark->Position()) {
1650 if (!(BeginMark =
Next(NextMark)))
1666 while (
cMark *NextMark =
Next(EndMark)) {
1667 if (EndMark->
Position() == NextMark->Position()) {
1668 if (!(EndMark =
Next(NextMark)))
1680 int NumSequences = 0;
1688 if (NumSequences == 1 && BeginMark->Position() == 0)
1692 return NumSequences;
1707 isyslog(
"executing '%s'", *cmd);
1714 #define IFG_BUFFER_SIZE KILOBYTE(100)
1720 virtual void Action(
void);
1727 :
cThread(
"index file generator")
1728 ,recordingName(RecordingName)
1740 bool IndexFileComplete =
false;
1741 bool IndexFileWritten =
false;
1742 bool Rewind =
false;
1751 off_t FrameOffset = -1;
1764 if (FrameDetector.
Synced()) {
1767 FrameOffset = FileSize;
1768 int Processed = FrameDetector.
Analyze(Data, Length);
1769 if (Processed > 0) {
1773 IndexFileWritten =
true;
1775 FileSize += Processed;
1776 Buffer.
Del(Processed);
1779 else if (PatPmtParser.
Vpid()) {
1781 int Processed = FrameDetector.
Analyze(Data, Length);
1782 if (Processed > 0) {
1783 if (FrameDetector.
Synced()) {
1787 Buffer.
Del(Processed);
1793 while (Length >= TS_SIZE) {
1797 else if (PatPmtParser.
IsPmtPid(Pid))
1801 if (PatPmtParser.
Vpid()) {
1809 Buffer.
Del(p - Data);
1813 else if (ReplayFile) {
1814 int Result = Buffer.
Read(ReplayFile, BufferChunks);
1824 IndexFileComplete =
true;
1828 if (IndexFileComplete) {
1829 if (IndexFileWritten) {
1831 if (RecordingInfo.
Read()) {
1834 RecordingInfo.
Write();
1850 #define INDEXFILESUFFIX "/index"
1853 #define MAXINDEXCATCHUP 8 // number of retries
1854 #define INDEXCATCHUPWAIT 100 // milliseconds
1868 tIndexTs(off_t Offset,
bool Independent, uint16_t Number)
1877 #define MAXWAITFORINDEXFILE 10 // max. time to wait for the regenerated index file (seconds)
1878 #define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
1879 #define INDEXFILETESTINTERVAL 10 // ms between tests for the size of the index file in case of pausing live video
1882 :resumeFile(FileName, IsPesRecording)
1892 if (!Record && PauseLive) {
1899 if (!Record && access(
fileName, R_OK) != 0) {
1908 }
while (access(
fileName, R_OK) != 0 && time(NULL) < tmax);
1914 delta = int(buf.st_size %
sizeof(
tIndexTs));
1917 esyslog(
"ERROR: invalid file size (%"PRId64
") in '%s'", buf.st_size, *
fileName);
1919 last = int((buf.st_size + delta) /
sizeof(
tIndexTs) - 1);
1920 if (!Record &&
last >= 0) {
1952 if ((
f = open(
fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
1954 esyslog(
"ERROR: padding index file with %d '0' bytes", delta);
1981 while (Count-- > 0) {
1982 memcpy(&IndexPes, IndexTs,
sizeof(IndexPes));
1993 while (Count-- > 0) {
1998 memcpy(IndexTs, &IndexPes,
sizeof(*IndexTs));
2010 for (
int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >=
last); i++) {
2012 if (fstat(
f, &buf) == 0) {
2013 int newLast = int(buf.st_size /
sizeof(
tIndexTs) - 1);
2014 if (newLast >
last) {
2016 if (NewSize <= newLast) {
2018 if (NewSize <= newLast)
2019 NewSize = newLast + 1;
2026 if (lseek(
f, offset, SEEK_SET) == offset) {
2028 esyslog(
"ERROR: can't read from index");
2043 esyslog(
"ERROR: can't realloc() index");
2056 return index != NULL;
2062 tIndexTs i(FileOffset, Independent, FileNumber);
2076 bool cIndexFile::Get(
int Index, uint16_t *FileNumber, off_t *FileOffset,
bool *Independent,
int *Length)
2079 if (Index >= 0 && Index <=
last) {
2088 if (fn == *FileNumber)
2089 *Length = int(fo - *FileOffset);
2105 int d = Forward ? 1 : -1;
2108 if (Index >= 0 && Index <=
last) {
2109 if (
index[Index].independent) {
2122 if (fn == *FileNumber)
2123 *Length = int(fo - *FileOffset);
2144 if (
index[Index].independent)
2150 if (
index[il].independent)
2157 if (
index[ih].independent)
2173 for (i = 0; i <=
last; i++) {
2174 if (
index[i].number > FileNumber || (
index[i].number == FileNumber) && off_t(
index[i].offset) >= FileOffset)
2203 if (*s && stat(s, &buf) == 0)
2212 if (Recording.
Name()) {
2215 unlink(IndexFileName);
2217 while (IndexFileGenerator->
Active())
2219 if (access(IndexFileName, R_OK) == 0)
2222 fprintf(stderr,
"cannot create '%s'\n", *IndexFileName);
2225 fprintf(stderr,
"'%s' is not a TS recording\n", FileName);
2228 fprintf(stderr,
"'%s' is not a recording\n", FileName);
2231 fprintf(stderr,
"'%s' is not a directory\n", FileName);
2237 #define MAXFILESPERRECORDINGPES 255
2238 #define RECORDFILESUFFIXPES "/%03d.vdr"
2239 #define MAXFILESPERRECORDINGTS 65535
2240 #define RECORDFILESUFFIXTS "/%05d.ts"
2241 #define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
2279 for (; Number > 0; Number--) {
2283 int fd = open(
fileName, O_RDONLY | O_LARGEFILE, DEFFILEMODE);
2285 off_t pos = lseek(fd, -
TS_SIZE, SEEK_END);
2289 while (read(fd, buf,
sizeof(buf)) ==
sizeof(buf)) {
2291 int Pid =
TsPid(buf);
2293 PatPmtParser.
ParsePat(buf,
sizeof(buf));
2294 else if (PatPmtParser.
IsPmtPid(Pid)) {
2295 PatPmtParser.
ParsePmt(buf,
sizeof(buf));
2296 if (PatPmtParser.
GetVersions(PatVersion, PmtVersion)) {
2307 pos = lseek(fd, pos -
TS_SIZE, SEEK_SET);
2321 int BlockingFlag =
blocking ? 0 : O_NONBLOCK;
2335 else if (errno != ENOENT)
2356 if (0 < Number && Number <= MaxFilesPerRecording) {
2364 if (buf.st_size != 0)
2368 dsyslog(
"cFileName::SetOffset: removing zero-sized file %s",
fileName);
2375 else if (errno != ENOENT) {
2389 esyslog(
"ERROR: max number of files (%d) exceeded", MaxFilesPerRecording);
2402 const char *Sign =
"";
2408 int f = int(modf((Index + 0.5) / FramesPerSecond, &Seconds) * FramesPerSecond + 1);
2409 int s = int(Seconds);
2410 int m = s / 60 % 60;
2413 return cString::sprintf(WithFrame ?
"%s%d:%02d:%02d.%02d" :
"%s%d:%02d:%02d", Sign, h, m, s, f);
2419 int n = sscanf(HMSF,
"%d:%d:%d.%d", &h, &m, &s, &f);
2423 return int(round((h * 3600 + m * 60 + s) * FramesPerSecond)) + f - 1;
2429 return int(round(Seconds * FramesPerSecond));
2438 else if (Length > Max) {
2439 esyslog(
"ERROR: frame larger than buffer (%d > %d)", Length, Max);
2442 int r = f->
Read(b, Length);
2461 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 ...