mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Version 0.61
- When scrolling through a list it now moves a full page up or down when the cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!). - Added missing '#include <sys/stat.h>' to recording.c. - The video directory can now be defined with the command line option -v. - There can now be more than one video directory (in case you have several disks). - Fixed learning key codes for PC keyboard. - New command line option '-l' to set the log level. - Times in timers.conf are now always printed with 4 digits (leading '0'). - Slow forward/back mode (thanks to Guido Fiala!). - The "Up" key in replay mode no longer restarts replay at the very beginning, but rather resumes normal replay mode after a "pause", "forward" or "backward" operation. Use the "Skip -60s" function repeatedly to go back to the beginning of the recording. - Improved reaction on user input in fast/slow forward/back modes. - No more upper limit for the value of 'Pnr'. - Checking if the video card is really a DVB card. - New SVDRP command UPDT to update an existing timer (or add a new one if it doesn't yet exist). - New version of the 'epg2timers' tool (with a modified channel list). - Bugfix in closing window in DEBUG_OSD mode.
This commit is contained in:
parent
1d22145c42
commit
9b40577867
@ -5,7 +5,14 @@ Carsten Koch <Carsten.Koch@icem.de>
|
|||||||
for making the 'Recordings' menu be listed alphabetically
|
for making the 'Recordings' menu be listed alphabetically
|
||||||
for implementing the 'Summary' feature
|
for implementing the 'Summary' feature
|
||||||
for adding the 'epg2timers' tool (see Tools/epg2timers)
|
for adding the 'epg2timers' tool (see Tools/epg2timers)
|
||||||
|
for his idea of using multiple disks (and for testing this feature)
|
||||||
|
|
||||||
Plamen Ganev <pganev@com-it.net>
|
Plamen Ganev <pganev@com-it.net>
|
||||||
for fixing the frequency offset for Hotbird channels
|
for fixing the frequency offset for Hotbird channels
|
||||||
for adding the 'xtvrc2vdr' tool (see Tools/xtvrc2vdr)
|
for adding the 'xtvrc2vdr' tool (see Tools/xtvrc2vdr)
|
||||||
|
|
||||||
|
Heino Goldenstein <heino.goldenstein@microplex.de>
|
||||||
|
for modifying scrolling through lists to make it page up and down
|
||||||
|
|
||||||
|
Guido Fiala <gfiala@s.netic.de>
|
||||||
|
for implementing slow forward/back
|
||||||
|
24
HISTORY
24
HISTORY
@ -99,3 +99,27 @@ Video Disk Recorder Revision History
|
|||||||
pressing "Ok". The summary field can only be filled in directly by editing
|
pressing "Ok". The summary field can only be filled in directly by editing
|
||||||
the 'timers.conf' file with a text editor, or by defining/modifying the timer
|
the 'timers.conf' file with a text editor, or by defining/modifying the timer
|
||||||
via the SVDRP interface.
|
via the SVDRP interface.
|
||||||
|
|
||||||
|
2000-08-06: Version 0.61
|
||||||
|
|
||||||
|
- When scrolling through a list it now moves a full page up or down when the
|
||||||
|
cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!).
|
||||||
|
- Added missing '#include <sys/stat.h>' to recording.c.
|
||||||
|
- The video directory can now be defined with the command line option -v.
|
||||||
|
- There can now be more than one video directory (in case you have several
|
||||||
|
disks).
|
||||||
|
- Fixed learning key codes for PC keyboard.
|
||||||
|
- New command line option '-l' to set the log level.
|
||||||
|
- Times in timers.conf are now always printed with 4 digits (leading '0').
|
||||||
|
- Slow forward/back mode (thanks to Guido Fiala!).
|
||||||
|
- The "Up" key in replay mode no longer restarts replay at the very beginning,
|
||||||
|
but rather resumes normal replay mode after a "pause", "forward" or "backward"
|
||||||
|
operation. Use the "Skip -60s" function repeatedly to go back to the beginning
|
||||||
|
of the recording.
|
||||||
|
- Improved reaction on user input in fast/slow forward/back modes.
|
||||||
|
- No more upper limit for the value of 'Pnr'.
|
||||||
|
- Checking if the video card is really a DVB card.
|
||||||
|
- New SVDRP command UPDT to update an existing timer (or add a new one if it
|
||||||
|
doesn't yet exist).
|
||||||
|
- New version of the 'epg2timers' tool (with a modified channel list).
|
||||||
|
- Bugfix in closing window in DEBUG_OSD mode.
|
||||||
|
40
INSTALL
40
INSTALL
@ -49,6 +49,9 @@ If the program shall run as a daemon, use the --daemon option. This
|
|||||||
will completely detach it from the terminal and will continue as a
|
will completely detach it from the terminal and will continue as a
|
||||||
background process.
|
background process.
|
||||||
|
|
||||||
|
Command line options:
|
||||||
|
---------------------
|
||||||
|
|
||||||
Use "vdr --help" for a list of available command line options.
|
Use "vdr --help" for a list of available command line options.
|
||||||
|
|
||||||
The video data directory:
|
The video data directory:
|
||||||
@ -57,14 +60,41 @@ The video data directory:
|
|||||||
All recordings are written into directories below "/video". Please
|
All recordings are written into directories below "/video". Please
|
||||||
make sure this directory exists, and that the user who runs the 'vdr'
|
make sure this directory exists, and that the user who runs the 'vdr'
|
||||||
program has read and write access to that directory.
|
program has read and write access to that directory.
|
||||||
If you prefer a different location for your video files, you can change
|
If you prefer a different location for your video files, you can use
|
||||||
the value of 'BaseDir' in recording.c.
|
the '-v' option to change that.
|
||||||
|
|
||||||
Note that the file system need not be 64-bit proof, since the 'vdr'
|
Note that the file system need not be 64-bit proof, since the 'vdr'
|
||||||
program splits video files into chunks of about 1GB. You should use
|
program splits video files into chunks of about 1GB. You should use
|
||||||
a disk with several gigabytes of free space. One GB can store roughly
|
a disk with several gigabytes of free space. One GB can store roughly
|
||||||
half an hour of video data.
|
half an hour of video data.
|
||||||
|
|
||||||
|
If you have more than one disk and don't want to combine them to form
|
||||||
|
one large logical volume, you can set up several video directories as
|
||||||
|
mount points for these disks. All of these directories must have the
|
||||||
|
same basic name and must end with a numeric part, which starts at 0 for
|
||||||
|
the main directory and has increasing values for the rest of the
|
||||||
|
directories. For example
|
||||||
|
|
||||||
|
/video0
|
||||||
|
/video1
|
||||||
|
/video2
|
||||||
|
|
||||||
|
would be a setup with three directories. You can use more than one
|
||||||
|
numeric digit, and the directories need not be directly under '/':
|
||||||
|
|
||||||
|
/mnt/MyVideos/vdr.00
|
||||||
|
/mnt/MyVideos/vdr.01
|
||||||
|
/mnt/MyVideos/vdr.02
|
||||||
|
...
|
||||||
|
/mnt/MyVideos/vdr.11
|
||||||
|
|
||||||
|
would set up twelve disks (wow, what a machine that would be!).
|
||||||
|
|
||||||
|
To use such a multi directory setup, you need to add the '-v' option
|
||||||
|
with the name of the basic directory when running 'vdr':
|
||||||
|
|
||||||
|
vdr -v /video0
|
||||||
|
|
||||||
Configuration files:
|
Configuration files:
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@ -78,6 +108,12 @@ The meaning of the data entries may still vary in future releases,
|
|||||||
so for the moment please look at the source code (config.c) to see
|
so for the moment please look at the source code (config.c) to see
|
||||||
the meaning of the various fields.
|
the meaning of the various fields.
|
||||||
|
|
||||||
|
The files that come with this package contain the author's selections,
|
||||||
|
so please make sure you adapt these to your personal taste. Also make sure
|
||||||
|
that the channels defined in 'channels.conf' are correct before attempting
|
||||||
|
to record anything. Channel parameters may vary and not all of the channels
|
||||||
|
listed in the default 'channels.conf' file have been verified by the author.
|
||||||
|
|
||||||
Learning the remote control keys:
|
Learning the remote control keys:
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
11
MANUAL
11
MANUAL
@ -10,7 +10,7 @@ Video Disk Recorder User's Manual
|
|||||||
|
|
||||||
Key Normal Main Channels Timer Edit/New Recordings Replay
|
Key Normal Main Channels Timer Edit/New Recordings Replay
|
||||||
|
|
||||||
Up Ch up Crsr up Crsr up Crsr up Crsr up Crsr up Begin
|
Up Ch up Crsr up Crsr up Crsr up Crsr up Crsr up Play
|
||||||
Down Ch down Crsr down Crsr down Crsr down Crsr down Crsr down Pause
|
Down Ch down Crsr down Crsr down Crsr down Crsr down Crsr down Pause
|
||||||
Left - - - Disable Decrement - Search back
|
Left - - - Disable Decrement - Search back
|
||||||
Right - - - Enable Increment - Search forward
|
Right - - - Enable Increment - Search forward
|
||||||
@ -93,15 +93,16 @@ Video Disk Recorder User's Manual
|
|||||||
|
|
||||||
The following keys have the listed meaning in Replay mode:
|
The following keys have the listed meaning in Replay mode:
|
||||||
|
|
||||||
- Up Positions to beginning of the recording and starts playback
|
- Up Resumes normal replay from any "pause", "forward" or "backward"
|
||||||
from there.
|
mode.
|
||||||
- Down Halts playback at the current position. Press again to continue
|
- Down Halts playback at the current position. Press again to continue
|
||||||
playback.
|
playback.
|
||||||
- Blue Stops playback and stores the current position, so that
|
- Blue Stops playback and stores the current position, so that
|
||||||
playback can be resumed later at that point.
|
playback can be resumed later at that point.
|
||||||
- Left
|
- Left
|
||||||
Right Runs playback forward or backward at a higher speed. Press
|
Right Runs playback forward or backward at a higher speed; press
|
||||||
again to resume normal speed.
|
again to resume normal speed. If in Pause mode, runs forward or
|
||||||
|
backward at a slower speed; press again to return to pause mode.
|
||||||
- Green
|
- Green
|
||||||
Yellow Skips about 60 seconds back or forward.
|
Yellow Skips about 60 seconds back or forward.
|
||||||
- Ok Brings up the replay progress display, which shows the date,
|
- Ok Brings up the replay progress display, which shows the date,
|
||||||
|
11
Makefile
11
Makefile
@ -4,9 +4,9 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Makefile 1.5 2000/07/23 11:57:14 kls Exp $
|
# $Id: Makefile 1.6 2000/07/28 14:37:44 kls Exp $
|
||||||
|
|
||||||
OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o
|
OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o videodir.o
|
||||||
|
|
||||||
ifndef REMOTE
|
ifndef REMOTE
|
||||||
REMOTE = KBD
|
REMOTE = KBD
|
||||||
@ -24,15 +24,16 @@ endif
|
|||||||
all: vdr
|
all: vdr
|
||||||
|
|
||||||
config.o : config.c config.h dvbapi.h interface.h tools.h
|
config.o : config.c config.h dvbapi.h interface.h tools.h
|
||||||
dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h
|
dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h videodir.h
|
||||||
interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h
|
interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h
|
||||||
menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
|
menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
|
||||||
osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h
|
osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h
|
||||||
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h
|
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h videodir.h
|
||||||
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h
|
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h videodir.h
|
||||||
remote.o : remote.c remote.h tools.h
|
remote.o : remote.c remote.h tools.h
|
||||||
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
|
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
|
||||||
tools.o : tools.c tools.h
|
tools.o : tools.c tools.h
|
||||||
|
videodir.o : videodir.c tools.h videodir.h
|
||||||
|
|
||||||
vdr: $(OBJS)
|
vdr: $(OBJS)
|
||||||
g++ -g -O2 $(OBJS) -lncurses -o vdr
|
g++ -g -O2 $(OBJS) -lncurses -o vdr
|
||||||
|
@ -34,21 +34,46 @@ static const char channel_line[] = "\t\t\t<tr><td bgcolor=\"#002b64\" align=c
|
|||||||
static const char title_line[] = "\t\t\t\t<td bgcolor=\"#002b64\" align=left width=100%><span id=\"fb-w10\">";
|
static const char title_line[] = "\t\t\t\t<td bgcolor=\"#002b64\" align=left width=100%><span id=\"fb-w10\">";
|
||||||
static const char summary_line[] = "\t\t\t<table border=0 cellpadding=10 cellspacing=0 bgcolor=\"white\" width=100%>";
|
static const char summary_line[] = "\t\t\t<table border=0 cellpadding=10 cellspacing=0 bgcolor=\"white\" width=100%>";
|
||||||
static const char * const channel_names[] =
|
static const char * const channel_names[] =
|
||||||
{"RTL", "SAT1", "PRO7", "RTL2", "ARD", "BR3", "HR3", "NDR", "SWF", "WDR", "BR Alpha", "SWR BW", "Phoenix",
|
{
|
||||||
"ZDF", "3sat", "Kinderkanal", "ARTE", "phoenix", "ORF Sat", "ZDF.info", "CNN", "Super RTL", "VOX", "DW TV",
|
"3sat",
|
||||||
"Kabel1", "TM3", "DSF", "HOT", "BloombergTV", "Sky News", "KinderNet", "Alice", "n-tv", "Grand Tour.", "TW1",
|
"ARTE",
|
||||||
"Eins Extra", "Eins Festival", "Eins MuXx", "MDR", "ORB", "B1", "ARD Online-Kanal", "Premiere World Promo",
|
"*B1 Berlin",
|
||||||
"Premiere", "Star Kino", "Cine Action", "Cine Comedy", "Sci Fantasy", "Romantic Movies", "Studio Universal",
|
"BR3",
|
||||||
"TV Niepokalanow", "Mosaico", "Andalucia TV", "TVC Internacional", "Nasza TV", "WishLine test", "Pro 7 Austria",
|
"Bloomberg TV",
|
||||||
"Kabel 1 Schweiz", "Kabel 1 Austria", "Pro 7 Schweiz", "Kiosque", "KTO", "TCM", "Cartoon Network France & Spain",
|
"BR Alpha",
|
||||||
"TVBS Europe", "TVBS Europe", "Travel", "TCM Espania", "MTV Spain", "TCM France", "RTL2 CH",
|
"CNN",
|
||||||
"La Cinquieme", "ARTE", "Post Filial TV", "Canal Canaris", "Canal Canaris", "Canal Canaris", "Canal Canaris",
|
"ARD",
|
||||||
"AB Sat Passion promo", "AB Channel 1", "Taquilla 0", "CSAT", "Mosaique", "Mosaique 2", "Mosaique 3", "Le Sesame C+",
|
"*DW-tv",
|
||||||
"FEED", "RTM 1", "ESC 1", "TV5 Europe", "TV7 Tunisia", "ARTE", "RAI Uno", "RTP International",
|
"Eins Extra",
|
||||||
"Fashion TV", "VideoService", "Beta Research promo", "Canal Canarias", "TVC International", "Fitur", "Astra Info 1",
|
"Eins Festival",
|
||||||
"Astra Info 2", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1", "Astra Vision 1",
|
"Eins MuXx",
|
||||||
"Astra Vision 1", "Astra Vision 1", "RTL Tele Letzebuerg", "Astra Mosaic", "MHP test", "Bloomberg TV Spain",
|
"euroNEWS",
|
||||||
"Video Italia", "AC 3 promo", ""
|
"HR3",
|
||||||
|
"Kabel1",
|
||||||
|
"Kinderkanal",
|
||||||
|
"MDR",
|
||||||
|
"MTV",
|
||||||
|
"NDR",
|
||||||
|
"NTV",
|
||||||
|
"ORB",
|
||||||
|
"*ORF1",
|
||||||
|
"Phoenix",
|
||||||
|
"PRO7",
|
||||||
|
"RTL",
|
||||||
|
"RTL2",
|
||||||
|
"SAT1",
|
||||||
|
"skynews",
|
||||||
|
"SWF",
|
||||||
|
"Super RTL",
|
||||||
|
"TM3",
|
||||||
|
"TW1",
|
||||||
|
"VOX",
|
||||||
|
"WDR",
|
||||||
|
"Theaterkanal",
|
||||||
|
"ZDF",
|
||||||
|
"ZDF.doku",
|
||||||
|
"ZDF.info",
|
||||||
|
""
|
||||||
};
|
};
|
||||||
static const int month_lengths[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
static const int month_lengths[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
@ -240,3 +265,4 @@ main()
|
|||||||
read_summary(summary);
|
read_summary(summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,4 +113,3 @@ MHP test:12604:h:1:22000:5632:8191:0:0
|
|||||||
Bloomberg TV Spain:12610:v:1:22000:45:49:0:0
|
Bloomberg TV Spain:12610:v:1:22000:45:49:0:0
|
||||||
Video Italia:12610:v:1:22000:121:122:0:0
|
Video Italia:12610:v:1:22000:121:122:0:0
|
||||||
AC 3 promo:12670:v:1:22000:308:256:0:0
|
AC 3 promo:12670:v:1:22000:308:256:0:0
|
||||||
Rtlneu:12188:h:1:27500:163:104:0:0
|
|
||||||
|
15
config.c
15
config.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.c 1.15 2000/07/25 16:21:20 kls Exp $
|
* $Id: config.c 1.17 2000/08/06 12:27:38 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -294,7 +294,7 @@ cTimer& cTimer::operator= (const cTimer &Timer)
|
|||||||
|
|
||||||
const char *cTimer::ToText(cTimer *Timer)
|
const char *cTimer::ToText(cTimer *Timer)
|
||||||
{
|
{
|
||||||
asprintf(&buffer, "%d:%d:%s:%d:%d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
|
asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,3 +473,14 @@ cChannels Channels;
|
|||||||
|
|
||||||
cTimers Timers;
|
cTimers Timers;
|
||||||
|
|
||||||
|
cTimer *cTimers::GetTimer(cTimer *Timer)
|
||||||
|
{
|
||||||
|
cTimer *ti = (cTimer *)First();
|
||||||
|
while (ti) {
|
||||||
|
if (ti->channel == Timer->channel && ti->day == Timer->day && ti->start == Timer->start && ti->stop == Timer->stop)
|
||||||
|
return ti;
|
||||||
|
ti = (cTimer *)ti->Next();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
10
config.h
10
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.h 1.11 2000/07/23 17:17:10 kls Exp $
|
* $Id: config.h 1.14 2000/08/06 12:22:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H
|
#ifndef __CONFIG_H
|
||||||
@ -17,6 +17,8 @@
|
|||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
|
#define VDRVERSION "0.61"
|
||||||
|
|
||||||
#define MaxBuffer 10000
|
#define MaxBuffer 10000
|
||||||
|
|
||||||
enum eKeys { // "Up" and "Down" must be the first two keys!
|
enum eKeys { // "Up" and "Down" must be the first two keys!
|
||||||
@ -181,7 +183,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class cChannels : public cConfig<cChannel> {};
|
class cChannels : public cConfig<cChannel> {};
|
||||||
class cTimers : public cConfig<cTimer> {};
|
|
||||||
|
class cTimers : public cConfig<cTimer> {
|
||||||
|
public:
|
||||||
|
cTimer *GetTimer(cTimer *Timer);
|
||||||
|
};
|
||||||
|
|
||||||
extern int CurrentChannel;
|
extern int CurrentChannel;
|
||||||
|
|
||||||
|
256
dvbapi.c
256
dvbapi.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: dvbapi.c 1.15 2000/07/21 13:18:02 kls Exp $
|
* $Id: dvbapi.c 1.22 2000/08/06 14:06:14 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#define VIDEODEVICE "/dev/video"
|
#define VIDEODEVICE "/dev/video"
|
||||||
|
|
||||||
@ -50,9 +51,12 @@
|
|||||||
// 'signed'), so let's use 1GB for absolute safety (the actual file size
|
// 'signed'), so let's use 1GB for absolute safety (the actual file size
|
||||||
// may be slightly higher because we stop recording only before the next
|
// may be slightly higher because we stop recording only before the next
|
||||||
// 'I' frame, to have a complete Group Of Pictures):
|
// 'I' frame, to have a complete Group Of Pictures):
|
||||||
#define MAXVIDEOFILESIZE (1024*1024*1024)
|
#define MAXVIDEOFILESIZE (1024*1024*1024) // Byte
|
||||||
#define MAXFILESPERRECORDING 255
|
#define MAXFILESPERRECORDING 255
|
||||||
|
|
||||||
|
#define MINFREEDISKSPACE (512) // MB
|
||||||
|
#define DISKCHECKINTERVAL 100 // seconds
|
||||||
|
|
||||||
#define INDEXFILESUFFIX "/index.vdr"
|
#define INDEXFILESUFFIX "/index.vdr"
|
||||||
#define RESUMEFILESUFFIX "/resume.vdr"
|
#define RESUMEFILESUFFIX "/resume.vdr"
|
||||||
#define RECORDFILESUFFIX "/%03d.vdr"
|
#define RECORDFILESUFFIX "/%03d.vdr"
|
||||||
@ -341,9 +345,8 @@ protected:
|
|||||||
int Free(void) { return ((tail >= head) ? size + head - tail : head - tail) - 1; }
|
int Free(void) { return ((tail >= head) ? size + head - tail : head - tail) - 1; }
|
||||||
int Available(void) { return (tail >= head) ? tail - head : size - head + tail; }
|
int Available(void) { return (tail >= head) ? tail - head : size - head + tail; }
|
||||||
int Readable(void) { return (tail >= head) ? size - tail - (head ? 0 : 1) : head - tail - 1; } // keep a 1 byte gap!
|
int Readable(void) { return (tail >= head) ? size - tail - (head ? 0 : 1) : head - tail - 1; } // keep a 1 byte gap!
|
||||||
int Writeable(void) { return (tail > head) ? tail - head : size - head; }
|
int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
|
||||||
int Byte(int Offset);
|
int Byte(int Offset);
|
||||||
bool WaitForOutFile(int Timeout);
|
|
||||||
public:
|
public:
|
||||||
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
||||||
virtual ~cRingBuffer();
|
virtual ~cRingBuffer();
|
||||||
@ -404,22 +407,6 @@ void cRingBuffer::Skip(int n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cRingBuffer::WaitForOutFile(int Timeout)
|
|
||||||
{
|
|
||||||
fd_set set;
|
|
||||||
FD_ZERO(&set);
|
|
||||||
FD_SET(*outFile, &set);
|
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = 0;
|
|
||||||
timeout.tv_usec = Timeout;
|
|
||||||
if (select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0) {
|
|
||||||
if (FD_ISSET(*outFile, &set))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
esyslog(LOG_ERR, "ERROR: timeout in WaitForOutFile(%d)", Timeout);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cRingBuffer::Read(int Max)
|
int cRingBuffer::Read(int Max)
|
||||||
{
|
{
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
@ -598,6 +585,8 @@ private:
|
|||||||
int recordFile;
|
int recordFile;
|
||||||
uchar tagAudio, tagVideo;
|
uchar tagAudio, tagVideo;
|
||||||
bool ok, synced;
|
bool ok, synced;
|
||||||
|
time_t lastDiskSpaceCheck;
|
||||||
|
bool RunningLowOnDiskSpace(void);
|
||||||
int Synchronize(void);
|
int Synchronize(void);
|
||||||
bool NextFile(void);
|
bool NextFile(void);
|
||||||
virtual int Write(int Max = -1);
|
virtual int Write(int Max = -1);
|
||||||
@ -615,6 +604,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
recordFile = -1;
|
recordFile = -1;
|
||||||
tagAudio = tagVideo = 0;
|
tagAudio = tagVideo = 0;
|
||||||
ok = synced = false;
|
ok = synced = false;
|
||||||
|
lastDiskSpaceCheck = time(NULL);
|
||||||
if (!fileName)
|
if (!fileName)
|
||||||
return;//XXX find a better way???
|
return;//XXX find a better way???
|
||||||
// Find the highest existing file suffix:
|
// Find the highest existing file suffix:
|
||||||
@ -636,7 +626,20 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
cRecordBuffer::~cRecordBuffer()
|
cRecordBuffer::~cRecordBuffer()
|
||||||
{
|
{
|
||||||
if (recordFile >= 0)
|
if (recordFile >= 0)
|
||||||
close(recordFile);
|
CloseVideoFile(recordFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
||||||
|
{
|
||||||
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||||
|
uint Free = FreeDiskSpaceMB(fileName);
|
||||||
|
lastDiskSpaceCheck = time(NULL);
|
||||||
|
if (Free < MINFREEDISKSPACE) {
|
||||||
|
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRecordBuffer::Synchronize(void)
|
int cRecordBuffer::Synchronize(void)
|
||||||
@ -714,20 +717,22 @@ int cRecordBuffer::Synchronize(void)
|
|||||||
|
|
||||||
bool cRecordBuffer::NextFile(void)
|
bool cRecordBuffer::NextFile(void)
|
||||||
{
|
{
|
||||||
if (recordFile >= 0 && fileSize > MAXVIDEOFILESIZE && pictureType == I_FRAME) {
|
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||||
if (close(recordFile) < 0)
|
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
|
||||||
LOG_ERROR;
|
if (CloseVideoFile(recordFile) < 0)
|
||||||
// don't return 'false', maybe we can still record into the next file
|
LOG_ERROR;
|
||||||
recordFile = -1;
|
// don't return 'false', maybe we can still record into the next file
|
||||||
fileNumber++;
|
recordFile = -1;
|
||||||
if (fileNumber == 0)
|
fileNumber++;
|
||||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
if (fileNumber == 0)
|
||||||
fileSize = 0;
|
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
||||||
|
fileSize = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (recordFile < 0) {
|
if (recordFile < 0) {
|
||||||
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
||||||
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
||||||
recordFile = open(fileName, O_RDWR | O_CREAT | O_NONBLOCK, S_IRUSR | S_IWUSR);
|
recordFile = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
|
||||||
if (recordFile < 0) {
|
if (recordFile < 0) {
|
||||||
LOG_ERROR;
|
LOG_ERROR;
|
||||||
return false;
|
return false;
|
||||||
@ -781,7 +786,7 @@ int cRecordBuffer::WriteWithTimeout(bool EndIfEmpty)
|
|||||||
|
|
||||||
// --- cReplayBuffer ---------------------------------------------------------
|
// --- cReplayBuffer ---------------------------------------------------------
|
||||||
|
|
||||||
enum eReplayMode { rmPlay, rmFastForward, rmFastRewind };
|
enum eReplayMode { rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
|
||||||
|
|
||||||
class cReplayBuffer : public cFileBuffer {
|
class cReplayBuffer : public cFileBuffer {
|
||||||
private:
|
private:
|
||||||
@ -790,6 +795,7 @@ private:
|
|||||||
eReplayMode mode;
|
eReplayMode mode;
|
||||||
bool skipAudio;
|
bool skipAudio;
|
||||||
int lastIndex;
|
int lastIndex;
|
||||||
|
int brakeCounter;
|
||||||
void SkipAudioBlocks(void);
|
void SkipAudioBlocks(void);
|
||||||
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
||||||
void Close(void);
|
void Close(void);
|
||||||
@ -811,6 +817,7 @@ cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
|||||||
fileOffset = 0;
|
fileOffset = 0;
|
||||||
replayFile = -1;
|
replayFile = -1;
|
||||||
mode = rmPlay;
|
mode = rmPlay;
|
||||||
|
brakeCounter = 0;
|
||||||
skipAudio = false;
|
skipAudio = false;
|
||||||
lastIndex = -1;
|
lastIndex = -1;
|
||||||
if (!fileName)
|
if (!fileName)
|
||||||
@ -841,6 +848,7 @@ void cReplayBuffer::SetMode(eReplayMode Mode)
|
|||||||
{
|
{
|
||||||
mode = Mode;
|
mode = Mode;
|
||||||
skipAudio = Mode != rmPlay;
|
skipAudio = Mode != rmPlay;
|
||||||
|
brakeCounter = 0;
|
||||||
if (mode != rmPlay)
|
if (mode != rmPlay)
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
@ -974,6 +982,10 @@ int cReplayBuffer::Read(int Max = -1)
|
|||||||
if (Index >= 0) {
|
if (Index >= 0) {
|
||||||
uchar FileNumber;
|
uchar FileNumber;
|
||||||
int FileOffset, Length;
|
int FileOffset, Length;
|
||||||
|
if (mode == rmSlowRewind && (brakeCounter++ % 24) != 0) {
|
||||||
|
// show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
|
||||||
|
Index = index->GetNextIFrame(Index, true, &FileNumber, &FileOffset, &Length); // jump ahead one frame
|
||||||
|
}
|
||||||
Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
|
Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
|
||||||
if (Index >= 0) {
|
if (Index >= 0) {
|
||||||
if (!NextFile(FileNumber, FileOffset))
|
if (!NextFile(FileNumber, FileOffset))
|
||||||
@ -1014,28 +1026,27 @@ int cReplayBuffer::Read(int Max = -1)
|
|||||||
int cReplayBuffer::Write(int Max)
|
int cReplayBuffer::Write(int Max)
|
||||||
{
|
{
|
||||||
int Written = 0;
|
int Written = 0;
|
||||||
|
int Av = Available();
|
||||||
do {
|
if (skipAudio) {
|
||||||
if (skipAudio) {
|
SkipAudioBlocks();
|
||||||
SkipAudioBlocks();
|
Max = GetAvPesLength();
|
||||||
Max = GetAvPesLength();
|
fileOffset += Av - Available();
|
||||||
}
|
}
|
||||||
while (Max) {
|
if (Max) {
|
||||||
int w = cFileBuffer::Write(Max);
|
int w;
|
||||||
if (w >= 0) {
|
do {
|
||||||
fileOffset += w;
|
w = cFileBuffer::Write(Max);
|
||||||
Written += w;
|
if (w >= 0) {
|
||||||
if (Max < 0)
|
fileOffset += w;
|
||||||
break;
|
Written += w;
|
||||||
Max -= w;
|
if (Max < 0)
|
||||||
}
|
break;
|
||||||
else
|
Max -= w;
|
||||||
return w;
|
|
||||||
//XXX??? Why does the buffer get empty here???
|
|
||||||
if (Empty() || !WaitForOutFile(1000000))
|
|
||||||
return Written;
|
|
||||||
}
|
}
|
||||||
} while (skipAudio && Available());
|
else
|
||||||
|
return w;
|
||||||
|
} while (Max > 0); // we MUST write this entire AV_PES block
|
||||||
|
}
|
||||||
return Written;
|
return Written;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1076,7 +1087,7 @@ cDvbApi::~cDvbApi()
|
|||||||
{
|
{
|
||||||
if (videoDev >= 0) {
|
if (videoDev >= 0) {
|
||||||
Close();
|
Close();
|
||||||
StopReplay();
|
Stop();
|
||||||
StopRecord();
|
StopRecord();
|
||||||
close(videoDev);
|
close(videoDev);
|
||||||
}
|
}
|
||||||
@ -1119,9 +1130,13 @@ bool cDvbApi::Init(void)
|
|||||||
dsyslog(LOG_INFO, "probing %s", fileName);
|
dsyslog(LOG_INFO, "probing %s", fileName);
|
||||||
int f = open(fileName, O_RDWR);
|
int f = open(fileName, O_RDWR);
|
||||||
if (f >= 0) {
|
if (f >= 0) {
|
||||||
|
struct video_capability cap;
|
||||||
|
int r = ioctl(f, VIDIOCGCAP, &cap);
|
||||||
close(f);
|
close(f);
|
||||||
dvbApi[i] = new cDvbApi(fileName);
|
if (r == 0 && (cap.type & VID_TYPE_DVB)) {
|
||||||
NumDvbApis++;
|
dvbApi[i] = new cDvbApi(fileName);
|
||||||
|
NumDvbApis++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (errno != ENODEV)
|
if (errno != ENODEV)
|
||||||
@ -1136,10 +1151,12 @@ bool cDvbApi::Init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrimaryDvbApi = dvbApi[0];
|
PrimaryDvbApi = dvbApi[0];
|
||||||
if (NumDvbApis > 0)
|
if (NumDvbApis > 0) {
|
||||||
isyslog(LOG_INFO, "found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
|
isyslog(LOG_INFO, "found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
|
||||||
else
|
} // need braces because of isyslog-macro
|
||||||
|
else {
|
||||||
esyslog(LOG_ERR, "ERROR: no video device found, giving up!");
|
esyslog(LOG_ERR, "ERROR: no video device found, giving up!");
|
||||||
|
}
|
||||||
return NumDvbApis > 0;
|
return NumDvbApis > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,7 +1239,10 @@ void cDvbApi::Open(int w, int h)
|
|||||||
void cDvbApi::Close(void)
|
void cDvbApi::Close(void)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_OSD
|
#ifdef DEBUG_OSD
|
||||||
delwin(window);
|
if (window) {
|
||||||
|
delwin(window);
|
||||||
|
window = 0;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
Cmd(OSD_Close);
|
Cmd(OSD_Close);
|
||||||
#endif
|
#endif
|
||||||
@ -1372,7 +1392,7 @@ bool cDvbApi::StartRecord(const char *FileName)
|
|||||||
}
|
}
|
||||||
if (videoDev >= 0) {
|
if (videoDev >= 0) {
|
||||||
|
|
||||||
StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
|
Stop(); // TODO: remove this if the driver is able to do record and replay at the same time
|
||||||
|
|
||||||
// Check FileName:
|
// Check FileName:
|
||||||
|
|
||||||
@ -1494,7 +1514,7 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
|
|||||||
esyslog(LOG_ERR, "ERROR: StartReplay() called while recording - ignored!");
|
esyslog(LOG_ERR, "ERROR: StartReplay() called while recording - ignored!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
StopReplay();
|
Stop();
|
||||||
if (videoDev >= 0) {
|
if (videoDev >= 0) {
|
||||||
|
|
||||||
lastProgress = lastTotal = -1;
|
lastProgress = lastTotal = -1;
|
||||||
@ -1565,49 +1585,69 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
|
|||||||
}
|
}
|
||||||
if (FD_ISSET(fromMain, &setIn)) {
|
if (FD_ISSET(fromMain, &setIn)) {
|
||||||
switch (readchar(fromMain)) {
|
switch (readchar(fromMain)) {
|
||||||
case dvbStop: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
case dvbStop: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
Buffer->Stop(); break;
|
Buffer->Stop();
|
||||||
case dvbPauseReplay: SetReplayMode(Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
|
break;
|
||||||
Paused = !Paused;
|
case dvbPause: SetReplayMode(Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
|
||||||
if (FastForward || FastRewind) {
|
Paused = !Paused;
|
||||||
SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
if (FastForward || FastRewind) {
|
||||||
Buffer->Clear();
|
SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
}
|
Buffer->Clear();
|
||||||
FastForward = FastRewind = false;
|
}
|
||||||
|
FastForward = FastRewind = false;
|
||||||
|
Buffer->SetMode(rmPlay);
|
||||||
|
break;
|
||||||
|
case dvbPlay: if (FastForward || FastRewind || Paused) {
|
||||||
|
SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
|
SetReplayMode(VID_PLAY_NORMAL);
|
||||||
|
FastForward = FastRewind = Paused = false;
|
||||||
Buffer->SetMode(rmPlay);
|
Buffer->SetMode(rmPlay);
|
||||||
break;
|
}
|
||||||
case dvbFastForward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
break;
|
||||||
|
case dvbForward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
|
Buffer->Clear();
|
||||||
|
FastForward = !FastForward;
|
||||||
|
FastRewind = false;
|
||||||
|
if (Paused) {
|
||||||
|
Buffer->SetMode(rmPlay);
|
||||||
|
SetReplayMode(FastForward ? VID_PLAY_SLOW_MOTION : VID_PLAY_PAUSE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SetReplayMode(VID_PLAY_NORMAL);
|
SetReplayMode(VID_PLAY_NORMAL);
|
||||||
FastForward = !FastForward;
|
|
||||||
FastRewind = Paused = false;
|
|
||||||
Buffer->Clear();
|
|
||||||
Buffer->SetMode(FastForward ? rmFastForward : rmPlay);
|
Buffer->SetMode(FastForward ? rmFastForward : rmPlay);
|
||||||
break;
|
}
|
||||||
case dvbFastRewind: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
break;
|
||||||
|
case dvbBackward: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
|
Buffer->Clear();
|
||||||
|
FastRewind = !FastRewind;
|
||||||
|
FastForward = false;
|
||||||
|
if (Paused) {
|
||||||
|
Buffer->SetMode(FastRewind ? rmSlowRewind : rmPlay);
|
||||||
|
SetReplayMode(FastRewind ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SetReplayMode(VID_PLAY_NORMAL);
|
SetReplayMode(VID_PLAY_NORMAL);
|
||||||
FastRewind = !FastRewind;
|
|
||||||
FastForward = Paused = false;
|
|
||||||
Buffer->Clear();
|
|
||||||
Buffer->SetMode(FastRewind ? rmFastRewind : rmPlay);
|
Buffer->SetMode(FastRewind ? rmFastRewind : rmPlay);
|
||||||
break;
|
|
||||||
case dvbSkip: {
|
|
||||||
int Seconds;
|
|
||||||
if (readint(fromMain, Seconds)) {
|
|
||||||
SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
|
||||||
SetReplayMode(VID_PLAY_NORMAL);
|
|
||||||
FastForward = FastRewind = Paused = false;
|
|
||||||
Buffer->SetMode(rmPlay);
|
|
||||||
Buffer->SkipSeconds(Seconds);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case dvbGetIndex: {
|
case dvbSkip: {
|
||||||
int Current, Total;
|
int Seconds;
|
||||||
Buffer->GetIndex(Current, Total);
|
if (readint(fromMain, Seconds)) {
|
||||||
writeint(toMain, Current);
|
SetReplayMode(VID_PLAY_CLEAR_BUFFER);
|
||||||
writeint(toMain, Total);
|
SetReplayMode(VID_PLAY_NORMAL);
|
||||||
}
|
FastForward = FastRewind = Paused = false;
|
||||||
break;
|
Buffer->SetMode(rmPlay);
|
||||||
|
Buffer->SkipSeconds(Seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case dvbGetIndex: {
|
||||||
|
int Current, Total;
|
||||||
|
Buffer->GetIndex(Current, Total);
|
||||||
|
writeint(toMain, Current);
|
||||||
|
writeint(toMain, Total);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1633,7 +1673,7 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::StopReplay(void)
|
void cDvbApi::Stop(void)
|
||||||
{
|
{
|
||||||
if (pidReplay) {
|
if (pidReplay) {
|
||||||
writechar(toReplay, dvbStop);
|
writechar(toReplay, dvbStop);
|
||||||
@ -1646,22 +1686,28 @@ void cDvbApi::StopReplay(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::PauseReplay(void)
|
void cDvbApi::Pause(void)
|
||||||
{
|
{
|
||||||
if (pidReplay)
|
if (pidReplay)
|
||||||
writechar(toReplay, dvbPauseReplay);
|
writechar(toReplay, dvbPause);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::FastForward(void)
|
void cDvbApi::Play(void)
|
||||||
{
|
{
|
||||||
if (pidReplay)
|
if (pidReplay)
|
||||||
writechar(toReplay, dvbFastForward);
|
writechar(toReplay, dvbPlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::FastRewind(void)
|
void cDvbApi::Forward(void)
|
||||||
{
|
{
|
||||||
if (pidReplay)
|
if (pidReplay)
|
||||||
writechar(toReplay, dvbFastRewind);
|
writechar(toReplay, dvbForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDvbApi::Backward(void)
|
||||||
|
{
|
||||||
|
if (pidReplay)
|
||||||
|
writechar(toReplay, dvbBackward);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::Skip(int Seconds)
|
void cDvbApi::Skip(int Seconds)
|
||||||
|
19
dvbapi.h
19
dvbapi.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: dvbapi.h 1.11 2000/06/24 14:03:57 kls Exp $
|
* $Id: dvbapi.h 1.12 2000/07/30 15:01:01 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __DVBAPI_H
|
#ifndef __DVBAPI_H
|
||||||
@ -104,9 +104,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
enum { dvbStop = 1, // let's not have 0 as a command
|
enum { dvbStop = 1, // let's not have 0 as a command
|
||||||
dvbPauseReplay,
|
dvbPause,
|
||||||
dvbFastForward,
|
dvbPlay,
|
||||||
dvbFastRewind,
|
dvbForward,
|
||||||
|
dvbBackward,
|
||||||
dvbSkip,
|
dvbSkip,
|
||||||
dvbGetIndex,
|
dvbGetIndex,
|
||||||
};
|
};
|
||||||
@ -136,13 +137,15 @@ public:
|
|||||||
// If there is already a replay session active, it will be stopped
|
// If there is already a replay session active, it will be stopped
|
||||||
// and the new file will be played back.
|
// and the new file will be played back.
|
||||||
// If provided Title will be used in the progress display.
|
// If provided Title will be used in the progress display.
|
||||||
void StopReplay(void);
|
void Stop(void);
|
||||||
// Stops the current replay session (if any).
|
// Stops the current replay session (if any).
|
||||||
void PauseReplay(void);
|
void Pause(void);
|
||||||
// Pauses the current replay session, or resumes a paused session.
|
// Pauses the current replay session, or resumes a paused session.
|
||||||
void FastForward(void);
|
void Play(void);
|
||||||
|
// Resumes normal replay mode.
|
||||||
|
void Forward(void);
|
||||||
// Runs the current replay session forward at a higher speed.
|
// Runs the current replay session forward at a higher speed.
|
||||||
void FastRewind(void);
|
void Backward(void);
|
||||||
// Runs the current replay session backwards at a higher speed.
|
// Runs the current replay session backwards at a higher speed.
|
||||||
void Skip(int Seconds);
|
void Skip(int Seconds);
|
||||||
// Skips the given number of seconds in the current replay session.
|
// Skips the given number of seconds in the current replay session.
|
||||||
|
16
menu.c
16
menu.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: menu.c 1.20 2000/07/24 16:25:53 kls Exp $
|
* $Id: menu.c 1.22 2000/08/06 07:02:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
@ -512,7 +512,7 @@ cMenuEditChannel::cMenuEditChannel(int Index)
|
|||||||
Add(new cMenuEditIntItem( "Vpid", &data.vpid, 0, 10000)); //TODO exact limits???
|
Add(new cMenuEditIntItem( "Vpid", &data.vpid, 0, 10000)); //TODO exact limits???
|
||||||
Add(new cMenuEditIntItem( "Apid", &data.apid, 0, 10000)); //TODO exact limits???
|
Add(new cMenuEditIntItem( "Apid", &data.apid, 0, 10000)); //TODO exact limits???
|
||||||
Add(new cMenuEditIntItem( "CA", &data.ca, 0, cDvbApi::NumDvbApis));
|
Add(new cMenuEditIntItem( "CA", &data.ca, 0, cDvbApi::NumDvbApis));
|
||||||
Add(new cMenuEditIntItem( "Pnr", &data.pnr, 0, 10000)); //TODO exact limits???
|
Add(new cMenuEditIntItem( "Pnr", &data.pnr, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,7 +1243,7 @@ cReplayControl::cReplayControl(void)
|
|||||||
cReplayControl::~cReplayControl()
|
cReplayControl::~cReplayControl()
|
||||||
{
|
{
|
||||||
Hide();
|
Hide();
|
||||||
dvbApi->StopReplay();
|
dvbApi->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cReplayControl::SetRecording(const char *FileName, const char *Title)
|
void cReplayControl::SetRecording(const char *FileName, const char *Title)
|
||||||
@ -1278,13 +1278,13 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
|
|||||||
if (visible)
|
if (visible)
|
||||||
shown = dvbApi->ShowProgress(!shown) || shown;
|
shown = dvbApi->ShowProgress(!shown) || shown;
|
||||||
switch (Key) {
|
switch (Key) {
|
||||||
case kUp: dvbApi->Skip(-INT_MAX); break;
|
case kUp: dvbApi->Play(); break;
|
||||||
case kDown: dvbApi->PauseReplay(); break;
|
case kDown: dvbApi->Pause(); break;
|
||||||
case kBlue: Hide();
|
case kBlue: Hide();
|
||||||
dvbApi->StopReplay();
|
dvbApi->Stop();
|
||||||
return osEnd;
|
return osEnd;
|
||||||
case kLeft: dvbApi->FastRewind(); break;
|
case kLeft: dvbApi->Backward(); break;
|
||||||
case kRight: dvbApi->FastForward(); break;
|
case kRight: dvbApi->Forward(); break;
|
||||||
case kGreen: dvbApi->Skip(-60); break;
|
case kGreen: dvbApi->Skip(-60); break;
|
||||||
case kYellow: dvbApi->Skip(60); break;
|
case kYellow: dvbApi->Skip(60); break;
|
||||||
case kMenu: Hide(); return osMenu; // allow direct switching to menu
|
case kMenu: Hide(); return osMenu; // allow direct switching to menu
|
||||||
|
22
osd.c
22
osd.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: osd.c 1.4 2000/04/24 09:44:31 kls Exp $
|
* $Id: osd.c 1.5 2000/07/26 17:35:09 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "osd.h"
|
#include "osd.h"
|
||||||
@ -166,14 +166,20 @@ void cOsdMenu::CursorUp(void)
|
|||||||
{
|
{
|
||||||
if (current > 0) {
|
if (current > 0) {
|
||||||
DisplayCurrent(false);
|
DisplayCurrent(false);
|
||||||
if (--current < first) {
|
if (current == first) {
|
||||||
first -= MAXOSDITEMS;
|
first -= MAXOSDITEMS;
|
||||||
if (first < 0)
|
if (first < 0)
|
||||||
first = 0;
|
first = 0;
|
||||||
|
if (current - MAXOSDITEMS > 0)
|
||||||
|
current -= MAXOSDITEMS;
|
||||||
|
else
|
||||||
|
current--;
|
||||||
Display();
|
Display();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
current--;
|
||||||
DisplayCurrent(true);
|
DisplayCurrent(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,14 +188,20 @@ void cOsdMenu::CursorDown(void)
|
|||||||
int count = Count();
|
int count = Count();
|
||||||
if (current < count - 1) {
|
if (current < count - 1) {
|
||||||
DisplayCurrent(false);
|
DisplayCurrent(false);
|
||||||
if (++current >= first + MAXOSDITEMS) {
|
if (current == first + MAXOSDITEMS - 1) {
|
||||||
first += MAXOSDITEMS;
|
first += MAXOSDITEMS;
|
||||||
if (first > count - MAXOSDITEMS)
|
if (first > count - MAXOSDITEMS)
|
||||||
first = count - MAXOSDITEMS;
|
first = count - MAXOSDITEMS;
|
||||||
|
if (current + MAXOSDITEMS < count)
|
||||||
|
current += MAXOSDITEMS;
|
||||||
|
else
|
||||||
|
current++;
|
||||||
Display();
|
Display();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
current++;
|
||||||
DisplayCurrent(true);
|
DisplayCurrent(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
recording.c
49
recording.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.c 1.12 2000/07/24 16:31:07 kls Exp $
|
* $Id: recording.c 1.15 2000/07/29 14:08:17 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -13,9 +13,11 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#define RECEXT ".rec"
|
#define RECEXT ".rec"
|
||||||
#define DELEXT ".del"
|
#define DELEXT ".del"
|
||||||
@ -24,40 +26,12 @@
|
|||||||
|
|
||||||
#define SUMMARYFILESUFFIX "/summary.vdr"
|
#define SUMMARYFILESUFFIX "/summary.vdr"
|
||||||
|
|
||||||
#define FINDCMD "find %s -type d -name '%s' | sort -df"
|
#define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
|
||||||
|
|
||||||
#define DFCMD "df -m %s"
|
|
||||||
#define MINDISKSPACE 1024 // MB
|
#define MINDISKSPACE 1024 // MB
|
||||||
|
|
||||||
#define DISKCHECKDELTA 300 // seconds between checks for free disk space
|
#define DISKCHECKDELTA 300 // seconds between checks for free disk space
|
||||||
|
|
||||||
const char *BaseDir = "/video";
|
|
||||||
|
|
||||||
static bool LowDiskSpace(void)
|
|
||||||
{
|
|
||||||
//TODO Find a simpler way to determine the amount of free disk space!
|
|
||||||
bool result = true;
|
|
||||||
char *cmd = NULL;
|
|
||||||
asprintf(&cmd, DFCMD, BaseDir);
|
|
||||||
FILE *p = popen(cmd, "r");
|
|
||||||
if (p) {
|
|
||||||
char *s;
|
|
||||||
while ((s = readline(p)) != NULL) {
|
|
||||||
if (*s == '/') {
|
|
||||||
int available;
|
|
||||||
sscanf(s, "%*s %*d %*d %d", &available);
|
|
||||||
result = available < MINDISKSPACE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pclose(p);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
|
|
||||||
delete cmd;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssertFreeDiskSpace(void)
|
void AssertFreeDiskSpace(void)
|
||||||
{
|
{
|
||||||
// With every call to this function we try to actually remove
|
// With every call to this function we try to actually remove
|
||||||
@ -65,7 +39,7 @@ void AssertFreeDiskSpace(void)
|
|||||||
// it will get removed during the next call.
|
// it will get removed during the next call.
|
||||||
static time_t LastFreeDiskCheck = 0;
|
static time_t LastFreeDiskCheck = 0;
|
||||||
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
|
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
|
||||||
if (LowDiskSpace()) {
|
if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
|
||||||
// Remove the oldest file that has been "deleted":
|
// Remove the oldest file that has been "deleted":
|
||||||
cRecordings Recordings;
|
cRecordings Recordings;
|
||||||
if (Recordings.Load(true)) {
|
if (Recordings.Load(true)) {
|
||||||
@ -123,7 +97,7 @@ cRecording::cRecording(const char *FileName)
|
|||||||
{
|
{
|
||||||
titleBuffer = NULL;
|
titleBuffer = NULL;
|
||||||
fileName = strdup(FileName);
|
fileName = strdup(FileName);
|
||||||
FileName += strlen(BaseDir) + 1;
|
FileName += strlen(VideoDirectory) + 1;
|
||||||
char *p = strrchr(FileName, '/');
|
char *p = strrchr(FileName, '/');
|
||||||
|
|
||||||
name = NULL;
|
name = NULL;
|
||||||
@ -189,7 +163,7 @@ const char *cRecording::FileName(void)
|
|||||||
{
|
{
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
struct tm *t = localtime(&start);
|
struct tm *t = localtime(&start);
|
||||||
asprintf(&fileName, NAMEFORMAT, BaseDir, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime);
|
asprintf(&fileName, NAMEFORMAT, VideoDirectory, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime);
|
||||||
if (fileName)
|
if (fileName)
|
||||||
strreplace(fileName, ' ', '_');
|
strreplace(fileName, ' ', '_');
|
||||||
}
|
}
|
||||||
@ -239,10 +213,7 @@ bool cRecording::Delete(void)
|
|||||||
if (strcmp(ext, RECEXT) == 0) {
|
if (strcmp(ext, RECEXT) == 0) {
|
||||||
strncpy(ext, DELEXT, strlen(ext));
|
strncpy(ext, DELEXT, strlen(ext));
|
||||||
isyslog(LOG_INFO, "deleting recording %s", FileName());
|
isyslog(LOG_INFO, "deleting recording %s", FileName());
|
||||||
if (rename(FileName(), NewName) == -1) {
|
result = RenameVideoFile(FileName(), NewName);
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName(), strerror(errno));
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete NewName;
|
delete NewName;
|
||||||
return result;
|
return result;
|
||||||
@ -251,7 +222,7 @@ bool cRecording::Delete(void)
|
|||||||
bool cRecording::Remove(void)
|
bool cRecording::Remove(void)
|
||||||
{
|
{
|
||||||
isyslog(LOG_INFO, "removing recording %s", FileName());
|
isyslog(LOG_INFO, "removing recording %s", FileName());
|
||||||
return RemoveFileOrDir(FileName());
|
return RemoveVideoFile(FileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cRecordings -----------------------------------------------------------
|
// --- cRecordings -----------------------------------------------------------
|
||||||
@ -261,7 +232,7 @@ bool cRecordings::Load(bool Deleted)
|
|||||||
Clear();
|
Clear();
|
||||||
bool result = false;
|
bool result = false;
|
||||||
char *cmd = NULL;
|
char *cmd = NULL;
|
||||||
asprintf(&cmd, FINDCMD, BaseDir, Deleted ? "*" DELEXT : "*" RECEXT);
|
asprintf(&cmd, FINDCMD, VideoDirectory, Deleted ? "*" DELEXT : "*" RECEXT);
|
||||||
FILE *p = popen(cmd, "r");
|
FILE *p = popen(cmd, "r");
|
||||||
if (p) {
|
if (p) {
|
||||||
char *s;
|
char *s;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.h 1.7 2000/07/23 19:06:14 kls Exp $
|
* $Id: recording.h 1.9 2000/07/28 13:53:54 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RECORDING_H
|
#ifndef __RECORDING_H
|
||||||
|
8
remote.c
8
remote.c
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
|
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
|
||||||
*
|
*
|
||||||
* $Id: remote.c 1.10 2000/07/15 16:34:35 kls Exp $
|
* $Id: remote.c 1.11 2000/07/29 16:23:47 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
@ -71,7 +71,11 @@ void cRcIoKBD::Flush(int WaitSeconds)
|
|||||||
bool cRcIoKBD::InputAvailable(bool Wait)
|
bool cRcIoKBD::InputAvailable(bool Wait)
|
||||||
{
|
{
|
||||||
timeout(Wait ? 1000 : 10);
|
timeout(Wait ? 1000 : 10);
|
||||||
return true;//XXX
|
int ch = getch();
|
||||||
|
if (ch == ERR)
|
||||||
|
return false;
|
||||||
|
ungetch(ch);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *)
|
bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *)
|
||||||
|
57
svdrp.c
57
svdrp.c
@ -10,7 +10,7 @@
|
|||||||
* and interact with the Video Disk Recorder - or write a full featured
|
* and interact with the Video Disk Recorder - or write a full featured
|
||||||
* graphical interface that sits on top of an SVDRP connection.
|
* graphical interface that sits on top of an SVDRP connection.
|
||||||
*
|
*
|
||||||
* $Id: svdrp.c 1.2 2000/07/24 16:43:51 kls Exp $
|
* $Id: svdrp.c 1.4 2000/08/06 12:52:04 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -145,7 +145,12 @@ const char *HelpPages[] = {
|
|||||||
" by the LSTC command.",
|
" by the LSTC command.",
|
||||||
"NEWT <settings>\n"
|
"NEWT <settings>\n"
|
||||||
" Create a new timer. Settings must be in the same format as returned\n"
|
" Create a new timer. Settings must be in the same format as returned\n"
|
||||||
" by the LSTT command.",
|
" by the LSTT command. It is an error if a timer with the same channel,\n"
|
||||||
|
" day, start and stop time already exists.",
|
||||||
|
"UPDT <settings>\n"
|
||||||
|
" Updates a timer. Settings must be in the same format as returned\n"
|
||||||
|
" by the LSTT command. If a timer with the same channel, day, start\n"
|
||||||
|
" and stop time does not yet exists, it will be created.",
|
||||||
"QUIT\n"
|
"QUIT\n"
|
||||||
" Exit vdr (SVDRP).\n"
|
" Exit vdr (SVDRP).\n"
|
||||||
" You can also hit Ctrl-D to exit.",
|
" You can also hit Ctrl-D to exit.",
|
||||||
@ -369,7 +374,7 @@ void cSVDRP::CmdHelp(const char *Option)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Reply(-214, "This is VDR version 0.6"); //XXX dynamically insert version number
|
Reply(-214, "This is VDR version %s", VDRVERSION);
|
||||||
Reply(-214, "Topics:");
|
Reply(-214, "Topics:");
|
||||||
const char **hp = HelpPages;
|
const char **hp = HelpPages;
|
||||||
while (*hp) {
|
while (*hp) {
|
||||||
@ -548,13 +553,48 @@ void cSVDRP::CmdNewt(const char *Option)
|
|||||||
if (*Option) {
|
if (*Option) {
|
||||||
cTimer *timer = new cTimer;
|
cTimer *timer = new cTimer;
|
||||||
if (timer->Parse(Option)) {
|
if (timer->Parse(Option)) {
|
||||||
Timers.Add(timer);
|
cTimer *t = Timers.GetTimer(timer);
|
||||||
Timers.Save();
|
if (!t) {
|
||||||
isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
|
Timers.Add(timer);
|
||||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
|
Timers.Save();
|
||||||
|
isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
|
||||||
|
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Reply(550, "Timer already defined: %d %s", t->Index() + 1, t->ToText());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Reply(501, "Error in timer settings");
|
Reply(501, "Error in timer settings");
|
||||||
|
delete timer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Reply(501, "Missing timer settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSVDRP::CmdUpdt(const char *Option)
|
||||||
|
{
|
||||||
|
if (*Option) {
|
||||||
|
cTimer *timer = new cTimer;
|
||||||
|
if (timer->Parse(Option)) {
|
||||||
|
cTimer *t = Timers.GetTimer(timer);
|
||||||
|
if (t) {
|
||||||
|
t->Parse(Option);
|
||||||
|
delete timer;
|
||||||
|
timer = t;
|
||||||
|
isyslog(LOG_INFO, "timer %d updated", timer->Index() + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Timers.Add(timer);
|
||||||
|
isyslog(LOG_INFO, "timer %d added", timer->Index() + 1);
|
||||||
|
}
|
||||||
|
Timers.Save();
|
||||||
|
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Reply(501, "Error in timer settings");
|
||||||
|
delete timer;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Reply(501, "Missing timer settings");
|
Reply(501, "Missing timer settings");
|
||||||
@ -583,6 +623,7 @@ void cSVDRP::Execute(char *Cmd)
|
|||||||
else if (CMD("MOVT")) CmdMovt(s);
|
else if (CMD("MOVT")) CmdMovt(s);
|
||||||
else if (CMD("NEWC")) CmdNewc(s);
|
else if (CMD("NEWC")) CmdNewc(s);
|
||||||
else if (CMD("NEWT")) CmdNewt(s);
|
else if (CMD("NEWT")) CmdNewt(s);
|
||||||
|
else if (CMD("UPDT")) CmdUpdt(s);
|
||||||
else if (CMD("QUIT")
|
else if (CMD("QUIT")
|
||||||
|| CMD("\x04")) Close();
|
|| CMD("\x04")) Close();
|
||||||
else Reply(500, "Command unrecognized: \"%s\"", Cmd);
|
else Reply(500, "Command unrecognized: \"%s\"", Cmd);
|
||||||
@ -598,7 +639,7 @@ void cSVDRP::Process(void)
|
|||||||
//TODO how can we get the *full* hostname?
|
//TODO how can we get the *full* hostname?
|
||||||
gethostname(buffer, sizeof(buffer));
|
gethostname(buffer, sizeof(buffer));
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
Reply(220, "%s SVDRP VideoDiskRecorder 0.6; %s", buffer, ctime(&now));//XXX dynamically insert version number
|
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", VDRVERSION, buffer, ctime(&now));
|
||||||
}
|
}
|
||||||
int rbytes = readstring(filedes, buffer, sizeof(buffer) - 1);
|
int rbytes = readstring(filedes, buffer, sizeof(buffer) - 1);
|
||||||
if (rbytes > 0) {
|
if (rbytes > 0) {
|
||||||
|
3
svdrp.h
3
svdrp.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: svdrp.h 1.1 2000/07/23 14:49:30 kls Exp $
|
* $Id: svdrp.h 1.2 2000/08/06 12:45:28 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SVDRP_H
|
#ifndef __SVDRP_H
|
||||||
@ -42,6 +42,7 @@ private:
|
|||||||
void CmdMovt(const char *Option);
|
void CmdMovt(const char *Option);
|
||||||
void CmdNewc(const char *Option);
|
void CmdNewc(const char *Option);
|
||||||
void CmdNewt(const char *Option);
|
void CmdNewt(const char *Option);
|
||||||
|
void CmdUpdt(const char *Option);
|
||||||
void Execute(char *Cmd);
|
void Execute(char *Cmd);
|
||||||
public:
|
public:
|
||||||
cSVDRP(int Port);
|
cSVDRP(int Port);
|
||||||
|
12
timers.conf
12
timers.conf
@ -1,9 +1,13 @@
|
|||||||
1:10:-T-----:2058:2202:99:10:Quarks:
|
0:10:-T-----:2058:2202:99:10:Quarks:
|
||||||
1:5:-T-----:2100:2205:99:10:RudisSuchmaschine:
|
0:5:-T-----:2100:2205:99:10:RudisSuchmaschine:
|
||||||
1:10:---T---:2158:2250:99:99:DiePlaneten:
|
1:10:---T---:2158:2250:99:99:DiePlaneten:
|
||||||
1:3:---T---:2211:2300:99:10:Switch:
|
0:3:---T---:2211:2300:99:10:Switch:
|
||||||
1:15:-----S-:1358:1435:99:7:Neues:
|
1:15:-----S-:1358:1435:99:7:Neues:
|
||||||
1:1:-----S-:1445:1600:99:30:Hammerman:
|
1:1:-----S-:1445:1610:99:30:Hammerman:
|
||||||
0:2:-----S-:2200:2350:99:30:Wochenshow:
|
0:2:-----S-:2200:2350:99:30:Wochenshow:
|
||||||
1:11:------S:2058:2120:99:10:Centauri:
|
1:11:------S:2058:2120:99:10:Centauri:
|
||||||
0:15:MTWTF--:1828:1901:10:5:nano:
|
0:15:MTWTF--:1828:1901:10:5:nano:
|
||||||
|
1:1:-TWTF--:0858:0940:99:99:Ellen:
|
||||||
|
1:2:----F--:2140:2225:10:10:WWW:
|
||||||
|
1:11:-----S-:2158:2235:99:99:Computer:
|
||||||
|
1:23:-----S-:2200:0020:99:99:BBC special:
|
||||||
|
77
tools.c
77
tools.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: tools.c 1.10 2000/07/23 13:16:54 kls Exp $
|
* $Id: tools.c 1.13 2000/07/29 18:41:45 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -145,6 +145,51 @@ bool isnumber(const char *s)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DFCMD "df -m %s"
|
||||||
|
|
||||||
|
uint FreeDiskSpaceMB(const char *Directory)
|
||||||
|
{
|
||||||
|
//TODO Find a simpler way to determine the amount of free disk space!
|
||||||
|
uint Free = 0;
|
||||||
|
char *cmd = NULL;
|
||||||
|
asprintf(&cmd, DFCMD, Directory);
|
||||||
|
FILE *p = popen(cmd, "r");
|
||||||
|
if (p) {
|
||||||
|
char *s;
|
||||||
|
while ((s = readline(p)) != NULL) {
|
||||||
|
if (*s == '/') {
|
||||||
|
uint available;
|
||||||
|
sscanf(s, "%*s %*d %*d %u", &available);
|
||||||
|
Free = available;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pclose(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
|
||||||
|
delete cmd;
|
||||||
|
return Free;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirectoryOk(const char *DirName, bool LogErrors)
|
||||||
|
{
|
||||||
|
struct stat ds;
|
||||||
|
if (stat(DirName, &ds) == 0) {
|
||||||
|
if (S_ISDIR(ds.st_mode)) {
|
||||||
|
if (access(DirName, R_OK | W_OK | X_OK) == 0)
|
||||||
|
return true;
|
||||||
|
else if (LogErrors)
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
|
||||||
|
}
|
||||||
|
else if (LogErrors)
|
||||||
|
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
|
||||||
|
}
|
||||||
|
else if (LogErrors)
|
||||||
|
LOG_ERROR_STR(DirName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MakeDirs(const char *FileName, bool IsDirectory)
|
bool MakeDirs(const char *FileName, bool IsDirectory)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
@ -157,7 +202,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
|||||||
*p = 0;
|
*p = 0;
|
||||||
struct stat fs;
|
struct stat fs;
|
||||||
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
||||||
isyslog(LOG_INFO, "creating directory %s", s);
|
dsyslog(LOG_INFO, "creating directory %s", s);
|
||||||
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
|
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
|
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
|
||||||
result = false;
|
result = false;
|
||||||
@ -173,7 +218,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveFileOrDir(const char *FileName)
|
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(FileName, &st) == 0) {
|
if (stat(FileName, &st) == 0) {
|
||||||
@ -185,23 +230,43 @@ bool RemoveFileOrDir(const char *FileName)
|
|||||||
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
||||||
char *buffer;
|
char *buffer;
|
||||||
asprintf(&buffer, "%s/%s", FileName, e->d_name);
|
asprintf(&buffer, "%s/%s", FileName, e->d_name);
|
||||||
|
if (FollowSymlinks) {
|
||||||
|
int size = strlen(buffer) * 2; // should be large enough
|
||||||
|
char *l = new char[size];
|
||||||
|
int n = readlink(buffer, l, size);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno != EINVAL)
|
||||||
|
LOG_ERROR_STR(buffer);
|
||||||
|
}
|
||||||
|
else if (n < size) {
|
||||||
|
l[n] = 0;
|
||||||
|
dsyslog(LOG_INFO, "removing %s", l);
|
||||||
|
if (remove(l) < 0)
|
||||||
|
LOG_ERROR_STR(l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
|
||||||
|
delete l;
|
||||||
|
}
|
||||||
|
dsyslog(LOG_INFO, "removing %s", buffer);
|
||||||
if (remove(buffer) < 0)
|
if (remove(buffer) < 0)
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", buffer, strerror(errno));
|
LOG_ERROR_STR(buffer);
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
LOG_ERROR_STR(FileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dsyslog(LOG_INFO, "removing %s", FileName);
|
||||||
if (remove(FileName) == 0)
|
if (remove(FileName) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
LOG_ERROR_STR(FileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
tools.h
6
tools.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: tools.h 1.10 2000/07/23 13:16:37 kls Exp $
|
* $Id: tools.h 1.12 2000/07/29 10:56:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TOOLS_H
|
#ifndef __TOOLS_H
|
||||||
@ -43,8 +43,10 @@ char *skipspace(char *s);
|
|||||||
int time_ms(void);
|
int time_ms(void);
|
||||||
void delay_ms(int ms);
|
void delay_ms(int ms);
|
||||||
bool isnumber(const char *s);
|
bool isnumber(const char *s);
|
||||||
|
uint FreeDiskSpaceMB(const char *Directory);
|
||||||
|
bool DirectoryOk(const char *DirName, bool LogErrors = false);
|
||||||
bool MakeDirs(const char *FileName, bool IsDirectory = false);
|
bool MakeDirs(const char *FileName, bool IsDirectory = false);
|
||||||
bool RemoveFileOrDir(const char *FileName);
|
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
|
||||||
bool CheckProcess(pid_t pid);
|
bool CheckProcess(pid_t pid);
|
||||||
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
|
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
|
||||||
|
|
||||||
|
59
vdr.c
59
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.23 2000/07/23 15:36:43 kls Exp $
|
* $Id: vdr.c 1.27 2000/07/29 19:01:57 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -36,6 +36,7 @@
|
|||||||
#include "recording.h"
|
#include "recording.h"
|
||||||
#include "svdrp.h"
|
#include "svdrp.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#ifdef REMOTE_KBD
|
#ifdef REMOTE_KBD
|
||||||
#define KEYS_CONF "keys-pc.conf"
|
#define KEYS_CONF "keys-pc.conf"
|
||||||
@ -64,38 +65,67 @@ int main(int argc, char *argv[])
|
|||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{ "daemon", no_argument, NULL, 'd' },
|
{ "daemon", no_argument, NULL, 'd' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "log", required_argument, NULL, 'l' },
|
||||||
{ "port", required_argument, NULL, 'p' },
|
{ "port", required_argument, NULL, 'p' },
|
||||||
|
{ "video", required_argument, NULL, 'v' },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
while ((c = getopt_long(argc, argv, "dhp:", long_options, &option_index)) != -1) {
|
while ((c = getopt_long(argc, argv, "dhl:p:v:", long_options, &option_index)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'd': DaemonMode = true; break;
|
case 'd': DaemonMode = true; break;
|
||||||
case 'h': printf("Usage: vdr [OPTION]\n\n"
|
case 'h': printf("Usage: vdr [OPTION]\n\n"
|
||||||
" -h, --help display this help and exit\n"
|
" -h, --help display this help and exit\n"
|
||||||
" -d, --daemon run in daemon mode\n"
|
" -d, --daemon run in daemon mode\n"
|
||||||
" -p PORT, --port=PORT use PORT for SVDRP ('0' turns off SVDRP)\n"
|
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
||||||
|
" 0 = no logging, 1 = errors only,\n"
|
||||||
|
" 2 = errors and info, 3 = errors, info and debug\n"
|
||||||
|
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
|
||||||
|
" 0 turns off SVDRP\n"
|
||||||
|
" -v DIR, --video=DIR use DIR as video directory (default is %s)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Report bugs to <vdr-bugs@cadsoft.de>\n"
|
"Report bugs to <vdr-bugs@cadsoft.de>\n",
|
||||||
|
DEFAULTSVDRPPORT,
|
||||||
|
VideoDirectory
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
case 'l': if (isnumber(optarg)) {
|
||||||
|
int l = atoi(optarg);
|
||||||
|
if (0 <= l && l <= 3) {
|
||||||
|
SysLogLevel = l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
case 'p': if (isnumber(optarg))
|
case 'p': if (isnumber(optarg))
|
||||||
SVDRPport = strtol(optarg, NULL, 10);
|
SVDRPport = atoi(optarg);
|
||||||
else {
|
else {
|
||||||
fprintf(stderr, "vdr: invalid port number: %s\n", optarg);
|
fprintf(stderr, "vdr: invalid port number: %s\n", optarg);
|
||||||
return 1;
|
abort();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'v': VideoDirectory = optarg;
|
||||||
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log file:
|
// Log file:
|
||||||
|
|
||||||
openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
|
if (SysLogLevel > 0)
|
||||||
|
openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
|
||||||
|
|
||||||
|
// Check the video directory:
|
||||||
|
|
||||||
|
if (!DirectoryOk(VideoDirectory, true)) {
|
||||||
|
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
// Daemon mode:
|
// Daemon mode:
|
||||||
|
|
||||||
@ -104,8 +134,8 @@ int main(int argc, char *argv[])
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
fprintf(stderr, "%s\n", strerror(errno));
|
fprintf(stderr, "%s\n", strerror(errno));
|
||||||
esyslog(LOG_ERR, strerror(errno));
|
esyslog(LOG_ERR, "ERROR: %s", strerror(errno));
|
||||||
return 1;
|
abort();
|
||||||
}
|
}
|
||||||
if (pid != 0)
|
if (pid != 0)
|
||||||
return 0; // initial program immediately returns
|
return 0; // initial program immediately returns
|
||||||
@ -117,12 +147,12 @@ int main(int argc, char *argv[])
|
|||||||
abort();
|
abort();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
isyslog(LOG_INFO, "started");
|
isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
|
||||||
|
|
||||||
// DVB interfaces:
|
// DVB interfaces:
|
||||||
|
|
||||||
if (!cDvbApi::Init())
|
if (!cDvbApi::Init())
|
||||||
return 1;
|
abort();
|
||||||
|
|
||||||
// Configuration data:
|
// Configuration data:
|
||||||
|
|
||||||
@ -241,6 +271,7 @@ int main(int argc, char *argv[])
|
|||||||
delete SVDRP;
|
delete SVDRP;
|
||||||
cDvbApi::Cleanup();
|
cDvbApi::Cleanup();
|
||||||
isyslog(LOG_INFO, "exiting");
|
isyslog(LOG_INFO, "exiting");
|
||||||
closelog();
|
if (SysLogLevel > 0)
|
||||||
|
closelog();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
182
videodir.c
Normal file
182
videodir.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* videodir.c: Functions to maintain a distributed video directory
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: videodir.c 1.1 2000/07/29 15:21:42 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "videodir.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
const char *VideoDirectory = "/video";
|
||||||
|
|
||||||
|
class cVideoDirectory {
|
||||||
|
private:
|
||||||
|
char *name, *stored, *adjusted;
|
||||||
|
int length, number, digits;
|
||||||
|
public:
|
||||||
|
cVideoDirectory(void);
|
||||||
|
~cVideoDirectory();
|
||||||
|
uint FreeMB(void);
|
||||||
|
const char *Name(void) { return name; }
|
||||||
|
const char *Stored(void) { return stored; }
|
||||||
|
int Length(void) { return length; }
|
||||||
|
bool IsDistributed(void) { return name != NULL; }
|
||||||
|
bool Next(void);
|
||||||
|
void Store(void);
|
||||||
|
const char *Adjust(const char *FileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
cVideoDirectory::cVideoDirectory(void)
|
||||||
|
{
|
||||||
|
length = strlen(VideoDirectory);
|
||||||
|
name = (VideoDirectory[length - 1] == '0') ? strdup(VideoDirectory) : NULL;
|
||||||
|
stored = adjusted = NULL;
|
||||||
|
number = -1;
|
||||||
|
digits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cVideoDirectory::~cVideoDirectory()
|
||||||
|
{
|
||||||
|
delete name;
|
||||||
|
delete stored;
|
||||||
|
delete adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint cVideoDirectory::FreeMB(void)
|
||||||
|
{
|
||||||
|
return FreeDiskSpaceMB(name ? name : VideoDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cVideoDirectory::Next(void)
|
||||||
|
{
|
||||||
|
if (name) {
|
||||||
|
if (number < 0) {
|
||||||
|
int l = length;
|
||||||
|
while (l-- > 0 && isdigit(name[l]))
|
||||||
|
;
|
||||||
|
l++;
|
||||||
|
digits = length - l;
|
||||||
|
int n = atoi(&name[l]);
|
||||||
|
if (n == 0)
|
||||||
|
number = n;
|
||||||
|
else
|
||||||
|
return false; // base video directory must end with zero
|
||||||
|
}
|
||||||
|
if (++number > 0) {
|
||||||
|
char buf[16];
|
||||||
|
if (sprintf(buf, "%0*d", digits, number) == digits) {
|
||||||
|
strcpy(&name[length - digits], buf);
|
||||||
|
return DirectoryOk(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cVideoDirectory::Store(void)
|
||||||
|
{
|
||||||
|
if (name) {
|
||||||
|
delete stored;
|
||||||
|
stored = strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cVideoDirectory::Adjust(const char *FileName)
|
||||||
|
{
|
||||||
|
if (stored) {
|
||||||
|
delete adjusted;
|
||||||
|
adjusted = strdup(FileName);
|
||||||
|
return strncpy(adjusted, stored, length);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenVideoFile(const char *FileName, int Flags)
|
||||||
|
{
|
||||||
|
const char *ActualFileName = FileName;
|
||||||
|
|
||||||
|
// Incoming name must be in base video directory:
|
||||||
|
if (strstr(FileName, VideoDirectory) != FileName) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: %s not in %s", FileName, VideoDirectory);
|
||||||
|
errno = ENOENT; // must set 'errno' - any ideas for a better value?
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Are we going to create a new file?
|
||||||
|
if ((Flags & O_CREAT) != 0) {
|
||||||
|
cVideoDirectory Dir;
|
||||||
|
if (Dir.IsDistributed()) {
|
||||||
|
// Find the directory with the most free space:
|
||||||
|
uint MaxFree = Dir.FreeMB();
|
||||||
|
while (Dir.Next()) {
|
||||||
|
uint Free = FreeDiskSpaceMB(Dir.Name());
|
||||||
|
if (Free > MaxFree) {
|
||||||
|
Dir.Store();
|
||||||
|
MaxFree = Free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Dir.Stored()) {
|
||||||
|
ActualFileName = Dir.Adjust(FileName);
|
||||||
|
if (!MakeDirs(ActualFileName, false))
|
||||||
|
return -1; // errno has been set by MakeDirs()
|
||||||
|
if (symlink(ActualFileName, FileName) < 0) {
|
||||||
|
LOG_ERROR_STR(FileName);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ActualFileName = strdup(ActualFileName); // must survive Dir!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int Result = open(ActualFileName, Flags, S_IRUSR | S_IWUSR);
|
||||||
|
if (ActualFileName != FileName)
|
||||||
|
delete ActualFileName;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseVideoFile(int FileHandle)
|
||||||
|
{
|
||||||
|
// just in case we ever decide to do something special when closing the file!
|
||||||
|
return close(FileHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenameVideoFile(const char *OldName, const char *NewName)
|
||||||
|
{
|
||||||
|
// Only the base video directory entry will be renamed, leaving the
|
||||||
|
// possible symlinks untouched. Going through all the symlinks and disks
|
||||||
|
// would be unnecessary work - maybe later...
|
||||||
|
if (rename(OldName, NewName) == -1) {
|
||||||
|
LOG_ERROR_STR(OldName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveVideoFile(const char *FileName)
|
||||||
|
{
|
||||||
|
return RemoveFileOrDir(FileName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoFileSpaceAvailable(unsigned int SizeMB)
|
||||||
|
{
|
||||||
|
cVideoDirectory Dir;
|
||||||
|
if (Dir.IsDistributed()) {
|
||||||
|
if (Dir.FreeMB() >= SizeMB * 2) // base directory needs additional space
|
||||||
|
return true;
|
||||||
|
while (Dir.Next()) {
|
||||||
|
if (Dir.FreeMB() >= SizeMB)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Dir.FreeMB() >= SizeMB;
|
||||||
|
}
|
21
videodir.h
Normal file
21
videodir.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* videodir.h: Functions to maintain a distributed video directory
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: videodir.h 1.1 2000/07/29 14:08:27 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __VIDEODIR_H
|
||||||
|
#define __VIDEODIR_H
|
||||||
|
|
||||||
|
extern const char *VideoDirectory;
|
||||||
|
|
||||||
|
int OpenVideoFile(const char *FileName, int Flags);
|
||||||
|
int CloseVideoFile(int FileHandle);
|
||||||
|
bool RenameVideoFile(const char *OldName, const char *NewName);
|
||||||
|
bool RemoveVideoFile(const char *FileName);
|
||||||
|
bool VideoFileSpaceAvailable(unsigned int SizeMB);
|
||||||
|
|
||||||
|
#endif //__VIDEODIR_H
|
Loading…
x
Reference in New Issue
Block a user