mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Support for more than one video directory
This commit is contained in:
parent
92096e097a
commit
0f2099b4f2
@ -5,6 +5,7 @@ Carsten Koch <Carsten.Koch@icem.de>
|
||||
for making the 'Recordings' menu be listed alphabetically
|
||||
for implementing the 'Summary' feature
|
||||
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>
|
||||
for fixing the frequency offset for Hotbird channels
|
||||
|
5
HISTORY
5
HISTORY
@ -100,9 +100,12 @@ Video Disk Recorder Revision History
|
||||
the 'timers.conf' file with a text editor, or by defining/modifying the timer
|
||||
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
|
||||
cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!).
|
||||
- Added missing '#include <sys/stat.h>' to recording.c.
|
||||
- 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
34
INSTALL
@ -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
|
||||
background process.
|
||||
|
||||
Command line options:
|
||||
---------------------
|
||||
|
||||
Use "vdr --help" for a list of available command line options.
|
||||
|
||||
The video data directory:
|
||||
@ -57,14 +60,41 @@ The video data directory:
|
||||
All recordings are written into directories below "/video". Please
|
||||
make sure this directory exists, and that the user who runs the 'vdr'
|
||||
program has read and write access to that directory.
|
||||
If you prefer a different location for your video files, you can change
|
||||
the value of 'BaseDir' in recording.c.
|
||||
If you prefer a different location for your video files, you can use
|
||||
the '-v' option to change that.
|
||||
|
||||
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
|
||||
a disk with several gigabytes of free space. One GB can store roughly
|
||||
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:
|
||||
--------------------
|
||||
|
||||
|
11
Makefile
11
Makefile
@ -4,9 +4,9 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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
|
||||
REMOTE = KBD
|
||||
@ -24,15 +24,16 @@ endif
|
||||
all: vdr
|
||||
|
||||
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
|
||||
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
|
||||
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h
|
||||
recording.o: recording.c config.h dvbapi.h interface.h recording.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 videodir.h
|
||||
remote.o : remote.c remote.h tools.h
|
||||
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
|
||||
tools.o : tools.c tools.h
|
||||
videodir.o : videodir.c tools.h videodir.h
|
||||
|
||||
vdr: $(OBJS)
|
||||
g++ -g -O2 $(OBJS) -lncurses -o vdr
|
||||
|
48
dvbapi.c
48
dvbapi.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#include "interface.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
|
||||
#define VIDEODEVICE "/dev/video"
|
||||
|
||||
@ -50,9 +51,12 @@
|
||||
// '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
|
||||
// 'I' frame, to have a complete Group Of Pictures):
|
||||
#define MAXVIDEOFILESIZE (1024*1024*1024)
|
||||
#define MAXVIDEOFILESIZE (1024*1024*1024) // Byte
|
||||
#define MAXFILESPERRECORDING 255
|
||||
|
||||
#define MINFREEDISKSPACE (512) // MB
|
||||
#define DISKCHECKINTERVAL 100 // seconds
|
||||
|
||||
#define INDEXFILESUFFIX "/index.vdr"
|
||||
#define RESUMEFILESUFFIX "/resume.vdr"
|
||||
#define RECORDFILESUFFIX "/%03d.vdr"
|
||||
@ -598,6 +602,8 @@ private:
|
||||
int recordFile;
|
||||
uchar tagAudio, tagVideo;
|
||||
bool ok, synced;
|
||||
time_t lastDiskSpaceCheck;
|
||||
bool RunningLowOnDiskSpace(void);
|
||||
int Synchronize(void);
|
||||
bool NextFile(void);
|
||||
virtual int Write(int Max = -1);
|
||||
@ -615,6 +621,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
||||
recordFile = -1;
|
||||
tagAudio = tagVideo = 0;
|
||||
ok = synced = false;
|
||||
lastDiskSpaceCheck = time(NULL);
|
||||
if (!fileName)
|
||||
return;//XXX find a better way???
|
||||
// Find the highest existing file suffix:
|
||||
@ -636,7 +643,20 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
||||
cRecordBuffer::~cRecordBuffer()
|
||||
{
|
||||
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)
|
||||
@ -714,20 +734,22 @@ int cRecordBuffer::Synchronize(void)
|
||||
|
||||
bool cRecordBuffer::NextFile(void)
|
||||
{
|
||||
if (recordFile >= 0 && fileSize > MAXVIDEOFILESIZE && pictureType == I_FRAME) {
|
||||
if (close(recordFile) < 0)
|
||||
LOG_ERROR;
|
||||
// don't return 'false', maybe we can still record into the next file
|
||||
recordFile = -1;
|
||||
fileNumber++;
|
||||
if (fileNumber == 0)
|
||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
||||
fileSize = 0;
|
||||
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
|
||||
if (CloseVideoFile(recordFile) < 0)
|
||||
LOG_ERROR;
|
||||
// don't return 'false', maybe we can still record into the next file
|
||||
recordFile = -1;
|
||||
fileNumber++;
|
||||
if (fileNumber == 0)
|
||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
||||
fileSize = 0;
|
||||
}
|
||||
}
|
||||
if (recordFile < 0) {
|
||||
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
||||
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) {
|
||||
LOG_ERROR;
|
||||
return false;
|
||||
|
42
recording.c
42
recording.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#include "interface.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
|
||||
#define RECEXT ".rec"
|
||||
#define DELEXT ".del"
|
||||
@ -25,40 +26,12 @@
|
||||
|
||||
#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 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)
|
||||
{
|
||||
// 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.
|
||||
static time_t LastFreeDiskCheck = 0;
|
||||
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
|
||||
if (LowDiskSpace()) {
|
||||
if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
|
||||
// Remove the oldest file that has been "deleted":
|
||||
cRecordings Recordings;
|
||||
if (Recordings.Load(true)) {
|
||||
@ -240,10 +213,7 @@ bool cRecording::Delete(void)
|
||||
if (strcmp(ext, RECEXT) == 0) {
|
||||
strncpy(ext, DELEXT, strlen(ext));
|
||||
isyslog(LOG_INFO, "deleting recording %s", FileName());
|
||||
if (rename(FileName(), NewName) == -1) {
|
||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName(), strerror(errno));
|
||||
result = false;
|
||||
}
|
||||
result = RenameVideoFile(FileName(), NewName);
|
||||
}
|
||||
delete NewName;
|
||||
return result;
|
||||
@ -252,7 +222,7 @@ bool cRecording::Delete(void)
|
||||
bool cRecording::Remove(void)
|
||||
{
|
||||
isyslog(LOG_INFO, "removing recording %s", FileName());
|
||||
return RemoveFileOrDir(FileName());
|
||||
return RemoveVideoFile(FileName());
|
||||
}
|
||||
|
||||
// --- cRecordings -----------------------------------------------------------
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -14,8 +14,6 @@
|
||||
#include "config.h"
|
||||
#include "tools.h"
|
||||
|
||||
extern const char *VideoDirectory;
|
||||
|
||||
void AssertFreeDiskSpace(void);
|
||||
|
||||
class cRecording : public cListObject {
|
||||
|
67
tools.c
67
tools.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -145,20 +145,47 @@ bool isnumber(const char *s)
|
||||
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;
|
||||
if (stat(DirName, &ds) == 0) {
|
||||
if (S_ISDIR(ds.st_mode)) {
|
||||
if (access(DirName, R_OK | W_OK | X_OK) == 0)
|
||||
return true;
|
||||
else
|
||||
else if (LogErrors)
|
||||
esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
|
||||
}
|
||||
else
|
||||
else if (LogErrors)
|
||||
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
|
||||
}
|
||||
else
|
||||
else if (LogErrors)
|
||||
LOG_ERROR_STR(DirName);
|
||||
return false;
|
||||
}
|
||||
@ -175,7 +202,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
||||
*p = 0;
|
||||
struct stat fs;
|
||||
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) {
|
||||
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
|
||||
result = false;
|
||||
@ -191,7 +218,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RemoveFileOrDir(const char *FileName)
|
||||
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(FileName, &st) == 0) {
|
||||
@ -203,23 +230,43 @@ bool RemoveFileOrDir(const char *FileName)
|
||||
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
||||
char *buffer;
|
||||
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)
|
||||
esyslog(LOG_ERR, "ERROR: %s: %s", buffer, strerror(errno));
|
||||
LOG_ERROR_STR(buffer);
|
||||
delete buffer;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
else {
|
||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
||||
LOG_ERROR_STR(FileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
dsyslog(LOG_INFO, "removing %s", FileName);
|
||||
if (remove(FileName) == 0)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
||||
LOG_ERROR_STR(FileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
7
tools.h
7
tools.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -43,9 +43,10 @@ char *skipspace(char *s);
|
||||
int time_ms(void);
|
||||
void delay_ms(int ms);
|
||||
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 RemoveFileOrDir(const char *FileName);
|
||||
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
|
||||
bool CheckProcess(pid_t pid);
|
||||
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
|
||||
|
||||
|
5
vdr.c
5
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* 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>
|
||||
@ -36,6 +36,7 @@
|
||||
#include "recording.h"
|
||||
#include "svdrp.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
|
||||
#ifdef REMOTE_KBD
|
||||
#define KEYS_CONF "keys-pc.conf"
|
||||
@ -105,7 +106,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Check the video directory:
|
||||
|
||||
if (!DirectoryOk(VideoDirectory)) {
|
||||
if (!DirectoryOk(VideoDirectory, true)) {
|
||||
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
|
||||
abort();
|
||||
}
|
||||
|
182
videodir.c
Normal file
182
videodir.c
Normal 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
21
videodir.h
Normal 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
|
Loading…
Reference in New Issue
Block a user