vdr/recording.h
Klaus Schmidinger 09a17d56e2 Version 1.7.12
- Changed the EVCONTENTMASK_* macros to enums and changed "mask" to "group".
- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
- The "Edit timer" menu can now set the folder for the recording from a list of
  folders stored in "folders.conf".
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- If svdrphosts.conf contains only the address of the local host, the SVDRP port
  is opened only for the local host (thanks to Manuel Reimer).
- Renamed 'runvdr' to 'runvdr.template' and no longer copying it to the BINDIR
  in 'make install' (thanks to Martin Dauskardt).
- Added plain text error messages to log entries from cOsd::SetAreas() (suggested
  by Rolf Ahrenberg).
- cPalette::ClosestColor() now treats fully transparent colors as "equal"; improved
  cDvbSpuBitmap::getMinBpp() (thanks to Matthieu Castet and Johann Friedrichs).
- The new setup option "Miscellaneous/Channels wrap" controls whether the current
  channel wraps around the beginning or end of the channel list when zapping (thanks
  to Matti Lehtimäki).
- Fixed determining the frame duration on channels where the PTS deltas jitter by
  +/-1 around 1800.
- The PCR pid in generated PMTs is now set to the channel's PCR pid again.
- Fixed determining the frame duration on channels where the PTS deltas jitter by
  +/-1 around 3600.
- The PCR pid is now recorded for channels where this is different from the video
  PID. To facilitate this, the interfaces of cTransfer, cTransferControl, cRecorder
  and cReceiver have been modified, so that the PIDs are no longer given in separate
  parameters, but rather the whole channel is handed down for processing. The old
  constructor of cReceiver is still available, but it is recommended to plugin authors
  that they switch to the new interface as soon as possible.
  When replaying such a recording, the PCR packets are sent to PlayTsVideo()
- The files "commands.conf" and "reccmd.conf" can now contain nested lists of
  commands. See vdr.5 for information about the new file format.
2010-01-31 15:42:00 +01:00

293 lines
10 KiB
C++

/*
* recording.h: Recording file handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.h 2.13 2010/01/16 11:16:20 kls Exp $
*/
#ifndef __RECORDING_H
#define __RECORDING_H
#include <time.h>
#include "channels.h"
#include "config.h"
#include "epg.h"
#include "thread.h"
#include "timers.h"
#include "tools.h"
#define FOLDERDELIMCHAR '~'
extern bool VfatFileSystem;
extern int InstanceId;
void RemoveDeletedRecordings(void);
void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
///< The special Priority value -1 means that we shall get rid of any
///< deleted recordings faster than normal (because we're cutting).
///< If Force is true, the check will be done even if the timeout
///< hasn't expired yet.
class cResumeFile {
private:
char *fileName;
bool isPesRecording;
public:
cResumeFile(const char *FileName, bool IsPesRecording);
~cResumeFile();
int Read(void);
bool Save(int Index);
void Delete(void);
};
class cRecordingInfo {
friend class cRecording;
private:
tChannelID channelID;
char *channelName;
const cEvent *event;
cEvent *ownEvent;
char *aux;
double framesPerSecond;
int priority;
int lifetime;
char *fileName;
cRecordingInfo(const cChannel *Channel = NULL, const cEvent *Event = NULL);
void SetData(const char *Title, const char *ShortText, const char *Description);
void SetAux(const char *Aux);
public:
cRecordingInfo(const char *FileName);
~cRecordingInfo();
tChannelID ChannelID(void) const { return channelID; }
const char *ChannelName(void) const { return channelName; }
const cEvent *GetEvent(void) const { return event; }
const char *Title(void) const { return event->Title(); }
const char *ShortText(void) const { return event->ShortText(); }
const char *Description(void) const { return event->Description(); }
const cComponents *Components(void) const { return event->Components(); }
const char *Aux(void) const { return aux; }
double FramesPerSecond(void) const { return framesPerSecond; }
void SetFramesPerSecond(double FramesPerSecond);
bool Read(FILE *f);
bool Write(FILE *f, const char *Prefix = "") const;
bool Read(void);
bool Write(void) const;
};
class cRecording : public cListObject {
friend class cRecordings;
private:
mutable int resume;
mutable char *titleBuffer;
mutable char *sortBuffer;
mutable char *fileName;
mutable char *name;
mutable int fileSizeMB;
int channel;
int instanceId;
bool isPesRecording;
double framesPerSecond;
cRecordingInfo *info;
cRecording(const cRecording&); // can't copy cRecording
cRecording &operator=(const cRecording &); // can't assign cRecording
static char *StripEpisodeName(char *s);
char *SortName(void) const;
int GetResume(void) const;
public:
time_t start;
int priority;
int lifetime;
time_t deleted;
cRecording(cTimer *Timer, const cEvent *Event);
cRecording(const char *FileName);
virtual ~cRecording();
virtual int Compare(const cListObject &ListObject) const;
const char *Name(void) const { return name; }
const char *FileName(void) const;
const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
const cRecordingInfo *Info(void) const { return info; }
const char *PrefixFileName(char Prefix);
int HierarchyLevels(void) const;
void ResetResume(void) const;
double FramesPerSecond(void) const { return framesPerSecond; }
bool IsNew(void) const { return GetResume() <= 0; }
bool IsEdited(void) const;
bool IsPesRecording(void) const { return isPesRecording; }
bool WriteInfo(void);
bool Delete(void);
// Changes the file name so that it will no longer be visible in the "Recordings" menu
// Returns false in case of error
bool Remove(void);
// Actually removes the file from the disk
// Returns false in case of error
bool Undelete(void);
// Changes the file name so that it will be visible in the "Recordings" menu again and
// not processed by cRemoveDeletedRecordingsThread.
// Returns false in case of error
};
class cRecordings : public cList<cRecording>, public cThread {
private:
static char *updateFileName;
bool deleted;
time_t lastUpdate;
int state;
const char *UpdateFileName(void);
void Refresh(bool Foreground = false);
void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
protected:
void Action(void);
public:
cRecordings(bool Deleted = false);
virtual ~cRecordings();
bool Load(void) { return Update(true); }
///< Loads the current list of recordings and returns true if there
///< is anything in it (for compatibility with older plugins - use
///< Update(true) instead).
bool Update(bool Wait = false);
///< Triggers an update of the list of recordings, which will run
///< as a separate thread if Wait is false. If Wait is true, the
///< function returns only after the update has completed.
///< Returns true if Wait is true and there is anyting in the list
///< of recordings, false otherwise.
void TouchUpdate(void);
///< Touches the '.update' file in the video directory, so that other
///< instances of VDR that access the same video directory can be triggered
///< to update their recordings list.
bool NeedsUpdate(void);
void ChangeState(void) { state++; }
bool StateChanged(int &State);
void ResetResume(const char *ResumeFileName = NULL);
cRecording *GetByName(const char *FileName);
void AddByName(const char *FileName, bool TriggerUpdate = true);
void DelByName(const char *FileName);
int TotalFileSizeMB(void); ///< Only for deleted recordings!
};
extern cRecordings Recordings;
extern cRecordings DeletedRecordings;
#define DEFAULTFRAMESPERSECOND 25.0
class cMark : public cListObject {
private:
double framesPerSecond;
public:
int position;
char *comment;
cMark(int Position = 0, const char *Comment = NULL, double FramesPerSecond = DEFAULTFRAMESPERSECOND);
virtual ~cMark();
cString ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
};
class cMarks : public cConfig<cMark> {
private:
double framesPerSecond;
public:
bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false);
void Sort(void);
cMark *Add(int Position);
cMark *Get(int Position);
cMark *GetPrev(int Position);
cMark *GetNext(int Position);
};
#define RUC_BEFORERECORDING "before"
#define RUC_AFTERRECORDING "after"
#define RUC_EDITEDRECORDING "edited"
class cRecordingUserCommand {
private:
static const char *command;
public:
static void SetCommand(const char *Command) { command = Command; }
static void InvokeCommand(const char *State, const char *RecordingFileName);
};
// The maximum size of a single frame (up to HDTV 1920x1080):
#define MAXFRAMESIZE (KILOBYTE(1024) / TS_SIZE * TS_SIZE) // multiple of TS_SIZE to avoid breaking up TS packets
// The maximum file size is limited by the range that can be covered
// with a 40 bit 'unsigned int', which is 1TB. The actual maximum value
// used is 6MB below the theoretical maximum, to have some safety (the
// actual file size may be slightly higher because we stop recording only
// before the next independent frame, to have a complete Group Of Pictures):
#define MAXVIDEOFILESIZETS 1048570 // MB
#define MAXVIDEOFILESIZEPES 2000 // MB
#define MINVIDEOFILESIZE 100 // MB
#define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
struct tIndexTs;
class cIndexFileGenerator;
class cIndexFile {
private:
int f;
char *fileName;
int size, last;
tIndexTs *index;
bool isPesRecording;
cResumeFile resumeFile;
cIndexFileGenerator *indexFileGenerator;
cMutex mutex;
void ConvertFromPes(tIndexTs *IndexTs, int Count);
void ConvertToPes(tIndexTs *IndexTs, int Count);
bool CatchUp(int Index = -1);
public:
cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false);
~cIndexFile();
bool Ok(void) { return index != NULL; }
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL);
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
int Get(uint16_t FileNumber, off_t FileOffset);
int Last(void) { CatchUp(); return last; }
int GetResume(void) { return resumeFile.Read(); }
bool StoreResume(int Index) { return resumeFile.Save(Index); }
bool IsStillRecording(void);
void Delete(void);
};
class cFileName {
private:
cUnbufferedFile *file;
uint16_t fileNumber;
char *fileName, *pFileNumber;
bool record;
bool blocking;
bool isPesRecording;
public:
cFileName(const char *FileName, bool Record, bool Blocking = false, bool IsPesRecording = false);
~cFileName();
const char *Name(void) { return fileName; }
uint16_t Number(void) { return fileNumber; }
bool GetLastPatPmtVersions(int &PatVersion, int &PmtVersion);
cUnbufferedFile *Open(void);
void Close(void);
cUnbufferedFile *SetOffset(int Number, off_t Offset = 0); // yes, Number is int for easier internal calculating
cUnbufferedFile *NextFile(void);
};
cString IndexToHMSF(int Index, bool WithFrame = false, double FramesPerSecond = DEFAULTFRAMESPERSECOND);
// Converts the given index to a string, optionally containing the frame number.
int HMSFToIndex(const char *HMSF, double FramesPerSecond = DEFAULTFRAMESPERSECOND);
// Converts the given string (format: "hh:mm:ss.ff") to an index.
int SecondsToFrames(int Seconds, double FramesPerSecond = DEFAULTFRAMESPERSECOND);
// Returns the number of frames corresponding to the given number of seconds.
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
char *ExchangeChars(char *s, bool ToFileSystem);
// Exchanges the characters in the given string to or from a file system
// specific representation (depending on ToFileSystem). The given string will
// be modified and may be reallocated if more space is needed. The return
// value points to the resulting string, which may be different from s.
bool GenerateIndex(const char *FileName);
#endif //__RECORDING_H