diff options
author | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-04-24 18:00:00 +0200 |
---|---|---|
committer | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-04-24 18:00:00 +0200 |
commit | 85b8e41e8bb16e4e66561768026456ec5f0ee276 (patch) | |
tree | dd52f93f8db69b395ed2170d641a3e9d574ed039 | |
parent | 37250a0568d5b1d689d5c59f983e9be228ed49c2 (diff) | |
download | vdr-patch-lnbsharing-85b8e41e8bb16e4e66561768026456ec5f0ee276.tar.gz vdr-patch-lnbsharing-85b8e41e8bb16e4e66561768026456ec5f0ee276.tar.bz2 |
Version 0.04vdr-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.
-rw-r--r-- | BUGS | 7 | ||||
-rw-r--r-- | HISTORY | 18 | ||||
-rw-r--r-- | INSTALL | 96 | ||||
-rw-r--r-- | MANUAL | 53 | ||||
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | README | 112 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | config.c | 5 | ||||
-rw-r--r-- | config.h | 4 | ||||
-rw-r--r-- | dvbapi.c | 171 | ||||
-rw-r--r-- | dvbapi.h | 21 | ||||
-rw-r--r-- | interface.c | 60 | ||||
-rw-r--r-- | interface.h | 15 | ||||
-rw-r--r-- | menu.c | 52 | ||||
-rw-r--r-- | menu.h | 13 | ||||
-rw-r--r-- | osd.c | 6 | ||||
-rw-r--r-- | osd.h | 10 | ||||
-rw-r--r-- | osm.c | 150 | ||||
-rw-r--r-- | recording.c | 40 | ||||
-rw-r--r-- | recording.h | 21 | ||||
-rw-r--r-- | remote.c | 47 | ||||
-rw-r--r-- | remote.h | 7 | ||||
-rw-r--r-- | timers.conf | 4 | ||||
-rw-r--r-- | tools.c | 60 | ||||
-rw-r--r-- | tools.h | 12 | ||||
-rw-r--r-- | vdr.c | 189 |
26 files changed, 748 insertions, 444 deletions
@@ -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? @@ -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. @@ -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. + @@ -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 @@ -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 @@ -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? ------------------ @@ -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. @@ -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; @@ -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 @@ -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; +} + @@ -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; @@ -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; +} + @@ -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 @@ -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; @@ -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); @@ -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> { @@ -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; @@ -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 @@ -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) @@ -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: @@ -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; +} |