summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUGS7
-rw-r--r--HISTORY18
-rw-r--r--INSTALL96
-rw-r--r--MANUAL53
-rw-r--r--Makefile16
-rw-r--r--README112
-rw-r--r--TODO3
-rw-r--r--config.c5
-rw-r--r--config.h4
-rw-r--r--dvbapi.c171
-rw-r--r--dvbapi.h21
-rw-r--r--interface.c60
-rw-r--r--interface.h15
-rw-r--r--menu.c52
-rw-r--r--menu.h13
-rw-r--r--osd.c6
-rw-r--r--osd.h10
-rw-r--r--osm.c150
-rw-r--r--recording.c40
-rw-r--r--recording.h21
-rw-r--r--remote.c47
-rw-r--r--remote.h7
-rw-r--r--timers.conf4
-rw-r--r--tools.c60
-rw-r--r--tools.h12
-rw-r--r--vdr.c189
26 files changed, 748 insertions, 444 deletions
diff --git a/BUGS b/BUGS
index bcb3772..66755f9 100644
--- a/BUGS
+++ b/BUGS
@@ -14,10 +14,3 @@ Video Disk Recorder - Known Bugs
Haven't figured out yet how to ensure that it switches back to
the current channel.
-* Every now and then the on-screen display shows nothing but
- "noise". If that occurs, I have to stop the 'osm' program
- and do a 'make reload' for the card driver. After that it
- works fine again.
- Presumably this is a problem in the card driver or firmware?
- Or could it be a problem with the hardware?
- Does anybody else observe this?
diff --git a/HISTORY b/HISTORY
index c013340..95cd350 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,5 +1,5 @@
-Video Disk Recorder OSM Revision History
-----------------------------------------
+Video Disk Recorder Revision History
+------------------------------------
2000-02-19: Version 0.01 (Initial revision).
@@ -18,3 +18,17 @@ Video Disk Recorder OSM Revision History
able to store some 18 hours in full quality, so we don't really need that).
- Termination signals are now caught and the program cleans up before exiting.
- Support for CICAM.
+
+2000-04-24: Version 0.04
+
+- Changed name from 'osm' to 'vdr' to avoid mixups with the 'oms' program that
+ appears to be in use with DVD replay.
+- Implemented a channel display in the top menu line.
+- Implemented replay progress display (press "Ok" when replaying to bring it up).
+- Implemented direct channel selecting by pressing the numeric keys.
+- Added several 'const' keywords to please stricter compilers.
+- The repeat function for the remote control no longer adapts dynamically
+ to the timing of the RCU (this sometimes caused the repeat function to
+ kick in too early).
+- Channel selection is now blocked when recording or replaying.
+- Improved process handling.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..d7acfb6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,96 @@
+Installation of the Video Disk Recorder
+---------------------------------------
+
+Compiling and running the program:
+----------------------------------
+
+Make sure the files from this package are located in a
+directory that is "parallel" to the DVB directory of the
+driver source for the Siemens DVB-S PCI card (refer to
+http://linuxtv.org/dvb/siemens_dvb.html for more information
+about that driver). For example, if the DVB driver was
+extracted into the directory /home/kls/vdr/DVB, then this
+package should be extracted into /home/kls/vdr/VDR.
+
+This program requires the card driver version 0.04 or higher
+to work properly.
+
+After extracting the package, change into the VDR directory
+and type 'make'. This should produce an executable file
+named 'vdr', which can be run after the DVB driver has been
+installed.
+
+There are two macros you can use to customize the 'vdr' program
+at compile time. Adding "DEBUG_REMOTE=1" to the 'make' call
+will use the PC's keyboard as input device instead of the "Remote
+Control Unit" (see http://www.cadsoft.de/people/kls/vdr/remote.htm).
+Adding "DEBUG_OSD=1" will use the PC screen (or current window)
+to display texts instead of the DVB card's on-screen display
+interface. These modes are useful when testing new menus if you
+only have a remote connection to the VDR (which, in my case, is
+located in the living room and has neither a monitor nor a keyboard).
+
+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.
+
+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.
+
+Configuration files:
+--------------------
+
+There are three configuration files that hold information about
+channels, remote control keys and timers. These files are currrently
+assumed to be located in the directory from which the 'vdr' program
+was started (this will become configurable later). The configuration
+files can be edited with any text editor, or will be written by the
+'vdr' program if any changes are made inside the on-screen menus.
+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.
+
+Learning the remote control keys:
+---------------------------------
+
+There is no default 'keys.conf' file, so if you compile the program
+without 'DEBUG_REMOTE=1' you will have to go through a "teach-in"
+session that allows the program to learn your remote control codes.
+It will first attempt to determine the basic data transfer mode and
+timing of your remote control unit, and then will ask you to press one
+key after the other so that it can learn the various key codes. You will
+at least need to provide an "Up" and a "Down" key, so that you can switch
+channels. The rest of the key definitions is optional, but the more keys
+you define, the more you will be able to navigate through the menus and
+control recording/replaying.
+If the program has been built with "DEBUG_REMOTE=1", it will use the
+key configuration file 'keys-pc.conf', so that you won't loose data
+when switching between normal and debug mode.
+
+The default PC key assignments are:
+
+ Up, Down, Left, Right Crsr keys in numeric block
+ Menu '5' in numeric block
+ Ok Enter
+ Back Backspace
+ 0..9 '0'..'9' in top row
+ Red, Green, Yellow, Blue 'F1'..'F4'
+ Record 'r'
+ Pause 'p'
+ Stop 's'
+ Begin 'B'
+ SearchForward 'f'
+ SearchBack 'b'
+ SkipForward 'PgDn' in numeric block
+ SkipBack 'PgUp' in numeric block
+
+If you prefer different key assignments, simply delete the file
+'keys-pc.conf' and restart 'vdr' to get into learning mode.
+
diff --git a/MANUAL b/MANUAL
index 9e5036c..36a6298 100644
--- a/MANUAL
+++ b/MANUAL
@@ -1,16 +1,55 @@
Video Disk Recorder User's Manual
---------------------------------
+* Navigating through the On Screen Menus
+
+ The "Main" menu can be called up with the "Menu" key of your remote
+ control unit. The "Up" and "Down" keys are used to select a specific
+ item. The "Left" and "Right" keys can be used to change options, and
+ the numeric keys allow direct input of numeric data. The "Ok" key
+ confirms any changes (or switches to a channel in the "Channels" menu).
+ The "Back" key goes back one level in the menu structure, discarding
+ any changes that might have been made in the current menu.
+
+ In the "Timers" menu, the current timer can be enabled or disabled with
+ the "Right" or "Left" key, respectively (enabled timers are marked with ">").
+ "Ok" here opens the "Edit timer" menu.
+
+ Textual options, like channel names or recording file names, can be edited
+ by pressing the "Right" button (which puts brackets around the current
+ character as in "[R]TL"), selecting the desired character position with
+ "Left" and "Right", and changing the character with the "Up" and "Down"
+ keys. "Ok" then confirms the changes.
+
+ The "Red", "Green", "Yellow" and "Blue" buttons have special meanings
+ in the various menus and are listed at the bottom of the on-screen-display.
+
+ At any point in the menu system, pressing the "Menu" key again will
+ immediately leave the menu system.
+
* Selecting a Channel
- You can select a channel either by pressing the "Up" or "Down" key (while
- no On Screen Menu is displayed), or browsing through the channel list in
- the menu and pressing "Ok" on the desired channel.
+ There are three ways to select a channel:
+
+ 1. With no On Screen Menu displayed press the "Up" or "Down" key to switch
+ to the next higher or lower channel.
+ 2. Press the "Menu" button to bring up the On Screen Menu, select "Channels"
+ and browse through the list with the "Up" and "Down" key; to switch to the
+ selected channel press "Ok".
+ 2. Directly type in the channel number with the numeric keys ('0'..'9');
+ if no key is pressed for about half a second, the digits collected so
+ far will define the channel number.
+
+ After switching to a different channel the channel number and name, as well
+ as the current time are displayed at the top of the screen. This line
+ automatically goes away after about two seconds, or if any key is pressed.
+ To bring up the channel display without switching channels you can press
+ the "Ok" button.
* Instant Recording
You can start recording the current channel by pressing the "Record"
- button. This will create a timer event named "instant" that start
+ button. This will create a timer event named "instant" that starts
at the current time and records for two hours.
If you want to modify the recording time you need to edit the timer.
Stop instant recording by disabling or deleting the timer.
@@ -23,6 +62,8 @@ Video Disk Recorder User's Manual
* Replay Control
+ The following keys have the listed meaning in Replay mode:
+
- "Begin" Positions to beginning of the recording and starts playback
from there.
- "Pause" Halts playback at the current frame. Press again to continue
@@ -32,6 +73,10 @@ Video Disk Recorder User's Manual
- "Search" Runs playback forward or backward at a higher speed. Press
again to resume normal speed.
- "Skip" Skips about 60 seconds forward or backward.
+ - "Ok" Brings up the replay progress display, which shows the date,
+ time and title of the recording, a progress bar and the
+ current and total time of the recording.
+ Press "Ok" again to turn off the progress display.
* Programming the Timer
diff --git a/Makefile b/Makefile
index b5153cf..0ed17ca 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,12 @@
#
# Makefile for the On Screen Menu of the Video Disk Recorder
#
-# See the main source file 'osm.c' for copyright information and
+# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.2 2000/03/05 13:51:57 kls Exp $
+# $Id: Makefile 1.3 2000/04/24 09:44:10 kls Exp $
-OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o tools.o osm.o
+OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o tools.o vdr.o
ifdef DEBUG_REMOTE
DEFINES += -DDEBUG_REMOTE
@@ -19,20 +19,20 @@ endif
%.o: %.c
g++ -g -O2 -Wall -c $(DEFINES) $<
-all: osm
+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
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
-osm.o : osm.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
+vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h
remote.o : remote.c remote.h tools.h
tools.o : tools.c tools.h
-osm: $(OBJS)
- g++ -g -O2 $(OBJS) -lncurses -o osm
+vdr: $(OBJS)
+ g++ -g -O2 $(OBJS) -lncurses -o vdr
clean:
- -rm $(OBJS) osm
+ -rm $(OBJS) vdr
diff --git a/README b/README
index e2b27a4..cfc10a7 100644
--- a/README
+++ b/README
@@ -7,6 +7,12 @@ of the LinuxTV project (http://linuxtv.org).
For details about the "Video Disk Recorder" project please
refer to http://www.cadsoft.de/people/kls/vdr.
+There is also a remote control unit described on those
+Web pages, which can be used within this program.
+
+Please see the INSTALL file for details on how to install
+this program on your computer.
+
The author can be contacted at kls@cadsoft.de.
Yet another "set-top-box"?
@@ -25,112 +31,6 @@ of commercial set-top-boxes usually are a lot more fancy than
the ones in this system, but here we have the full source code
and can modify the menus in whatever way desired.
-Compiling and running the program:
-----------------------------------
-
-Make sure the files from this package are located in a
-directory that is "parallel" to the DVB directory of the
-driver source for the Siemens DVB-S PCI card (refer to
-http://linuxtv.org/dvb/siemens_dvb.html for more information
-about that driver). For example, if the DVB driver was
-extracted into the directory /home/kls/vdr/DVB, then this
-package should be extracted into /home/kls/vdr/OSM.
-
-This program requires the card driver version 0.04 or higher
-to work properly.
-
-After extracting the package, change into the OSM directory
-and type 'make'. This should produce an executable file
-named 'osm', which can be run after the DVB driver has been
-installed.
-
-There are two macros you can use to customize the 'osm' program
-at compile time. Adding "DEBUG_REMOTE=1" to the 'make' call
-will use the PC's keyboard as input device instead of the "Remote
-Control Unit" (see http://www.cadsoft.de/people/kls/vdr/remote.htm).
-Adding "DEBUG_OSD=1" will use the PC screen (or current window)
-to display texts instead of the DVB card's on-screen display
-interface. These modes are useful when testing new menus if you
-only have a remote connection to the VDR (which, in my case, is
-located in the living room and has neither a monitor nor a keyboard).
-
-Configuration files:
---------------------
-
-There are three configuration files that hold information about
-channels, remote control keys and timers. These files are currrently
-assumed to be located in the directory from which the 'osm' program
-was started (this will become configurable later). The configuration
-files can be edited with any text editor, or will be written by the
-'osm' program if any changes are made inside the on-screen menus.
-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.
-
-Learning the remote control keys:
----------------------------------
-
-There is no default 'keys.conf' file, so if you compile the program
-without 'DEBUG_REMOTE=1' you will have to go through a "teach-in"
-session that allows the program to learn your remote control codes.
-It will first attempt to determine the basic data transfer mode and
-timing of your remote control unit, and then will ask you to press one
-key after the other so that it can learn the various key codes. You will
-at least need to provide an "Up" and a "Down" key, so that you can switch
-channels. The rest of the key definitions is optional, but the more keys
-you define, the more you will be able to navigate through the menus and
-control recording/replaying.
-If the program has been built with "DEBUG_REMOTE=1", it will use the
-key configuration file 'keys-pc.conf', so that you won't loose data
-when switching between normal and debug mode.
-
-The default PC key assignments are:
-
- Up, Down, Left, Right Crsr keys in numeric block
- Menu '5' in numeric block
- Ok Enter
- Back Backspace
- 0..9 '0'..'9' in top row
- Red, Green, Yellow, Blue 'F1'..'F4'
- Record 'r'
- Pause 'p'
- Stop 's'
- Begin 'B'
- SearchForward 'f'
- SearchBack 'b'
- SkipForward 'PgDn' in numeric block
- SkipBack 'PgUp' in numeric block
-
-If you prefer different key assignments, simply delete the file
-'keys-pc.conf' and restart 'osm' to get into learning mode.
-
-Navigating through the On Screen Menus:
----------------------------------------
-
-The "Main" menu can be called up with the "Menu" key of your remote
-control unit. The "Up" and "Down" keys are used to select a specific
-item. The "Left" and "Right" keys can be used to change options, and
-the numeric keys allow direct input of numeric data. The "Ok" key
-confirms any changes (or switches to a channel in the "Channels" menu).
-The "Back" key goes back one level in the menu structure, discarding
-any changes that might have been made in the current menu.
-
-In the "Timers" menu, the current timer can be enabled or disabled with
-the "Right" or "Left" key, respectively (enabled timers are marked with ">").
-"Ok" here opens the "Edit timer" menu.
-
-Textual options, like channel names or recording file names, can be edited
-by pressing the "Right" button (which puts brackets around the current
-character as in "[R]TL"), selecting the desired character position with
-"Left" and "Right", and changing the character with the "Up" and "Down"
-keys. "Ok" then confirms the changes.
-
-The "Red", "Green", "Yellow" and "Blue" buttons have special meanings
-in the various menus and are listed at the bottom of the on-screen-display.
-
-At any point in the menu system, pressing the "Menu" key again will
-immediately leave the menu system.
-
What do you think?
------------------
diff --git a/TODO b/TODO
index 8154c01..8f5d2f3 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,6 @@
TODO list for the Video Disk Recorder project
---------------------------------------------
-* Channel select via numeric keys.
* Make it work with two DVB-S PCI cards to allow simultaneous
recording of one programme, while replaying another programme
(or maybe the same one, but time delayed).
@@ -10,7 +9,5 @@ TODO list for the Video Disk Recorder project
* Implement "on-disk editing" to allow "cutting out" of certain
scenes in order to archive them (or, reversely, cut out
commercial breaks).
-* Implement on-screen display of replay progress (progress bar
- and/or time index).
* Implement channel scanning.
* Better support for encrypted channels.
diff --git a/config.c b/config.c
index c29fea8..0a39cc5 100644
--- a/config.c
+++ b/config.c
@@ -1,10 +1,10 @@
/*
* config.c: Configuration file handling
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.3 2000/04/15 12:48:00 kls Exp $
+ * $Id: config.c 1.5 2000/04/24 09:44:15 kls Exp $
*/
#include "config.h"
@@ -206,7 +206,6 @@ bool cChannel::Switch(void)
if (!DvbApi.Recording()) {
isyslog(LOG_INFO, "switching to channel %d", Index() + 1);
CurrentChannel = Index();
- Interface.DisplayChannel(CurrentChannel + 1, name);
for (int i = 3; --i;) {
if (DvbApi.SetChannel(frequency, polarization, diseqc, srate, vpid, apid, ca, pnr))
return true;
diff --git a/config.h b/config.h
index 6184f56..42af0f8 100644
--- a/config.h
+++ b/config.h
@@ -1,10 +1,10 @@
/*
* config.h: Configuration file handling
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.3 2000/04/15 12:44:23 kls Exp $
+ * $Id: config.h 1.4 2000/04/24 09:44:17 kls Exp $
*/
#ifndef __CONFIG_H
diff --git a/dvbapi.c b/dvbapi.c
index ef2f808..719437d 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -1,21 +1,19 @@
/*
* dvbapi.c: Interface to the DVB driver
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.3 2000/04/15 14:45:04 kls Exp $
+ * $Id: dvbapi.c 1.8 2000/04/24 15:30:35 kls Exp $
*/
#include "dvbapi.h"
#include <errno.h>
#include <fcntl.h>
-#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <sys/wait.h>
#include <unistd.h>
#include "interface.h"
#include "tools.h"
@@ -59,7 +57,6 @@
#define RESUMEFILESUFFIX "/resume.vdr"
#define RECORDFILESUFFIX "/%03d.vdr"
#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
-#define MAXPROCESSTIMEOUT 3 // seconds
// The number of frames to back up when resuming an interrupted replay session:
#define RESUMEBACKUP (10 * FRAMESPERSEC)
@@ -85,7 +82,7 @@ public:
int Last(void) { return last; }
int GetResume(void) { return resume; }
bool StoreResume(int Index);
- static char *Str(int Index);
+ static char *Str(int Index, bool WithFrame = false);
};
cIndexFile::cIndexFile(const char *FileName, bool Record)
@@ -256,7 +253,7 @@ bool cIndexFile::StoreResume(int Index)
return false;
}
-char *cIndexFile::Str(int Index)
+char *cIndexFile::Str(int Index, bool WithFrame)
{
static char buffer[16];
int f = (Index % FRAMESPERSEC) + 1;
@@ -264,7 +261,7 @@ char *cIndexFile::Str(int Index)
int m = s / 60 % 60;
int h = s / 3600;
s %= 60;
- snprintf(buffer, sizeof(buffer), "%d:%02d:%02d.%02d", h, m, s, f);
+ snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
return buffer;
}
@@ -554,7 +551,7 @@ private:
public:
cRecordBuffer(int *InFile, const char *FileName);
virtual ~cRecordBuffer();
- int WriteWithTimeout(void);
+ int WriteWithTimeout(bool EndIfEmpty = false);
};
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
@@ -720,13 +717,13 @@ int cRecordBuffer::Write(int Max)
return 0;
}
-int cRecordBuffer::WriteWithTimeout(void)
+int cRecordBuffer::WriteWithTimeout(bool EndIfEmpty)
{
int w, written = 0;
int t0 = time_ms();
while ((w = Write()) > 0 && time_ms() - t0 < MAXRECORDWRITETIME)
written += w;
- return w < 0 ? w : written;
+ return w < 0 ? w : (written == 0 && EndIfEmpty ? -1 : written);
}
// --- cReplayBuffer ---------------------------------------------------------
@@ -752,6 +749,7 @@ public:
int Resume(void);
bool Save(void);
void SkipSeconds(int Seconds);
+ void GetIndex(int &Current, int &Total);
};
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
@@ -840,7 +838,7 @@ void cReplayBuffer::SkipSeconds(int Seconds)
}
Index += Seconds * FRAMESPERSEC;
if (Index < 0)
- Index = 1;
+ Index = 1; // not '0', to allow GetNextIFrame() below to work!
uchar FileNumber;
int FileOffset;
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
@@ -849,6 +847,16 @@ void cReplayBuffer::SkipSeconds(int Seconds)
}
}
+void cReplayBuffer::GetIndex(int &Current, int &Total)
+{
+ if (index) {
+ Current = index->Get(fileNumber, fileOffset);
+ Total = index->Last();
+ }
+ else
+ Current = Total = -1;
+}
+
void cReplayBuffer::SkipAudioBlocks(void)
{
int Length;
@@ -1001,8 +1009,10 @@ cDvbApi::cDvbApi(void)
memset(&colorPairs, 0, sizeof(colorPairs));
start_color();
leaveok(stdscr, TRUE);
- window = stdscr;
+ window = NULL;
#endif
+ lastProgress = -1;
+ replayTitle = NULL;
}
cDvbApi::~cDvbApi()
@@ -1018,6 +1028,7 @@ cDvbApi::~cDvbApi()
endwin();
#endif
}
+ delete replayTitle;
}
#ifdef DEBUG_OSD
@@ -1056,17 +1067,21 @@ void cDvbApi::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
void cDvbApi::Open(int w, int h)
{
+ int d = (h < 0) ? MenuLines + h : 0;
+ h = abs(h);
cols = w;
rows = h;
#ifdef DEBUG_OSD
- //XXX size...
+ window = subwin(stdscr, h, w, d, 0);
+ syncok(window, TRUE);
#define B2C(b) (((b) * 1000) / 255)
#define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b))
#else
w *= charWidth;
h *= lineHeight;
- int x = (720 - w) / 2; //TODO PAL vs. NTSC???
- int y = (576 - h) / 2;
+ d *= lineHeight;
+ int x = (720 - MenuColumns * charWidth) / 2; //TODO PAL vs. NTSC???
+ int y = (576 - MenuLines * lineHeight) / 2 + d;
Cmd(OSD_Open, 4, x, y, x + w - 1, y + h - 1);
#define SETCOLOR(n, r, g, b, o) Cmd(OSD_SetColor, n, r, g, b, o)
#endif
@@ -1079,6 +1094,8 @@ void cDvbApi::Open(int w, int h)
SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255);
SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255);
SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255);
+
+ lastProgress = -1;
}
void cDvbApi::Close(void)
@@ -1086,6 +1103,7 @@ void cDvbApi::Close(void)
#ifndef DEBUG_OSD
Cmd(OSD_Close);
#endif
+ lastProgress = -1;
}
void cDvbApi::Clear(void)
@@ -1108,6 +1126,7 @@ void cDvbApi::Fill(int x, int y, int w, int h, eDvbColor color)
wmove(window, y + r, x); // ncurses wants 'y' before 'x'!
whline(window, ' ', w);
}
+ wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work
#else
Cmd(OSD_FillBlock, color, x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1);
#endif
@@ -1131,6 +1150,52 @@ void cDvbApi::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor col
#endif
}
+bool cDvbApi::ShowProgress(bool Initial)
+{
+ int Current, Total;
+
+ if (GetIndex(&Current, &Total)) {
+ if (Initial) {
+ if (replayTitle)
+ Text(0, 0, replayTitle);
+ Text(-7, 2, cIndexFile::Str(Total));
+ }
+#ifdef DEBUG_OSD
+ int p = cols * Current / Total;
+ Fill(0, 1, p, 1, clrGreen);
+ Fill(p, 1, cols - p, 1, clrWhite);
+#else
+ int w = cols * charWidth;
+ int p = w * Current / Total;
+ if (p != lastProgress) {
+ int y1 = 1 * lineHeight;
+ int y2 = 2 * lineHeight - 1;
+ int x1, x2;
+ eDvbColor color;
+ if (lastProgress < p) {
+ x1 = lastProgress + 1;
+ x2 = p;
+ if (p >= w)
+ p = w - 1;
+ color = clrGreen;
+ }
+ else {
+ x1 = p + 1;
+ x2 = lastProgress;
+ color = clrWhite;
+ }
+ if (lastProgress < 0)
+ Cmd(OSD_FillBlock, clrWhite, 0, y1, w - 1, y2);
+ Cmd(OSD_FillBlock, color, x1, y1, x2, y2);
+ lastProgress = p;
+ }
+#endif
+ Text(0, 2, cIndexFile::Str(Current));
+ return true;
+ }
+ return false;
+}
+
bool cDvbApi::SetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Ca, int Pnr)
{
if (videoDev >= 0) {
@@ -1160,35 +1225,17 @@ bool cDvbApi::SetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Sr
return false;
}
-void cDvbApi::KillProcess(pid_t pid)
-{
- pid_t Pid2Wait4 = pid;
- for (time_t t0 = time(NULL); time(NULL) - t0 < MAXPROCESSTIMEOUT; ) {
- int status;
- pid_t pid = waitpid(Pid2Wait4, &status, WNOHANG);
- if (pid < 0) {
- if (errno != ECHILD)
- LOG_ERROR;
- return;
- }
- if (pid == Pid2Wait4)
- return;
- }
- esyslog(LOG_ERR, "ERROR: process %d won't end (waited %d seconds) - terminating it...", Pid2Wait4, MAXPROCESSTIMEOUT);
- if (kill(Pid2Wait4, SIGTERM) < 0) {
- esyslog(LOG_ERR, "ERROR: process %d won't terminate (%s) - killing it...", Pid2Wait4, strerror(errno));
- if (kill(Pid2Wait4, SIGKILL) < 0)
- esyslog(LOG_ERR, "ERROR: process %d won't die (%s) - giving up", Pid2Wait4, strerror(errno));
- }
-}
-
bool cDvbApi::Recording(void)
{
+ if (pidRecord && !CheckProcess(pidRecord))
+ pidRecord = 0;
return pidRecord;
}
bool cDvbApi::Replaying(void)
{
+ if (pidReplay && !CheckProcess(pidReplay))
+ pidReplay = 0;
return pidReplay;
}
@@ -1239,6 +1286,7 @@ bool cDvbApi::StartRecord(const char *FileName)
dsyslog(LOG_INFO, "start recording process (pid=%d)", getpid());
isMainProcess = false;
+ bool DataStreamBroken = false;
int fromMain = toRecordPipe[0];
int toMain = fromRecordPipe[1];
cRecordBuffer *Buffer = new cRecordBuffer(&videoDev, FileName);
@@ -1251,21 +1299,26 @@ bool cDvbApi::StartRecord(const char *FileName)
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
+ bool ForceEnd = false;
if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0) {
if (FD_ISSET(videoDev, &set)) {
if (Buffer->Read() < 0)
break;
+ DataStreamBroken = false;
}
if (FD_ISSET(fromMain, &set)) {
switch (readchar(fromMain)) {
- case dvbStop: Buffer->Stop(); break;
- break;
+ case dvbStop: Buffer->Stop();
+ ForceEnd = DataStreamBroken;
+ break;
}
}
}
- else
+ else {
+ DataStreamBroken = true;
esyslog(LOG_ERR, "ERROR: video data stream broken");
- if (Buffer->WriteWithTimeout() < 0)
+ }
+ if (Buffer->WriteWithTimeout(ForceEnd) < 0)
break;
}
delete Buffer;
@@ -1309,7 +1362,7 @@ void cDvbApi::SetReplayMode(int Mode)
}
}
-bool cDvbApi::StartReplay(const char *FileName)
+bool cDvbApi::StartReplay(const char *FileName, const char *Title)
{
if (Recording()) {
esyslog(LOG_ERR, "ERROR: StartReplay() called while recording - ignored!");
@@ -1318,6 +1371,13 @@ bool cDvbApi::StartReplay(const char *FileName)
StopReplay();
if (videoDev >= 0) {
+ lastProgress = -1;
+ delete replayTitle;
+ if (Title) {
+ if ((replayTitle = strdup(Title)) == NULL)
+ esyslog(LOG_ERR, "ERROR: StartReplay: can't copy title '%s'", Title);
+ }
+
// Check FileName:
if (!FileName) {
@@ -1361,7 +1421,7 @@ bool cDvbApi::StartReplay(const char *FileName)
bool FastRewind = false;
int ResumeIndex = Buffer->Resume();
if (ResumeIndex >= 0)
- isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, cIndexFile::Str(ResumeIndex));
+ isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, cIndexFile::Str(ResumeIndex, true));
for (;;) {
if (Buffer->Read() < 0)
break;
@@ -1405,6 +1465,12 @@ bool cDvbApi::StartReplay(const char *FileName)
Buffer->SkipSeconds(Seconds);
}
}
+ case dvbGetIndex: {
+ int Current, Total;
+ Buffer->GetIndex(Current, Total);
+ writeint(toMain, Current);
+ writeint(toMain, Total);
+ }
break;
}
}
@@ -1470,3 +1536,20 @@ void cDvbApi::Skip(int Seconds)
}
}
+bool cDvbApi::GetIndex(int *Current, int *Total)
+{
+ if (pidReplay) {
+ int total;
+ purge(fromReplay);
+ writechar(toReplay, dvbGetIndex);
+ if (readint(fromReplay, *Current) && readint(fromReplay, total)) {
+ if (Total)
+ *Total = total;
+ }
+ else
+ *Current = -1;
+ return *Current >= 0;
+ }
+ return false;
+}
+
diff --git a/dvbapi.h b/dvbapi.h
index 3c447cf..91e554e 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -1,10 +1,10 @@
/*
* dvbapi.h: Interface to the DVB driver
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.h 1.3 2000/04/15 13:36:10 kls Exp $
+ * $Id: dvbapi.h 1.8 2000/04/24 15:31:07 kls Exp $
*/
#ifndef __DVBAPI_H
@@ -21,6 +21,9 @@ typedef unsigned char __u8;
#include <stdio.h>
#include "../DVB/driver/dvb.h"
+#define MenuLines 15
+#define MenuColumns 40
+
enum eDvbColor { clrBackground,
#ifndef DEBUG_OSD
clrOBSOLETE, //FIXME apparently color '1' can't be used as FgColor with e.g. clrRed as BgColor???
@@ -66,6 +69,14 @@ public:
void ClrEol(int x, int y, eDvbColor color = clrBackground);
void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground);
+ // Progress Display facilities
+
+private:
+ int lastProgress;
+ char *replayTitle;
+public:
+ bool ShowProgress(bool Initial = false);
+
// Channel facilities
bool SetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Ca, int Pnr);
@@ -78,13 +89,13 @@ private:
dvbFastForward,
dvbFastRewind,
dvbSkip,
+ dvbGetIndex,
};
bool isMainProcess;
pid_t pidRecord, pidReplay;
int fromRecord, toRecord;
int fromReplay, toReplay;
void SetReplayMode(int Mode);
- void KillProcess(pid_t pid);
public:
bool Recording(void);
// Returns true if we are currently recording.
@@ -102,10 +113,11 @@ public:
// returned.
void StopRecord(void);
// Stops the current recording session (if any).
- bool StartReplay(const char *FileName);
+ bool StartReplay(const char *FileName, const char *Title = NULL);
// Starts replaying the given file.
// 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);
// Stops the current replay session (if any).
void PauseReplay(void);
@@ -119,6 +131,7 @@ public:
// The sign of 'Seconds' determines the direction in which to skip.
// Use a very large negative value to go all the way back to the
// beginning of the recording.
+ bool GetIndex(int *Current, int *Total = NULL);
};
#endif //__DVBAPI_H
diff --git a/interface.c b/interface.c
index a546513..446f9c4 100644
--- a/interface.c
+++ b/interface.c
@@ -1,19 +1,16 @@
/*
* interface.c: Abstract user interface layer
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.c 1.3 2000/04/15 17:38:11 kls Exp $
+ * $Id: interface.c 1.6 2000/04/24 09:44:23 kls Exp $
*/
#include "interface.h"
#include <unistd.h>
#include "remote.h"
-#define MenuLines 15
-#define MenuColumns 40
-
#ifndef DEBUG_REMOTE
cRcIo RcIo("/dev/ttyS1");
#endif
@@ -26,6 +23,7 @@ cInterface::cInterface(void)
{
open = 0;
cols[0] = 0;
+ keyFromWait = kNone;
}
void cInterface::Init(void)
@@ -35,10 +33,10 @@ void cInterface::Init(void)
#endif
}
-void cInterface::Open(void)
+void cInterface::Open(int NumCols, int NumLines)
{
if (!open++)
- DvbApi.Open(MenuColumns, MenuLines);
+ DvbApi.Open(NumCols, NumLines);
}
void cInterface::Close(void)
@@ -49,35 +47,45 @@ void cInterface::Close(void)
DvbApi.Close();
}
-unsigned int cInterface::GetCh(void)
+unsigned int cInterface::GetCh(bool Wait)
{
#ifdef DEBUG_REMOTE
+ timeout(Wait ? 1000 :10);
int c = getch();
return (c > 0) ? c : 0;
#else
-//XXX #ifdef DEBUG_OSD
-//XXX wrefresh(window);//XXX
-//XXX #endif
- unsigned int Command;
- return RcIo.GetCommand(&Command) ? Command : 0;
+#ifdef DEBUG_OSD
+ timeout(0);
+ getch(); // just to make 'ncurses' display the window:
+#endif
+ if (Wait || RcIo.InputAvailable()) {
+ unsigned int Command;
+ return RcIo.GetCommand(&Command, NULL) ? Command : 0;
+ }
+ return 0;
#endif
}
-eKeys cInterface::GetKey(void)
+eKeys cInterface::GetKey(bool Wait)
{
- return Keys.Get(GetCh());
+ eKeys Key = keyFromWait != kNone ? keyFromWait : Keys.Get(GetCh(Wait));
+ keyFromWait = kNone;
+ return Key;
}
-eKeys cInterface::Wait(int Seconds)
+eKeys cInterface::Wait(int Seconds, bool KeepChar)
{
int t0 = time_ms();
+ eKeys Key = kNone;
while (time_ms() - t0 < Seconds * 1000) {
- eKeys Key = GetKey();
+ Key = GetKey();
if (Key != kNone)
- return Key;
+ break;
}
- return kNone;
+ if (KeepChar)
+ keyFromWait = Key;
+ return Key;
}
void cInterface::Clear(void)
@@ -312,8 +320,20 @@ void cInterface::LearnKeys(void)
void cInterface::DisplayChannel(int Number, const char *Name)
{
-//TODO
#ifndef DEBUG_REMOTE
RcIo.Number(Number);
#endif
+ if (Name) {
+ Open(MenuColumns, 1);
+ char buffer[MenuColumns + 1];
+ snprintf(buffer, sizeof(buffer), "%d %s", Number, Name ? Name : "");
+ Write(0, 0, buffer);
+ time_t t = time(NULL);
+ struct tm *now = localtime(&t);
+ snprintf(buffer, sizeof(buffer), "%02d:%02d", now->tm_hour, now->tm_min);
+ Write(-5, 0, buffer);
+ if (Wait(2, true) == kOk)
+ GetKey();
+ Close();
+ }
}
diff --git a/interface.h b/interface.h
index 1e5329d..eb4e76b 100644
--- a/interface.h
+++ b/interface.h
@@ -1,10 +1,10 @@
/*
* interface.h: Abstract user interface layer
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.h 1.3 2000/03/19 14:03:28 kls Exp $
+ * $Id: interface.h 1.7 2000/04/24 09:44:25 kls Exp $
*/
#ifndef __INTERFACE_H
@@ -19,16 +19,17 @@ public:
private:
int open;
int cols[MaxCols];
- unsigned int GetCh(void);
+ eKeys keyFromWait;
+ unsigned int GetCh(bool Wait = true);
void QueryKeys(void);
void HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor);
- eKeys Wait(int Seconds = 1);
+ eKeys Wait(int Seconds = 1, bool KeepChar = false);
public:
cInterface(void);
void Init(void);
- void Open(void);
+ void Open(int NumCols = MenuColumns, int NumLines = MenuLines);
void Close(void);
- eKeys GetKey(void);
+ eKeys GetKey(bool Wait = true);
void Clear(void);
void ClearEol(int x, int y, eDvbColor Color = clrBackground);
void SetCols(int *c);
@@ -41,7 +42,7 @@ public:
bool Confirm(const char *s);
void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL);
void LearnKeys(void);
- void DisplayChannel(int Number, const char *Name);
+ void DisplayChannel(int Number, const char *Name = NULL);
};
extern cInterface Interface;
diff --git a/menu.c b/menu.c
index 92c0bd3..2a4cfa9 100644
--- a/menu.c
+++ b/menu.c
@@ -1,10 +1,10 @@
/*
* menu.c: The actual menu implementations
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.3 2000/04/15 15:07:36 kls Exp $
+ * $Id: menu.c 1.8 2000/04/24 15:32:11 kls Exp $
*/
#include "menu.h"
@@ -428,7 +428,7 @@ void cMenuEditStrItem::Set(void)
char buf[1000];
if (pos >= 0) {
strncpy(buf, value, pos);
- char *s = value[pos] != ' ' ? value + pos + 1 : "";
+ const char *s = value[pos] != ' ' ? value + pos + 1 : "";
snprintf(buf + pos, sizeof(buf) - pos - 2, "[%c]%s", *(value + pos), s);
SetValue(buf);
}
@@ -915,16 +915,7 @@ cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording)
void cMenuRecordingItem::Set(void)
{
- char *buffer = NULL;
- struct tm *t = localtime(&recording->start);
- asprintf(&buffer, "%02d.%02d.%04d\t%02d:%02d\t%s",
- t->tm_mday,
- t->tm_mon + 1,
- t->tm_year + 1900,
- t->tm_hour,
- t->tm_min,
- recording->name);
- SetText(buffer, false);
+ SetText(recording->Title('\t'));
}
// --- cMenuRecordings -------------------------------------------------------
@@ -957,7 +948,7 @@ eOSState cMenuRecordings::Play(void)
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri) {
//XXX what if this recording's file is currently in use???
- if (ri->recording->Play())
+ if (DvbApi.StartReplay(ri->recording->FileName(), ri->recording->Title()))
return osEnd;
}
return osContinue;
@@ -1022,3 +1013,36 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
return state;
}
+// --- cReplayDisplay --------------------------------------------------------
+
+cReplayDisplay::cReplayDisplay(void)
+{
+ Interface.Open(MenuColumns, -3);
+ shown = DvbApi.ShowProgress(true);
+}
+
+cReplayDisplay::~cReplayDisplay()
+{
+ Interface.Close();
+}
+
+eKeys cReplayDisplay::ProcessKey(eKeys Key)
+{
+ if (!DvbApi.Replaying())
+ return kOk; // will turn off replay display
+ shown = DvbApi.ShowProgress(!shown);
+ switch (Key) {
+ case kBegin:
+ case kPause:
+ case kStop:
+ case kSearchBack:
+ case kSearchForward:
+ case kSkipBack:
+ case kSkipForward: break; // will be done in main loop
+ case kMenu: break; // allow direct switching to menu
+ case kOk: break; // switches off replay display
+ default: Key = kNone; // ignore anything not explicitly known here
+ }
+ return Key;
+}
+
diff --git a/menu.h b/menu.h
index 813beec..f665799 100644
--- a/menu.h
+++ b/menu.h
@@ -1,10 +1,10 @@
/*
* menu.h: The actual menu implementations
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.2 2000/03/05 10:57:27 kls Exp $
+ * $Id: menu.h 1.5 2000/04/24 15:31:53 kls Exp $
*/
#ifndef _MENU_H
@@ -18,4 +18,13 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
+class cReplayDisplay {
+private:
+ bool shown;
+public:
+ cReplayDisplay(void);
+ ~cReplayDisplay();
+ eKeys ProcessKey(eKeys Key);
+ };
+
#endif //_MENU_H
diff --git a/osd.c b/osd.c
index 673d075..ee9ba28 100644
--- a/osd.c
+++ b/osd.c
@@ -1,10 +1,10 @@
/*
* osd.c: Abstract On Screen Display layer
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 1.2 2000/02/27 17:23:07 kls Exp $
+ * $Id: osd.c 1.4 2000/04/24 09:44:31 kls Exp $
*/
#include "osd.h"
@@ -35,7 +35,7 @@ cOsdItem::~cOsdItem()
delete text;
}
-void cOsdItem::SetText(char *Text, bool Copy)
+void cOsdItem::SetText(const char *Text, bool Copy)
{
delete text;
text = Copy ? strdup(Text) : Text;
diff --git a/osd.h b/osd.h
index 5ec1334..99821b8 100644
--- a/osd.h
+++ b/osd.h
@@ -1,10 +1,10 @@
/*
* osd.h: Abstract On Screen Display layer
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 1.2 2000/03/05 11:33:11 kls Exp $
+ * $Id: osd.h 1.4 2000/04/24 09:44:32 kls Exp $
*/
#ifndef __OSD_H
@@ -28,7 +28,7 @@ enum eOSState { osUnknown,
class cOsdItem : public cListObject {
private:
- char *text;
+ const char *text;
int offset;
eOSState state;
protected:
@@ -37,8 +37,8 @@ public:
cOsdItem(eOSState State = osUnknown);
cOsdItem(char *Text, eOSState State = osUnknown);
virtual ~cOsdItem();
- void SetText(char *Text, bool Copy = true);
- char *Text(void) { return text; }
+ void SetText(const char *Text, bool Copy = true);
+ const char *Text(void) { return text; }
void Display(int Offset = -1, bool Current = false);
virtual void Set(void) {}
virtual eOSState ProcessKey(eKeys Key);
diff --git a/osm.c b/osm.c
deleted file mode 100644
index 70305ce..0000000
--- a/osm.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * osm.c: On Screen Menu for the Video Disk Recorder
- *
- * Copyright (C) 2000 Klaus Schmidinger
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- * The author can be reached at kls@cadsoft.de
- *
- * The project's page is at http://www.cadsoft.de/people/kls/vdr
- *
- * $Id: osm.c 1.3 2000/04/15 14:04:21 kls Exp $
- */
-
-#include <signal.h>
-#include "config.h"
-#include "interface.h"
-#include "menu.h"
-#include "recording.h"
-#include "tools.h"
-
-#ifdef DEBUG_REMOTE
-#define KEYS_CONF "keys-pc.conf"
-#else
-#define KEYS_CONF "keys.conf"
-#endif
-
-static int Interrupted = 0;
-
-void SignalHandler(int signum)
-{
- Interrupted = signum;
-}
-
-int main(int argc, char *argv[])
-{
- openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
- isyslog(LOG_INFO, "started");
-
- Channels.Load("channels.conf");
- Timers.Load("timers.conf");
- if (!Keys.Load(KEYS_CONF))
- Interface.LearnKeys();
- Interface.Init();
-
- cChannel::SwitchTo(CurrentChannel);
-
- if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
- if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN);
- if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
-
- cMenuMain *Menu = NULL;
- cTimer *Timer = NULL;
- cRecording *Recording = NULL;
-
- while (!Interrupted) {
- AssertFreeDiskSpace();
- if (!Recording && !Timer && (Timer = cTimer::GetMatch()) != NULL) {
- DELETENULL(Menu);
- // make sure the timer won't be deleted:
- Timer->SetRecording(true);
- // switch to channel:
- cChannel::SwitchTo(Timer->channel - 1);
- // start recording:
- Recording = new cRecording(Timer);
- if (!Recording->Record())
- DELETENULL(Recording);
- }
- if (Timer && !Timer->Matches()) {
- // stop recording:
- if (Recording) {
- Recording->Stop();
- DELETENULL(Recording);
- }
- // release timer:
- Timer->SetRecording(false);
- // clear single event timer:
- if (Timer->IsSingleEvent()) {
- DELETENULL(Menu); // must make sure no menu uses it
- isyslog(LOG_INFO, "deleting timer %d", Timer->Index() + 1);
- Timers.Del(Timer);
- Timers.Save();
- }
- Timer = NULL;
- }
- eKeys key = Interface.GetKey();
- if (Menu) {
- switch (Menu->ProcessKey(key)) {
- default: if (key != kMenu)
- break;
- case osBack:
- case osEnd: DELETENULL(Menu);
- break;
- }
- }
- else {
- switch (key) {
- // Record/Replay Control:
- case kBegin: DvbApi.Skip(-INT_MAX); break;
- case kRecord: if (!DvbApi.Recording()) {
- cTimer *timer = new cTimer(true);
- Timers.Add(timer);
- Timers.Save();
- }
- else
- Interface.Error("Already recording!");
- break;
- case kPause: DvbApi.PauseReplay(); break;
- case kStop: DvbApi.StopReplay(); break;
- case kSearchBack: DvbApi.FastRewind(); break;
- case kSearchForward: DvbApi.FastForward(); break;
- case kSkipBack: DvbApi.Skip(-60); break;
- case kSkipForward: DvbApi.Skip(60); break;
- // Menu Control:
- case kMenu: Menu = new cMenuMain;
- Menu->Display();
- break;
- case kUp:
- case kDown: {
- int n = CurrentChannel + (key == kUp ? 1 : -1);
- cChannel *channel = Channels.Get(n);
- if (channel)
- channel->Switch();
- }
- break;
- default: break;
- }
- }
- }
- isyslog(LOG_INFO, "caught signal %d", Interrupted);
- DvbApi.StopRecord();
- DvbApi.StopReplay();
- //TODO kill any remaining sub-processes!
- isyslog(LOG_INFO, "exiting", Interrupted);
- closelog();
- return 0;
-}
diff --git a/recording.c b/recording.c
index 15c7163..46fe53f 100644
--- a/recording.c
+++ b/recording.c
@@ -1,10 +1,10 @@
/*
* recording.h: Recording file handling
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.c 1.2 2000/04/15 13:29:02 kls Exp $
+ * $Id: recording.c 1.6 2000/04/24 09:45:13 kls Exp $
*/
#define _GNU_SOURCE
@@ -104,6 +104,7 @@ void AssertFreeDiskSpace(void)
cRecording::cRecording(const char *Name, time_t Start, int Priority, int LifeTime)
{
+ titleBuffer = NULL;
fileName = NULL;
name = strdup(Name);
start = Start;
@@ -113,6 +114,7 @@ cRecording::cRecording(const char *Name, time_t Start, int Priority, int LifeTim
cRecording::cRecording(cTimer *Timer)
{
+ titleBuffer = NULL;
fileName = NULL;
name = strdup(Timer->file);
start = Timer->StartTime();
@@ -122,6 +124,7 @@ cRecording::cRecording(cTimer *Timer)
cRecording::cRecording(const char *FileName)
{
+ titleBuffer = NULL;
fileName = strdup(FileName);
FileName += strlen(BaseDir) + 1;
char *p = strrchr(FileName, '/');
@@ -144,6 +147,7 @@ cRecording::cRecording(const char *FileName)
cRecording::~cRecording()
{
+ delete titleBuffer;
delete fileName;
delete name;
}
@@ -157,6 +161,23 @@ const char *cRecording::FileName(void)
return fileName;
}
+const char *cRecording::Title(char Delimiter)
+{
+ delete titleBuffer;
+ titleBuffer = NULL;
+ struct tm *t = localtime(&start);
+ asprintf(&titleBuffer, "%02d.%02d.%04d%c%02d:%02d%c%s",
+ t->tm_mday,
+ t->tm_mon + 1,
+ t->tm_year + 1900,
+ Delimiter,
+ t->tm_hour,
+ t->tm_min,
+ Delimiter,
+ name);
+ return titleBuffer;
+}
+
bool cRecording::Delete(void)
{
bool result = true;
@@ -180,21 +201,6 @@ bool cRecording::Remove(void)
return RemoveFileOrDir(FileName());
}
-bool cRecording::Record(void)
-{
- return DvbApi.StartRecord(FileName());
-}
-
-bool cRecording::Play(void)
-{
- return DvbApi.StartReplay(FileName());
-}
-
-void cRecording::Stop(void)
-{
- DvbApi.StopRecord();
-}
-
// --- cRecordings -----------------------------------------------------------
bool cRecordings::Load(bool Deleted)
diff --git a/recording.h b/recording.h
index 5a5e8de..eeea560 100644
--- a/recording.h
+++ b/recording.h
@@ -1,10 +1,10 @@
/*
* recording.h: Recording file handling
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.h 1.2 2000/04/14 15:12:42 kls Exp $
+ * $Id: recording.h 1.6 2000/04/24 09:45:49 kls Exp $
*/
#ifndef __RECORDING_H
@@ -12,15 +12,17 @@
#include <time.h>
#include "config.h"
-#include "dvbapi.h"
#include "tools.h"
void AssertFreeDiskSpace(void);
class cRecording : public cListObject {
-public:
- char *name;
+ friend class cRecordings;
+private:
+ char *titleBuffer;
char *fileName;
+ char *name;
+public:
time_t start;
int priority;
int lifetime;
@@ -29,18 +31,13 @@ public:
cRecording(const char *FileName);
~cRecording();
const char *FileName(void);
+ const char *Title(char Delimiter = ' ');
bool Delete(void);
- // Changes the file name so that it will no longer be visible in the OSM
+ // Changes the file name so that it will no longer be visible in the "Recordings" menu
// Returns false in case of error
bool Remove(void);
// Actually removes the file from the disk
// Returns false in case of error
- bool Record(void);
- // Starts recording of the file
- bool Play(void);
- // Starts playback of the file
- void Stop(void);
- // Stops recording or playback of the file
};
class cRecordings : public cList<cRecording> {
diff --git a/remote.c b/remote.c
index bf5b41f..2ba5b22 100644
--- a/remote.c
+++ b/remote.c
@@ -1,10 +1,10 @@
/*
* remote.c: Interface to the Remote Control Unit
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remote.c 1.2 2000/04/15 16:00:51 kls Exp $
+ * $Id: remote.c 1.6 2000/04/24 09:45:56 kls Exp $
*/
#include "remote.h"
@@ -17,6 +17,9 @@
#include <unistd.h>
#include "tools.h"
+#define REPEATLIMIT 100 // ms
+#define REPEATDELAY 250 // ms
+
cRcIo::cRcIo(char *DeviceName)
{
dp = 0;
@@ -25,7 +28,6 @@ cRcIo::cRcIo(char *DeviceName)
address = 0xFFFF;
t = 0;
firstTime = lastTime = 0;
- minDelta = 0;
lastCommand = 0;
if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
struct termios t;
@@ -35,8 +37,11 @@ cRcIo::cRcIo(char *DeviceName)
if (tcsetattr(f, TCSAFLUSH, &t) == 0)
return;
}
+ LOG_ERROR_STR(DeviceName);
close(f);
}
+ else
+ LOG_ERROR_STR(DeviceName);
f = -1;
}
@@ -46,9 +51,8 @@ cRcIo::~cRcIo()
close(f);
}
-int cRcIo::ReceiveByte(bool Wait)
+bool cRcIo::InputAvailable(bool Wait)
{
- // Returns the byte if one was received within 1 second, -1 otherwise
if (f >= 0) {
fd_set set;
struct timeval timeout;
@@ -56,13 +60,19 @@ int cRcIo::ReceiveByte(bool Wait)
timeout.tv_usec = Wait ? 0 : 10000;
FD_ZERO(&set);
FD_SET(f, &set);
- if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0) {
- if (FD_ISSET(f, &set)) {
- unsigned char b;
- if (read(f, &b, 1) == 1)
- return b;
- }
- }
+ if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0)
+ return FD_ISSET(f, &set);
+ }
+ return false;
+}
+
+int cRcIo::ReceiveByte(bool Wait)
+{
+ // Returns the byte if one was received within a timeout, -1 otherwise
+ if (InputAvailable(Wait)) {
+ unsigned char b;
+ if (read(f, &b, 1) == 1)
+ return b;
}
return -1;
}
@@ -112,7 +122,6 @@ bool cRcIo::SetCode(unsigned char Code, unsigned short Address)
{
code = Code;
address = Address;
- minDelta = 200;
return SendCommand(code);
}
@@ -157,12 +166,10 @@ bool cRcIo::GetCommand(unsigned int *Command, unsigned short *Address)
// let's have a timeout to avoid getting overrun by commands
int now = time_ms();
int delta = now - lastTime;
- if (delta < minDelta)
- minDelta = delta; // dynamically adjust to the smallest delta
lastTime = now;
- if (delta < minDelta * 1.3) { // if commands come in rapidly...
- if (now - firstTime < 250)
- return false; // ...repeat function kicks in after 250ms
+ if (delta < REPEATLIMIT) { // if commands come in rapidly...
+ if (now - firstTime < REPEATDELAY)
+ return false; // ...repeat function kicks in after a short delay
return true;
}
}
@@ -209,12 +216,12 @@ bool cRcIo::Number(int n, bool Hex)
bool cRcIo::String(char *s)
{
- char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
+ const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
int n = 0;
for (int i = 0; *s && i < 4; s++, i++) {
n <<= 4;
- for (char *c = chars; *c; c++) {
+ for (const char *c = chars; *c; c++) {
if (*c == *s) {
n |= c - chars;
break;
diff --git a/remote.h b/remote.h
index 6827929..73e50eb 100644
--- a/remote.h
+++ b/remote.h
@@ -1,10 +1,10 @@
/*
* remote.h: Interface to the Remote Control Unit
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remote.h 1.1 2000/02/19 13:36:48 kls Exp $
+ * $Id: remote.h 1.4 2000/04/24 09:46:00 kls Exp $
*/
#ifndef __REMOTE_H
@@ -19,7 +19,7 @@ private:
unsigned char dp, code, mode;
unsigned short address;
time_t t;
- int firstTime, lastTime, minDelta;
+ int firstTime, lastTime;
unsigned int lastCommand;
bool SendCommand(unsigned char Cmd);
int ReceiveByte(bool Wait = true);
@@ -29,6 +29,7 @@ public:
enum { modeH = 'h', modeB = 'b', modeS = 's' };
cRcIo(char *DeviceName);
~cRcIo();
+ bool InputAvailable(bool Wait = false);
void Flush(int WaitSeconds = 0);
bool SetCode(unsigned char Code, unsigned short Address);
bool SetMode(unsigned char Mode);
diff --git a/timers.conf b/timers.conf
index 1996be4..6fff8d8 100644
--- a/timers.conf
+++ b/timers.conf
@@ -1,5 +1,5 @@
-1:15:MTWTF--:1828:1901:10:5:nano
-1:3:M------:2110:2230:99:10:SevenDays
+0:15:MTWTF--:1828:1901:10:5:nano
+0:3:M------:2110:2230:99:10:SevenDays
1:10:-T-----:2058:2150:99:10:Quarks
1:3:---T---:2158:2300:99:10:Switch
1:2:----F--:2110:2155:99:10:Anke
diff --git a/tools.c b/tools.c
index e356e0f..eb0a14a 100644
--- a/tools.c
+++ b/tools.c
@@ -1,16 +1,17 @@
/*
* tools.c: Various tools
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.3 2000/04/15 15:10:05 kls Exp $
+ * $Id: tools.c 1.7 2000/04/24 15:01:35 kls Exp $
*/
#define _GNU_SOURCE
#include "tools.h"
#include <dirent.h>
#include <errno.h>
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
@@ -21,6 +22,17 @@
int SysLogLevel = 3;
+bool DataAvailable(int filedes)
+{
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(filedes, &set);
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(filedes, &set);
+}
+
void writechar(int filedes, char c)
{
write(filedes, &c, sizeof(c));
@@ -40,8 +52,13 @@ char readchar(int filedes)
bool readint(int filedes, int &n)
{
- //XXX timeout!!
- return read(filedes, &n, sizeof(n));
+ return DataAvailable(filedes) && read(filedes, &n, sizeof(n)) == sizeof(n);
+}
+
+void purge(int filedes)
+{
+ while (DataAvailable(filedes))
+ readchar(filedes);
}
char *readline(FILE *f)
@@ -135,6 +152,41 @@ bool RemoveFileOrDir(const char *FileName)
return false;
}
+bool CheckProcess(pid_t pid)
+{
+ pid_t Pid2Check = pid;
+ int status;
+ pid = waitpid(Pid2Check, &status, WNOHANG);
+ if (pid < 0) {
+ if (errno != ECHILD)
+ LOG_ERROR;
+ return false;
+ }
+ return true;
+}
+
+void KillProcess(pid_t pid, int Timeout)
+{
+ pid_t Pid2Wait4 = pid;
+ for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
+ int status;
+ pid_t pid = waitpid(Pid2Wait4, &status, WNOHANG);
+ if (pid < 0) {
+ if (errno != ECHILD)
+ LOG_ERROR;
+ return;
+ }
+ if (pid == Pid2Wait4)
+ return;
+ }
+ esyslog(LOG_ERR, "ERROR: process %d won't end (waited %d seconds) - terminating it...", Pid2Wait4, Timeout);
+ if (kill(Pid2Wait4, SIGTERM) < 0) {
+ esyslog(LOG_ERR, "ERROR: process %d won't terminate (%s) - killing it...", Pid2Wait4, strerror(errno));
+ if (kill(Pid2Wait4, SIGKILL) < 0)
+ esyslog(LOG_ERR, "ERROR: process %d won't die (%s) - giving up", Pid2Wait4, strerror(errno));
+ }
+}
+
// --- cListObject -----------------------------------------------------------
cListObject::cListObject(void)
diff --git a/tools.h b/tools.h
index 48665a8..57b2065 100644
--- a/tools.h
+++ b/tools.h
@@ -1,17 +1,20 @@
/*
* tools.h: Various tools
*
- * See the main source file 'osm.c' for copyright information and
+ * See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.3 2000/04/15 15:09:47 kls Exp $
+ * $Id: tools.h 1.7 2000/04/24 15:01:49 kls Exp $
*/
#ifndef __TOOLS_H
#define __TOOLS_H
+#include <errno.h>
#include <stdio.h>
#include <syslog.h>
+#include <sys/wait.h>
+#include <sys/types.h>
extern int SysLogLevel;
@@ -23,18 +26,23 @@ extern int SysLogLevel;
#define LOG_ERROR_STR(s) esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
#define SECSINDAY 86400
+#define MAXPROCESSTIMEOUT 3 // seconds
#define DELETENULL(p) (delete (p), p = NULL)
+bool DataAvailable(int filedes);
void writechar(int filedes, char c);
void writeint(int filedes, int n);
char readchar(int filedes);
bool readint(int filedes, int &n);
+void purge(int filedes);
char *readline(FILE *f);
int time_ms(void);
void delay_ms(int ms);
bool MakeDirs(const char *FileName, bool IsDirectory = false);
bool RemoveFileOrDir(const char *FileName);
+bool CheckProcess(pid_t pid);
+void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
class cListObject {
private:
diff --git a/vdr.c b/vdr.c
new file mode 100644
index 0000000..cbc0950
--- /dev/null
+++ b/vdr.c
@@ -0,0 +1,189 @@
+/*
+ * vdr.c: Video Disk Recorder main program
+ *
+ * Copyright (C) 2000 Klaus Schmidinger
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * The author can be reached at kls@cadsoft.de
+ *
+ * The project's page is at http://www.cadsoft.de/people/kls/vdr
+ *
+ * $Id: vdr.c 1.12 2000/04/24 13:36:39 kls Exp $
+ */
+
+#include <signal.h>
+#include "config.h"
+#include "interface.h"
+#include "menu.h"
+#include "recording.h"
+#include "tools.h"
+
+#ifdef DEBUG_REMOTE
+#define KEYS_CONF "keys-pc.conf"
+#else
+#define KEYS_CONF "keys.conf"
+#endif
+
+#define DIRECTCHANNELTIMEOUT 500 //ms
+
+static int Interrupted = 0;
+
+void SignalHandler(int signum)
+{
+ Interrupted = signum;
+}
+
+int main(int argc, char *argv[])
+{
+ openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
+ isyslog(LOG_INFO, "started");
+
+ Channels.Load("channels.conf");
+ Timers.Load("timers.conf");
+ if (!Keys.Load(KEYS_CONF))
+ Interface.LearnKeys();
+ Interface.Init();
+
+ cChannel::SwitchTo(CurrentChannel);
+
+ if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
+ if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN);
+ if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
+
+ cMenuMain *Menu = NULL;
+ cReplayDisplay *ReplayDisplay = NULL;
+ cTimer *Timer = NULL;
+ int dcTime = 0, dcNumber = 0;
+ int LastChannel = -1;
+
+ while (!Interrupted) {
+ // Channel display:
+ if (CurrentChannel != LastChannel) {
+ if (!Menu && !ReplayDisplay) {
+ cChannel *channel = Channels.Get(CurrentChannel);
+ if (channel)
+ Interface.DisplayChannel(CurrentChannel + 1, channel->name);
+ }
+ LastChannel = CurrentChannel;
+ }
+ // Direct Channel Select (action):
+ if (dcNumber) {
+ Interface.DisplayChannel(dcNumber);
+ if (time_ms() - dcTime > DIRECTCHANNELTIMEOUT) {
+ cChannel::SwitchTo(dcNumber - 1);
+ dcNumber = 0;
+ LastChannel = -1; // in case an invalid channel number was entered!
+ }
+ }
+ // Timer Processing:
+ else {
+ AssertFreeDiskSpace();
+ if (!Timer && (Timer = cTimer::GetMatch()) != NULL) {
+ DELETENULL(Menu);
+ DELETENULL(ReplayDisplay);
+ // make sure the timer won't be deleted:
+ Timer->SetRecording(true);
+ // switch to channel:
+ cChannel::SwitchTo(Timer->channel - 1);
+ // start recording:
+ cRecording Recording(Timer);
+ DvbApi.StartRecord(Recording.FileName());
+ }
+ if (Timer && !Timer->Matches()) {
+ // stop recording:
+ DvbApi.StopRecord();
+ // release timer:
+ Timer->SetRecording(false);
+ // clear single event timer:
+ if (Timer->IsSingleEvent()) {
+ DELETENULL(Menu); // must make sure no menu uses it
+ isyslog(LOG_INFO, "deleting timer %d", Timer->Index() + 1);
+ Timers.Del(Timer);
+ Timers.Save();
+ }
+ Timer = NULL;
+ }
+ }
+ // User Input:
+ eKeys key = Interface.GetKey(!ReplayDisplay);
+ if (Menu) {
+ switch (Menu->ProcessKey(key)) {
+ default: if (key != kMenu)
+ break;
+ case osBack:
+ case osEnd: DELETENULL(Menu);
+ break;
+ }
+ }
+ else if (!ReplayDisplay || (key = ReplayDisplay->ProcessKey(key)) != kNone) {
+ switch (key) {
+ // Direct Channel Select (input):
+ case k0: case k1: case k2: case k3: case k4: case k5: case k6: case k7: case k8: case k9:
+ {
+ if (!(DvbApi.Recording() || DvbApi.Replaying())) {
+ dcNumber = dcNumber * 10 + key - k0;
+ dcTime = time_ms();
+ }
+ }
+ // Record/Replay Control:
+ case kBegin: DvbApi.Skip(-INT_MAX); break;
+ case kRecord: if (!(DvbApi.Recording() || DvbApi.Replaying())) {
+ cTimer *timer = new cTimer(true);
+ Timers.Add(timer);
+ Timers.Save();
+ }
+ break;
+ case kPause: DvbApi.PauseReplay(); break;
+ case kStop: DELETENULL(ReplayDisplay);
+ DvbApi.StopReplay();
+ break;
+ case kSearchBack: DvbApi.FastRewind(); break;
+ case kSearchForward: DvbApi.FastForward(); break;
+ case kSkipBack: DvbApi.Skip(-60); break;
+ case kSkipForward: DvbApi.Skip(60); break;
+ // Menu Control:
+ case kMenu: DELETENULL(ReplayDisplay);
+ Menu = new cMenuMain;
+ Menu->Display();
+ break;
+ // Up/Down Channel Select:
+ case kUp:
+ case kDown: if (!(DvbApi.Recording() || DvbApi.Replaying())) {
+ int n = CurrentChannel + (key == kUp ? 1 : -1);
+ cChannel *channel = Channels.Get(n);
+ if (channel)
+ channel->Switch();
+ }
+ break;
+ // Viewing Control:
+ case kOk: if (ReplayDisplay)
+ DELETENULL(ReplayDisplay);
+ else if (DvbApi.Replaying())
+ ReplayDisplay = new cReplayDisplay;
+ else
+ LastChannel = -1; break; // forces channel display
+ default: break;
+ }
+ }
+ }
+ isyslog(LOG_INFO, "caught signal %d", Interrupted);
+ DvbApi.StopRecord();
+ DvbApi.StopReplay();
+ isyslog(LOG_INFO, "exiting");
+ closelog();
+ return 0;
+}