Support for more than one video directory

This commit is contained in:
Klaus Schmidinger 2000-07-29 15:21:42 +02:00
parent 92096e097a
commit 0f2099b4f2
12 changed files with 352 additions and 75 deletions

View File

@ -5,6 +5,7 @@ Carsten Koch <Carsten.Koch@icem.de>
for making the 'Recordings' menu be listed alphabetically for making the 'Recordings' menu be listed alphabetically
for implementing the 'Summary' feature for implementing the 'Summary' feature
for adding the 'epg2timers' tool (see Tools/epg2timers) for adding the 'epg2timers' tool (see Tools/epg2timers)
for his idea of using multiple disks (and for testing this feature)
Plamen Ganev <pganev@com-it.net> Plamen Ganev <pganev@com-it.net>
for fixing the frequency offset for Hotbird channels for fixing the frequency offset for Hotbird channels

View File

@ -100,9 +100,12 @@ Video Disk Recorder Revision History
the 'timers.conf' file with a text editor, or by defining/modifying the timer the 'timers.conf' file with a text editor, or by defining/modifying the timer
via the SVDRP interface. via the SVDRP interface.
2000-07-28: 2000-07-29:
- When scrolling through a list it now moves a full page up or down when the - When scrolling through a list it now moves a full page up or down when the
cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!). cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!).
- Added missing '#include <sys/stat.h>' to recording.c. - Added missing '#include <sys/stat.h>' to recording.c.
- The video directory can now be defined with the command line option -v. - The video directory can now be defined with the command line option -v.
- There can now be more than one video directory (in case you have several
disks).
- Fixed learning key codes for PC keyboard.

34
INSTALL
View File

@ -49,6 +49,9 @@ If the program shall run as a daemon, use the --daemon option. This
will completely detach it from the terminal and will continue as a will completely detach it from the terminal and will continue as a
background process. background process.
Command line options:
---------------------
Use "vdr --help" for a list of available command line options. Use "vdr --help" for a list of available command line options.
The video data directory: The video data directory:
@ -57,14 +60,41 @@ The video data directory:
All recordings are written into directories below "/video". Please All recordings are written into directories below "/video". Please
make sure this directory exists, and that the user who runs the 'vdr' make sure this directory exists, and that the user who runs the 'vdr'
program has read and write access to that directory. program has read and write access to that directory.
If you prefer a different location for your video files, you can change If you prefer a different location for your video files, you can use
the value of 'BaseDir' in recording.c. the '-v' option to change that.
Note that the file system need not be 64-bit proof, since the 'vdr' Note that the file system need not be 64-bit proof, since the 'vdr'
program splits video files into chunks of about 1GB. You should use program splits video files into chunks of about 1GB. You should use
a disk with several gigabytes of free space. One GB can store roughly a disk with several gigabytes of free space. One GB can store roughly
half an hour of video data. half an hour of video data.
If you have more than one disk and don't want to combine them to form
one large logical volume, you can set up several video directories as
mount points for these disks. All of these directories must have the
same basic name and must end with a numeric part, which starts at 0 for
the main directory and has increasing values for the rest of the
directories. For example
/video0
/video1
/video2
would be a setup with three directories. You can use more than one
numeric digit, and the directories need not be directly under '/':
/mnt/MyVideos/vdr.00
/mnt/MyVideos/vdr.01
/mnt/MyVideos/vdr.02
...
/mnt/MyVideos/vdr.11
would set up twelve disks (wow, what a machine that would be!).
To use such a multi directory setup, you need to add the '-v' option
with the name of the basic directory when running 'vdr':
vdr -v /video0
Configuration files: Configuration files:
-------------------- --------------------

View File

@ -4,9 +4,9 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: Makefile 1.5 2000/07/23 11:57:14 kls Exp $ # $Id: Makefile 1.6 2000/07/28 14:37:44 kls Exp $
OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o videodir.o
ifndef REMOTE ifndef REMOTE
REMOTE = KBD REMOTE = KBD
@ -24,15 +24,16 @@ endif
all: vdr all: vdr
config.o : config.c config.h dvbapi.h interface.h tools.h config.o : config.c config.h dvbapi.h interface.h tools.h
dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h videodir.h
interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h
menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h videodir.h
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h videodir.h
remote.o : remote.c remote.h tools.h remote.o : remote.c remote.h tools.h
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
tools.o : tools.c tools.h tools.o : tools.c tools.h
videodir.o : videodir.c tools.h videodir.h
vdr: $(OBJS) vdr: $(OBJS)
g++ -g -O2 $(OBJS) -lncurses -o vdr g++ -g -O2 $(OBJS) -lncurses -o vdr

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbapi.c 1.15 2000/07/21 13:18:02 kls Exp $ * $Id: dvbapi.c 1.16 2000/07/29 14:49:46 kls Exp $
*/ */
#include "dvbapi.h" #include "dvbapi.h"
@ -17,6 +17,7 @@
#include <unistd.h> #include <unistd.h>
#include "interface.h" #include "interface.h"
#include "tools.h" #include "tools.h"
#include "videodir.h"
#define VIDEODEVICE "/dev/video" #define VIDEODEVICE "/dev/video"
@ -50,9 +51,12 @@
// 'signed'), so let's use 1GB for absolute safety (the actual file size // 'signed'), so let's use 1GB for absolute safety (the actual file size
// may be slightly higher because we stop recording only before the next // may be slightly higher because we stop recording only before the next
// 'I' frame, to have a complete Group Of Pictures): // 'I' frame, to have a complete Group Of Pictures):
#define MAXVIDEOFILESIZE (1024*1024*1024) #define MAXVIDEOFILESIZE (1024*1024*1024) // Byte
#define MAXFILESPERRECORDING 255 #define MAXFILESPERRECORDING 255
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
#define INDEXFILESUFFIX "/index.vdr" #define INDEXFILESUFFIX "/index.vdr"
#define RESUMEFILESUFFIX "/resume.vdr" #define RESUMEFILESUFFIX "/resume.vdr"
#define RECORDFILESUFFIX "/%03d.vdr" #define RECORDFILESUFFIX "/%03d.vdr"
@ -598,6 +602,8 @@ private:
int recordFile; int recordFile;
uchar tagAudio, tagVideo; uchar tagAudio, tagVideo;
bool ok, synced; bool ok, synced;
time_t lastDiskSpaceCheck;
bool RunningLowOnDiskSpace(void);
int Synchronize(void); int Synchronize(void);
bool NextFile(void); bool NextFile(void);
virtual int Write(int Max = -1); virtual int Write(int Max = -1);
@ -615,6 +621,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
recordFile = -1; recordFile = -1;
tagAudio = tagVideo = 0; tagAudio = tagVideo = 0;
ok = synced = false; ok = synced = false;
lastDiskSpaceCheck = time(NULL);
if (!fileName) if (!fileName)
return;//XXX find a better way??? return;//XXX find a better way???
// Find the highest existing file suffix: // Find the highest existing file suffix:
@ -636,7 +643,20 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
cRecordBuffer::~cRecordBuffer() cRecordBuffer::~cRecordBuffer()
{ {
if (recordFile >= 0) if (recordFile >= 0)
close(recordFile); CloseVideoFile(recordFile);
}
bool cRecordBuffer::RunningLowOnDiskSpace(void)
{
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
uint Free = FreeDiskSpaceMB(fileName);
lastDiskSpaceCheck = time(NULL);
if (Free < MINFREEDISKSPACE) {
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
return true;
}
}
return false;
} }
int cRecordBuffer::Synchronize(void) int cRecordBuffer::Synchronize(void)
@ -714,20 +734,22 @@ int cRecordBuffer::Synchronize(void)
bool cRecordBuffer::NextFile(void) bool cRecordBuffer::NextFile(void)
{ {
if (recordFile >= 0 && fileSize > MAXVIDEOFILESIZE && pictureType == I_FRAME) { if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
if (close(recordFile) < 0) if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
LOG_ERROR; if (CloseVideoFile(recordFile) < 0)
// don't return 'false', maybe we can still record into the next file LOG_ERROR;
recordFile = -1; // don't return 'false', maybe we can still record into the next file
fileNumber++; recordFile = -1;
if (fileNumber == 0) fileNumber++;
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING); if (fileNumber == 0)
fileSize = 0; esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
fileSize = 0;
}
} }
if (recordFile < 0) { if (recordFile < 0) {
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber); sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
dsyslog(LOG_INFO, "recording to '%s'", fileName); dsyslog(LOG_INFO, "recording to '%s'", fileName);
recordFile = open(fileName, O_RDWR | O_CREAT | O_NONBLOCK, S_IRUSR | S_IWUSR); recordFile = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
if (recordFile < 0) { if (recordFile < 0) {
LOG_ERROR; LOG_ERROR;
return false; return false;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.c 1.14 2000/07/28 12:47:54 kls Exp $ * $Id: recording.c 1.15 2000/07/29 14:08:17 kls Exp $
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
@ -17,6 +17,7 @@
#include <unistd.h> #include <unistd.h>
#include "interface.h" #include "interface.h"
#include "tools.h" #include "tools.h"
#include "videodir.h"
#define RECEXT ".rec" #define RECEXT ".rec"
#define DELEXT ".del" #define DELEXT ".del"
@ -25,40 +26,12 @@
#define SUMMARYFILESUFFIX "/summary.vdr" #define SUMMARYFILESUFFIX "/summary.vdr"
#define FINDCMD "find %s -type d -name '%s' | sort -df" #define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
#define DFCMD "df -m %s"
#define MINDISKSPACE 1024 // MB #define MINDISKSPACE 1024 // MB
#define DISKCHECKDELTA 300 // seconds between checks for free disk space #define DISKCHECKDELTA 300 // seconds between checks for free disk space
const char *VideoDirectory = "/video";
static bool LowDiskSpace(void)
{
//TODO Find a simpler way to determine the amount of free disk space!
bool result = true;
char *cmd = NULL;
asprintf(&cmd, DFCMD, VideoDirectory);
FILE *p = popen(cmd, "r");
if (p) {
char *s;
while ((s = readline(p)) != NULL) {
if (*s == '/') {
int available;
sscanf(s, "%*s %*d %*d %d", &available);
result = available < MINDISKSPACE;
break;
}
}
pclose(p);
}
else
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
delete cmd;
return result;
}
void AssertFreeDiskSpace(void) void AssertFreeDiskSpace(void)
{ {
// With every call to this function we try to actually remove // With every call to this function we try to actually remove
@ -66,7 +39,7 @@ void AssertFreeDiskSpace(void)
// it will get removed during the next call. // it will get removed during the next call.
static time_t LastFreeDiskCheck = 0; static time_t LastFreeDiskCheck = 0;
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) { if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
if (LowDiskSpace()) { if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
// Remove the oldest file that has been "deleted": // Remove the oldest file that has been "deleted":
cRecordings Recordings; cRecordings Recordings;
if (Recordings.Load(true)) { if (Recordings.Load(true)) {
@ -240,10 +213,7 @@ bool cRecording::Delete(void)
if (strcmp(ext, RECEXT) == 0) { if (strcmp(ext, RECEXT) == 0) {
strncpy(ext, DELEXT, strlen(ext)); strncpy(ext, DELEXT, strlen(ext));
isyslog(LOG_INFO, "deleting recording %s", FileName()); isyslog(LOG_INFO, "deleting recording %s", FileName());
if (rename(FileName(), NewName) == -1) { result = RenameVideoFile(FileName(), NewName);
esyslog(LOG_ERR, "ERROR: %s: %s", FileName(), strerror(errno));
result = false;
}
} }
delete NewName; delete NewName;
return result; return result;
@ -252,7 +222,7 @@ bool cRecording::Delete(void)
bool cRecording::Remove(void) bool cRecording::Remove(void)
{ {
isyslog(LOG_INFO, "removing recording %s", FileName()); isyslog(LOG_INFO, "removing recording %s", FileName());
return RemoveFileOrDir(FileName()); return RemoveVideoFile(FileName());
} }
// --- cRecordings ----------------------------------------------------------- // --- cRecordings -----------------------------------------------------------

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.h 1.8 2000/07/28 12:48:06 kls Exp $ * $Id: recording.h 1.9 2000/07/28 13:53:54 kls Exp $
*/ */
#ifndef __RECORDING_H #ifndef __RECORDING_H
@ -14,8 +14,6 @@
#include "config.h" #include "config.h"
#include "tools.h" #include "tools.h"
extern const char *VideoDirectory;
void AssertFreeDiskSpace(void); void AssertFreeDiskSpace(void);
class cRecording : public cListObject { class cRecording : public cListObject {

67
tools.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.c 1.11 2000/07/28 13:22:10 kls Exp $ * $Id: tools.c 1.12 2000/07/29 14:02:41 kls Exp $
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
@ -145,20 +145,47 @@ bool isnumber(const char *s)
return true; return true;
} }
bool DirectoryOk(const char *DirName) #define DFCMD "df -m %s"
uint FreeDiskSpaceMB(const char *Directory)
{
//TODO Find a simpler way to determine the amount of free disk space!
uint Free = 0;
char *cmd = NULL;
asprintf(&cmd, DFCMD, Directory);
FILE *p = popen(cmd, "r");
if (p) {
char *s;
while ((s = readline(p)) != NULL) {
if (*s == '/') {
uint available;
sscanf(s, "%*s %*d %*d %u", &available);
Free = available;
break;
}
}
pclose(p);
}
else
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
delete cmd;
return Free;
}
bool DirectoryOk(const char *DirName, bool LogErrors)
{ {
struct stat ds; struct stat ds;
if (stat(DirName, &ds) == 0) { if (stat(DirName, &ds) == 0) {
if (S_ISDIR(ds.st_mode)) { if (S_ISDIR(ds.st_mode)) {
if (access(DirName, R_OK | W_OK | X_OK) == 0) if (access(DirName, R_OK | W_OK | X_OK) == 0)
return true; return true;
else else if (LogErrors)
esyslog(LOG_ERR, "ERROR: can't access %s", DirName); esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
} }
else else if (LogErrors)
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName); esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
} }
else else if (LogErrors)
LOG_ERROR_STR(DirName); LOG_ERROR_STR(DirName);
return false; return false;
} }
@ -175,7 +202,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
*p = 0; *p = 0;
struct stat fs; struct stat fs;
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) { if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
isyslog(LOG_INFO, "creating directory %s", s); dsyslog(LOG_INFO, "creating directory %s", s);
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) { if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno)); esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
result = false; result = false;
@ -191,7 +218,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
return result; return result;
} }
bool RemoveFileOrDir(const char *FileName) bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
{ {
struct stat st; struct stat st;
if (stat(FileName, &st) == 0) { if (stat(FileName, &st) == 0) {
@ -203,23 +230,43 @@ bool RemoveFileOrDir(const char *FileName)
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
char *buffer; char *buffer;
asprintf(&buffer, "%s/%s", FileName, e->d_name); asprintf(&buffer, "%s/%s", FileName, e->d_name);
if (FollowSymlinks) {
int size = strlen(buffer) * 2; // should be large enough
char *l = new char[size];
int n = readlink(buffer, l, size);
if (n < 0) {
if (errno != EINVAL)
LOG_ERROR_STR(buffer);
}
else if (n < size) {
l[n] = 0;
dsyslog(LOG_INFO, "removing %s", l);
if (remove(l) < 0)
LOG_ERROR_STR(l);
}
else
esyslog(LOG_ERR, "symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
delete l;
}
dsyslog(LOG_INFO, "removing %s", buffer);
if (remove(buffer) < 0) if (remove(buffer) < 0)
esyslog(LOG_ERR, "ERROR: %s: %s", buffer, strerror(errno)); LOG_ERROR_STR(buffer);
delete buffer; delete buffer;
} }
} }
closedir(d); closedir(d);
} }
else { else {
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno)); LOG_ERROR_STR(FileName);
return false; return false;
} }
} }
dsyslog(LOG_INFO, "removing %s", FileName);
if (remove(FileName) == 0) if (remove(FileName) == 0)
return true; return true;
} }
else else
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno)); LOG_ERROR_STR(FileName);
return false; return false;
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.h 1.11 2000/07/28 13:02:05 kls Exp $ * $Id: tools.h 1.12 2000/07/29 10:56:00 kls Exp $
*/ */
#ifndef __TOOLS_H #ifndef __TOOLS_H
@ -43,9 +43,10 @@ char *skipspace(char *s);
int time_ms(void); int time_ms(void);
void delay_ms(int ms); void delay_ms(int ms);
bool isnumber(const char *s); bool isnumber(const char *s);
bool DirectoryOk(const char *DirName); uint FreeDiskSpaceMB(const char *Directory);
bool DirectoryOk(const char *DirName, bool LogErrors = false);
bool MakeDirs(const char *FileName, bool IsDirectory = false); bool MakeDirs(const char *FileName, bool IsDirectory = false);
bool RemoveFileOrDir(const char *FileName); bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
bool CheckProcess(pid_t pid); bool CheckProcess(pid_t pid);
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT); void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);

5
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/people/kls/vdr * The project's page is at http://www.cadsoft.de/people/kls/vdr
* *
* $Id: vdr.c 1.24 2000/07/28 13:14:19 kls Exp $ * $Id: vdr.c 1.25 2000/07/28 15:55:31 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -36,6 +36,7 @@
#include "recording.h" #include "recording.h"
#include "svdrp.h" #include "svdrp.h"
#include "tools.h" #include "tools.h"
#include "videodir.h"
#ifdef REMOTE_KBD #ifdef REMOTE_KBD
#define KEYS_CONF "keys-pc.conf" #define KEYS_CONF "keys-pc.conf"
@ -105,7 +106,7 @@ int main(int argc, char *argv[])
// Check the video directory: // Check the video directory:
if (!DirectoryOk(VideoDirectory)) { if (!DirectoryOk(VideoDirectory, true)) {
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory); fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
abort(); abort();
} }

182
videodir.c Normal file
View File

@ -0,0 +1,182 @@
/*
* videodir.c: Functions to maintain a distributed video directory
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: videodir.c 1.1 2000/07/29 15:21:42 kls Exp $
*/
#include "videodir.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "tools.h"
const char *VideoDirectory = "/video";
class cVideoDirectory {
private:
char *name, *stored, *adjusted;
int length, number, digits;
public:
cVideoDirectory(void);
~cVideoDirectory();
uint FreeMB(void);
const char *Name(void) { return name; }
const char *Stored(void) { return stored; }
int Length(void) { return length; }
bool IsDistributed(void) { return name != NULL; }
bool Next(void);
void Store(void);
const char *Adjust(const char *FileName);
};
cVideoDirectory::cVideoDirectory(void)
{
length = strlen(VideoDirectory);
name = (VideoDirectory[length - 1] == '0') ? strdup(VideoDirectory) : NULL;
stored = adjusted = NULL;
number = -1;
digits = 0;
}
cVideoDirectory::~cVideoDirectory()
{
delete name;
delete stored;
delete adjusted;
}
uint cVideoDirectory::FreeMB(void)
{
return FreeDiskSpaceMB(name ? name : VideoDirectory);
}
bool cVideoDirectory::Next(void)
{
if (name) {
if (number < 0) {
int l = length;
while (l-- > 0 && isdigit(name[l]))
;
l++;
digits = length - l;
int n = atoi(&name[l]);
if (n == 0)
number = n;
else
return false; // base video directory must end with zero
}
if (++number > 0) {
char buf[16];
if (sprintf(buf, "%0*d", digits, number) == digits) {
strcpy(&name[length - digits], buf);
return DirectoryOk(name);
}
}
}
return false;
}
void cVideoDirectory::Store(void)
{
if (name) {
delete stored;
stored = strdup(name);
}
}
const char *cVideoDirectory::Adjust(const char *FileName)
{
if (stored) {
delete adjusted;
adjusted = strdup(FileName);
return strncpy(adjusted, stored, length);
}
return NULL;
}
int OpenVideoFile(const char *FileName, int Flags)
{
const char *ActualFileName = FileName;
// Incoming name must be in base video directory:
if (strstr(FileName, VideoDirectory) != FileName) {
esyslog(LOG_ERR, "ERROR: %s not in %s", FileName, VideoDirectory);
errno = ENOENT; // must set 'errno' - any ideas for a better value?
return -1;
}
// Are we going to create a new file?
if ((Flags & O_CREAT) != 0) {
cVideoDirectory Dir;
if (Dir.IsDistributed()) {
// Find the directory with the most free space:
uint MaxFree = Dir.FreeMB();
while (Dir.Next()) {
uint Free = FreeDiskSpaceMB(Dir.Name());
if (Free > MaxFree) {
Dir.Store();
MaxFree = Free;
}
}
if (Dir.Stored()) {
ActualFileName = Dir.Adjust(FileName);
if (!MakeDirs(ActualFileName, false))
return -1; // errno has been set by MakeDirs()
if (symlink(ActualFileName, FileName) < 0) {
LOG_ERROR_STR(FileName);
return -1;
}
ActualFileName = strdup(ActualFileName); // must survive Dir!
}
}
}
int Result = open(ActualFileName, Flags, S_IRUSR | S_IWUSR);
if (ActualFileName != FileName)
delete ActualFileName;
return Result;
}
int CloseVideoFile(int FileHandle)
{
// just in case we ever decide to do something special when closing the file!
return close(FileHandle);
}
bool RenameVideoFile(const char *OldName, const char *NewName)
{
// Only the base video directory entry will be renamed, leaving the
// possible symlinks untouched. Going through all the symlinks and disks
// would be unnecessary work - maybe later...
if (rename(OldName, NewName) == -1) {
LOG_ERROR_STR(OldName);
return false;
}
return true;
}
bool RemoveVideoFile(const char *FileName)
{
return RemoveFileOrDir(FileName, true);
}
bool VideoFileSpaceAvailable(unsigned int SizeMB)
{
cVideoDirectory Dir;
if (Dir.IsDistributed()) {
if (Dir.FreeMB() >= SizeMB * 2) // base directory needs additional space
return true;
while (Dir.Next()) {
if (Dir.FreeMB() >= SizeMB)
return true;
}
return false;
}
return Dir.FreeMB() >= SizeMB;
}

21
videodir.h Normal file
View File

@ -0,0 +1,21 @@
/*
* videodir.h: Functions to maintain a distributed video directory
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: videodir.h 1.1 2000/07/29 14:08:27 kls Exp $
*/
#ifndef __VIDEODIR_H
#define __VIDEODIR_H
extern const char *VideoDirectory;
int OpenVideoFile(const char *FileName, int Flags);
int CloseVideoFile(int FileHandle);
bool RenameVideoFile(const char *OldName, const char *NewName);
bool RemoveVideoFile(const char *FileName);
bool VideoFileSpaceAvailable(unsigned int SizeMB);
#endif //__VIDEODIR_H