summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2000-08-06 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2000-08-06 18:00:00 +0200
commit9b405778674d5325b225b32ab694d9216b099527 (patch)
treedbce3ee646e63439d0690ffbcdf6cf93d7314d25
parent1d22145c423f2524c7766b4ae30ee2c26174113d (diff)
downloadvdr-patch-lnbsharing-9b405778674d5325b225b32ab694d9216b099527.tar.gz
vdr-patch-lnbsharing-9b405778674d5325b225b32ab694d9216b099527.tar.bz2
Version 0.61vdr-0.61
- 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. - New command line option '-l' to set the log level. - Times in timers.conf are now always printed with 4 digits (leading '0'). - Slow forward/back mode (thanks to Guido Fiala!). - The "Up" key in replay mode no longer restarts replay at the very beginning, but rather resumes normal replay mode after a "pause", "forward" or "backward" operation. Use the "Skip -60s" function repeatedly to go back to the beginning of the recording. - Improved reaction on user input in fast/slow forward/back modes. - No more upper limit for the value of 'Pnr'. - Checking if the video card is really a DVB card. - New SVDRP command UPDT to update an existing timer (or add a new one if it doesn't yet exist). - New version of the 'epg2timers' tool (with a modified channel list). - Bugfix in closing window in DEBUG_OSD mode.
-rw-r--r--CONTRIBUTORS7
-rw-r--r--HISTORY24
-rw-r--r--INSTALL40
-rw-r--r--MANUAL11
-rw-r--r--Makefile11
-rw-r--r--Tools/epg2timers/epg2timers.cxx56
-rw-r--r--channels.conf1
-rw-r--r--config.c15
-rw-r--r--config.h10
-rw-r--r--dvbapi.c256
-rw-r--r--dvbapi.h19
-rw-r--r--menu.c16
-rw-r--r--osd.c22
-rw-r--r--recording.c49
-rw-r--r--recording.h2
-rw-r--r--remote.c8
-rw-r--r--svdrp.c53
-rw-r--r--svdrp.h3
-rw-r--r--timers.conf12
-rw-r--r--tools.c77
-rw-r--r--tools.h6
-rw-r--r--vdr.c59
-rw-r--r--videodir.c182
-rw-r--r--videodir.h21
24 files changed, 727 insertions, 233 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 0e2794f..74390cf 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -5,7 +5,14 @@ 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
for adding the 'xtvrc2vdr' tool (see Tools/xtvrc2vdr)
+
+Heino Goldenstein <heino.goldenstein@microplex.de>
+ for modifying scrolling through lists to make it page up and down
+
+Guido Fiala <gfiala@s.netic.de>
+ for implementing slow forward/back
diff --git a/HISTORY b/HISTORY
index 765f4d1..04cc2d5 100644
--- a/HISTORY
+++ b/HISTORY
@@ -99,3 +99,27 @@ Video Disk Recorder Revision History
pressing "Ok". The summary field can only be filled in directly by editing
the 'timers.conf' file with a text editor, or by defining/modifying the timer
via the SVDRP interface.
+
+2000-08-06: Version 0.61
+
+- 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.
+- New command line option '-l' to set the log level.
+- Times in timers.conf are now always printed with 4 digits (leading '0').
+- Slow forward/back mode (thanks to Guido Fiala!).
+- The "Up" key in replay mode no longer restarts replay at the very beginning,
+ but rather resumes normal replay mode after a "pause", "forward" or "backward"
+ operation. Use the "Skip -60s" function repeatedly to go back to the beginning
+ of the recording.
+- Improved reaction on user input in fast/slow forward/back modes.
+- No more upper limit for the value of 'Pnr'.
+- Checking if the video card is really a DVB card.
+- New SVDRP command UPDT to update an existing timer (or add a new one if it
+ doesn't yet exist).
+- New version of the 'epg2timers' tool (with a modified channel list).
+- Bugfix in closing window in DEBUG_OSD mode.
diff --git a/INSTALL b/INSTALL
index 3261ef0..938487c 100644
--- a/INSTALL
+++ b/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:
--------------------
@@ -78,6 +108,12 @@ The meaning of the data entries may still vary in future releases,
so for the moment please look at the source code (config.c) to see
the meaning of the various fields.
+The files that come with this package contain the author's selections,
+so please make sure you adapt these to your personal taste. Also make sure
+that the channels defined in 'channels.conf' are correct before attempting
+to record anything. Channel parameters may vary and not all of the channels
+listed in the default 'channels.conf' file have been verified by the author.
+
Learning the remote control keys:
---------------------------------
diff --git a/MANUAL b/MANUAL
index c9b71a3..b05c350 100644
--- a/MANUAL
+++ b/MANUAL
@@ -10,7 +10,7 @@ Video Disk Recorder User's Manual
Key Normal Main Channels Timer Edit/New Recordings Replay
- Up Ch up Crsr up Crsr up Crsr up Crsr up Crsr up Begin
+ Up Ch up Crsr up Crsr up Crsr up Crsr up Crsr up Play
Down Ch down Crsr down Crsr down Crsr down Crsr down Crsr down Pause
Left - - - Disable Decrement - Search back
Right - - - Enable Increment - Search forward
@@ -93,15 +93,16 @@ Video Disk Recorder User's Manual
The following keys have the listed meaning in Replay mode:
- - Up Positions to beginning of the recording and starts playback
- from there.
+ - Up Resumes normal replay from any "pause", "forward" or "backward"
+ mode.
- Down Halts playback at the current position. Press again to continue
playback.
- Blue Stops playback and stores the current position, so that
playback can be resumed later at that point.
- Left
- Right Runs playback forward or backward at a higher speed. Press
- again to resume normal speed.
+ Right Runs playback forward or backward at a higher speed; press
+ again to resume normal speed. If in Pause mode, runs forward or
+ backward at a slower speed; press again to return to pause mode.
- Green
Yellow Skips about 60 seconds back or forward.
- Ok Brings up the replay progress display, which shows the date,
diff --git a/Makefile b/Makefile
index 8fb678b..f71236b 100644
--- a/Makefile
+++ b/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
diff --git a/Tools/epg2timers/epg2timers.cxx b/Tools/epg2timers/epg2timers.cxx
index 4a8f333..07e8182 100644
--- a/Tools/epg2timers/epg2timers.cxx
+++ b/Tools/epg2timers/epg2timers.cxx
@@ -34,21 +34,46 @@ static const char channel_line[] = "\t\t\t<tr><td bgcolor=\"#002b64\" align=c
static const char title_line[] = "\t\t\t\t<td bgcolor=\"#002b64\" align=left width=100%><span id=\"fb-w10\">";
static const char summary_line[] = "\t\t\t<table border=0 cellpadding=10 cellspacing=0 bgcolor=\"white\" width=100%>";
static const char * const channel_names[] =
-{"RTL", "SAT1", "PRO7", "RTL2", "ARD", "BR3", "HR3", "NDR", "SWF", "WDR", "BR Alpha", "SWR BW", "Phoenix",
- "ZDF", "3sat", "Kinderkanal", "ARTE", "phoenix", "ORF Sat", "ZDF.info", "CNN", "Super RTL", "VOX", "DW TV",
- "Kabel1", "TM3", "DSF", "HOT", "BloombergTV", "Sky News", "KinderNet", "Alice", "n-tv", "Grand Tour.", "TW1",
- "Eins Extra", "Eins Festival", "Eins MuXx", "MDR", "ORB", "B1", "ARD Online-Kanal", "Premiere World Promo",
- "Premiere", "Star Kino", "Cine Action", "Cine Comedy", "Sci Fantasy", "Romantic Movies", "Studio Universal",
- "TV Niepokalanow", "Mosaico", "Andalucia TV", "TVC Internacional", "Nasza TV", "WishLine test", "Pro 7 Austria",
- "Kabel 1 Schweiz", "Kabel 1 Austria", "Pro 7 Schweiz", "Kiosque", "KTO", "TCM", "Cartoon Network France & Spain",
- "TVBS Europe", "TVBS Europe", "Travel", "TCM Espania", "MTV Spain", "TCM France", "RTL2 CH",
- "La Cinquieme", "ARTE", "Post Filial TV", "Canal Canaris", "Canal Canaris", "Canal Canaris", "Canal Canaris",
- "AB Sat Passion promo", "AB Channel 1", "Taquilla 0", "CSAT", "Mosaique", "Mosaique 2", "Mosaique 3", "Le Sesame C+",
- "FEED", "RTM 1", "ESC 1", "TV5 Europe", "TV7 Tunisia", "ARTE", "RAI Uno", "RTP International",
- "Fashion TV", "VideoService", "Beta Research promo", "Canal Canarias", "TVC International", "Fitur", "Astra Info 1",
- "Astra Info 2", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1",
- "Astra Vision 1", "Astra Vision 1", "RTL Tele Letzebuerg", "Astra Mosaic", "MHP test", "Bloomberg TV Spain",
- "Video Italia", "AC 3 promo", ""
+{
+"3sat",
+"ARTE",
+"*B1 Berlin",
+"BR3",
+"Bloomberg TV",
+"BR Alpha",
+"CNN",
+"ARD",
+"*DW-tv",
+"Eins Extra",
+"Eins Festival",
+"Eins MuXx",
+"euroNEWS",
+"HR3",
+"Kabel1",
+"Kinderkanal",
+"MDR",
+"MTV",
+"NDR",
+"NTV",
+"ORB",
+"*ORF1",
+"Phoenix",
+"PRO7",
+"RTL",
+"RTL2",
+"SAT1",
+"skynews",
+"SWF",
+"Super RTL",
+"TM3",
+"TW1",
+"VOX",
+"WDR",
+"Theaterkanal",
+"ZDF",
+"ZDF.doku",
+"ZDF.info",
+""
};
static const int month_lengths[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
@@ -240,3 +265,4 @@ main()
read_summary(summary);
}
}
+
diff --git a/channels.conf b/channels.conf
index 870a6d6..eefaf52 100644
--- a/channels.conf
+++ b/channels.conf
@@ -113,4 +113,3 @@ MHP test:12604:h:1:22000:5632:8191:0:0
Bloomberg TV Spain:12610:v:1:22000:45:49:0:0
Video Italia:12610:v:1:22000:121:122:0:0
AC 3 promo:12670:v:1:22000:308:256:0:0
-Rtlneu:12188:h:1:27500:163:104:0:0
diff --git a/config.c b/config.c
index 6b5e049..c144d6c 100644
--- a/config.c
+++ b/config.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.15 2000/07/25 16:21:20 kls Exp $
+ * $Id: config.c 1.17 2000/08/06 12:27:38 kls Exp $
*/
#include "config.h"
@@ -294,7 +294,7 @@ cTimer& cTimer::operator= (const cTimer &Timer)
const char *cTimer::ToText(cTimer *Timer)
{
- asprintf(&buffer, "%d:%d:%s:%d:%d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
+ asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
return buffer;
}
@@ -473,3 +473,14 @@ cChannels Channels;
cTimers Timers;
+cTimer *cTimers::GetTimer(cTimer *Timer)
+{
+ cTimer *ti = (cTimer *)First();
+ while (ti) {
+ if (ti->channel == Timer->channel && ti->day == Timer->day && ti->start == Timer->start && ti->stop == Timer->stop)
+ return ti;
+ ti = (cTimer *)ti->Next();
+ }
+ return NULL;
+}
+
diff --git a/config.h b/config.h
index be1c7ec..1b21d34 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.11 2000/07/23 17:17:10 kls Exp $
+ * $Id: config.h 1.14 2000/08/06 12:22:52 kls Exp $
*/
#ifndef __CONFIG_H
@@ -17,6 +17,8 @@
#include "dvbapi.h"
#include "tools.h"
+#define VDRVERSION "0.61"
+
#define MaxBuffer 10000
enum eKeys { // "Up" and "Down" must be the first two keys!
@@ -181,7 +183,11 @@ public:
};
class cChannels : public cConfig<cChannel> {};
-class cTimers : public cConfig<cTimer> {};
+
+class cTimers : public cConfig<cTimer> {
+public:
+ cTimer *GetTimer(cTimer *Timer);
+ };
extern int CurrentChannel;
diff --git a/dvbapi.c b/dvbapi.c
index a9673a7..0562a4f 100644
--- a/dvbapi.c
+++ b/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.22 2000/08/06 14:06:14 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"
@@ -341,9 +345,8 @@ protected:
int Free(void) { return ((tail >= head) ? size + head - tail : head - tail) - 1; }
int Available(void) { return (tail >= head) ? tail - head : size - head + tail; }
int Readable(void) { return (tail >= head) ? size - tail - (head ? 0 : 1) : head - tail - 1; } // keep a 1 byte gap!
- int Writeable(void) { return (tail > head) ? tail - head : size - head; }
+ int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
int Byte(int Offset);
- bool WaitForOutFile(int Timeout);
public:
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
virtual ~cRingBuffer();
@@ -404,22 +407,6 @@ void cRingBuffer::Skip(int n)
}
}
-bool cRingBuffer::WaitForOutFile(int Timeout)
-{
- fd_set set;
- FD_ZERO(&set);
- FD_SET(*outFile, &set);
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = Timeout;
- if (select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0) {
- if (FD_ISSET(*outFile, &set))
- return true;
- }
- esyslog(LOG_ERR, "ERROR: timeout in WaitForOutFile(%d)", Timeout);
- return false;
-}
-
int cRingBuffer::Read(int Max)
{
if (buffer) {
@@ -598,6 +585,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 +604,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 +626,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 +717,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;
@@ -781,7 +786,7 @@ int cRecordBuffer::WriteWithTimeout(bool EndIfEmpty)
// --- cReplayBuffer ---------------------------------------------------------
-enum eReplayMode { rmPlay, rmFastForward, rmFastRewind };
+enum eReplayMode { rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
class cReplayBuffer : public cFileBuffer {
private:
@@ -790,6 +795,7 @@ private:
eReplayMode mode;
bool skipAudio;
int lastIndex;
+ int brakeCounter;
void SkipAudioBlocks(void);
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
void Close(void);
@@ -811,6 +817,7 @@ cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
fileOffset = 0;
replayFile = -1;
mode = rmPlay;
+ brakeCounter = 0;
skipAudio = false;
lastIndex = -1;
if (!fileName)
@@ -841,6 +848,7 @@ void cReplayBuffer::SetMode(eReplayMode Mode)
{
mode = Mode;
skipAudio = Mode != rmPlay;
+ brakeCounter = 0;
if (mode != rmPlay)
Clear();
}
@@ -974,6 +982,10 @@ int cReplayBuffer::Read(int Max = -1)
if (Index >= 0) {
uchar FileNumber;
int FileOffset, Length;
+ if (mode == rmSlowRewind && (brakeCounter++ % 24) != 0) {
+ // show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
+ Index = index->GetNextIFrame(Index, true, &FileNumber, &FileOffset, &Length); // jump ahead one frame
+ }
Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
if (Index >= 0) {
if (!NextFile(FileNumber, FileOffset))
@@ -1014,28 +1026,27 @@ int cReplayBuffer::Read(int Max = -1)
int cReplayBuffer::Write(int Max)
{
int Written = 0;
-
- do {
- if (skipAudio) {
- SkipAudioBlocks();
- Max = GetAvPesLength();
- }
- while (Max) {
- int w = cFileBuffer::Write(Max);
- if (w >= 0) {
- fileOffset += w;
- Written += w;
- if (Max < 0)
- break;
- Max -= w;
- }
- else
- return w;
- //XXX??? Why does the buffer get empty here???
- if (Empty() || !WaitForOutFile(1000000))
- return Written;
+ int Av = Available();
+ if (skipAudio) {
+ SkipAudioBlocks();
+ Max = GetAvPesLength();
+ fileOffset += Av - Available();
+ }
+ if (Max) {
+ int w;
+ do {
+ w = cFileBuffer::Write(Max);
+ if (w >= 0) {
+ fileOffset += w;
+ Written += w;
+ if (Max < 0)
+ break;
+ Max -= w;
}
- } while (skipAudio && Available());
+ else
+ return w;
+ } while (Max > 0); // we MUST write this entire AV_PES block
+ }
return Written;
}
@@ -1076,7 +1087,7 @@ cDvbApi::~cDvbApi()
{
if (videoDev >= 0) {
Close();
- StopReplay();
+ Stop();
StopRecord();
close(videoDev);
}
@@ -1119,9 +1130,13 @@ bool cDvbApi::Init(void)
dsyslog(LOG_INFO, "probing %s", fileName);
int f = open(fileName, O_RDWR);
if (f >= 0) {
+ struct video_capability cap;
+ int r = ioctl(f, VIDIOCGCAP, &cap);
close(f);
- dvbApi[i] = new cDvbApi(fileName);
- NumDvbApis++;
+ if (r == 0 && (cap.type & VID_TYPE_DVB)) {
+ dvbApi[i] = new cDvbApi(fileName);
+ NumDvbApis++;
+ }
}
else {
if (errno != ENODEV)
@@ -1136,10 +1151,12 @@ bool cDvbApi::Init(void)
}
}
PrimaryDvbApi = dvbApi[0];
- if (NumDvbApis > 0)
+ if (NumDvbApis > 0) {
isyslog(LOG_INFO, "found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
- else
+ } // need braces because of isyslog-macro
+ else {
esyslog(LOG_ERR, "ERROR: no video device found, giving up!");
+ }
return NumDvbApis > 0;
}
@@ -1222,7 +1239,10 @@ void cDvbApi::Open(int w, int h)
void cDvbApi::Close(void)
{
#ifdef DEBUG_OSD
- delwin(window);
+ if (window) {
+ delwin(window);
+ window = 0;
+ }
#else
Cmd(OSD_Close);
#endif
@@ -1372,7 +1392,7 @@ bool cDvbApi::StartRecord(const char *FileName)
}
if (videoDev >= 0) {
- StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
+ Stop(); // TODO: remove this if the driver is able to do record and replay at the same time
// Check FileName:
@@ -1494,7 +1514,7 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
esyslog(LOG_ERR, "ERROR: StartReplay() called while recording - ignored!");
return false;
}
- StopReplay();
+ Stop();
if (videoDev >= 0) {
lastProgress = lastTotal = -1;
@@ -1565,49 +1585,69 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
}
if (FD_ISSET(fromMain, &setIn)) {
switch (readchar(fromMain)) {
- case dvbStop: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- Buffer->Stop(); break;
- case dvbPauseReplay: SetReplayMode(Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
- Paused = !Paused;
- if (FastForward || FastRewind) {
- SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- Buffer->Clear();
- }
- FastForward = FastRewind = false;
+ case dvbStop: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ Buffer->Stop();
+ break;
+ case dvbPause: SetReplayMode(Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
+ Paused = !Paused;
+ if (FastForward || FastRewind) {
+ SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ Buffer->Clear();
+ }
+ FastForward = FastRewind = false;
+ Buffer->SetMode(rmPlay);
+ break;
+ case dvbPlay: if (FastForward || FastRewind || Paused) {
+ SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ SetReplayMode(VID_PLAY_NORMAL);
+ FastForward = FastRewind = Paused = false;
Buffer->SetMode(rmPlay);
- break;
- case dvbFastForward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ }
+ break;
+ case dvbForward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ Buffer->Clear();
+ FastForward = !FastForward;
+ FastRewind = false;
+ if (Paused) {
+ Buffer->SetMode(rmPlay);
+ SetReplayMode(FastForward ? VID_PLAY_SLOW_MOTION : VID_PLAY_PAUSE);
+ }
+ else {
SetReplayMode(VID_PLAY_NORMAL);
- FastForward = !FastForward;
- FastRewind = Paused = false;
- Buffer->Clear();
Buffer->SetMode(FastForward ? rmFastForward : rmPlay);
- break;
- case dvbFastRewind: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ }
+ break;
+ case dvbBackward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ Buffer->Clear();
+ FastRewind = !FastRewind;
+ FastForward = false;
+ if (Paused) {
+ Buffer->SetMode(FastRewind ? rmSlowRewind : rmPlay);
+ SetReplayMode(FastRewind ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
+ }
+ else {
SetReplayMode(VID_PLAY_NORMAL);
- FastRewind = !FastRewind;
- FastForward = Paused = false;
- Buffer->Clear();
Buffer->SetMode(FastRewind ? rmFastRewind : rmPlay);
- break;
- case dvbSkip: {
- int Seconds;
- if (readint(fromMain, Seconds)) {
- SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- SetReplayMode(VID_PLAY_NORMAL);
- FastForward = FastRewind = Paused = false;
- Buffer->SetMode(rmPlay);
- Buffer->SkipSeconds(Seconds);
- }
- }
- break;
- case dvbGetIndex: {
- int Current, Total;
- Buffer->GetIndex(Current, Total);
- writeint(toMain, Current);
- writeint(toMain, Total);
}
- break;
+ break;
+ case dvbSkip: {
+ int Seconds;
+ if (readint(fromMain, Seconds)) {
+ SetReplayMode(VID_PLAY_CLEAR_BUFFER);
+ SetReplayMode(VID_PLAY_NORMAL);
+ FastForward = FastRewind = Paused = false;
+ Buffer->SetMode(rmPlay);
+ Buffer->SkipSeconds(Seconds);
+ }
+ }
+ break;
+ case dvbGetIndex: {
+ int Current, Total;
+ Buffer->GetIndex(Current, Total);
+ writeint(toMain, Current);
+ writeint(toMain, Total);
+ }
+ break;
}
}
}
@@ -1633,7 +1673,7 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
return false;
}
-void cDvbApi::StopReplay(void)
+void cDvbApi::Stop(void)
{
if (pidReplay) {
writechar(toReplay, dvbStop);
@@ -1646,22 +1686,28 @@ void cDvbApi::StopReplay(void)
}
}
-void cDvbApi::PauseReplay(void)
+void cDvbApi::Pause(void)
+{
+ if (pidReplay)
+ writechar(toReplay, dvbPause);
+}
+
+void cDvbApi::Play(void)
{
if (pidReplay)
- writechar(toReplay, dvbPauseReplay);
+ writechar(toReplay, dvbPlay);
}
-void cDvbApi::FastForward(void)
+void cDvbApi::Forward(void)
{
if (pidReplay)
- writechar(toReplay, dvbFastForward);
+ writechar(toReplay, dvbForward);
}
-void cDvbApi::FastRewind(void)
+void cDvbApi::Backward(void)
{
if (pidReplay)
- writechar(toReplay, dvbFastRewind);
+ writechar(toReplay, dvbBackward);
}
void cDvbApi::Skip(int Seconds)
diff --git a/dvbapi.h b/dvbapi.h
index a43ba0e..25452cf 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.h 1.11 2000/06/24 14:03:57 kls Exp $
+ * $Id: dvbapi.h 1.12 2000/07/30 15:01:01 kls Exp $
*/
#ifndef __DVBAPI_H
@@ -104,9 +104,10 @@ public:
private:
enum { dvbStop = 1, // let's not have 0 as a command
- dvbPauseReplay,
- dvbFastForward,
- dvbFastRewind,
+ dvbPause,
+ dvbPlay,
+ dvbForward,
+ dvbBackward,
dvbSkip,
dvbGetIndex,
};
@@ -136,13 +137,15 @@ public:
// If there is already a replay session active, it will be stopped
// and the new file will be played back.
// If provided Title will be used in the progress display.
- void StopReplay(void);
+ void Stop(void);
// Stops the current replay session (if any).
- void PauseReplay(void);
+ void Pause(void);
// Pauses the current replay session, or resumes a paused session.
- void FastForward(void);
+ void Play(void);
+ // Resumes normal replay mode.
+ void Forward(void);
// Runs the current replay session forward at a higher speed.
- void FastRewind(void);
+ void Backward(void);
// Runs the current replay session backwards at a higher speed.
void Skip(int Seconds);
// Skips the given number of seconds in the current replay session.
diff --git a/menu.c b/menu.c
index 17a9b0b..15c8379 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.20 2000/07/24 16:25:53 kls Exp $
+ * $Id: menu.c 1.22 2000/08/06 07:02:52 kls Exp $
*/
#include "menu.h"
@@ -512,7 +512,7 @@ cMenuEditChannel::cMenuEditChannel(int Index)
Add(new cMenuEditIntItem( "Vpid", &data.vpid, 0, 10000)); //TODO exact limits???
Add(new cMenuEditIntItem( "Apid", &data.apid, 0, 10000)); //TODO exact limits???
Add(new cMenuEditIntItem( "CA", &data.ca, 0, cDvbApi::NumDvbApis));
- Add(new cMenuEditIntItem( "Pnr", &data.pnr, 0, 10000)); //TODO exact limits???
+ Add(new cMenuEditIntItem( "Pnr", &data.pnr, 0));
}
}
@@ -1243,7 +1243,7 @@ cReplayControl::cReplayControl(void)
cReplayControl::~cReplayControl()
{
Hide();
- dvbApi->StopReplay();
+ dvbApi->Stop();
}
void cReplayControl::SetRecording(const char *FileName, const char *Title)
@@ -1278,13 +1278,13 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
if (visible)
shown = dvbApi->ShowProgress(!shown) || shown;
switch (Key) {
- case kUp: dvbApi->Skip(-INT_MAX); break;
- case kDown: dvbApi->PauseReplay(); break;
+ case kUp: dvbApi->Play(); break;
+ case kDown: dvbApi->Pause(); break;
case kBlue: Hide();
- dvbApi->StopReplay();
+ dvbApi->Stop();
return osEnd;
- case kLeft: dvbApi->FastRewind(); break;
- case kRight: dvbApi->FastForward(); break;
+ case kLeft: dvbApi->Backward(); break;
+ case kRight: dvbApi->Forward(); break;
case kGreen: dvbApi->Skip(-60); break;
case kYellow: dvbApi->Skip(60); break;
case kMenu: Hide(); return osMenu; // allow direct switching to menu
diff --git a/osd.c b/osd.c
index ee9ba28..3c32373 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 1.4 2000/04/24 09:44:31 kls Exp $
+ * $Id: osd.c 1.5 2000/07/26 17:35:09 kls Exp $
*/
#include "osd.h"
@@ -166,14 +166,20 @@ void cOsdMenu::CursorUp(void)
{
if (current > 0) {
DisplayCurrent(false);
- if (--current < first) {
+ if (current == first) {
first -= MAXOSDITEMS;
if (first < 0)
first = 0;
+ if (current - MAXOSDITEMS > 0)
+ current -= MAXOSDITEMS;
+ else
+ current--;
Display();
}
- else
+ else {
+ current--;
DisplayCurrent(true);
+ }
}
}
@@ -182,14 +188,20 @@ void cOsdMenu::CursorDown(void)
int count = Count();
if (current < count - 1) {
DisplayCurrent(false);
- if (++current >= first + MAXOSDITEMS) {
+ if (current == first + MAXOSDITEMS - 1) {
first += MAXOSDITEMS;
if (first > count - MAXOSDITEMS)
first = count - MAXOSDITEMS;
+ if (current + MAXOSDITEMS < count)
+ current += MAXOSDITEMS;
+ else
+ current++;
Display();
}
- else
+ else {
+ current++;
DisplayCurrent(true);
+ }
}
}
diff --git a/recording.c b/recording.c
index e37ccd8..9f3b388 100644
--- a/recording.c
+++ b/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.12 2000/07/24 16:31:07 kls Exp $
+ * $Id: recording.c 1.15 2000/07/29 14:08:17 kls Exp $
*/
#define _GNU_SOURCE
@@ -13,9 +13,11 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
+#include <sys/stat.h>
#include <unistd.h>
#include "interface.h"
#include "tools.h"
+#include "videodir.h"
#define RECEXT ".rec"
#define DELEXT ".del"
@@ -24,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 *BaseDir = "/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, BaseDir);
- 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
@@ -65,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)) {
@@ -123,7 +97,7 @@ cRecording::cRecording(const char *FileName)
{
titleBuffer = NULL;
fileName = strdup(FileName);
- FileName += strlen(BaseDir) + 1;
+ FileName += strlen(VideoDirectory) + 1;
char *p = strrchr(FileName, '/');
name = NULL;
@@ -189,7 +163,7 @@ const char *cRecording::FileName(void)
{
if (!fileName) {
struct tm *t = localtime(&start);
- asprintf(&fileName, NAMEFORMAT, BaseDir, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime);
+ asprintf(&fileName, NAMEFORMAT, VideoDirectory, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime);
if (fileName)
strreplace(fileName, ' ', '_');
}
@@ -239,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;
@@ -251,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 -----------------------------------------------------------
@@ -261,7 +232,7 @@ bool cRecordings::Load(bool Deleted)
Clear();
bool result = false;
char *cmd = NULL;
- asprintf(&cmd, FINDCMD, BaseDir, Deleted ? "*" DELEXT : "*" RECEXT);
+ asprintf(&cmd, FINDCMD, VideoDirectory, Deleted ? "*" DELEXT : "*" RECEXT);
FILE *p = popen(cmd, "r");
if (p) {
char *s;
diff --git a/recording.h b/recording.h
index e501af8..dc3b3d7 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.h 1.7 2000/07/23 19:06:14 kls Exp $
+ * $Id: recording.h 1.9 2000/07/28 13:53:54 kls Exp $
*/
#ifndef __RECORDING_H
diff --git a/remote.c b/remote.c
index 931cfd3..0476962 100644
--- a/remote.c
+++ b/remote.c
@@ -6,7 +6,7 @@
*
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
- * $Id: remote.c 1.10 2000/07/15 16:34:35 kls Exp $
+ * $Id: remote.c 1.11 2000/07/29 16:23:47 kls Exp $
*/
#include "remote.h"
@@ -71,7 +71,11 @@ void cRcIoKBD::Flush(int WaitSeconds)
bool cRcIoKBD::InputAvailable(bool Wait)
{
timeout(Wait ? 1000 : 10);
- return true;//XXX
+ int ch = getch();
+ if (ch == ERR)
+ return false;
+ ungetch(ch);
+ return true;
}
bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *)
diff --git a/svdrp.c b/svdrp.c
index 6873d16..9fef184 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.2 2000/07/24 16:43:51 kls Exp $
+ * $Id: svdrp.c 1.4 2000/08/06 12:52:04 kls Exp $
*/
#define _GNU_SOURCE
@@ -145,7 +145,12 @@ const char *HelpPages[] = {
" by the LSTC command.",
"NEWT <settings>\n"
" Create a new timer. Settings must be in the same format as returned\n"
- " by the LSTT command.",
+ " by the LSTT command. It is an error if a timer with the same channel,\n"
+ " day, start and stop time already exists.",
+ "UPDT <settings>\n"
+ " Updates a timer. Settings must be in the same format as returned\n"
+ " by the LSTT command. If a timer with the same channel, day, start\n"
+ " and stop time does not yet exists, it will be created.",
"QUIT\n"
" Exit vdr (SVDRP).\n"
" You can also hit Ctrl-D to exit.",
@@ -369,7 +374,7 @@ void cSVDRP::CmdHelp(const char *Option)
}
}
else {
- Reply(-214, "This is VDR version 0.6"); //XXX dynamically insert version number
+ Reply(-214, "This is VDR version %s", VDRVERSION);
Reply(-214, "Topics:");
const char **hp = HelpPages;
while (*hp) {
@@ -548,13 +553,48 @@ void cSVDRP::CmdNewt(const char *Option)
if (*Option) {
cTimer *timer = new cTimer;
if (timer->Parse(Option)) {
- Timers.Add(timer);
+ cTimer *t = Timers.GetTimer(timer);
+ if (!t) {
+ Timers.Add(timer);
+ Timers.Save();
+ isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
+ Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
+ return;
+ }
+ else
+ Reply(550, "Timer already defined: %d %s", t->Index() + 1, t->ToText());
+ }
+ else
+ Reply(501, "Error in timer settings");
+ delete timer;
+ }
+ else
+ Reply(501, "Missing timer settings");
+}
+
+void cSVDRP::CmdUpdt(const char *Option)
+{
+ if (*Option) {
+ cTimer *timer = new cTimer;
+ if (timer->Parse(Option)) {
+ cTimer *t = Timers.GetTimer(timer);
+ if (t) {
+ t->Parse(Option);
+ delete timer;
+ timer = t;
+ isyslog(LOG_INFO, "timer %d updated", timer->Index() + 1);
+ }
+ else {
+ Timers.Add(timer);
+ isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
+ }
Timers.Save();
- isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
+ return;
}
else
Reply(501, "Error in timer settings");
+ delete timer;
}
else
Reply(501, "Missing timer settings");
@@ -583,6 +623,7 @@ void cSVDRP::Execute(char *Cmd)
else if (CMD("MOVT")) CmdMovt(s);
else if (CMD("NEWC")) CmdNewc(s);
else if (CMD("NEWT")) CmdNewt(s);
+ else if (CMD("UPDT")) CmdUpdt(s);
else if (CMD("QUIT")
|| CMD("\x04")) Close();
else Reply(500, "Command unrecognized: \"%s\"", Cmd);
@@ -598,7 +639,7 @@ void cSVDRP::Process(void)
//TODO how can we get the *full* hostname?
gethostname(buffer, sizeof(buffer));
time_t now = time(NULL);
- Reply(220, "%s SVDRP VideoDiskRecorder 0.6; %s", buffer, ctime(&now));//XXX dynamically insert version number
+ Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", VDRVERSION, buffer, ctime(&now));
}
int rbytes = readstring(filedes, buffer, sizeof(buffer) - 1);
if (rbytes > 0) {
diff --git a/svdrp.h b/svdrp.h
index c638542..3c1cafa 100644
--- a/svdrp.h
+++ b/svdrp.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: svdrp.h 1.1 2000/07/23 14:49:30 kls Exp $
+ * $Id: svdrp.h 1.2 2000/08/06 12:45:28 kls Exp $
*/
#ifndef __SVDRP_H
@@ -42,6 +42,7 @@ private:
void CmdMovt(const char *Option);
void CmdNewc(const char *Option);
void CmdNewt(const char *Option);
+ void CmdUpdt(const char *Option);
void Execute(char *Cmd);
public:
cSVDRP(int Port);
diff --git a/timers.conf b/timers.conf
index 659faaa..47b4067 100644
--- a/timers.conf
+++ b/timers.conf
@@ -1,9 +1,13 @@
-1:10:-T-----:2058:2202:99:10:Quarks:
-1:5:-T-----:2100:2205:99:10:RudisSuchmaschine:
+0:10:-T-----:2058:2202:99:10:Quarks:
+0:5:-T-----:2100:2205:99:10:RudisSuchmaschine:
1:10:---T---:2158:2250:99:99:DiePlaneten:
-1:3:---T---:2211:2300:99:10:Switch:
+0:3:---T---:2211:2300:99:10:Switch:
1:15:-----S-:1358:1435:99:7:Neues:
-1:1:-----S-:1445:1600:99:30:Hammerman:
+1:1:-----S-:1445:1610:99:30:Hammerman:
0:2:-----S-:2200:2350:99:30:Wochenshow:
1:11:------S:2058:2120:99:10:Centauri:
0:15:MTWTF--:1828:1901:10:5:nano:
+1:1:-TWTF--:0858:0940:99:99:Ellen:
+1:2:----F--:2140:2225:10:10:WWW:
+1:11:-----S-:2158:2235:99:99:Computer:
+1:23:-----S-:2200:0020:99:99:BBC special:
diff --git a/tools.c b/tools.c
index 998d91b..0dca69e 100644
--- a/tools.c
+++ b/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.10 2000/07/23 13:16:54 kls Exp $
+ * $Id: tools.c 1.13 2000/07/29 18:41:45 kls Exp $
*/
#define _GNU_SOURCE
@@ -145,6 +145,51 @@ bool isnumber(const char *s)
return true;
}
+#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 if (LogErrors)
+ esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
+ }
+ else if (LogErrors)
+ esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
+ }
+ else if (LogErrors)
+ LOG_ERROR_STR(DirName);
+ return false;
+}
+
bool MakeDirs(const char *FileName, bool IsDirectory)
{
bool result = true;
@@ -157,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;
@@ -173,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) {
@@ -185,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, "ERROR: 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;
}
diff --git a/tools.h b/tools.h
index ecf42be..563f3d0 100644
--- a/tools.h
+++ b/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.10 2000/07/23 13:16:37 kls Exp $
+ * $Id: tools.h 1.12 2000/07/29 10:56:00 kls Exp $
*/
#ifndef __TOOLS_H
@@ -43,8 +43,10 @@ char *skipspace(char *s);
int time_ms(void);
void delay_ms(int ms);
bool isnumber(const char *s);
+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);
diff --git a/vdr.c b/vdr.c
index 626e6dc..69eceed 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.23 2000/07/23 15:36:43 kls Exp $
+ * $Id: vdr.c 1.27 2000/07/29 19:01:57 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"
@@ -64,38 +65,67 @@ int main(int argc, char *argv[])
static struct option long_options[] = {
{ "daemon", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
+ { "log", required_argument, NULL, 'l' },
{ "port", required_argument, NULL, 'p' },
+ { "video", required_argument, NULL, 'v' },
{ 0 }
};
int c;
int option_index = 0;
- while ((c = getopt_long(argc, argv, "dhp:", long_options, &option_index)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhl:p:v:", long_options, &option_index)) != -1) {
switch (c) {
case 'd': DaemonMode = true; break;
case 'h': printf("Usage: vdr [OPTION]\n\n"
- " -h, --help display this help and exit\n"
- " -d, --daemon run in daemon mode\n"
- " -p PORT, --port=PORT use PORT for SVDRP ('0' turns off SVDRP)\n"
+ " -h, --help display this help and exit\n"
+ " -d, --daemon run in daemon mode\n"
+ " -l LEVEL, --log=LEVEL set log level (default: 3)\n"
+ " 0 = no logging, 1 = errors only,\n"
+ " 2 = errors and info, 3 = errors, info and debug\n"
+ " -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
+ " 0 turns off SVDRP\n"
+ " -v DIR, --video=DIR use DIR as video directory (default is %s)\n"
"\n"
- "Report bugs to <vdr-bugs@cadsoft.de>\n"
+ "Report bugs to <vdr-bugs@cadsoft.de>\n",
+ DEFAULTSVDRPPORT,
+ VideoDirectory
);
return 0;
break;
+ case 'l': if (isnumber(optarg)) {
+ int l = atoi(optarg);
+ if (0 <= l && l <= 3) {
+ SysLogLevel = l;
+ break;
+ }
+ }
+ fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
+ abort();
+ break;
case 'p': if (isnumber(optarg))
- SVDRPport = strtol(optarg, NULL, 10);
+ SVDRPport = atoi(optarg);
else {
fprintf(stderr, "vdr: invalid port number: %s\n", optarg);
- return 1;
+ abort();
}
break;
+ case 'v': VideoDirectory = optarg;
+ break;
default: abort();
}
}
// Log file:
- openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
+ if (SysLogLevel > 0)
+ openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
+
+ // Check the video directory:
+
+ if (!DirectoryOk(VideoDirectory, true)) {
+ fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
+ abort();
+ }
// Daemon mode:
@@ -104,8 +134,8 @@ int main(int argc, char *argv[])
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "%s\n", strerror(errno));
- esyslog(LOG_ERR, strerror(errno));
- return 1;
+ esyslog(LOG_ERR, "ERROR: %s", strerror(errno));
+ abort();
}
if (pid != 0)
return 0; // initial program immediately returns
@@ -117,12 +147,12 @@ int main(int argc, char *argv[])
abort();
#endif
}
- isyslog(LOG_INFO, "started");
+ isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
// DVB interfaces:
if (!cDvbApi::Init())
- return 1;
+ abort();
// Configuration data:
@@ -241,6 +271,7 @@ int main(int argc, char *argv[])
delete SVDRP;
cDvbApi::Cleanup();
isyslog(LOG_INFO, "exiting");
- closelog();
+ if (SysLogLevel > 0)
+ closelog();
return 0;
}
diff --git a/videodir.c b/videodir.c
new file mode 100644
index 0000000..7bd6299
--- /dev/null
+++ b/videodir.c
@@ -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;
+}
diff --git a/videodir.h b/videodir.h
new file mode 100644
index 0000000..7ce1531
--- /dev/null
+++ b/videodir.h
@@ -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