summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS6
-rw-r--r--FORMATS23
-rw-r--r--HISTORY38
-rw-r--r--INSTALL17
-rw-r--r--MANUAL46
-rw-r--r--Makefile8
-rw-r--r--README2
-rw-r--r--TODO3
-rw-r--r--channels.conf291
-rw-r--r--config.c29
-rw-r--r--config.h73
-rw-r--r--dvb.c.071.diff100
-rw-r--r--dvbapi.c1533
-rw-r--r--dvbapi.h64
-rw-r--r--dvbosd.c12
-rw-r--r--dvbosd.h3
-rw-r--r--eit.c58
-rw-r--r--eit.h5
-rw-r--r--epg2html.pl96
-rw-r--r--i18n.c140
-rw-r--r--interface.c8
-rw-r--r--interface.h3
-rw-r--r--menu.c265
-rw-r--r--menu.h11
-rw-r--r--osd.h3
-rw-r--r--recording.c112
-rw-r--r--recording.h25
-rw-r--r--remote.c6
-rw-r--r--svdrp.c3
-rw-r--r--thread.c36
-rw-r--r--thread.h7
-rw-r--r--timers.conf14
-rw-r--r--tools.c119
-rw-r--r--tools.h33
-rw-r--r--vdr.c15
-rw-r--r--videodir.c19
-rw-r--r--videodir.h3
37 files changed, 2056 insertions, 1173 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 279706d..ee44ada 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -43,3 +43,9 @@ Matthias Schniedermeyer <ms@citd.de>
Miha Setina <mihasetina@softhome.net>
for translating the OSD texts to the Slovenian language.
+
+Alberto Carraro <bertocar@tin.it>
+ for translating the OSD texts to the Italian language.
+
+Deti Fliegl <deti@fliegl.de>
+ for implementing the 'CurrentChannel' setup parameter.
diff --git a/FORMATS b/FORMATS
index 3eee494..ed52518 100644
--- a/FORMATS
+++ b/FORMATS
@@ -90,3 +90,26 @@ Video Disk Recorder File Formats
CPU status : /usr/loval/bin/cpustatus 2>&1
Disk space : df -h | grep '/video' | awk '{ print 100 - $5 "% free"; }'
+* marks.vdr
+
+ This file (if present in a recording directory) contains the editing marks
+ defined for this recording.
+
+ Each line contains the definition of one mark in the following format:
+
+ hh:mm:ss.ff comment
+
+ where 'hh:mm:ss.ff' is a frame position within the recording, given as "hours,
+ minutes, seconds and (optional) frame number". 'comment' can be any string
+ and may be used to describe this mark. If present, 'comment' must be separated
+ from the frame position by at least one blank.
+
+ The lines in this file need not necessarily appear in the correct temporal
+ sequence, they will be automatically sorted by time index.
+
+ CURRENT RESTRICTIONS:
+
+ - the 'comment' is currently not used by VDR
+ - marks must have a frame number, and that frame MUST be an I-frame (this
+ means that only marks generated by VDR itself can be used, since they
+ will always be guaranteed to mark I-frames).
diff --git a/HISTORY b/HISTORY
index 485f24a..8d5e884 100644
--- a/HISTORY
+++ b/HISTORY
@@ -311,3 +311,41 @@ Video Disk Recorder Revision History
can receive a certain channel on the primary interface. This is currently in
an early state and may still cause some problems, but it appears to work nice
already.
+
+2001-01-18: Version 0.70
+
+- VDR now requires driver version 0.8.1 or higher.
+- Recordings are now saved in PES mode. Note that you now need to install the
+ driver *WITHOUT* 'outstream=0'! This is the default when you 'make insmod' in
+ the DVB/driver directory.
+ Old recordings (in AV_PES mode) can still be replayed (as long as the driver
+ still supports replaying AV_PES files). The only limitation with this is that
+ in fast forward/back mode the picture may be slightly distorted and there may
+ be sound fragments.
+- The EPG data is now dumped into the file /video/epg.data every ten minutes.
+ Use the Perl script 'epg2html.pl' to convert the raw EPG data into a simple
+ HTML programme listing.
+- Fixed handling of channel switching with the "Blue" button in the "What's on
+ now/next?" menus.
+- Fixed saving the MarginStop setup parameter.
+- Fixed missing initialization in cConfig.
+- Implemented "On Disk Editing".
+- There is no more default 'timers.conf' file.
+- Added Italian language texts (thanks to Alberto Carraro).
+- Fixed starting a replay session when the program is currently in "transfer
+ mode".
+- Fixed setting/modifying timers via SVDRP with empty summary fields.
+- Fixed a problem with recordings that have a single quote character in their
+ name (this is now mapped to 0x01).
+- Changed the value for Diseqc to '0' in the default 'channels.conf'.
+- Fixed displaying channels and recording status in the RCU's LED display when
+ a recording is interrupted due to higher priority.
+- Implemented safe writing of config files (first writes into a temporary file
+ and then renames it).
+- In case the video data stream is broken the log message will come only every
+ 5 seconds.
+- The current channel is now saved in the 'setup.conf' file when VDR is cancelled,
+ and will be restored next time it is started (thanks to Deti Fliegl).
+- The EIT scanning thread is now locked when switching channels to avoid problems.
+- Encrypted channels can now be selected even without knowing the PNR (however, it
+ is still necessary for the EPG info).
diff --git a/INSTALL b/INSTALL
index 7def168..e2a67a0 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,13 +15,11 @@ If you have the DVB driver source in a different location
you will have to change the definition of DVBDIR in the
Makefile.
-This program requires the card driver version 0.71 or higher
-to work properly. Currently you need to load the dvb.o module with
-option outstream=0, so your insmod statement should read
-'insmod dvb.o outstream=0'. This is necessary because 'vdr' works
-with AV_PES data and will change once it has been modified to work
-directly with MPEG2. You also need to apply the patch 'dvb.c.071.diff'
-for the On Screen Display to work properly.
+This program requires the card driver version 0.8.1 or higher
+to work properly. You need to load the dvb.o module *without* option
+'outstream=0' (previous versions of VDR required this option to have
+the driver supply the data in AV_PES format; as of version 0.70 VDR
+works with PES format).
After extracting the package, change into the VDR directory
and type 'make'. This should produce an executable file
@@ -39,7 +37,7 @@ following values 'make' call to activate the respective control mode:
REMOTE=RCU control via the "Remote Control Unit" receiver
(see http://www.cadsoft.de/people/kls/vdr/remote.htm)
REMOTE=LIRC control via the "Linux Infrared Remote Control"
- (see http://fsinfo.cs.uni-sb.de/~columbus/lirc)
+ (see http://www.lirc.org)
Adding "DEBUG_OSD=1" will use the PC screen (or current window)
to display texts instead of the DVB card's on-screen display
@@ -168,6 +166,5 @@ into learning mode.
If the program has been compiled with 'REMOTE=LIRC', no 'keys.conf' file
will be used. Instead, the key names as listed in the source file 'config.c'
-must be used when setting up LIRC. See http://www2.arnes.si/~mthale1 for
-more about LIRC.
+must be used when setting up LIRC. See http://www.lirc.org for more about LIRC.
diff --git a/MANUAL b/MANUAL
index 46466bf..b0e9abe 100644
--- a/MANUAL
+++ b/MANUAL
@@ -174,6 +174,52 @@ Video Disk Recorder User's Manual
used to easily delete a recording after watching it, or to switch
to a different recording.
+* Editing a Recording
+
+ While in Replay mode, the following keys can be used to manipulate editing
+ marks:
+
+ - 0 Toggles an editing mark. If the mark indicator shows a red triangle,
+ the current mark is deleted. Otherwise a new mark is set at the
+ current position.
+ - 4, 6 Move an editing mark back and forward. You need to first jump to
+ an editing mark for this to work.
+ - 7, 9 Jump back and forward between editing marks. Replay goes into still
+ mode after jumping to a mark.
+ - 8 Positions replay at a point 3 seconds before the current or next
+ "start" mark and starts replay.
+ - 2 Start the actual cutting process.
+
+ Editing marks are represented by black, vertical lines in the progress display.
+ A small black triangle at the top of the mark means that this is a "start"
+ mark, and a triangle at the bottom means that this is an "end" mark.
+ The cutting process will save all video data between "start" and "end" marks
+ into a new file (the original recording remains untouched). The new file will
+ have the same name as the original recording, preceeded with a '%' character
+ (imagine the '%' somehow looking like a pair of scissors ;-). Red bars in the
+ progress display indicate which video sequences will be saved by the cutting
+ process.
+
+ The video sequences to be saved by the cutting process are determined by an
+ "even/odd" algorithm. This means that every odd numbered editing mark (i.e.
+ 1, 3, 5,...) represents a "start" mark, while every even numbered mark (2, 4,
+ 6,...) is an "end" mark. Inserting or toggling a mark on or off automatically
+ adjusts the sequence to the right side of that mark.
+
+ Use the keys described under "Replay Control" to position to, e.g., the
+ beginning and end of commercial breaks and press the '0' key to set the
+ necessary editing marks. After that you may want to use the '7' and '9'
+ keys to jump to each mark and maybe use the '4' and '6' keys to fine tune
+ them. Once all marks are in place, press '2' to start the actual cutting
+ process, which will run as a background process. When replaying the edited
+ version of the recording you can use the '8' key to jump to a point just
+ before the next cut and have a look at the resulting sequence.
+
+ Currently editing marks can only be set at I-frames, which typically is
+ every 12th frame. So editing can be done with a resolution of roughly half
+ a second. A "start" mark marks the first frame of a resulting video
+ sequence, and an "end" mark marks the last frame of that sequence.
+
* Programming the Timer
Use the "Timer" menu to maintain your list of timer controlled recordings.
diff --git a/Makefile b/Makefile
index 739d394..4505c7b 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.16 2000/11/18 14:58:10 kls Exp $
+# $Id: Makefile 1.18 2001/01/13 12:26:43 kls Exp $
DVBDIR = ../DVB
@@ -37,9 +37,9 @@ font: genfontfile fontfix.c fontosd.c
# Dependencies:
config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
-dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h videodir.h
+dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
-eit.o : eit.c eit.h thread.h tools.h
+eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
font.o : font.c font.h fontfix.c fontosd.c tools.h
i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
@@ -48,7 +48,7 @@ osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h os
recording.o: recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
-thread.o : thread.c thread.h
+thread.o : thread.c thread.h tools.h
tools.o : tools.c tools.h
vdr.o : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
videodir.o : videodir.c tools.h videodir.h
diff --git a/README b/README
index b1e071e..0dfc875 100644
--- a/README
+++ b/README
@@ -27,7 +27,7 @@ driver software (of course, the hardware still has to be bought).
The on screen menu system is simple, but shall provide all the
possibilites necessary to perform timer controlled recording,
-file management and, maybe, even "on disk editing". The menus
+file management and even "on disk editing". The menus
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.
diff --git a/TODO b/TODO
index f2fb5ea..1ae7d71 100644
--- a/TODO
+++ b/TODO
@@ -3,8 +3,5 @@ TODO list for the Video Disk Recorder project
* Implement simultaneous record/replay with a single DVB card once
the card driver/firmware allows this.
-* Implement "on-disk editing" to allow "cutting out" of certain
- scenes in order to archive them (or, reversely, cut out
- commercial breaks).
* Implement channel scanning.
* Implement remaining commands in SVDRP.
diff --git a/channels.conf b/channels.conf
index ce30919..43e69cd 100644
--- a/channels.conf
+++ b/channels.conf
@@ -1,147 +1,148 @@
-RTL:12188:h:1:27500:163:104:0:12003
-Sat.1:12480:v:1:27500:1791:1792:0:46
-Pro-7:12480:v:1:27500:255:256:0:898
-RTL2:12188:h:1:27500:166:128:0:12020
-ARD:11837:h:1:27500:101:102:0:28106
-BR3:11837:h:1:27500:201:202:0:28107
-Hessen-3:11837:h:1:27500:301:302:0:28108
-N3:12110:h:1:27500:2401:2402:0:28224
-SR3:11837:h:1:27500:501:502:0:28110
-WDR:11837:h:1:27500:601:602:0:28111
-BR-alpha:11837:h:1:27500:701:702:0:28112
-SWR BW:11837:h:1:27500:801:802:0:28113
-Phoenix:11837:h:1:27500:901:902:0:28114
-ZDF:11954:h:1:27500:110:120:0:28006
-3sat:11954:h:1:27500:210:220:0:28007
-KiKa:11954:h:1:27500:310:320:0:28008
-arte:11836:h:1:27500:401:402:0:28109
-ORF Sat:11954:h:1:27500:506:507:0:28010
-ZDF.info:11954:h:1:27500:610:620:0:28011
-CNN:12168:v:1:27500:165:100:0:28512
-Super RTL:12188:h:1:27500:165:120:0:12040
-VOX:12188:h:1:27500:167:136:0:12060
-DW TV:12363:v:1:27500:305:306:0:8905
-Kabel 1:12480:v:1:27500:511:512:0:899
-tm3:12480:v:1:27500:767:768:0:897
-DSF:12480:v:1:27500:1023:1024:0:900
-HOT:12480:v:1:27500:1279:1280:0:40
-Bloomberg TV Germany:12551:v:1:22000:162:99:0:12160
-BLOOMBERG TV:11817:v:1:27500:163:92:0:8004
-Bloomberg:12168:v:1:27500:167:112:0:12721
-Sky News:12552:v:1:22000:305:306:0:3995
-KinderNet:12574:h:1:22000:163:92:0:5020
-Alice:12610:v:1:22000:162:96:0:12200
-n-tv:12669:v:1:22000:162:96:0:12730
-Grand Tourisme:12670:v:1:22000:289:290:0:17300
-TW1:12692:h:1:22000:166:167:0:13013
-Eurosport:11954:h:1:27500:410:420:0:28009
-EinsExtra:12110:H:1:27500:101:102:0:28201
-EinsFestival:12110:H:1:27500:201:202:0:28202
-EinsMuXx:12110:H:1:27500:301:302:0:28203
-ZDF Theaterkanal:11954:H:1:27500:1110:1120:0:28016
-ZDF.doku:11954:H:1:27500:660:670:0:28014
-MDR:12110:h:1:27500:401:402:0:28204
-NICK-PARAMOUNT:12246:v:1:27500:167:108:0:29312
-ORB:12110:h:1:27500:501:502:0:28205
-B1:12110:h:1:27500:601:602:0:28206
-ARD Online-Kanal:12722:h:1:22000:8191:701:0:0
+RTL:12188:h:0:27500:163:104:0:12003
+Sat.1:12480:v:0:27500:1791:1792:0:46
+Pro-7:12480:v:0:27500:255:256:0:898
+RTL2:12188:h:0:27500:166:128:0:12020
+ARD:11837:h:0:27500:101:102:0:28106
+BR3:11837:h:0:27500:201:202:0:28107
+Hessen-3:11837:h:0:27500:301:302:0:28108
+N3:12110:h:0:27500:2401:2402:0:28224
+SR3:11837:h:0:27500:501:502:0:28110
+WDR:11837:h:0:27500:601:602:0:28111
+BR-alpha:11837:h:0:27500:701:702:0:28112
+SWR BW:11837:h:0:27500:801:802:0:28113
+Phoenix:11837:h:0:27500:901:902:0:28114
+ZDF:11954:h:0:27500:110:120:0:28006
+3sat:11954:h:0:27500:210:220:0:28007
+KiKa:11954:h:0:27500:310:320:0:28008
+arte:11836:h:0:27500:401:402:0:28109
+ORF Sat:11954:h:0:27500:506:507:0:28010
+ZDF.info:11954:h:0:27500:610:620:0:28011
+CNN:12168:v:0:27500:165:100:0:28512
+Super RTL:12188:h:0:27500:165:120:0:12040
+VOX:12188:h:0:27500:167:136:0:12060
+DW TV:12363:v:0:27500:305:306:0:8905
+Kabel 1:12480:v:0:27500:511:512:0:899
+tm3:12480:v:0:27500:767:768:0:897
+DSF:12480:v:0:27500:1023:1024:0:900
+HOT:12480:v:0:27500:1279:1280:0:40
+Bloomberg TV Germany:12551:v:0:22000:162:99:0:12160
+BLOOMBERG TV:11817:v:0:27500:163:92:0:8004
+Bloomberg:12168:v:0:27500:167:112:0:12721
+Sky News:12552:v:0:22000:305:306:0:3995
+KinderNet:12574:h:0:22000:163:92:0:5020
+Alice:12610:v:0:22000:162:96:0:12200
+n-tv:12669:v:0:22000:162:96:0:12730
+Grand Tourisme:12670:v:0:22000:289:290:0:17300
+TW1:12692:h:0:22000:166:167:0:13013
+Eurosport:11954:h:0:27500:410:420:0:28009
+EinsExtra:12110:h:0:27500:101:102:0:28201
+EinsFestival:12110:h:0:27500:201:202:0:28202
+EinsMuXx:12110:h:0:27500:301:302:0:28203
+ZDF Theaterkanal:11954:h:0:27500:1110:1120:0:28016
+ZDF.doku:11954:h:0:27500:660:670:0:28014
+MDR:12110:h:0:27500:401:402:0:28204
+NICK-PARAMOUNT:12246:v:0:27500:167:108:0:29312
+ORB:12110:h:0:27500:501:502:0:28205
+B1:12110:h:0:27500:601:602:0:28206
+ARD Online-Kanal:12722:h:0:22000:8191:701:0:0
:Premiere World
-Premiere World Promo:11798:h:1:27500:255:256:0:8
-Premiere:11798:h:1:27500:511:512:2:10
-Star Kino:11798:h:1:27500:767:768:2:9
-Cine Action:11798:h:1:27500:1023:1024:2:20
-Cine Comedy:11798:h:1:27500:1279:1280:2:29
-Sci Fantasy:11798:h:1:27500:1535:1536:2:41
-Romantic Movies:11798:h:1:27500:1791:1792:2:11
-Studio Universal:11798:h:1:27500:2047:2048:2:21
-13th Street:11797:h:1:27500:2303:2304:2:43
-Junior:12031:h:1:27500:255:256:2:19
-K-Toon:12032:h:1:27500:511:512:2:12
-Disney Channel:12031:h:1:27500:767:768:2:15
-Fox Kids:11798:h:1:27500:255:256:2:0
-Sunset:12031:h:1:27500:1023:1024:2:16
-Comedy:12031:h:1:27500:1279:1280:2:28
-Planet:12031:h:1:27500:2047:2048:2:13
-Discovery Channel:12031:h:1:27500:1791:1792:2:14
-Krimi&Co:12031:h:1:27500:1535:1536:2:23
-Filmpalast:12090:v:1:27500:255:256:2:36
-Heimatkanal:11758:h:1:27500:2815:2816:2:517
-Goldstar:11758:h:1:27500:3839:3840:2:518
-Classica:12090:v:1:27500:767:768:2:34
-Seasons:12090:v:1:27500:511:512:2:33
-Blue Channel:11758:h:1:27500:2559:2560:2:516
-Feed (F1 Boxengasse):11720:h:1:27500:2559:2560:2:242
-Feed (F1 Data):11720:h:1:27500:3071:3072:2:244
-Feed (F1 Multi):11720:h:1:27500:2815:2816:2:243
-Feed (F1 On Board):11720:h:1:27500:2303:2304:2:241
-Feed (F1 Verfolger):11720:h:1:27500:2047:2048:2:240
+Premiere World Promo:11798:h:0:27500:255:256:0:8
+Premiere:11798:h:0:27500:511:512:2:10
+Star Kino:11798:h:0:27500:767:768:2:9
+Cine Action:11798:h:0:27500:1023:1024:2:20
+Cine Comedy:11798:h:0:27500:1279:1280:2:29
+Sci Fantasy:11798:h:0:27500:1535:1536:2:41
+Romantic Movies:11798:h:0:27500:1791:1792:2:11
+Studio Universal:11798:h:0:27500:2047:2048:2:21
+13th Street:11797:h:0:27500:2303:2304:2:43
+Junior:12031:h:0:27500:255:256:2:19
+K-Toon:12032:h:0:27500:511:512:2:12
+Disney Channel:12031:h:0:27500:767:768:2:15
+Fox Kids:11798:h:0:27500:255:256:2:0
+Sunset:12031:h:0:27500:1023:1024:2:16
+Comedy:12031:h:0:27500:1279:1280:2:28
+Planet:12031:h:0:27500:2047:2048:2:13
+Discovery Channel:12031:h:0:27500:1791:1792:2:14
+Krimi&Co:12031:h:0:27500:1535:1536:2:23
+Filmpalast:12090:v:0:27500:255:256:2:36
+Heimatkanal:11758:h:0:27500:2815:2816:2:517
+Goldstar:11758:h:0:27500:3839:3840:2:518
+Classica:12090:v:0:27500:767:768:2:34
+Seasons:12090:v:0:27500:511:512:2:33
+Blue Channel:11758:h:0:27500:2559:2560:2:516
+Feed (F1 Boxengasse):11720:h:0:27500:2559:2560:2:242
+Feed (F1 Data):11720:h:0:27500:3071:3072:2:244
+Feed (F1 Multi):11720:h:0:27500:2815:2816:2:243
+Feed (F1 On Board):11720:h:0:27500:2303:2304:2:241
+Feed (F1 Verfolger):11720:h:0:27500:2047:2048:2:240
:
-TV Niepokalanow:11876:h:1:27500:305:321:0:20601
-Mosaico:11934:v:1:27500:165:100:0:29010
-Andalucia TV:11934:v:1:27500:166:104:0:29011
-TVC Internacional:11934:v:1:27500:167:108:0:0
-Nasza TV:11992:h:1:27500:165:98:0:0
-WishLine test:12012:v:1:27500:163:90:0:0
-Pro 7 Austria:12051:v:1:27500:161:84:0:0
-Kabel 1 Schweiz:12051:v:1:27500:162:163:0:0
-Kabel 1 Austria:12051:v:1:27500:166:167:0:0
-Pro 7 Schweiz:12051:v:1:27500:289:290:0:0
-Kiosque:12129:v:1:27500:160:80:0:0
-KTO:12129:v:1:27500:170:120:0:0
-TCM:12168:v:1:27500:160:80:0:0
-Cartoon Network France & Spain:12168:v:1:27500:161:84:0:0
-TVBS Europe:12168:v:1:27500:162:88:0:0
-TVBS Europe:12168:v:1:27500:162:89:0:0
-Travel:12168:v:1:27500:163:92:0:0
-TCM Espania:12168:v:1:27500:164:96:0:0
-MTV Spain:12168:v:1:27500:167:112:0:0
-TCM France:12168:v:1:27500:169:64:0:0
-RTL2 CH:12188:h:1:27500:164:112:0:0
-La Cinquieme:12207:v:1:27500:160:80:0:0
-ARTE:12207:v:1:27500:165:100:0:0
-Post Filial TV:12226:h:1:27500:255:256:0:0
-Canal Canaris:12246:v:1:27500:160:80:0:0
-Canal Canaris:12246:v:1:27500:160:81:0:0
-Canal Canaris:12246:v:1:27500:160:82:0:0
-Canal Canaris:12246:v:1:27500:160:83:0:0
-AB Sat Passion promo:12266:h:1:27500:160:80:0:0
-AB Channel 1:12266:h:1:27500:161:84:0:0
-Taquilla 0:12285:v:1:27500:165:100:0:0
-CSAT:12324:v:1:27500:160:80:0:0
-Mosaique:12324:v:1:27500:162:88:0:0
-Mosaique 2:12324:v:1:27500:163:92:0:0
-Mosaique 3:12324:v:1:27500:164:96:0:0
-Le Sesame C+:12324:v:1:27500:165:1965:0:0
-FEED:12344:h:1:27500:163:92:0:0
-RTM 1:12363:v:1:27500:162:96:0:0
-ESC 1:12363:v:1:27500:163:104:0:0
-TV5 Europe:12363:v:1:27500:164:112:0:0
-TV7 Tunisia:12363:v:1:27500:166:128:0:0
-ARTE:12363:v:1:27500:167:137:0:0
-RAI Uno:12363:v:1:27500:289:290:0:8904
-RTP International:12363:v:1:27500:300:301:0:0
-Fashion TV:12402:v:1:27500:163:92:0:0
-VideoService:12422:h:1:27500:255:256:0:0
-Beta Research promo:12422:h:1:27500:1023:1024:0:0
-Canal Canarias:12441:v:1:27500:160:80:0:0
-TVC International:12441:v:1:27500:512:660:0:0
-Fitur:12441:v:1:27500:514:662:0:0
-Astra Info 1:12552:v:1:22000:164:112:0:0
-Astra Info 2:12552:v:1:22000:165:120:0:0
-Astra Vision 1:12552:v:1:22000:168:144:0:0
-Astra Vision 1:12552:v:1:22000:168:145:0:0
-Astra Vision 1:12552:v:1:22000:168:146:0:0
-Astra Vision 1:12552:v:1:22000:168:147:0:0
-Astra Vision 1:12552:v:1:22000:168:148:0:0
-Astra Vision 1:12552:v:1:22000:168:149:0:0
-Astra Vision 1:12552:v:1:22000:168:150:0:0
-RTL Tele Letzebuerg:12552:v:1:22000:168:144:0:0
-Astra Mosaic:12552:v:1:22000:175:176:0:0
-MHP test:12604:h:1:22000:5632:8191:0:0
-VERONICA:12574:h:1:22000:161:84:0:5010
-VH1 Classic:12699:v:1:22000:3071:3072:0:28647
-VH-1 Germany:12699:v:1:22000:3081:3082:0:28648
-Via 1 - Schöner Reisen:12148:h:1:27500:511:512:0:44
-Video Italia:12610:v:1:22000:121:122:0:12220
-AC 3 promo:12670:v:1:22000:308:256:0:0
-ORF/ZDF:12699:h:1:22000:506:507:0:13012
+TV Niepokalanow:11876:h:0:27500:305:321:0:20601
+Mosaico:11934:v:0:27500:165:100:0:29010
+Andalucia TV:11934:v:0:27500:166:104:0:29011
+TVC Internacional:11934:v:0:27500:167:108:0:0
+Nasza TV:11992:h:0:27500:165:98:0:0
+WishLine test:12012:v:0:27500:163:90:0:0
+Pro 7 Austria:12051:v:0:27500:161:84:0:0
+Kabel 1 Schweiz:12051:v:0:27500:162:163:0:0
+Kabel 1 Austria:12051:v:0:27500:166:167:0:0
+Pro 7 Schweiz:12051:v:0:27500:289:290:0:0
+Kiosque:12129:v:0:27500:160:80:0:0
+KTO:12129:v:0:27500:170:120:0:0
+TCM:12168:v:0:27500:160:80:0:0
+Cartoon Network France & Spain:12168:v:0:27500:161:84:0:0
+TVBS Europe:12168:v:0:27500:162:88:0:0
+TVBS Europe:12168:v:0:27500:162:89:0:0
+Travel:12168:v:0:27500:163:92:0:0
+TCM Espania:12168:v:0:27500:164:96:0:0
+MTV Spain:12168:v:0:27500:167:112:0:0
+TCM France:12168:v:0:27500:169:64:0:0
+RTL2 CH:12188:h:0:27500:164:112:0:0
+La Cinquieme:12207:v:0:27500:160:80:0:0
+ARTE:12207:v:0:27500:165:100:0:0
+Post Filial TV:12226:h:0:27500:255:256:0:0
+Canal Canaris:12246:v:0:27500:160:80:0:0
+Canal Canaris:12246:v:0:27500:160:81:0:0
+Canal Canaris:12246:v:0:27500:160:82:0:0
+Canal Canaris:12246:v:0:27500:160:83:0:0
+AB Sat Passion promo:12266:h:0:27500:160:80:0:0
+AB Channel 1:12266:h:0:27500:161:84:0:0
+Taquilla 0:12285:v:0:27500:165:100:0:0
+CSAT:12324:v:0:27500:160:80:0:0
+Mosaique:12324:v:0:27500:162:88:0:0
+Mosaique 2:12324:v:0:27500:163:92:0:0
+Mosaique 3:12324:v:0:27500:164:96:0:0
+Le Sesame C+:12324:v:0:27500:165:1965:0:0
+FEED:12344:h:0:27500:163:92:0:0
+RTM 1:12363:v:0:27500:162:96:0:0
+ESC 1:12363:v:0:27500:163:104:0:0
+TV5 Europe:12363:v:0:27500:164:112:0:0
+TV7 Tunisia:12363:v:0:27500:166:128:0:0
+ARTE:12363:v:0:27500:167:137:0:0
+RAI Uno:12363:v:0:27500:289:290:0:8904
+RTP International:12363:v:0:27500:300:301:0:0
+Fashion TV:12402:v:0:27500:163:92:0:0
+VideoService:12422:h:0:27500:255:256:0:0
+Beta Research promo:12422:h:0:27500:1023:1024:0:0
+Canal Canarias:12441:v:0:27500:160:80:0:0
+TVC International:12441:v:0:27500:512:660:0:0
+Fitur:12441:v:0:27500:514:662:0:0
+Astra Info 1:12552:v:0:22000:164:112:0:0
+Astra Info 2:12552:v:0:22000:165:120:0:0
+Astra Vision 1:12552:v:0:22000:168:144:0:0
+Astra Vision 1:12552:v:0:22000:168:145:0:0
+Astra Vision 1:12552:v:0:22000:168:146:0:0
+Astra Vision 1:12552:v:0:22000:168:147:0:0
+Astra Vision 1:12552:v:0:22000:168:148:0:0
+Astra Vision 1:12552:v:0:22000:168:149:0:0
+Astra Vision 1:12552:v:0:22000:168:150:0:0
+RTL Tele Letzebuerg:12552:v:0:22000:168:144:0:0
+Astra Mosaic:12552:v:0:22000:175:176:0:0
+MHP test:12604:h:0:22000:5632:8191:0:0
+VERONICA:12574:h:0:22000:161:84:0:5010
+VH1 Classic:12699:v:0:22000:3071:3072:0:28647
+VH-1 Germany:12699:v:0:22000:3081:3082:0:28648
+Via 1 - Schöner Reisen:12148:h:0:27500:511:512:0:44
+Video Italia:12610:v:0:22000:121:122:0:12220
+AC 3 promo:12670:v:0:22000:308:256:0:0
+ORF/ZDF:12699:h:0:22000:506:507:0:13012
+VIVA:12670:v:0:22000:309:310:0:12732
diff --git a/config.c b/config.c
index 8964e5a..5838c68 100644
--- a/config.c
+++ b/config.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.34 2000/11/18 13:26:36 kls Exp $
+ * $Id: config.c 1.39 2001/01/14 15:29:15 kls Exp $
*/
#include "config.h"
@@ -119,10 +119,9 @@ bool cKeys::Load(const char *FileName)
bool cKeys::Save(void)
{
- //TODO make backup copies???
bool result = true;
- FILE *f = fopen(fileName, "w");
- if (f) {
+ cSafeFile f(fileName);
+ if (f.Open()) {
if (fprintf(f, "Code\t%c\nAddress\t%04X\n", code, address) > 0) {
for (tKey *k = keys; k->type != kNone; k++) {
if (fprintf(f, "%s\t%08X\n", k->name, k->code) <= 0) {
@@ -133,7 +132,7 @@ bool cKeys::Save(void)
}
else
result = false;
- fclose(f);
+ f.Close();
}
else
result = false;
@@ -196,7 +195,7 @@ cChannel::cChannel(const cChannel *Channel)
strcpy(name, Channel ? Channel->name : "Pro7");
frequency = Channel ? Channel->frequency : 12480;
polarization = Channel ? Channel->polarization : 'v';
- diseqc = Channel ? Channel->diseqc : 1;
+ diseqc = Channel ? Channel->diseqc : 0;
srate = Channel ? Channel->srate : 27500;
vpid = Channel ? Channel->vpid : 255;
apid = Channel ? Channel->apid : 256;
@@ -435,9 +434,11 @@ bool cTimer::Parse(const char *s)
//XXX to hear about that!
char *s2 = NULL;
int l2 = strlen(s);
- if (s[l2 - 2] == ':') { // note that 's' has a trailing '\n'
- s2 = (char *)malloc(l2 + 2);
- strcat(strn0cpy(s2, s, l2), " \n");
+ while (l2 > 0 && isspace(s[l2 - 1]))
+ l2--;
+ if (s[l2 - 1] == ':') {
+ s2 = (char *)malloc(l2 + 3);
+ strcat(strn0cpy(s2, s, l2 + 1), " \n");
s = s2;
}
if (8 <= sscanf(s, "%d:%d:%a[^:]:%d:%d:%d:%d:%a[^:\n]:%a[^\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
@@ -723,6 +724,7 @@ cSetup::cSetup(void)
MarginStart = 2;
MarginStop = 10;
EPGScanTimeout = 5;
+ CurrentChannel = -1;
}
bool cSetup::Parse(char *s)
@@ -742,6 +744,7 @@ bool cSetup::Parse(char *s)
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
else
return false;
return true;
@@ -780,8 +783,8 @@ bool cSetup::Save(const char *FileName)
if (!FileName)
FileName = fileName;
if (FileName) {
- FILE *f = fopen(FileName, "w");
- if (f) {
+ cSafeFile f(FileName);
+ if (f.Open()) {
fprintf(f, "# VDR Setup\n");
fprintf(f, "OSDLanguage = %d\n", OSDLanguage);
fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB);
@@ -792,8 +795,10 @@ bool cSetup::Save(const char *FileName)
fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi);
fprintf(f, "SetSystemTime = %d\n", SetSystemTime);
fprintf(f, "MarginStart = %d\n", MarginStart);
+ fprintf(f, "MarginStop = %d\n", MarginStop);
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
- fclose(f);
+ fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
+ f.Close();
isyslog(LOG_INFO, "saved setup to %s", FileName);
return true;
}
diff --git a/config.h b/config.h
index db3f1ed..277e957 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.34 2000/11/18 13:25:53 kls Exp $
+ * $Id: config.h 1.38 2001/01/14 15:29:27 kls Exp $
*/
#ifndef __CONFIG_H
@@ -14,11 +14,12 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
+#include <unistd.h>
#include "dvbapi.h"
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "0.68"
+#define VDRVERSION "0.70"
#define MaxBuffer 10000
@@ -42,6 +43,15 @@ enum eKeys { // "Up" and "Down" must be the first two keys!
k_Flags = k_Repeat | k_Release,
};
+// This is in preparation for having more key codes:
+#define kMarkToggle k0
+#define kMarkMoveBack k4
+#define kMarkMoveForward k6
+#define kMarkJumpBack k7
+#define kMarkJumpForward k9
+#define kEditCut k2
+#define kEditTest k8
+
#define RAWKEY(k) ((k) & ~k_Flags)
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
#define NORMALKEY(k) ((k) & ~k_Repeat)
@@ -157,43 +167,45 @@ private:
cList<T>::Clear();
}
public:
+ cConfig(void) { fileName = NULL; }
+ virtual ~cConfig() { delete fileName; }
virtual bool Load(const char *FileName)
{
- isyslog(LOG_INFO, "loading %s", FileName);
- bool result = true;
Clear();
fileName = strdup(FileName);
- FILE *f = fopen(fileName, "r");
- if (f) {
- int line = 0;
- char buffer[MaxBuffer];
- while (fgets(buffer, sizeof(buffer), f) > 0) {
- line++;
- T *l = new T;
- if (l->Parse(buffer))
- Add(l);
- else {
- esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line);
- delete l;
- result = false;
- break;
+ bool result = false;
+ if (access(FileName, F_OK) == 0) {
+ isyslog(LOG_INFO, "loading %s", FileName);
+ FILE *f = fopen(fileName, "r");
+ if (f) {
+ int line = 0;
+ char buffer[MaxBuffer];
+ result = true;
+ while (fgets(buffer, sizeof(buffer), f) > 0) {
+ line++;
+ T *l = new T;
+ if (l->Parse(buffer))
+ Add(l);
+ else {
+ esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line);
+ delete l;
+ result = false;
+ break;
+ }
}
- }
- fclose(f);
- }
- else {
- LOG_ERROR_STR(fileName);
- result = false;
+ fclose(f);
+ }
+ else
+ LOG_ERROR_STR(fileName);
}
return result;
}
bool Save(void)
{
- //TODO make backup copies???
bool result = true;
T *l = (T *)First();
- FILE *f = fopen(fileName, "w");
- if (f) {
+ cSafeFile f(fileName);
+ if (f.Open()) {
while (l) {
if (!l->Save(f)) {
result = false;
@@ -201,12 +213,10 @@ public:
}
l = (T *)l->Next();
}
- fclose(f);
+ f.Close();
}
- else {
- LOG_ERROR_STR(fileName);
+ else
result = false;
- }
return result;
}
};
@@ -258,6 +268,7 @@ public:
int SetSystemTime;
int MarginStart, MarginStop;
int EPGScanTimeout;
+ int CurrentChannel;
cSetup(void);
bool Load(const char *FileName);
bool Save(const char *FileName = NULL);
diff --git a/dvb.c.071.diff b/dvb.c.071.diff
deleted file mode 100644
index ac7ba3a..0000000
--- a/dvb.c.071.diff
+++ /dev/null
@@ -1,100 +0,0 @@
---- dvb.c.001 Sun Sep 17 22:02:37 2000
-+++ dvb.c Tue Oct 3 12:11:46 2000
-@@ -1143,6 +1143,8 @@
- {
- int bpp;
- int i;
-+ int d, delta; //XXX kls: additional variables for data compression
-+ u8 c; //XXX kls: additional variables for data compression
- DECLARE_WAITQUEUE(wait, current);
-
- if (dvb->bmp_state==BMP_LOADING) {
-@@ -1160,27 +1162,38 @@
- if (dvb->bmp_state==BMP_LOADING)
- return -1;
- dvb->bmp_state=BMP_LOADING;
-- if (format==BITMAP8) bpp=8;
-- else if (format==BITMAP4) bpp=4;
-- else if (format==BITMAP2) bpp=2;
-- else if (format==BITMAP1) bpp=1;
-+ if (format==BITMAP8) { bpp=8; delta = 1; } //XXX kls: initialize 'delta', too
-+ else if (format==BITMAP4) { bpp=4; delta = 2; }
-+ else if (format==BITMAP2) { bpp=2; delta = 4; }
-+ else if (format==BITMAP1) { bpp=1; delta = 8; }
- else {
- dvb->bmp_state=BMP_NONE;
- return -1;
- }
-- dvb->bmplen= (dx*dy*bpp)/8;
-+ dvb->bmplen= ((dx*dy*bpp+7)&~7)/8; //XXX kls: need to round up to include partial bytes
- dvb->bmpp=0;
- if (dvb->bmplen>32768) {
- dvb->bmp_state=BMP_NONE;
- return -1;
- }
- for (i=0; i<dy; i++) {
-- if (copy_from_user(dvb->bmpbuf+1024+i*dx, data+i*inc, (dx*bpp)/8)) {
-+ if (copy_from_user(dvb->bmpbuf+1024+i*dx, data+i*inc, dx)) { //XXX kls: incoming data is "1 byte per pixel"
- dvb->bmp_state=BMP_NONE;
- return -1;
- }
-
- }
-+ // XXX kls: Incoming data is always "one byte per pixel", so we need to compress
-+ // the data in case we have a lower resolution than 8 bpp:
-+ if (format != BITMAP8) {
-+ for (i=0; i<dx*dy/delta; i++) {
-+ c = ((u8 *)dvb->bmpbuf)[1024+i*delta+delta-1];
-+ for (d=delta-2; d>=0; d--) {
-+ c |= (((u8 *)dvb->bmpbuf)[1024+i*delta+d] << ((delta-d-1)*bpp));
-+ ((u8 *)dvb->bmpbuf)[1024+i] = c;
-+ }
-+ }
-+ }
- dvb->bmplen+=1024;
- return outcom(dvb, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
- }
-@@ -1256,24 +1269,25 @@
- int i;
-
- w=x1-x0+1; h=y1-y0+1;
-- if (inc>0)
-- w=inc;
-- if (w>720 || h>576)
-+ if (inc<=0)
-+ inc=w; //XXX kls: see dvb_4l.h: "inc<=0 uses blockwidth as linewidth"
-+ if (w<=0 || w>720 || h<=0 || h>576) //XXX kls: checking lower bounds, too
- return -1;
-- bpp=8; //dvb->osdbpp[dvb->osdwin];
-- bpl=w*bpp/8;
-+ bpp=dvb->osdbpp[dvb->osdwin]+1; //XXX kls: 'bpp' needs to be taken from the window
-+ bpl=((w*bpp+7)&~7)/8; //XXX kls: need to round up to include partial bytes
- size=h*bpl;
-- lpb=(64*1024)/bpl;
-+ lpb=(32*1024)/bpl; //XXX kls: apparently 32K is the maximum possible value
- bnum=size/(lpb*bpl);
- brest=size-bnum*lpb*bpl;
-
- for (i=0; i<bnum; i++) {
-- LoadBitmap(dvb, BITMAP8, w, lpb, inc, data);
-+ LoadBitmap(dvb, bpp2bit[dvb->osdbpp[dvb->osdwin]], w, lpb, inc, data); //XXX kls: need to take the format from the actual window
- BlitBitmap(dvb, dvb->osdwin, x0, y0+i*lpb, 1);
-- data+=bpl;
-+ data+=lpb*inc; //XXX kls: incrementing must be done in "one byte per pixel"
-+ ddelay(3); //XXX kls: without this the block is sometimes not fully displayed - firmware bug?
- }
- if (brest) {
-- LoadBitmap(dvb, BITMAP8, w, brest/bpl, inc, data);
-+ LoadBitmap(dvb, bpp2bit[dvb->osdbpp[dvb->osdwin]], w, brest/bpl, inc, data); //XXX kls: need to take the format from the actual window
- BlitBitmap(dvb, dvb->osdwin, x0, y0+bnum*lpb, 1);
- }
- ReleaseBitmap(dvb);
-@@ -6141,7 +6155,7 @@
- init_waitqueue_head(&dvb->bmpq);
- spin_lock_init (&(dvb->bmplock));
- dvb->bmpp=dvb->bmplen=0;
-- dvb->bmpbuf=vmalloc(32768+1024);
-+ dvb->bmpbuf=vmalloc(8*32768+1024); //XXX kls: '8*' to be prepared for the maximum possible incoming data at 1 bpp
-
- init_waitqueue_head(&dvb->debiq);
- dvb->debilock=SPIN_LOCK_UNLOCKED;
diff --git a/dvbapi.c b/dvbapi.c
index 5e4f2e6..e583baf 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.40 2000/11/19 16:46:37 kls Exp $
+ * $Id: dvbapi.c 1.50 2001/01/18 19:53:54 kls Exp $
*/
#include "dvbapi.h"
@@ -21,6 +21,7 @@ extern "C" {
#include <unistd.h>
#include "config.h"
#include "interface.h"
+#include "recording.h"
#include "tools.h"
#include "videodir.h"
@@ -32,23 +33,25 @@ extern "C" {
// The minimum amount of video data necessary to identify frames
// (must be smaller than VIDEOBUFSIZE!):
-#define MINVIDEODATA (20*1024) // just a safe guess (max. size of any AV_PES block, plus some safety)
+#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
// The maximum time the buffer is allowed to write data to disk when recording:
#define MAXRECORDWRITETIME 50 // ms
-#define AV_PES_HEADER_LEN 8
-
-// AV_PES block types:
-#define AV_PES_VIDEO 1
-#define AV_PES_AUDIO 2
-
// Picture types:
#define NO_PICTURE 0
#define I_FRAME 1
#define P_FRAME 2
#define B_FRAME 3
+// Start codes:
+#define SC_PICTURE 0x00 // "picture header"
+#define SC_SEQU 0xB3 // "sequence header"
+#define SC_PHEAD 0xBA // "pack header"
+#define SC_SHEAD 0xBB // "system header"
+#define SC_AUDIO 0xC0
+#define SC_VIDEO 0xE0
+
#define FRAMESPERSEC 25
// The maximum file size is limited by the range that can be covered
@@ -73,6 +76,35 @@ extern "C" {
typedef unsigned char uchar;
+static void SetPlayMode(int VideoDev, int Mode)
+{
+ if (VideoDev >= 0) {
+ struct video_play_mode pmode;
+ pmode.mode = Mode;
+ ioctl(VideoDev, VIDIOCSPLAYMODE, &pmode);
+ }
+}
+
+const char *IndexToHMSF(int Index, bool WithFrame)
+{
+ static char buffer[16];
+ int f = (Index % FRAMESPERSEC) + 1;
+ int s = (Index / FRAMESPERSEC);
+ int m = s / 60 % 60;
+ int h = s / 3600;
+ s %= 60;
+ snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
+ return buffer;
+}
+
+int HMSFToIndex(const char *HMSF)
+{
+ int h, m, s, f = 0;
+ if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
+ return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+ return 0;
+}
+
// --- cResumeFile ------------------------------------------------------------
cResumeFile::cResumeFile(const char *FileName)
@@ -135,16 +167,15 @@ private:
cResumeFile resumeFile;
bool CatchUp(void);
public:
- cIndexFile(const char *FileName, bool Record = false);
+ cIndexFile(const char *FileName, bool Record);
~cIndexFile();
void Write(uchar PictureType, uchar FileNumber, int FileOffset);
- bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL);
- int GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length = NULL);
+ bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
+ int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL);
int Get(uchar FileNumber, int FileOffset);
- int Last(void) { return last; }
+ int Last(void) { CatchUp(); return last; }
int GetResume(void) { return resumeFile.Read(); }
bool StoreResume(int Index) { return resumeFile.Save(Index); }
- static char *Str(int Index, bool WithFrame = false);
};
cIndexFile::cIndexFile(const char *FileName, bool Record)
@@ -276,7 +307,7 @@ void cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
}
}
-bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType)
+bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
{
if (index) {
CatchUp();
@@ -285,6 +316,14 @@ bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *Pictu
*FileOffset = index[Index].offset;
if (PictureType)
*PictureType = index[Index].type;
+ if (Length) {
+ int fn = index[Index + 1].number;
+ int fo = index[Index + 1].offset;
+ if (fn == *FileNumber)
+ *Length = fo - *FileOffset;
+ else
+ *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
+ }
return true;
}
}
@@ -301,8 +340,14 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *F
Index += d;
if (Index >= 0 && Index <= last - 100) { // '- 100': need to stay off the end!
if (index[Index].type == I_FRAME) {
- *FileNumber = index[Index].number;
- *FileOffset = index[Index].offset;
+ if (FileNumber)
+ *FileNumber = index[Index].number;
+ else
+ FileNumber = &index[Index].number;
+ if (FileOffset)
+ *FileOffset = index[Index].offset;
+ else
+ FileOffset = &index[Index].offset;
if (Length) {
// all recordings end with a non-I_FRAME, so the following should be safe:
int fn = index[Index + 1].number;
@@ -339,18 +384,6 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
return -1;
}
-char *cIndexFile::Str(int Index, bool WithFrame)
-{
- static char buffer[16];
- int f = (Index % FRAMESPERSEC) + 1;
- int s = (Index / FRAMESPERSEC);
- int m = s / 60 % 60;
- int h = s / 3600;
- s %= 60;
- snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
- return buffer;
-}
-
// --- cRingBuffer -----------------------------------------------------------
/* cRingBuffer reads data from an input file, stores it in a buffer and writes
@@ -378,6 +411,12 @@ protected:
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 Byte(int Offset);
+ bool Set(int Offset, int Length, int Value);
+protected:
+ int GetStartCode(int Offset) { return (Byte(Offset) == 0x00 && Byte(Offset + 1) == 0x00 && Byte(Offset + 2) == 0x01) ? Byte(Offset + 3) : -1; }
+ int GetPictureType(int Offset) { return (Byte(Offset + 5) >> 3) & 0x07; }
+ int FindStartCode(uchar Code, int Offset = 0);
+ int GetPacketLength(int Offset = 0);
public:
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
virtual ~cRingBuffer();
@@ -422,19 +461,36 @@ int cRingBuffer::Byte(int Offset)
return -1;
}
-void cRingBuffer::Skip(int n)
+bool cRingBuffer::Set(int Offset, int Length, int Value)
{
- if (head < tail) {
- head += n;
- if (head > tail)
- head = tail;
+ if (buffer && Offset + Length <= Available() ) {
+ Offset += head;
+ while (Length--) {
+ if (Offset >= size)
+ Offset -= size;
+ buffer[Offset] = Value;
+ Offset++;
+ }
+ return true;
}
- else if (head > tail) {
- head += n;
- if (head >= size)
- head -= size;
- if (head > tail)
- head = tail;
+ return false;
+}
+
+void cRingBuffer::Skip(int n)
+{
+ if (n > 0) {
+ if (head < tail) {
+ head += n;
+ if (head > tail)
+ head = tail;
+ }
+ else if (head > tail) {
+ head += n;
+ if (head >= size)
+ head -= size;
+ if (head > tail)
+ head = tail;
+ }
}
}
@@ -477,9 +533,11 @@ int cRingBuffer::Read(int Max)
if (tail >= size)
tail = 0;
}
- else if (r < 0 && errno != EAGAIN) {
- LOG_ERROR;
- return -1;
+ else if (r < 0) {
+ if (errno != EAGAIN) {
+ LOG_ERROR;
+ return -1;
+ }
}
else
eof = true;
@@ -533,37 +591,53 @@ int cRingBuffer::Write(int Max)
return -1;
}
-// --- cFileBuffer -----------------------------------------------------------
+int cRingBuffer::FindStartCode(uchar Code, int Offset)
+{
+ // Searches for a start code (beginning at Offset) and returns the number
+ // of bytes from Offset to the start code.
-class cFileBuffer : public cRingBuffer {
-protected:
- cIndexFile *index;
- uchar fileNumber;
+ int n = Available() - Offset;
+
+ for (int i = 0; i < n; i++) {
+ int c = GetStartCode(Offset + i);
+ if (c == Code)
+ return i;
+ if (i > 0 && c == SC_PHEAD)
+ break; // found another block start while looking for a different code
+ }
+ return -1;
+}
+
+int cRingBuffer::GetPacketLength(int Offset)
+{
+ // Returns the entire length of the packet starting at offset.
+ return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
+}
+
+// --- cFileName -------------------------------------------------------------
+
+class cFileName {
+private:
+ int file;
+ int fileNumber;
char *fileName, *pFileNumber;
- bool stop;
- int GetAvPesLength(void)
- {
- if (Byte(0) == 'A' && Byte(1) == 'V' && Byte(4) == 'U')
- return (Byte(6) << 8) + Byte(7) + AV_PES_HEADER_LEN;
- return 0;
- }
- int GetAvPesType(void) { return Byte(2); }
- int GetAvPesTag(void) { return Byte(3); }
- int FindAvPesBlock(void);
- int GetPictureType(int Offset) { return (Byte(Offset + 5) >> 3) & 0x07; }
- int FindPictureStartCode(int Length);
+ bool record;
public:
- cFileBuffer(int *InFile, int *OutFile, const char *FileName, bool Recording, int Size, int FreeLimit = 0, int AvailLimit = 0);
- virtual ~cFileBuffer();
- void Stop(void) { stop = true; }
+ cFileName(const char *FileName, bool Record);
+ ~cFileName();
+ const char *Name(void) { return fileName; }
+ int Number(void) { return fileNumber; }
+ int Open(void);
+ void Close(void);
+ int SetOffset(int Number, int Offset = 0);
+ int NextFile(void);
};
-cFileBuffer::cFileBuffer(int *InFile, int *OutFile, const char *FileName, bool Recording, int Size, int FreeLimit = 0, int AvailLimit = 0)
-:cRingBuffer(InFile, OutFile, Size, FreeLimit, AvailLimit)
+cFileName::cFileName(const char *FileName, bool Record)
{
- index = NULL;
+ file = -1;
fileNumber = 0;
- stop = false;
+ record = Record;
// Prepare the file name:
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
if (!fileName) {
@@ -572,98 +646,165 @@ cFileBuffer::cFileBuffer(int *InFile, int *OutFile, const char *FileName, bool R
}
strcpy(fileName, FileName);
pFileNumber = fileName + strlen(fileName);
- // Create the index file:
- index = new cIndexFile(FileName, Recording);
- if (!index)
- esyslog(LOG_ERR, "ERROR: can't allocate index");
- // let's continue without index, so we'll at least have the recording
+ SetOffset(1);
}
-cFileBuffer::~cFileBuffer()
+cFileName::~cFileName()
{
- delete index;
+ Close();
delete fileName;
}
-int cFileBuffer::FindAvPesBlock(void)
+int cFileName::Open(void)
+{
+ if (file < 0) {
+ if (record) {
+ dsyslog(LOG_INFO, "recording to '%s'", fileName);
+ file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
+ if (file < 0)
+ LOG_ERROR_STR(fileName);
+ }
+ else {
+ if (access(fileName, R_OK) == 0) {
+ dsyslog(LOG_INFO, "playing '%s'", fileName);
+ file = open(fileName, O_RDONLY | O_NONBLOCK);
+ if (file < 0)
+ LOG_ERROR_STR(fileName);
+ }
+ else if (errno != ENOENT)
+ LOG_ERROR_STR(fileName);
+ }
+ }
+ return file;
+}
+
+void cFileName::Close(void)
{
- int Skipped = 0;
+ if (file >= 0) {
+ if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
+ LOG_ERROR_STR(fileName);
+ file = -1;
+ }
+}
- while (Available() > MINVIDEODATA) {
- if (GetAvPesLength())
- return Skipped;
- Skip(1);
- Skipped++;
+int cFileName::SetOffset(int Number, int Offset)
+{
+ if (fileNumber != Number)
+ Close();
+ if (0 < Number && Number <= MAXFILESPERRECORDING) {
+ fileNumber = Number;
+ sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
+ if (record) {
+ if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
+ return SetOffset(Number + 1);
+ else if (errno != ENOENT) { // something serious has happened
+ LOG_ERROR_STR(fileName);
+ return -1;
+ }
+ // found a non existing file suffix
}
+ if (Open() >= 0) {
+ if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
+ LOG_ERROR_STR(fileName);
+ return -1;
+ }
+ }
+ return file;
+ }
+ esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
return -1;
}
-int cFileBuffer::FindPictureStartCode(int Length)
+int cFileName::NextFile(void)
{
- for (int i = AV_PES_HEADER_LEN; i < Length; i++) {
- if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1 && Byte(i + 3) == 0)
- return i;
- }
- return -1;
+ return SetOffset(fileNumber + 1);
}
// --- cRecordBuffer ---------------------------------------------------------
-class cRecordBuffer : public cFileBuffer {
+class cRecordBuffer : public cRingBuffer, public cThread {
private:
+ cFileName fileName;
+ cIndexFile *index;
uchar pictureType;
int fileSize;
+ int videoDev;
int recordFile;
- uchar tagAudio, tagVideo;
- bool ok, synced;
+ bool ok, synced, stop;
time_t lastDiskSpaceCheck;
bool RunningLowOnDiskSpace(void);
+ int ScanVideoPacket(int *PictureType, int Offset);
int Synchronize(void);
bool NextFile(void);
virtual int Write(int Max = -1);
+ bool WriteWithTimeout(void);
+protected:
+ virtual void Action(void);
public:
cRecordBuffer(int *InFile, const char *FileName);
virtual ~cRecordBuffer();
- int WriteWithTimeout(bool EndIfEmpty = false);
};
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
-:cFileBuffer(InFile, &recordFile, FileName, true, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
+:cRingBuffer(InFile, &recordFile, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
+,fileName(FileName, true)
{
+ index = NULL;
pictureType = NO_PICTURE;
fileSize = 0;
- recordFile = -1;
- tagAudio = tagVideo = 0;
- ok = synced = false;
+ videoDev = *InFile;
+ recordFile = fileName.Open();
+ ok = synced = stop = false;
lastDiskSpaceCheck = time(NULL);
- if (!fileName)
- return;//XXX find a better way???
- // Find the highest existing file suffix:
- for (;;) {
- sprintf(pFileNumber, RECORDFILESUFFIX, ++fileNumber);
- if (access(fileName, F_OK) < 0) {
- if (errno == ENOENT)
- break; // found a non existing file suffix
- LOG_ERROR;
- return;
- }
- }
+ if (!fileName.Name())
+ return;
+ // Create the index file:
+ index = new cIndexFile(FileName, true);
+ if (!index)
+ esyslog(LOG_ERR, "ERROR: can't allocate index");
+ // let's continue without index, so we'll at least have the recording
ok = true;
- //XXX hack to make the video device go into 'recording' mode:
- char dummy;
- read(*InFile, &dummy, sizeof(dummy));
+ Start();
}
cRecordBuffer::~cRecordBuffer()
{
- if (recordFile >= 0)
- CloseVideoFile(recordFile);
+ stop = true;
+ Cancel(3);
+ delete index;
+}
+
+void cRecordBuffer::Action(void)
+{
+ dsyslog(LOG_INFO, "recording thread started (pid=%d)", getpid());
+
+ time_t t = time(NULL);
+ for (;;) {
+ usleep(1); // this keeps the CPU load low
+
+ LOCK_THREAD;
+
+ int r = Read();
+ if (r >= 0) {
+ if (r > 0)
+ t = time(NULL);
+ if (!WriteWithTimeout())
+ break;
+ }
+ if (r < 0 || (r == 0 && time(NULL) - t > 5)) {
+ esyslog(LOG_ERR, "ERROR: video data stream broken");
+ t = time(NULL);
+ }
+ }
+ SetPlayMode(videoDev, VID_PLAY_RESET);
+
+ dsyslog(LOG_INFO, "end recording thread");
}
bool cRecordBuffer::RunningLowOnDiskSpace(void)
{
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
- uint Free = FreeDiskSpaceMB(fileName);
+ uint Free = FreeDiskSpaceMB(fileName.Name());
lastDiskSpaceCheck = time(NULL);
if (Free < MINFREEDISKSPACE) {
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
@@ -673,109 +814,109 @@ bool cRecordBuffer::RunningLowOnDiskSpace(void)
return false;
}
+int cRecordBuffer::ScanVideoPacket(int *PictureType, int Offset)
+{
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+
+ int Length = GetPacketLength(Offset);
+ if (Length <= Available()) {
+ for (int i = Offset; i < Offset + Length; i++) {
+ if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
+ switch (Byte(i + 3)) {
+ case SC_PICTURE: *PictureType = GetPictureType(i);
+ return Length;
+ }
+ }
+ }
+ *PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
int cRecordBuffer::Synchronize(void)
{
- int Length = 0;
- int Skipped = 0;
- int s;
-
- while ((s = FindAvPesBlock()) >= 0) {
- pictureType = NO_PICTURE;
- Skipped += s;
- Length = GetAvPesLength();
- if (Length <= MINVIDEODATA) {
- switch (GetAvPesType()) {
- case AV_PES_VIDEO:
- {
- int PictureOffset = FindPictureStartCode(Length);
- if (PictureOffset >= 0) {
- pictureType = GetPictureType(PictureOffset);
- if (pictureType < I_FRAME || pictureType > B_FRAME)
- esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pictureType);
- }
- }
- if (!synced) {
- tagVideo = GetAvPesTag();
- if (pictureType == I_FRAME) {
- synced = true;
- Skipped = 0;
- }
- else {
- Skip(Length - 1);
- Length = 0;
- }
- }
- else {
- if (++tagVideo != GetAvPesTag()) {
- esyslog(LOG_ERR, "ERROR: missed video data block #%d (next block is #%d)", tagVideo, GetAvPesTag());
- tagVideo = GetAvPesTag();
- }
- }
- break;
- case AV_PES_AUDIO:
- if (!synced) {
- tagAudio = GetAvPesTag();
- Skip(Length - 1);
- Length = 0;
- }
- else {
- //XXX might get fooled the first time!!!
- if (++tagAudio != GetAvPesTag()) {
- esyslog(LOG_ERR, "ERROR: missed audio data block #%d (next block is #%d)", tagAudio, GetAvPesTag());
- tagAudio = GetAvPesTag();
- }
- }
- break;
- default: // unknown data
- Length = 0; // don't skip entire block - maybe we're just not in sync with AV_PES yet
- if (synced)
- esyslog(LOG_ERR, "ERROR: unknown data type '%d'", GetAvPesType());
- }
- if (Length > 0)
- break;
- }
- else if (synced) {
- esyslog(LOG_ERR, "ERROR: block length too big (%d)", Length);
- Length = 0;
+ // Positions to the start of a data block (skipping everything up to
+ // an I-frame if not synced) and returns the block length.
+
+ int LastPackHeader = -1;
+
+ pictureType = NO_PICTURE;
+
+ //XXX remove this once the buffer is handled with two separate threads:
+ if (!synced && Free() < 100000) {
+ dsyslog(LOG_INFO, "unable to synchronize, dropped %d bytes", Available());
+ Clear();
+ return 0;
+ }
+ for (int i = 0; Available() > MINVIDEODATA && i < MINVIDEODATA; i++) {
+ if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
+ switch (Byte(i + 3)) {
+ case SC_PHEAD: LastPackHeader = i;
+ break;
+ case SC_VIDEO: {
+ int pt = NO_PICTURE;
+ int l = ScanVideoPacket(&pt, i);
+ if (l < 0)
+ return 0; // no useful data found, wait for more
+ if (pt != NO_PICTURE) {
+ if (pt < I_FRAME || B_FRAME < pt) {
+ esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
+ }
+ else if (pictureType == NO_PICTURE) {
+ if (!synced) {
+ if (LastPackHeader == 0) {
+ if (pt == I_FRAME)
+ synced = true;
+ }
+ else if (LastPackHeader > 0) {
+ Skip(LastPackHeader);
+ LastPackHeader = -1;
+ i = 0;
+ break;
+ }
+ else { // LastPackHeader < 0
+ Skip(i + l);
+ i = 0;
+ break;
+ }
+ }
+ if (synced)
+ pictureType = pt;
+ }
+ else if (LastPackHeader > 0)
+ return LastPackHeader;
+ else
+ return i;
+ }
+ i += l - 1; // -1 to compensate for i++ in the loop!
+ LastPackHeader = -1;
+ }
+ break;
+ case SC_AUDIO: i += GetPacketLength(i) - 1; // -1 to compensate for i++ in the loop!
+ break;
}
- Skip(1);
- Skipped++;
- }
- if (synced && Skipped)
- esyslog(LOG_ERR, "ERROR: skipped %d bytes", Skipped);
- return Length;
+ }
+ }
+ return 0; // no useful data found, wait for more
}
bool cRecordBuffer::NextFile(void)
{
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
- if (CloseVideoFile(recordFile) < 0)
- LOG_ERROR;
- // don't return 'false', maybe we can still record into the next file
- recordFile = -1;
- fileNumber++;
- if (fileNumber == 0)
- esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
+ recordFile = fileName.NextFile();
fileSize = 0;
}
}
- if (recordFile < 0) {
- sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
- dsyslog(LOG_INFO, "recording to '%s'", fileName);
- recordFile = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
- if (recordFile < 0) {
- LOG_ERROR;
- return false;
- }
- }
- return true;
+ return recordFile >= 0;
}
int cRecordBuffer::Write(int Max)
{
// This function ignores the incoming 'Max'!
- // It tries to write out exactly *one* block of AV_PES data.
+ // It tries to write out exactly *one* frame block.
if (!ok)
return -1;
int n = Synchronize();
@@ -786,10 +927,10 @@ int cRecordBuffer::Write(int Max)
}
if (NextFile()) {
if (index && pictureType != NO_PICTURE)
- index->Write(pictureType, fileNumber, fileSize);
+ index->Write(pictureType, fileName.Number(), fileSize);
int written = 0;
for (;;) {
- int w = cFileBuffer::Write(n);
+ int w = cRingBuffer::Write(n);
if (w >= 0) {
fileSize += w;
written += w;
@@ -806,30 +947,41 @@ int cRecordBuffer::Write(int Max)
return 0;
}
-int cRecordBuffer::WriteWithTimeout(bool EndIfEmpty)
+bool cRecordBuffer::WriteWithTimeout(void)
{
- int w, written = 0;
int t0 = time_ms();
- while ((w = Write()) > 0 && time_ms() - t0 < MAXRECORDWRITETIME)
- written += w;
- return w < 0 ? w : (written == 0 && EndIfEmpty ? -1 : written);
+ do {
+ int w = Write();
+ if (w < 0)
+ return false;
+ if (w == 0)
+ break;
+ } while (time_ms() - t0 < MAXRECORDWRITETIME);
+ return true;
}
// --- cReplayBuffer ---------------------------------------------------------
-enum eReplayMode { rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
-
-class cReplayBuffer : public cFileBuffer {
+class cReplayBuffer : public cRingBuffer, public cThread {
private:
+ enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
+ enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
+ cIndexFile *index;
+ cFileName fileName;
int fileOffset;
+ int videoDev;
int replayFile;
eReplayMode mode;
- bool skipAudio;
- int lastIndex;
+ int lastIndex, stillIndex;
int brakeCounter;
- void SkipAudioBlocks(void);
+ eReplayCmd command;
+ bool active;
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
void Close(void);
+ void SetCmd(eReplayCmd Cmd) { LOCK_THREAD; command = Cmd; }
+ void SetTemporalReference(void);
+protected:
+ virtual void Action(void);
public:
cReplayBuffer(int *OutFile, const char *FileName);
virtual ~cReplayBuffer();
@@ -838,38 +990,136 @@ public:
void SetMode(eReplayMode Mode);
int Resume(void);
bool Save(void);
+ void Pause(void) { SetCmd(rcPause); }
+ void Play(void) { SetCmd(rcPlay); }
+ void Forward(void) { SetCmd(rcForward); }
+ void Backward(void) { SetCmd(rcBackward); }
+ int SkipFrames(int Frames);
void SkipSeconds(int Seconds);
- void GetIndex(int &Current, int &Total);
+ void Goto(int Position, bool Still = false);
+ void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
};
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
-:cFileBuffer(&replayFile, OutFile, FileName, false, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+:cRingBuffer(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+,fileName(FileName, false)
{
+ index = NULL;
fileOffset = 0;
- replayFile = -1;
+ videoDev = *OutFile;
+ replayFile = fileName.Open();
mode = rmPlay;
brakeCounter = 0;
- skipAudio = false;
- lastIndex = -1;
- if (!fileName)
- return;//XXX find a better way???
- // All recordings start with '1':
- fileNumber = 1; //TODO what if it doesn't start with '1'???
- //XXX hack to make the video device go into 'replaying' mode:
- char *dummy = "AV"; // must be "AV" to make the driver go into AV_PES mode!
- write(*OutFile, dummy, strlen(dummy));
+ command = rcNone;
+ lastIndex = stillIndex = -1;
+ active = false;
+ if (!fileName.Name())
+ return;
+ // Create the index file:
+ index = new cIndexFile(FileName, false);
+ if (!index)
+ esyslog(LOG_ERR, "ERROR: can't allocate index");
+ // let's continue without index, so we'll at least have the recording
+ Start();
}
cReplayBuffer::~cReplayBuffer()
{
+ active = false;
+ Cancel(3);
Close();
+ SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ SetPlayMode(videoDev, VID_PLAY_RESET);
+ delete index;
+}
+
+void cReplayBuffer::Action(void)
+{
+ dsyslog(LOG_INFO, "replay thread started (pid=%d)", getpid());
+
+ bool Paused = false;
+ bool FastForward = false;
+ bool FastRewind = false;
+
+ int ResumeIndex = Resume();
+ if (ResumeIndex >= 0)
+ isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, IndexToHMSF(ResumeIndex, true));
+ active = true;
+ while (active) {
+ usleep(1); // this keeps the CPU load low
+
+ LOCK_THREAD;
+
+ if (command != rcNone) {
+ switch (command) {
+ case rcStill: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ SetPlayMode(videoDev, VID_PLAY_NORMAL);
+ SetMode(rmStill);
+ Paused = FastForward = FastRewind = false;
+ break;
+ case rcPause: SetPlayMode(videoDev, Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
+ Paused = !Paused;
+ if (FastForward || FastRewind) {
+ SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ Clear();
+ }
+ FastForward = FastRewind = false;
+ SetMode(rmPlay);
+ if (!Paused)
+ stillIndex = -1;
+ break;
+ case rcPlay: if (FastForward || FastRewind || Paused) {
+ SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ SetPlayMode(videoDev, VID_PLAY_NORMAL);
+ FastForward = FastRewind = Paused = false;
+ SetMode(rmPlay);
+ }
+ stillIndex = -1;
+ break;
+ case rcForward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ Clear();
+ FastForward = !FastForward;
+ FastRewind = false;
+ if (Paused) {
+ SetMode(rmPlay);
+ SetPlayMode(videoDev, FastForward ? VID_PLAY_SLOW_MOTION : VID_PLAY_PAUSE);
+ }
+ else {
+ SetPlayMode(videoDev, VID_PLAY_NORMAL);
+ SetMode(FastForward ? rmFastForward : rmPlay);
+ }
+ stillIndex = -1;
+ break;
+ case rcBackward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ Clear();
+ FastRewind = !FastRewind;
+ FastForward = false;
+ if (Paused) {
+ SetMode(FastRewind ? rmSlowRewind : rmPlay);
+ SetPlayMode(videoDev, FastRewind ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
+ }
+ else {
+ SetPlayMode(videoDev, VID_PLAY_NORMAL);
+ SetMode(FastRewind ? rmFastRewind : rmPlay);
+ }
+ stillIndex = -1;
+ break;
+ default: break;
+ }
+ command = rcNone;
+ }
+ if (Read() < 0 || Write() < 0)
+ break;
+ }
+ Save();
+
+ dsyslog(LOG_INFO, "end replaying thread");
}
void cReplayBuffer::Close(void)
{
if (replayFile >= 0) {
- if (close(replayFile) < 0)
- LOG_ERROR;
+ fileName.Close();
replayFile = -1;
fileOffset = 0;
}
@@ -878,7 +1128,6 @@ void cReplayBuffer::Close(void)
void cReplayBuffer::SetMode(eReplayMode Mode)
{
mode = Mode;
- skipAudio = Mode != rmPlay;
brakeCounter = 0;
if (mode != rmPlay)
Clear();
@@ -901,14 +1150,11 @@ int cReplayBuffer::Resume(void)
bool cReplayBuffer::Save(void)
{
if (index) {
- int Index = index->Get(fileNumber, fileOffset);
+ int Index = index->Get(fileName.Number(), fileOffset);
if (Index >= 0) {
Index -= RESUMEBACKUP;
- if (Index > 0) {
- uchar FileNumber;
- int FileOffset;
- Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset);
- }
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
else
Index = 0;
if (Index >= 0)
@@ -918,15 +1164,39 @@ bool cReplayBuffer::Save(void)
return false;
}
+int cReplayBuffer::SkipFrames(int Frames)
+{
+ if (index && Frames) {
+
+ LOCK_THREAD;
+
+ int Current, Total;
+ GetIndex(Current, Total, true);
+ int OldCurrent = Current;
+ Current = index->GetNextIFrame(Current + Frames, Frames > 0);
+ return Current >= 0 ? Current : OldCurrent;
+ }
+ return -1;
+}
+
void cReplayBuffer::SkipSeconds(int Seconds)
{
+ LOCK_THREAD;
+
+ SetPlayMode(videoDev, VID_PLAY_PAUSE);
+ SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ SetPlayMode(videoDev, VID_PLAY_NORMAL);
+ command = rcPlay;
+ SetMode(rmPlay);
+ Clear();
+
if (index && Seconds) {
- int Index = index->Get(fileNumber, fileOffset);
+ int Index = index->Get(fileName.Number(), fileOffset);
if (Index >= 0) {
if (Seconds < 0) {
int sec = index->Last() / FRAMESPERSEC;
if (Seconds < -sec)
- Seconds = - sec;
+ Seconds = -sec;
}
Index += Seconds * FRAMESPERSEC;
if (Index < 0)
@@ -934,103 +1204,120 @@ void cReplayBuffer::SkipSeconds(int Seconds)
uchar FileNumber;
int FileOffset;
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
- if ((Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset)) >= 0)
NextFile(FileNumber, FileOffset);
}
}
}
-void cReplayBuffer::GetIndex(int &Current, int &Total)
+void cReplayBuffer::Goto(int Index, bool Still)
{
- if (index) {
- Current = index->Get(fileNumber, fileOffset);
- Total = index->Last();
- }
- else
- Current = Total = -1;
+ LOCK_THREAD;
+
+ if (Still)
+ command = rcStill;
+ if (++Index <= 0)
+ Index = 1; // not '0', to allow GetNextIFrame() below to work!
+ uchar FileNumber;
+ int FileOffset;
+ if ((stillIndex = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset)) >= 0)
+ NextFile(FileNumber, FileOffset);
+ SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
+ Clear();
}
-void cReplayBuffer::SkipAudioBlocks(void)
+void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
- int Length;
+ if (index) {
- while ((Length = GetAvPesLength()) > 0) {
- if (GetAvPesType() == AV_PES_AUDIO)
- Skip(Length);
- else
- break;
+ LOCK_THREAD;
+
+ if (stillIndex >= 0)
+ Current = stillIndex;
+ else {
+ Current = index->Get(fileName.Number(), fileOffset);
+ if (SnapToIFrame) {
+ int i1 = index->GetNextIFrame(Current + 1, false);
+ int i2 = index->GetNextIFrame(Current, true);
+ Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
+ }
}
+ Total = index->Last();
+ }
+ else
+ Current = Total = -1;
}
bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0) {
Clear();
- if (FileNumber != fileNumber) {
- Close();
- fileNumber = FileNumber;
- }
+ fileOffset = FileOffset;
+ replayFile = fileName.SetOffset(FileNumber, FileOffset);
}
- if (replayFile >= 0 && EndOfFile()) {
+ else if (replayFile >= 0 && EndOfFile()) {
Close();
- fileNumber++;
- if (fileNumber == 0)
- esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
+ replayFile = fileName.NextFile();
}
- if (replayFile < 0) {
- sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
- if (access(fileName, R_OK) == 0) {
- dsyslog(LOG_INFO, "playing '%s'", fileName);
- replayFile = open(fileName, O_RDONLY | O_NONBLOCK);
- if (replayFile < 0) {
- LOG_ERROR;
- return false;
+ return replayFile >= 0;
+}
+
+void cReplayBuffer::SetTemporalReference(void)
+{
+ for (int i = 0; i < Available(); i++) {
+ if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
+ switch (Byte(i + 3)) {
+ case SC_PICTURE: {
+ unsigned short m = (Byte(i + 4) << 8) | Byte(i + 5);
+ m &= 0x003F;
+ Set(i + 4, 1, m >> 8);
+ Set(i + 5, 1, m & 0xFF);
+ }
+ return;
}
- }
- else if (errno != ENOENT)
- LOG_ERROR;
- }
- if (replayFile >= 0) {
- if (FileOffset >= 0) {
- if ((fileOffset = lseek(replayFile, FileOffset, SEEK_SET)) != FileOffset)
- LOG_ERROR;
- // don't return 'false', maybe we can still replay the file
- }
- return true;
- }
- return false;
+ }
+ }
}
int cReplayBuffer::Read(int Max = -1)
{
- if (stop)
- return -1;
if (mode != rmPlay) {
if (index) {
if (Available())
return 0; // write out the entire block
- int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileNumber, fileOffset);
- if (Index >= 0) {
+ if (mode == rmStill) {
uchar FileNumber;
int FileOffset, Length;
- if (mode == rmSlowRewind && (brakeCounter++ % 24) != 0) {
- // show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
- Index = index->GetNextIFrame(Index, true, &FileNumber, &FileOffset, &Length); // jump ahead one frame
- }
- Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
- if (Index >= 0) {
+ if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
if (!NextFile(FileNumber, FileOffset))
return -1;
Max = Length;
}
- lastIndex = Index;
+ command = rcPause;
}
- if (Index < 0) {
- // This results in normal replay after a fast rewind.
- // After a fast forward it will stop.
- // TODO Could we cause it to pause at the last frame?
- SetMode(rmPlay);
- return 0;
+ else {
+ int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
+ if (Index >= 0) {
+ 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); // jump ahead one frame
+ }
+ uchar FileNumber;
+ int FileOffset, Length;
+ Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0) {
+ if (!NextFile(FileNumber, FileOffset))
+ return -1;
+ Max = Length;
+ }
+ lastIndex = Index;
+ }
+ if (Index < 0) {
+ // This results in normal replay after a fast rewind.
+ // After a fast forward it will stop.
+ // TODO Could we cause it to pause at the last frame?
+ SetMode(rmPlay);
+ return 0;
+ }
}
}
}
@@ -1041,12 +1328,22 @@ int cReplayBuffer::Read(int Max = -1)
int readin = 0;
do {
// If Max is > 0 here we need to make sure we read in the entire block!
- int r = cFileBuffer::Read(Max);
+ int r = cRingBuffer::Read(Max);
if (r >= 0)
readin += r;
else
return -1;
} while (readin < Max && Free() > 0);
+ if (mode != rmPlay) {
+ // delete the audio data in modes other than rmPlay:
+ int AudioOffset, StartOffset = 0;
+ while ((AudioOffset = FindStartCode(SC_AUDIO, StartOffset)) >= 0) {
+ if (!Set(StartOffset + AudioOffset, GetPacketLength(StartOffset + AudioOffset), 0))
+ break; // to be able to replay old AV_PES recordings!
+ StartOffset += AudioOffset;
+ }
+ SetTemporalReference();
+ }
return readin;
}
if (Available() > 0)
@@ -1057,16 +1354,10 @@ int cReplayBuffer::Read(int Max = -1)
int cReplayBuffer::Write(int Max)
{
int Written = 0;
- int Av = Available();
- if (skipAudio) {
- SkipAudioBlocks();
- Max = GetAvPesLength();
- fileOffset += Av - Available();
- }
if (Max) {
int w;
do {
- w = cFileBuffer::Write(Max);
+ w = cRingBuffer::Write(Max);
if (w >= 0) {
fileOffset += w;
Written += w;
@@ -1076,7 +1367,7 @@ int cReplayBuffer::Write(int Max)
}
else
return w;
- } while (Max > 0); // we MUST write this entire AV_PES block
+ } while (Max > 0); // we MUST write this entire frame block
}
return Written;
}
@@ -1098,44 +1389,188 @@ cTransferBuffer::cTransferBuffer(int FromDevice, int ToDevice)
{
fromDevice = FromDevice;
toDevice = ToDevice;
- active = true;
+ active = false;
Start();
}
cTransferBuffer::~cTransferBuffer()
{
- {
- LOCK_THREAD;
- active = false;
- }
- for (time_t t0 = time(NULL); time(NULL) - t0 < 3; ) {
- LOCK_THREAD;
- if (active)
- break;
- }
+ active = false;
+ Cancel(3);
+ SetPlayMode(fromDevice, VID_PLAY_RESET);
+ SetPlayMode(toDevice, VID_PLAY_RESET);
}
void cTransferBuffer::Action(void)
{
dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
- //XXX hack to make the video device go into 'replaying' mode:
- char *dummy = "AV"; // must be "AV" to make the driver go into AV_PES mode!
- write(toDevice, dummy, strlen(dummy));
- {
- cRingBuffer Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
- while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start
- Buffer.Read(); // initializes fromDevice for reading
- usleep(1); // this keeps the CPU load low
- }
- for (; active;) {
+
+ cRingBuffer Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
+ active = true;
+ while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start
+ Buffer.Read(); // initializes fromDevice for reading
+ usleep(1); // this keeps the CPU load low
+ }
+ while (active) {
if (Buffer.Read() < 0 || Buffer.Write() < 0)
break;
usleep(1); // this keeps the CPU load low
}
- }
dsyslog(LOG_INFO, "data transfer thread stopped (pid=%d)", getpid());
- LOCK_THREAD;
- active = true;
+}
+
+// --- cCuttingBuffer --------------------------------------------------------
+
+class cCuttingBuffer : public cRingBuffer, public cThread {
+private:
+ bool active;
+ int fromFile, toFile;
+ cFileName *fromFileName, *toFileName;
+ cIndexFile *fromIndex, *toIndex;
+ cMarks fromMarks, toMarks;
+protected:
+ virtual void Action(void);
+public:
+ cCuttingBuffer(const char *FromFileName, const char *ToFileName);
+ virtual ~cCuttingBuffer();
+ };
+
+cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
+:cRingBuffer(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+{
+ active = false;
+ fromFile = toFile = -1;
+ fromFileName = toFileName = NULL;
+ fromIndex = toIndex = NULL;
+ if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
+ fromFileName = new cFileName(FromFileName, false);
+ toFileName = new cFileName(ToFileName, true);
+ fromIndex = new cIndexFile(FromFileName, false);
+ toIndex = new cIndexFile(ToFileName, true);
+ toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
+ Start();
+ }
+ else
+ esyslog(LOG_ERR, "no editing marks found for %s", FromFileName);
+}
+
+cCuttingBuffer::~cCuttingBuffer()
+{
+ active = false;
+ Cancel(3);
+ delete fromFileName;
+ delete toFileName;
+ delete fromIndex;
+ delete toIndex;
+}
+
+void cCuttingBuffer::Action(void)
+{
+ dsyslog(LOG_INFO, "video cutting thread started (pid=%d)", getpid());
+
+ cMark *Mark = fromMarks.First();
+ if (Mark) {
+ fromFile = fromFileName->Open();
+ toFile = toFileName->Open();
+ active = fromFile >= 0 && toFile >= 0;
+ int Index = Mark->position;
+ Mark = fromMarks.Next(Mark);
+ int FileSize = 0;
+ int CurrentFileNumber = 0;
+ int LastIFrame = 0;
+ toMarks.Add(0);
+ toMarks.Save();
+ while (active) {
+ uchar FileNumber;
+ int FileOffset, Length;
+ uchar PictureType;
+
+ Clear(); // makes sure one frame is completely read and written
+
+ // Read one frame:
+
+ if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
+ if (FileNumber != CurrentFileNumber) {
+ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+ CurrentFileNumber = FileNumber;
+ }
+ if (fromFile >= 0)
+ Length = cRingBuffer::Read(Length);
+ else
+ break;
+ }
+ else
+ break;
+
+ // Write one frame:
+
+ if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
+ if (FileSize > MAXVIDEOFILESIZE) {
+ toFile = toFileName->NextFile();
+ if (toFile < 0)
+ break;
+ FileSize = 0;
+ }
+ LastIFrame = 0;
+ }
+ cRingBuffer::Write(Length);
+ toIndex->Write(PictureType, toFileName->Number(), FileSize);
+ FileSize += Length;
+ if (!LastIFrame)
+ LastIFrame = toIndex->Last();
+
+ // Check editing marks:
+
+ if (Mark && Index >= Mark->position) {
+ Mark = fromMarks.Next(Mark);
+ if (Mark) {
+ Index = Mark->position;
+ Mark = fromMarks.Next(Mark);
+ CurrentFileNumber = 0; // triggers SetOffset before reading next frame
+ toMarks.Add(LastIFrame);
+ toMarks.Add(toIndex->Last() + 1);
+ toMarks.Save();
+ }
+ else
+ break; // final end mark reached
+ }
+ }
+ }
+ else
+ esyslog(LOG_ERR, "no editing marks found!");
+ dsyslog(LOG_INFO, "end video cutting thread");
+}
+
+// --- cVideoCutter ----------------------------------------------------------
+
+cCuttingBuffer *cVideoCutter::cuttingBuffer = NULL;
+
+bool cVideoCutter::Start(const char *FileName)
+{
+ if (!cuttingBuffer) {
+ const char *EditedVersionName = PrefixVideoFileName(FileName, '%');
+ if (EditedVersionName && RemoveVideoFile(EditedVersionName) && MakeDirs(EditedVersionName, true)) {
+ cuttingBuffer = new cCuttingBuffer(FileName, EditedVersionName);
+ return true;
+ }
+ }
+ return false;
+}
+
+void cVideoCutter::Stop(void)
+{
+ delete cuttingBuffer;
+ cuttingBuffer = NULL;
+}
+
+bool cVideoCutter::Active(void)
+{
+ if (cuttingBuffer) {
+ if (cuttingBuffer->Active())
+ return true;
+ Stop();
+ }
+ return false;
}
// --- cDvbApi ---------------------------------------------------------------
@@ -1147,13 +1582,12 @@ cDvbApi *cDvbApi::PrimaryDvbApi = NULL;
cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
{
siProcessor = NULL;
- pidRecord = pidReplay = 0;
- fromRecord = toRecord = -1;
- fromReplay = toReplay = -1;
- ca = 0;
- priority = -1;
+ recordBuffer = NULL;
+ replayBuffer = NULL;
transferBuffer = NULL;
transferringFromDvbApi = NULL;
+ ca = 0;
+ priority = -1;
videoDev = open(VideoFileName, O_RDWR | O_NONBLOCK);
if (videoDev >= 0) {
siProcessor = new cSIProcessor(VbiFileName);
@@ -1191,8 +1625,6 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
#else
osd = NULL;
#endif
- lastProgress = lastTotal = -1;
- replayTitle = NULL;
currentChannel = 1;
}
@@ -1201,7 +1633,7 @@ cDvbApi::~cDvbApi()
if (videoDev >= 0) {
delete siProcessor;
Close();
- Stop();
+ StopReplay();
StopRecord();
StopTransfer();
OvlO(false); //Overlay off!
@@ -1211,7 +1643,6 @@ cDvbApi::~cDvbApi()
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
endwin();
#endif
- delete replayTitle;
}
bool cDvbApi::SetPrimaryDvbApi(int n)
@@ -1618,8 +2049,6 @@ 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 = lastTotal = -1;
}
void cDvbApi::Close(void)
@@ -1633,7 +2062,6 @@ void cDvbApi::Close(void)
delete osd;
osd = NULL;
#endif
- lastProgress = lastTotal = -1;
}
void cDvbApi::Clear(void)
@@ -1662,6 +2090,13 @@ void cDvbApi::Fill(int x, int y, int w, int h, eDvbColor color)
#endif
}
+void cDvbApi::SetBitmap(int x, int y, const cBitmap &Bitmap)
+{
+#ifndef DEBUG_OSD
+ osd->SetBitmap(x, y, Bitmap);
+#endif
+}
+
void cDvbApi::ClrEol(int x, int y, eDvbColor color)
{
Fill(x, y, cols - x, 1, color);
@@ -1676,6 +2111,15 @@ int cDvbApi::CellWidth(void)
#endif
}
+int cDvbApi::LineHeight(void)
+{
+#ifdef DEBUG_OSD
+ return 1;
+#else
+ return lineHeight;
+#endif
+}
+
int cDvbApi::Width(unsigned char c)
{
#ifdef DEBUG_OSD
@@ -1724,67 +2168,12 @@ void cDvbApi::Flush(void)
#endif
}
-bool cDvbApi::ShowProgress(bool Initial)
-{
- int Current, Total;
-
- if (GetIndex(&Current, &Total)) {
- if (Initial) {
- Clear();
- if (replayTitle)
- Text(0, 0, replayTitle);
- }
- if (Total != lastTotal)
- Text(-7, 2, cIndexFile::Str(Total));
- Flush();
-#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)
- osd->Fill(0, y1, w - 1, y2, clrWhite);
- osd->Fill(x1, y1, x2, y2, color);
- lastProgress = p;
- }
- Flush();
-#endif
- Text(0, 2, cIndexFile::Str(Current));
- Flush();
- lastTotal = Total;
- return true;
- }
- return false;
-}
-
bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Ca, int Pnr)
{
if (videoDev >= 0) {
+ cThreadLock ThreadLock(siProcessor); // makes sure the siProcessor won't access the vbi-device while switching
StopTransfer();
- if (transferringFromDvbApi) {
- transferringFromDvbApi->StopTransfer();
- transferringFromDvbApi = NULL;
- }
- SetReplayMode(VID_PLAY_RESET);
+ SetPlayMode(videoDev, VID_PLAY_RESET);
struct frontend front;
ioctl(videoDev, VIDIOCGFRONTEND, &front);
unsigned int freq = FrequencyMHz;
@@ -1793,8 +2182,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
freq -= Setup.LnbFrequLo;
else
freq -= Setup.LnbFrequHi;
- front.channel_flags = Ca ? DVB_CHANNEL_CA : DVB_CHANNEL_FTA;
- front.pnr = Pnr;
+ front.pnr = 0;
front.freq = freq * 1000000UL;
front.diseqc = Diseqc;
front.srate = Srate * 1000;
@@ -1821,7 +2209,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
}
return true;
}
- esyslog(LOG_ERR, "ERROR: channel not sync'ed (front.sync=%X)!", front.sync);
+ esyslog(LOG_ERR, "ERROR: channel %d not sync'ed (front.sync=%X)!", ChannelNumber, front.sync);
}
return false;
}
@@ -1843,22 +2231,31 @@ void cDvbApi::StopTransfer(void)
if (transferBuffer) {
delete transferBuffer;
transferBuffer = NULL;
- SetReplayMode(VID_PLAY_RESET);
+ SetPlayMode(videoDev, VID_PLAY_RESET);
+ }
+ if (transferringFromDvbApi) {
+ transferringFromDvbApi->StopTransfer();
+ transferringFromDvbApi = NULL;
}
}
+int cDvbApi::SecondsToFrames(int Seconds)
+{
+ return Seconds * FRAMESPERSEC;
+}
+
bool cDvbApi::Recording(void)
{
- if (pidRecord && !CheckProcess(pidRecord))
- pidRecord = 0;
- return pidRecord;
+ if (recordBuffer && !recordBuffer->Active())
+ StopRecord();
+ return recordBuffer != NULL;
}
bool cDvbApi::Replaying(void)
{
- if (pidReplay && !CheckProcess(pidReplay))
- pidReplay = 0;
- return pidReplay;
+ if (replayBuffer && !replayBuffer->Active())
+ StopReplay();
+ return replayBuffer != NULL;
}
bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
@@ -1871,7 +2268,7 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
StopTransfer();
- Stop(); // TODO: remove this if the driver is able to do record and replay at the same time
+ StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
// Check FileName:
@@ -1886,129 +2283,41 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
if (!MakeDirs(FileName, true))
return false;
- // Open pipes for recording process:
+ // Create recording buffer:
- int fromRecordPipe[2], toRecordPipe[2];
+ recordBuffer = new cRecordBuffer(&videoDev, FileName);
- if (pipe(fromRecordPipe) != 0) {
- LOG_ERROR;
- return false;
- }
- if (pipe(toRecordPipe) != 0) {
- LOG_ERROR;
- return false;
- }
-
- // Create recording process:
-
- pidRecord = fork();
- if (pidRecord < 0) {
- LOG_ERROR;
- return false;
- }
- if (pidRecord == 0) {
-
- // This is the actual recording process
-
- dsyslog(LOG_INFO, "start recording process (pid=%d)", getpid());
- bool DataStreamBroken = false;
- int fromMain = toRecordPipe[0];
- int toMain = fromRecordPipe[1];
- cRecordBuffer *Buffer = new cRecordBuffer(&videoDev, FileName);
- if (Buffer) {
- for (;;) {
- fd_set set;
- FD_ZERO(&set);
- FD_SET(videoDev, &set);
- FD_SET(fromMain, &set);
- 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();
- ForceEnd = DataStreamBroken;
- break;
- }
- }
- }
- else {
- DataStreamBroken = true;
- esyslog(LOG_ERR, "ERROR: video data stream broken");
- }
- if (Buffer->WriteWithTimeout(ForceEnd) < 0)
- break;
- }
- delete Buffer;
- }
- else
- esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
- close(fromMain);
- close(toMain);
- dsyslog(LOG_INFO, "end recording process");
- exit(0);
+ if (recordBuffer) {
+ ca = Ca;
+ priority = Priority;
+ return true;
}
-
- // Establish communication with the recording process:
-
- fromRecord = fromRecordPipe[0];
- toRecord = toRecordPipe[1];
-
- ca = Ca;
- priority = Priority;
- return true;
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
}
return false;
}
void cDvbApi::StopRecord(void)
{
- if (pidRecord) {
- writechar(toRecord, dvbStop);
- close(toRecord);
- close(fromRecord);
- toRecord = fromRecord = -1;
- KillProcess(pidRecord);
- pidRecord = 0;
+ if (recordBuffer) {
+ delete recordBuffer;
+ recordBuffer = NULL;
ca = 0;
priority = -1;
- SetReplayMode(VID_PLAY_RESET); //XXX
}
}
-void cDvbApi::SetReplayMode(int Mode)
-{
- if (videoDev >= 0) {
- struct video_play_mode pmode;
- pmode.mode = Mode;
- ioctl(videoDev, VIDIOCSPLAYMODE, &pmode);
- }
-}
-
-bool cDvbApi::StartReplay(const char *FileName, const char *Title)
+bool cDvbApi::StartReplay(const char *FileName)
{
if (Recording()) {
esyslog(LOG_ERR, "ERROR: StartReplay() called while recording - ignored!");
return false;
}
StopTransfer();
- Stop();
+ StopReplay();
if (videoDev >= 0) {
- lastProgress = lastTotal = -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) {
@@ -2017,209 +2326,77 @@ bool cDvbApi::StartReplay(const char *FileName, const char *Title)
}
isyslog(LOG_INFO, "replay %s", FileName);
- // Open pipes for replay process:
-
- int fromReplayPipe[2], toReplayPipe[2];
-
- if (pipe(fromReplayPipe) != 0) {
- LOG_ERROR;
- return false;
- }
- if (pipe(toReplayPipe) != 0) {
- LOG_ERROR;
- return false;
- }
-
- // Create replay process:
+ // Create replay buffer:
- pidReplay = fork();
- if (pidReplay < 0) {
- LOG_ERROR;
- return false;
- }
- if (pidReplay == 0) {
-
- // This is the actual replaying process
-
- dsyslog(LOG_INFO, "start replaying process (pid=%d)", getpid());
- int fromMain = toReplayPipe[0];
- int toMain = fromReplayPipe[1];
- cReplayBuffer *Buffer = new cReplayBuffer(&videoDev, FileName);
- if (Buffer) {
- bool Paused = false;
- bool FastForward = false;
- bool FastRewind = false;
- int ResumeIndex = Buffer->Resume();
- if (ResumeIndex >= 0)
- isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, cIndexFile::Str(ResumeIndex, true));
- for (;;) {
- if (Buffer->Read() < 0)
- break;
- fd_set setIn, setOut;
- FD_ZERO(&setIn);
- FD_ZERO(&setOut);
- FD_SET(fromMain, &setIn);
- FD_SET(videoDev, &setOut);
- struct timeval timeout;
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
- if (select(FD_SETSIZE, &setIn, &setOut, NULL, &timeout) > 0) {
- if (FD_ISSET(videoDev, &setOut)) {
- if (Buffer->Write() < 0)
- break;
- }
- if (FD_ISSET(fromMain, &setIn)) {
- switch (readchar(fromMain)) {
- case dvbStop: SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- Buffer->Stop();
- break;
- case dvbPause: SetReplayMode(Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
- Paused = !Paused;
- if (FastForward || FastRewind) {
- SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- Buffer->Clear();
- }
- FastForward = FastRewind = false;
- Buffer->SetMode(rmPlay);
- break;
- case dvbPlay: if (FastForward || FastRewind || Paused) {
- SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- SetReplayMode(VID_PLAY_NORMAL);
- FastForward = FastRewind = Paused = false;
- Buffer->SetMode(rmPlay);
- }
- break;
- case 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);
- Buffer->SetMode(FastForward ? rmFastForward : rmPlay);
- }
- 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);
- Buffer->SetMode(FastRewind ? rmFastRewind : rmPlay);
- }
- break;
- case dvbSkip: {
- int Seconds;
- if (readint(fromMain, Seconds)) {
- SetReplayMode(VID_PLAY_CLEAR_BUFFER);
- SetReplayMode(VID_PLAY_NORMAL);
- FastForward = FastRewind = Paused = false;
- Buffer->SetMode(rmPlay);
- Buffer->SkipSeconds(Seconds);
- }
- }
- break;
- case dvbGetIndex: {
- int Current, Total;
- Buffer->GetIndex(Current, Total);
- writeint(toMain, Current);
- writeint(toMain, Total);
- }
- break;
- }
- }
- }
- }
- Buffer->Save();
- delete Buffer;
- }
- else
- esyslog(LOG_ERR, "ERROR: can't allocate replaying buffer");
- close(fromMain);
- close(toMain);
- SetReplayMode(VID_PLAY_RESET); //XXX
- dsyslog(LOG_INFO, "end replaying process");
- exit(0);
- }
-
- // Establish communication with the replay process:
-
- fromReplay = fromReplayPipe[0];
- toReplay = toReplayPipe[1];
- return true;
+ replayBuffer = new cReplayBuffer(&videoDev, FileName);
+ if (replayBuffer)
+ return true;
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate replaying buffer");
}
return false;
}
-void cDvbApi::Stop(void)
+void cDvbApi::StopReplay(void)
{
- if (pidReplay) {
- writechar(toReplay, dvbStop);
- close(toReplay);
- close(fromReplay);
- toReplay = fromReplay = -1;
- KillProcess(pidReplay);
- pidReplay = 0;
- SetReplayMode(VID_PLAY_RESET); //XXX
+ if (replayBuffer) {
+ delete replayBuffer;
+ replayBuffer = NULL;
}
}
void cDvbApi::Pause(void)
{
- if (pidReplay)
- writechar(toReplay, dvbPause);
+ if (replayBuffer)
+ replayBuffer->Pause();
}
void cDvbApi::Play(void)
{
- if (pidReplay)
- writechar(toReplay, dvbPlay);
+ if (replayBuffer)
+ replayBuffer->Play();
}
void cDvbApi::Forward(void)
{
- if (pidReplay)
- writechar(toReplay, dvbForward);
+ if (replayBuffer)
+ replayBuffer->Forward();
}
void cDvbApi::Backward(void)
{
- if (pidReplay)
- writechar(toReplay, dvbBackward);
+ if (replayBuffer)
+ replayBuffer->Backward();
}
-void cDvbApi::Skip(int Seconds)
+void cDvbApi::SkipSeconds(int Seconds)
{
- if (pidReplay) {
- writechar(toReplay, dvbSkip);
- writeint(toReplay, Seconds);
- }
+ if (replayBuffer)
+ replayBuffer->SkipSeconds(Seconds);
}
-bool cDvbApi::GetIndex(int *Current, int *Total)
+int cDvbApi::SkipFrames(int Frames)
{
- 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;
+ if (replayBuffer)
+ return replayBuffer->SkipFrames(Frames);
+ return -1;
+}
+
+bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
+{
+ if (replayBuffer) {
+ replayBuffer->GetIndex(Current, Total, SnapToIFrame);
+ return true;
}
return false;
}
+void cDvbApi::Goto(int Position, bool Still)
+{
+ if (replayBuffer)
+ replayBuffer->Goto(Position, Still);
+}
+
// --- cEITScanner -----------------------------------------------------------
cEITScanner::cEITScanner(void)
diff --git a/dvbapi.h b/dvbapi.h
index f6640ff..cf0584f 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.h 1.26 2000/11/19 14:09:41 kls Exp $
+ * $Id: dvbapi.h 1.30 2001/01/07 15:56:10 kls Exp $
*/
#ifndef __DVBAPI_H
@@ -22,6 +22,7 @@ typedef unsigned char __u8;
#include <dvb.h>
#include "dvbosd.h"
#include "eit.h"
+#include "thread.h"
// Overlay facilities
#define MAXCLIPRECTS 100
@@ -42,7 +43,24 @@ public:
bool Save(int Index);
};
+const char *IndexToHMSF(int Index, bool WithFrame = false);
+ // Converts the given index to a string, optionally containing the frame number.
+int HMSFToIndex(const char *HMSF);
+ // Converts the given string (format: "hh:mm:ss.ff") to an index.
+
+class cRecordBuffer;
+class cReplayBuffer;
class cTransferBuffer;
+class cCuttingBuffer;
+
+class cVideoCutter {
+private:
+ static cCuttingBuffer *cuttingBuffer;
+public:
+ static bool Start(const char *FileName);
+ static void Stop(void);
+ static bool Active(void);
+ };
class cDvbApi {
private:
@@ -129,22 +147,16 @@ public:
void Close(void);
void Clear(void);
void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
+ void SetBitmap(int x, int y, const cBitmap &Bitmap);
void ClrEol(int x, int y, eDvbColor color = clrBackground);
int CellWidth(void);
+ int LineHeight(void);
int Width(unsigned char c);
int WidthInCells(const char *s);
eDvbFont SetFont(eDvbFont Font);
void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground);
void Flush(void);
- // Progress Display facilities
-
-private:
- int lastProgress, lastTotal;
- char *replayTitle;
-public:
- bool ShowProgress(bool Initial = false);
-
// Channel facilities
private:
@@ -171,20 +183,10 @@ private:
// Record/Replay facilities
private:
- enum { dvbStop = 1, // let's not have 0 as a command
- dvbPause,
- dvbPlay,
- dvbForward,
- dvbBackward,
- dvbSkip,
- dvbGetIndex,
- };
- pid_t pidRecord, pidReplay;
- int fromRecord, toRecord;
- int fromReplay, toReplay;
+ cRecordBuffer *recordBuffer;
+ cReplayBuffer *replayBuffer;
int ca;
int priority;
- void SetReplayMode(int Mode);
protected:
int Ca(void) { return ca; }
// Returns the ca of the current recording session (0..MAXDVBAPI).
@@ -192,6 +194,8 @@ protected:
// Returns the priority of the current recording session (0..99),
// or -1 if no recording is currently active.
public:
+ int SecondsToFrames(int Seconds);
+ // Returns the number of frames corresponding to the given number of seconds.
bool Recording(void);
// Returns true if we are currently recording.
bool Replaying(void);
@@ -209,12 +213,11 @@ public:
// returned.
void StopRecord(void);
// Stops the current recording session (if any).
- bool StartReplay(const char *FileName, const char *Title = NULL);
+ bool StartReplay(const char *FileName);
// 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 Stop(void);
+ void StopReplay(void);
// Stops the current replay session (if any).
void Pause(void);
// Pauses the current replay session, or resumes a paused session.
@@ -224,12 +227,21 @@ public:
// Runs the current replay session forward at a higher speed.
void Backward(void);
// Runs the current replay session backwards at a higher speed.
- void Skip(int Seconds);
+ void SkipSeconds(int Seconds);
// Skips the given number of seconds in the current replay session.
// 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);
+ int SkipFrames(int Frames);
+ // Returns the new index into the current replay session after skipping
+ // the given number of frames (no actual repositioning is done!).
+ // The sign of 'Frames' determines the direction in which to skip.
+ bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
+ // Returns the current and total frame index, optionally snapped to the
+ // nearest I-frame.
+ void Goto(int Index, bool Still = false);
+ // Positions to the given index and displays that frame as a still picture
+ // if Still is true.
};
class cEITScanner {
diff --git a/dvbosd.c b/dvbosd.c
index 457175d..395ad32 100644
--- a/dvbosd.c
+++ b/dvbosd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbosd.c 1.6 2000/11/18 15:36:51 kls Exp $
+ * $Id: dvbosd.c 1.7 2000/12/09 11:13:00 kls Exp $
*/
#include "dvbosd.h"
@@ -82,6 +82,16 @@ void cBitmap::SetPixel(int x, int y, eDvbColor Color)
}
}
+void cBitmap::SetBitmap(int x, int y, const cBitmap &Bitmap)
+{
+ if (bitmap && Bitmap.bitmap) {
+ for (int ix = 0; ix < Bitmap.width; ix++) {
+ for (int iy = 0; iy < Bitmap.height; iy++)
+ SetPixel(x + ix, y + iy, eDvbColor(Bitmap.bitmap[Bitmap.width * iy + ix]));
+ }
+ }
+}
+
int cBitmap::Width(unsigned char c)
{
return font ? font->Width(c) : -1;
diff --git a/dvbosd.h b/dvbosd.h
index e2a424a..eefdb62 100644
--- a/dvbosd.h
+++ b/dvbosd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbosd.h 1.4 2000/11/18 15:25:25 kls Exp $
+ * $Id: dvbosd.h 1.5 2000/12/09 10:32:47 kls Exp $
*/
#ifndef __DVBOSD_H
@@ -57,6 +57,7 @@ public:
eDvbFont SetFont(eDvbFont Font);
bool Dirty(void);
void SetPixel(int x, int y, eDvbColor Color);
+ void SetBitmap(int x, int y, const cBitmap &Bitmap);
int Width(unsigned char c);
int Width(const char *s);
void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground);
diff --git a/eit.c b/eit.c
index 7985528..e7c60c1 100644
--- a/eit.c
+++ b/eit.c
@@ -13,7 +13,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: eit.c 1.9 2000/11/18 13:42:28 kls Exp $
+ * $Id: eit.c 1.11 2000/12/03 15:33:37 kls Exp $
***************************************************************************/
#include "eit.h"
@@ -33,6 +33,8 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
+#include "config.h"
+#include "videodir.h"
// --- cMJD ------------------------------------------------------------------
@@ -129,7 +131,7 @@ bool cMJD::SetSystemTime()
isyslog(LOG_INFO, "System Time = %s (%ld)\n", ctime(&loctim), loctim);
isyslog(LOG_INFO, "Local Time = %s (%ld)\n", ctime(&mjdtime), mjdtime);
if (stime(&mjdtime) < 0)
- esyslog(LOG_ERR, "ERROR while setting system time: %s", strerror(errno));
+ esyslog(LOG_ERR, "ERROR while setting system time: %m");
return true;
}
@@ -393,6 +395,21 @@ unsigned short cEventInfo::GetServiceID() const
return uServiceID;
}
+/** */
+void cEventInfo::Dump(FILE *f) const
+{
+ if (tTime + lDuration >= time(NULL)) {
+ fprintf(f, "E %u %ld %ld\n", uEventID, tTime, lDuration);
+ if (!isempty(pTitle))
+ fprintf(f, "T %s\n", pTitle);
+ if (!isempty(pSubtitle))
+ fprintf(f, "S %s\n", pSubtitle);
+ if (!isempty(pExtendedDescription))
+ fprintf(f, "D %s\n", pExtendedDescription);
+ fprintf(f, "e\n");
+ }
+}
+
// --- cSchedule -------------------------------------------------------------
cSchedule::cSchedule(unsigned short servid)
@@ -529,6 +546,19 @@ void cSchedule::Cleanup(time_t tTime)
}
}
+/** */
+void cSchedule::Dump(FILE *f) const
+{
+ cChannel *channel = Channels.GetByServiceID(uServiceID);
+ if (channel)
+ {
+ fprintf(f, "C %u %s\n", uServiceID, channel->name);
+ for (cEventInfo *p = Events.First(); p; p = Events.Next(p))
+ p->Dump(f);
+ fprintf(f, "c\n");
+ }
+}
+
// --- cSchedules ------------------------------------------------------------
cSchedules::cSchedules()
@@ -590,6 +620,13 @@ void cSchedules::Cleanup()
}
}
+/** */
+void cSchedules::Dump(FILE *f) const
+{
+ for (cSchedule *p = First(); p; p = Next(p))
+ p->Dump(f);
+}
+
// --- cEIT ------------------------------------------------------------------
#define DEC(N) dec << setw(N) << setfill(int('0'))
@@ -1080,7 +1117,7 @@ cSIProcessor::~cSIProcessor()
{
if (fsvbi >= 0)
{
- Stop();
+ Cancel();
ShutDownFilters();
delete filters;
if (!--numSIProcessors) // the last one deletes it
@@ -1105,6 +1142,7 @@ void cSIProcessor::Action()
unsigned int seclen;
unsigned int pid;
time_t lastCleanup = time(NULL);
+ time_t lastDump = time(NULL);
struct pollfd pfd;
while(true)
@@ -1123,6 +1161,19 @@ void cSIProcessor::Action()
schedulesMutex.Unlock();
lastCleanup = now;
}
+ if (now - lastDump > 600)
+ {
+ LOCK_THREAD;
+
+ schedulesMutex.Lock();
+ FILE *f = fopen(AddDirectory(VideoDirectory, "epg.data"), "w");
+ if (f) {
+ schedules->Dump(f);
+ fclose(f);
+ }
+ lastDump = now;
+ schedulesMutex.Unlock();
+ }
}
/* wait data become ready from the bitfilter */
@@ -1283,4 +1334,3 @@ bool cSIProcessor::RefreshFilters()
return ret;
}
-
diff --git a/eit.h b/eit.h
index d5c4ebd..bedead7 100644
--- a/eit.h
+++ b/eit.h
@@ -13,7 +13,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: eit.h 1.3 2000/11/17 16:14:27 kls Exp $
+ * $Id: eit.h 1.4 2000/11/24 14:35:22 kls Exp $
***************************************************************************/
#ifndef __EIT_H
@@ -66,6 +66,7 @@ public:
unsigned short GetServiceID(void) const;
int GetChannelNumber(void) const { return nChannelNumber; }
void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
+ void Dump(FILE *f) const;
};
class cSchedule : public cListObject {
@@ -92,6 +93,7 @@ public:
const cEventInfo *GetEvent(time_t tTime) const;
const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
int NumEvents(void) const { return Events.Count(); }
+ void Dump(FILE *f) const;
};
class cSchedules : public cList<cSchedule> {
@@ -107,6 +109,7 @@ public:
~cSchedules();
const cSchedule *GetSchedule(unsigned short servid) const;
const cSchedule *GetSchedule(void) const;
+ void Dump(FILE *f) const;
};
typedef struct sip_filter {
diff --git a/epg2html.pl b/epg2html.pl
new file mode 100644
index 0000000..215eb73
--- /dev/null
+++ b/epg2html.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+
+# A simple EPG to HTML converter
+#
+# Converts the EPG data written by 'vdr' into the file /video/epg.data
+# into a simple HTML programme listing, consisting of one file per channel
+# plus an 'index.htm' file. All output files are written into the current
+# directory.
+#
+# Usage: epg2html.pl < /video/epg.data
+#
+# See the main source file 'vdr.c' for copyright information and
+# how to reach the author.
+#
+# $Id: epg2html.pl 1.2 2000/12/01 18:37:46 kls Exp $
+
+@Index = ();
+
+sub GetDay
+{
+ return substr(localtime(shift), 0, 10);
+}
+
+sub GetTime
+{
+ return substr(localtime(shift), 11, 5);
+}
+
+sub Tags
+{
+ my $s = shift;
+ $s =~ s/\&/&amp;/g;
+ $s =~ s/</&lt;/g;
+ $s =~ s/>/&gt;/g;
+ return $s;
+}
+
+while (<>) {
+ chomp;
+ if (/^C ([^ ]+) *(.*)/) {
+ my $Channel = $2;
+ (my $Page = $Channel) =~ y/\/ /-_/;
+ $Page .= ".htm";
+ $Channel = Tags($Channel);
+ push(@Index, qq{<a href="$Page">$Channel</a><br>\n});
+ my %Events = ();
+ while (<>) {
+ if (/^E (.*) (.*) (.*)/) {
+ (my $Time, $Duration) = ($2, $3);
+ my $Title = "", $Subtitle = "", $Description = "";
+ while (<>) {
+ if (/^T (.*)/) { $Title = Tags($1); }
+ elsif (/^S (.*)/) { $Subtitle = Tags($1); }
+ elsif (/^D (.*)/) { $Description = Tags($1); }
+ elsif (/^e/) {
+ $Events{$Time} = [($Duration, $Title, $Subtitle, $Description)];
+ last;
+ }
+ }
+ }
+ elsif (/^c/) {
+ my @Schedule = ();
+ my $Day = "";
+ for $t (sort keys %Events) {
+ (my $Duration, $Title, $Subtitle, $Description) = @{$Events{$t}};
+ my $d = GetDay($t);
+ if ($d ne $Day) {
+ push(@Schedule, "</table>\n") if ($Day && @Schedule);
+ push(@Schedule, "<h2>$d</h2>\n");
+ push(@Schedule, "<table cellspacing=2>\n");
+ $Day = $d;
+ }
+ my $Entry = $Title;
+ $Entry .= "<br><i>$Subtitle</i>" if $Subtitle;
+ $Entry .= "<br>$Description" if $Description;
+ push(@Schedule, "<tr><td valign=top>" . GetTime($t) . "</td><td>$Entry</td></tr>\n");
+ }
+ push(@Schedule, "</table>\n") if (@Schedule);
+ open(PAGE, ">$Page") or die "$Page: $!\n";
+ print PAGE "<html>\n<head><title>$Channel</title><head>\n<body>\n";
+ print PAGE "<h1>$Channel</h1>\n";
+ print PAGE @Schedule;
+ print PAGE "</body>\n</html>\n";
+ close(PAGE);
+ last;
+ }
+ }
+ }
+ }
+
+open(INDEX, ">index.htm") or die "index.htm: $!\n";
+print INDEX "<html>\n<head><title>EPG Index</title><head>\n<body>\n";
+print INDEX sort { lc($a) cmp lc($b) } @Index;
+print INDEX "</body>\n</html>\n";
+close(INDEX);
+
diff --git a/i18n.c b/i18n.c
index 7b93062..cf1785f 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,9 +4,10 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.6 2000/11/19 12:12:53 kls Exp $
+ * $Id: i18n.c 1.8 2001/01/06 16:17:39 kls Exp $
*
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
+ * Italian translations provided by Alberto Carraro <bertocar@tin.it>
*
*/
@@ -31,9 +32,8 @@
* "Italiano",
* },
*
- * and so on. Insert your language so that all the entries
- * following 'English' will be sorted alphabetically, and write
- * the name of your language in your language (not in English,
+ * and so on. Append your language after the last existing language
+ * and write the name of your language in your language (not in English,
* which means that it should be 'Italiano', not 'Italian').
* Note that only the characters defined in 'fontosd.c' will
* be available!
@@ -48,7 +48,7 @@
#include "config.h"
#include "tools.h"
-const int NumLanguages = 3;
+const int NumLanguages = 4;
typedef const char *tPhrase[NumLanguages];
@@ -57,401 +57,523 @@ const tPhrase Phrases[] = {
{ "English",
"Deutsch",
"Slovenski",
+ "Italiano",
},
// Menu titles:
{ "Main",
"Hauptmenü",
"Glavni meni",
+ "Principale",
},
{ "Schedule",
"Programm",
"Urnik",
+ "Programmi",
},
{ "Channels",
"Kanäle",
"Kanali",
+ "Canali",
},
{ "Timers",
"Timer",
"Termini",
+ "Timer",
},
{ "Recordings",
"Aufzeichnungen",
"Posnetki",
+ "Registrazioni",
},
{ "Setup",
"Einstellungen",
"Nastavitve",
+ "Opzioni",
},
{ "Commands",
"Befehle",
"Ukazi",
+ "Comandi",
},
{ "Edit Channel",
"Kanal Editieren",
"Uredi kanal",
+ "Modifica canale",
},
{ "Edit Timer",
"Timer Editieren",
"Uredi termin",
+ "Modifica Timer",
},
{ "Event",
"Sendung",
"Oddaja",
+ "Eventi",
},
{ "Summary",
"Inhalt",
"Vsebina",
+ "Sommario",
},
{ "Schedule - %s",
"Programm - %s",
"Urnik - %s",
+ "Programma - %s",
},
{ "What's on now?",
"Was läuft jetzt?",
"Kaj je na sporedu?",
+ "In programmazione",
},
{ "What's on next?",
"Was läuft als nächstes?",
"Kaj sledi?",
+ "Prossimi programmi",
},
// Button texts (must not be more than 10 characters!):
{ "Edit",
"Editieren",
"Uredi",
+ "Modifica",
},
{ "New",
"Neu",
"Novo",
+ "Nuovo",
},
{ "Delete",
"Löschen",
"Odstrani",
+ "Cancella",
},
{ "Mark",
"Markieren",
"Oznaci",
+ "Marca",
},
{ "Record",
"Aufnehmen",
"Posnemi",
+ "Registra",
},
{ "Play",
"Wiedergabe",
"Predavajaj",
+ "Riproduci",
},
{ "Resume",
"Weiter",
"Nadaljuj",
+ "Riprendi",
},
{ "Summary",
"Inhalt",
"Vsebina",
+ "Sommario",
},
{ "Switch",
"Umschalten",
"Preklopi",
+ "Cambia",
},
{ "Now",
"Jetzt",
"Sedaj",
+ "Adesso",
},
{ "Next",
"Nächste",
"Naslednji",
+ "Prossimo",
},
{ "Schedule",
"Programm",
"Urnik",
+ "Programma",
},
// Confirmations:
- { "Delete Channel?",
+ { "Delete channel?",
"Kanal löschen?",
"Odstrani kanal?",
+ "Cancello il canale?",
},
- { "Delete Timer?",
+ { "Delete timer?",
"Timer löschen?",
"Odstani termin?",
+ "Cancello il timer?",
},
- { "Delete Recording?",
+ { "Delete recording?",
"Aufzeichnung löschen?",
"Odstrani posnetek?",
+ "Cancello la registrazione?",
},
- { "Stop Recording?",
+ { "Stop recording?",
"Aufzeichnung beenden?",
"Koncaj snemanje?",
+ "Fermo la registrazione?",
+ },
+ { "Cancel editing?",
+ "Schneiden abbrechen?",
+ "Zelite prekiniti urejanje?",
+ "Annullo la modifica?",
},
// Channel parameters:
{ "Name",
"Name",
"Naziv",
+ "Nome",
},
{ "Frequency",
"Frequenz",
"Frekvenca",
+ "Frequenza",
},
{ "Polarization",
"Polarisation",
"Polarizacija",
+ "Polarizzazione",
},
{ "Diseqc",
"Diseqc",
"Diseqc",
+ "Diseqc",
},
{ "Srate",
"Srate",
"Srate",
+ "Srate",
},
{ "Vpid",
"Vpid",
"Vpid",
+ "Vpid",
},
{ "Apid",
"Apid",
"Apid",
+ "Apid",
},
{ "CA",
"CA",
"CA",
+ "CA",
},
{ "Pnr",
"Pnr",
"Pnr",
+ "Pnr",
},
// Timer parameters:
{ "Active",
"Aktiv",
"Aktivno",
+ "Attivo",
},
{ "Channel",
"Kanal",
"Kanal",
+ "Canale",
},
{ "Day",
"Tag",
"Dan",
+ "Giorno",
},
{ "Start",
"Anfang",
"Zacetek",
+ "Inizio",
},
{ "Stop",
"Ende",
"Konec",
+ "Fine",
},
{ "Priority",
"Priorität",
"Prioriteta",
+ "Priorita",
},
{ "Lifetime",
"Lebensdauer",
"Veljavnost",
+ "Durata",
},
{ "File",
"Datei",
"Datoteka",
+ "Nome",
},
// Error messages:
{ "Channel is being used by a timer!",
"Kanal wird von einem Timer benutzt!",
"Urnik zaseda kanal!",
+ "Canale occupato da un timer!",
},
{ "Can't switch channel!",
"Kanal kann nicht umgeschaltet werden!",
"Ne morem preklopiti kanala!",
+ "Impossibile cambiare canale!",
},
{ "Timer is recording!",
"Timer zeichnet gerade auf!",
"Snemanje po urniku!",
+ "Registrazione di un timer in corso!",
},
{ "Error while deleting recording!",
"Fehler beim Löschen der Aufzeichnung!",
"Napaka pri odstranjevanju posnetka!",
+ "Errore durante la canc del filmato!",
},
{ "*** Invalid Channel ***",
"*** Ungültiger Kanal ***",
"*** Neznan kanal ***",
+ "*** CANALE INVALIDO ***",
},
{ "No free DVB device to record!",
"Keine freie DVB-Karte zum Aufnehmen!",
"Ni proste DVB naprave za snemanje!",
+ "Nessuna card DVB disp per registrare!",
},
{ "Channel locked (recording)!",
"Kanal blockiert (zeichnet auf)!",
"Zaklenjen kanal (snemanje)!",
+ "Canale bloccato (in registrazione)!",
+ },
+ { "Can't start editing process!",
+ "Schnitt kann nicht gestartet werden!",
+ "Ne morem zaceti urejanja!",
+ "Imposs iniziare processo di modifica",
+ },
+ { "Editing process already active!",
+ "Schnitt bereits aktiv!",
+ "Urejanje je ze aktivno!",
+ "Processo di modifica gia` attivo",
},
// Setup parameters:
{ "OSD-Language",
"OSD-Sprache",
"OSD-jezik",
+ "Linguaggio OSD",
},
{ "PrimaryDVB",
"Primäres Interface",
"Primarna naprava",
+ "Scheda DVB primaria",
},
{ "ShowInfoOnChSwitch",
"Info zeigen",
"Pokazi naziv kanala",
+ "Vis info nel cambio canale",
},
{ "MenuScrollPage",
"Seitenweise scrollen",
"Drsni meni",
+ "Scrolla pagina nel menu",
},
{ "MarkInstantRecord",
"Direktaufz. markieren",
"Oznaci direktno snemanje",
+ "Marca la registrazione",
},
{ "LnbFrequLo",
"Untere LNB-Frequenz",
"Spodnja LNB-frek.",
+ "Freq LO LNB",
},
{ "LnbFrequHi",
"Obere LNB-Frequenz",
"Zgornja LNB-frek.",
+ "Freq HI LNB",
},
{ "SetSystemTime",
"Systemzeit stellen",
"Sistemski cas",
+ "Setta orario auto",
},
{ "MarginStart",
"Zeitpuffer bei Anfang",
"Premor pred zacetkom",
+ "Min margine inizio",
},
{ "MarginStop",
"Zeitpuffer bei Ende",
"Premor za koncem",
+ "Min margine fine",
},
{ "EPGScanTimeout",
"Zeit bis EPG Scan",
"Cas do EPG pregleda",
+ "Timeout EPG",
},
// The days of the week:
{ "MTWTFSS",
"MDMDFSS",
"PTSCPSN",
+ "DLMMGVS",
},
// Learning keys:
{ "Learning Remote Control Keys",
"Fernbedienungs-Codes lernen",
"Ucim se kod upravljalca",
+ "Apprendimento tasti unita` remota",
},
{ "Phase 1: Detecting RC code type",
"Phase 1: FB Code feststellen",
"Faza 1: Sprejemanje IR kode",
+ "Fase 1: tipo ricevitore RC",
},
{ "Press any key on the RC unit",
"Eine Taste auf der FB drücken",
"Pritisnite tipko na upravljalcu",
+ "Premere un tasto nell'unita` RC",
},
{ "RC code detected!",
"FB Code erkannt!",
"IR koda sprejeta!",
+ "Codice RC rilevato!",
},
{ "Do not press any key...",
"Keine Taste drücken...",
"Ne pritiskajte tipk...",
+ "Non premere alcun tasto...",
},
{ "Phase 2: Learning specific key codes",
"Phase 2: Einzelne Tastencodes lernen",
"Faza 2: Ucenje posebnih kod",
+ "Fase 2: Codici specifici dei tasti",
},
{ "Press key for '%s'",
"Taste für '%s' drücken",
"Pritisnite tipko za '%s'",
+ "Premere il tasto per '%s'",
},
{ "Press 'Up' to confirm",
"'Auf' drücken zum Bestätigen",
"Pritisnite tipko 'Gor' za potrditev",
+ "Premere 'Su' per confermare",
},
{ "Press 'Down' to continue",
"'Ab' drücken zum Weitermachen",
"Pritisnite tipko 'Dol' za nadaljevanje",
+ "Premere 'Giu' per confermare",
},
{ "(press 'Up' to go back)",
"('Auf' drücken um zurückzugehen)",
"(pritisnite 'Gor' za nazaj)",
+ "(premere 'Su' per tornare indietro)",
},
{ "(press 'Down' to end key definition)",
"('Ab' drücken zum Beenden",
"(pritisnite 'Dol' za konec)",
+ "('Giu' per finire la definiz tasti)",
},
{ "Phase 3: Saving key codes",
"Phase 3: Codes abspeichern",
"Faza 3: Shranjujem kodo",
+ "Fase 3: Salvataggio key codes",
},
{ "Press 'Up' to save, 'Down' to cancel",
"'Auf' speichert, 'Ab' bricht ab",
"'Gor' za potrditev, 'Dol' za prekinitev",
+ "'Su' per salvare, 'Giu' per annullare",
},
// Key names:
{ "Up",
"Auf",
"Gor",
+ "Su",
},
{ "Down",
"Ab",
"Dol",
+ "Giu",
},
{ "Menu",
"Menü",
"Meni",
+ "Menu",
},
{ "Ok",
"Ok",
"Ok",
+ "Ok",
},
{ "Back",
"Zurück",
"Nazaj",
+ "Indietro",
},
{ "Left",
"Links",
"Levo",
+ "Sinistra",
},
{ "Right",
"Rechts",
"Desno",
+ "Destra",
},
{ "Red",
"Rot",
"Rdeca",
+ "Rosso",
},
{ "Green",
"Grün",
"Zelena",
+ "Verde",
},
{ "Yellow",
"Gelb",
"Rumena",
+ "Giallo",
},
{ "Blue",
"Blau",
"Modra",
+ "Blu",
},
// Miscellaneous:
{ "yes",
"ja",
"da",
+ "si",
},
{ "no",
"nein",
"ne",
+ "no",
},
{ "Stop replaying",
"Wiedergabe beenden",
"Prekini ponavljanje",
+ "Interrompi riproduzione",
},
{ "Stop recording ", // note the trailing blank!
"Aufzeichnung beenden ",
"Prekini shranjevanje ",
+ "Interrompi registrazione ",
+ },
+ { "Cancel editing",
+ "Schneiden abbrechen",
+ "Prekini urejanje",
+ "Annulla modifiche",
},
{ "Switching primary DVB...",
"Primäres Interface wird umgeschaltet...",
"Preklapljanje primarne naprave...",
+ "Cambio su card DVB primaria...",
},
{ "Up/Dn for new location - OK to move",
"Auf/Ab für neue Position - dann OK",
"Gor/Dol za novo poz. - Ok za premik",
+ "Su/Giu per nuova posizione - OK per muovere",
+ },
+ { "Editing process started",
+ "Schnitt gestartet",
+ "Urejanje se je zacelo",
+ "Processo di modifica iniziato",
},
{ NULL }
};
diff --git a/interface.c b/interface.c
index e5cc1d2..ef8368a 100644
--- a/interface.c
+++ b/interface.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.c 1.32 2000/11/18 15:28:50 kls Exp $
+ * $Id: interface.c 1.33 2000/12/09 11:04:10 kls Exp $
*/
#include "interface.h"
@@ -121,6 +121,12 @@ void cInterface::Fill(int x, int y, int w, int h, eDvbColor Color)
cDvbApi::PrimaryDvbApi->Fill(x, y, w, h, Color);
}
+void cInterface::SetBitmap(int x, int y, const cBitmap &Bitmap)
+{
+ if (open)
+ cDvbApi::PrimaryDvbApi->SetBitmap(x, y, Bitmap);
+}
+
void cInterface::Flush(void)
{
if (open)
diff --git a/interface.h b/interface.h
index cd46fba..50c3761 100644
--- a/interface.h
+++ b/interface.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.h 1.20 2000/11/18 15:27:59 kls Exp $
+ * $Id: interface.h 1.21 2000/12/09 10:48:41 kls Exp $
*/
#ifndef __INTERFACE_H
@@ -41,6 +41,7 @@ public:
void Clear(void);
void ClearEol(int x, int y, eDvbColor Color = clrBackground);
void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
+ void SetBitmap(int x, int y, const cBitmap &Bitmap);
void Flush(void);
void SetCols(int *c);
eDvbFont SetFont(eDvbFont Font);
diff --git a/menu.c b/menu.c
index 3cf8328..144233f 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.52 2000/11/18 16:30:13 kls Exp $
+ * $Id: menu.c 1.58 2001/01/13 13:07:43 kls Exp $
*/
#include "menu.h"
@@ -669,7 +669,7 @@ eOSState cMenuChannels::Del(void)
return osContinue;
}
}
- if (Interface->Confirm(tr("Delete Channel?"))) {
+ if (Interface->Confirm(tr("Delete channel?"))) {
// Move and renumber the channels:
Channels.Del(channel);
Channels.ReNumber();
@@ -1039,7 +1039,7 @@ eOSState cMenuTimers::Del(void)
cTimer *ti = Timers.Get(Index);
if (ti) {
if (!ti->recording) {
- if (Interface->Confirm(tr("Delete Timer?"))) {
+ if (Interface->Confirm(tr("Delete timer?"))) {
Timers.Del(Timers.Get(Index));
cOsdMenu::Del(Index);
Timers.Save();
@@ -1310,7 +1310,9 @@ private:
cThreadLock threadLock;
const cSchedules *schedules;
bool now, next;
+ int otherChannel;
eOSState Record(void);
+ eOSState Switch(void);
void PrepareSchedule(cChannel *Channel);
void PrepareWhatsOnNext(bool On);
public:
@@ -1322,6 +1324,7 @@ cMenuSchedule::cMenuSchedule(void)
:cOsdMenu("", 6, 6)
{
now = next = false;
+ otherChannel = 0;
cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
if (channel) {
schedules = cDvbApi::PrimaryDvbApi->Schedules(&threadLock);
@@ -1383,6 +1386,16 @@ eOSState cMenuSchedule::Record(void)
return osContinue;
}
+eOSState cMenuSchedule::Switch(void)
+{
+ if (otherChannel) {
+ if (Channels.SwitchTo(otherChannel))
+ return osEnd;
+ }
+ Interface->Error(tr("Can't switch channel!"));
+ return osContinue;
+}
+
eOSState cMenuSchedule::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
@@ -1398,8 +1411,9 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
next = !next;
return AddSubMenu(new cMenuWhatsOn(schedules, now));
case kYellow: return AddSubMenu(new cMenuWhatsOn(schedules, false));
+ case kBlue: return Switch();
case kOk: if (Count())
- return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->eventInfo));
+ return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->eventInfo, otherChannel));
break;
default: break;
}
@@ -1411,6 +1425,10 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
cChannel *channel = Channels.GetByServiceID(ei->GetServiceID());
if (channel) {
PrepareSchedule(channel);
+ if (channel->number != cDvbApi::CurrentChannel()) {
+ otherChannel = channel->number;
+ SetHelp(tr("Record"), tr("Now"), tr("Next"), tr("Switch"));
+ }
Display();
}
}
@@ -1471,7 +1489,7 @@ eOSState cMenuRecordings::Del(void)
if (ri) {
//XXX what if this recording's file is currently in use???
//XXX if (!ti->recording) {
- if (Interface->Confirm(tr("Delete Recording?"))) {
+ if (Interface->Confirm(tr("Delete recording?"))) {
if (ri->recording->Delete()) {
cReplayControl::ClearLastReplayed(ri->recording->FileName());
cOsdMenu::Del(Current());
@@ -1645,6 +1663,8 @@ cMenuMain::cMenuMain(bool Replaying)
Add(new cOsdItem(buffer, osStopRecord));
delete buffer;
}
+ if (cVideoCutter::Active())
+ Add(new cOsdItem(tr("Cancel editing"), osCancelEdit));
SetHelp(tr("Record"), NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
Display();
lastActivity = time(NULL);
@@ -1661,13 +1681,19 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
case osRecordings: return AddSubMenu(new cMenuRecordings);
case osSetup: return AddSubMenu(new cMenuSetup);
case osCommands: return AddSubMenu(new cMenuCommands);
- case osStopRecord: if (Interface->Confirm(tr("Stop Recording?"))) {
+ case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
cOsdItem *item = Get(Current());
if (item) {
cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING));
return osEnd;
}
}
+ break;
+ case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
+ cVideoCutter::Stop();
+ return osEnd;
+ }
+ break;
default: switch (Key) {
case kMenu: state = osEnd; break;
case kRed: if (!HasSubMenu())
@@ -1726,23 +1752,21 @@ cDisplayChannel::~cDisplayChannel()
void cDisplayChannel::DisplayChannel(const cChannel *Channel)
{
- if (!Interface->Recording()) {
- if (Channel && Channel->number)
- Interface->DisplayChannelNumber(Channel->number);
- int BufSize = Width() + 1;
- char buffer[BufSize];
- if (Channel && Channel->number)
- snprintf(buffer, BufSize, "%d %s", Channel->number, Channel->name);
- else
- snprintf(buffer, BufSize, "%s", Channel ? Channel->name : tr("*** Invalid Channel ***"));
- Interface->Fill(0, 0, MenuColumns, 1, clrBackground);
- Interface->Write(0, 0, buffer);
- time_t t = time(NULL);
- struct tm *now = localtime(&t);
- snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min);
- Interface->Write(-5, 0, buffer);
- Interface->Flush();
- }
+ if (Channel && Channel->number)
+ Interface->DisplayChannelNumber(Channel->number);
+ int BufSize = Width() + 1;
+ char buffer[BufSize];
+ if (Channel && Channel->number)
+ snprintf(buffer, BufSize, "%d %s", Channel->number, Channel->name);
+ else
+ snprintf(buffer, BufSize, "%s", Channel ? Channel->name : tr("*** Invalid Channel ***"));
+ Interface->Fill(0, 0, MenuColumns, 1, clrBackground);
+ Interface->Write(0, 0, buffer);
+ time_t t = time(NULL);
+ struct tm *now = localtime(&t);
+ snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min);
+ Interface->Write(-5, 0, buffer);
+ Interface->Flush();
}
void cDisplayChannel::DisplayInfo(void)
@@ -1874,7 +1898,6 @@ cRecordControl::~cRecordControl()
{
Stop(true);
delete instantId;
- Interface->DisplayRecording(dvbApi->Index(), false);
}
void cRecordControl::Stop(bool KeepInstant)
@@ -1890,6 +1913,7 @@ void cRecordControl::Stop(bool KeepInstant)
Timers.Save();
}
timer = NULL;
+ Interface->DisplayRecording(dvbApi->Index(), false);
}
}
@@ -1946,7 +1970,7 @@ void cRecordControls::Stop(cDvbApi *DvbApi)
if (RecordControls[i]) {
if (RecordControls[i]->Uses(DvbApi)) {
isyslog(LOG_INFO, "stopping recording on DVB device %d due to higher priority", DvbApi->Index() + 1);
- RecordControls[i]->Stop();
+ RecordControls[i]->Stop(true);
}
}
}
@@ -1975,6 +1999,50 @@ void cRecordControls::Process(void)
}
}
+// --- cProgressBar ----------------------------------------------------------
+
+class cProgressBar : public cBitmap {
+protected:
+ int total;
+ int Pos(int p) { return p * width / total; }
+ void Mark(int x, bool Start, bool Current);
+public:
+ cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks);
+ };
+
+cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks)
+:cBitmap(Width, Height)
+{
+ total = Total;
+ if (total > 0) {
+ int p = Pos(Current);
+ Fill(0, 0, p, Height - 1, clrGreen);
+ Fill(p + 1, 0, Width - 1, Height - 1, clrWhite);
+ bool Start = true;
+ for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) {
+ int p1 = Pos(m->position);
+ if (Start) {
+ const cMark *m2 = Marks.Next(m);
+ int p2 = Pos(m2 ? m2->position : total);
+ int h = Height / 3;
+ Fill(p1, h, p2, Height - h, clrRed);
+ }
+ Mark(p1, Start, m->position == Current);
+ Start = !Start;
+ }
+ }
+}
+
+void cProgressBar::Mark(int x, bool Start, bool Current)
+{
+ Fill(x, 0, x, height - 1, clrBlack);
+ const int d = height / (Current ? 3 : 9);
+ for (int i = 0; i < d; i++) {
+ int h = Start ? i : height - 1 - i;
+ Fill(x - d + i, h, x + d - i, h, Current ? clrRed : clrBlack);
+ }
+}
+
// --- cReplayControl --------------------------------------------------------
char *cReplayControl::fileName = NULL;
@@ -1982,16 +2050,18 @@ char *cReplayControl::title = NULL;
cReplayControl::cReplayControl(void)
{
- dvbApi = cDvbApi::PrimaryDvbApi;//XXX
- visible = shown = false;
- if (fileName)
- dvbApi->StartReplay(fileName, title);
+ dvbApi = cDvbApi::PrimaryDvbApi;
+ visible = shown = displayFrames = false;
+ if (fileName) {
+ marks.Load(fileName);
+ dvbApi->StartReplay(fileName);
+ }
}
cReplayControl::~cReplayControl()
{
Hide();
- dvbApi->Stop();
+ dvbApi->StopReplay();
}
void cReplayControl::SetRecording(const char *FileName, const char *Title)
@@ -2020,7 +2090,7 @@ void cReplayControl::Show(void)
if (!visible) {
Interface->Open(MenuColumns, -3);
needsFastResponse = visible = true;
- shown = dvbApi->ShowProgress(true);
+ shown = ShowProgress(true);
}
}
@@ -2032,27 +2102,148 @@ void cReplayControl::Hide(void)
}
}
+bool cReplayControl::ShowProgress(bool Initial)
+{
+ int Current, Total;
+
+ if (dvbApi->GetIndex(Current, Total) && Total > 0) {
+ if (Initial) {
+ Interface->Clear();
+ if (title)
+ Interface->Write(0, 0, title);
+ displayFrames = marks.Count() > 0;
+ }
+ Interface->Write(-7, 2, IndexToHMSF(Total));
+ Interface->Flush();
+#ifdef DEBUG_OSD
+ int p = Width() * Current / Total;
+ Interface->Fill(0, 1, p, 1, clrGreen);
+ Interface->Fill(p, 1, Width() - p, 1, clrWhite);
+#else
+ cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks);
+ Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar);
+ Interface->Flush();
+#endif
+ Interface->Write(0, 2, IndexToHMSF(Current, displayFrames));
+ Interface->Flush();
+ return true;
+ }
+ return false;
+}
+
+void cReplayControl::MarkToggle(void)
+{
+ int Current, Total;
+ if (dvbApi->GetIndex(Current, Total, true)) {
+ cMark *m = marks.Get(Current);
+ if (m)
+ marks.Del(m);
+ else
+ marks.Add(Current);
+ marks.Save();
+ }
+ displayFrames = marks.Count() > 0;
+ if (!displayFrames)
+ Interface->Fill(0, 2, Width() / 2, 1, clrBackground);
+}
+
+void cReplayControl::MarkJump(bool Forward)
+{
+ int Current, Total;
+ if (dvbApi->GetIndex(Current, Total)) {
+ cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
+ if (m)
+ dvbApi->Goto(m->position, true);
+ }
+}
+
+void cReplayControl::MarkMove(bool Forward)
+{
+ int Current, Total;
+ if (dvbApi->GetIndex(Current, Total)) {
+ cMark *m = marks.Get(Current);
+ if (m) {
+ int p = dvbApi->SkipFrames(Forward ? 1 : -1);
+ cMark *m2;
+ if (Forward) {
+ if ((m2 = marks.Next(m)) != NULL && m2->position <= p)
+ return;
+ }
+ else {
+ if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
+ return;
+ }
+ dvbApi->Goto(m->position = p, true);
+ marks.Save();
+ }
+ }
+}
+
+void cReplayControl::EditCut(void)
+{
+ Hide();
+ if (!cVideoCutter::Active()) {
+ if (!cVideoCutter::Start(fileName))
+ Interface->Error(tr("Can't start editing process!"));
+ else
+ Interface->Info(tr("Editing process started"));
+ }
+ else
+ Interface->Error(tr("Editing process already active!"));
+}
+
+void cReplayControl::EditTest(void)
+{
+ int Current, Total;
+ if (dvbApi->GetIndex(Current, Total)) {
+ cMark *m = marks.Get(Current);
+ if (!m)
+ m = marks.GetNext(Current);
+ if (m) {
+ if ((m->Index() & 0x01) != 0)
+ m = marks.Next(m);
+ if (m) {
+ dvbApi->Goto(m->position - dvbApi->SecondsToFrames(3));
+ dvbApi->Play();
+ }
+ }
+ }
+}
+
eOSState cReplayControl::ProcessKey(eKeys Key)
{
if (!dvbApi->Replaying())
return osEnd;
if (visible)
- shown = dvbApi->ShowProgress(!shown) || shown;
+ shown = ShowProgress(!shown) || shown;
switch (Key) {
+ // Positioning:
case kUp: dvbApi->Play(); break;
case kDown: dvbApi->Pause(); break;
- case kBlue: Hide();
- dvbApi->Stop();
- return osEnd;
case kLeft: dvbApi->Backward(); break;
case kRight: dvbApi->Forward(); break;
case kLeft|k_Release:
case kRight|k_Release:
dvbApi->Play(); break;
case kGreen|k_Repeat:
- case kGreen: dvbApi->Skip(-60); break;
+ case kGreen: dvbApi->SkipSeconds(-60); break;
case kYellow|k_Repeat:
- case kYellow: dvbApi->Skip(60); break;
+ case kYellow: dvbApi->SkipSeconds(60); break;
+ case kBlue: Hide();
+ dvbApi->StopReplay();
+ return osEnd;
+ // Editing:
+ //XXX should we do this only when the ProgressDisplay is on???
+ case kMarkToggle: MarkToggle(); break;
+ case kMarkJumpBack: MarkJump(false); break;
+ case kMarkJumpForward: MarkJump(true); break;
+ case kMarkMoveBack|k_Repeat:
+ case kMarkMoveBack: MarkMove(false); break;
+ case kMarkMoveForward|k_Repeat:
+ case kMarkMoveForward: MarkMove(true); break;
+ case kEditCut: EditCut(); break;
+ case kEditTest: EditTest(); break;
+ // Menu control:
case kMenu: Hide(); return osMenu; // allow direct switching to menu
case kOk: visible ? Hide() : Show(); break;
case kBack: return osRecordings;
diff --git a/menu.h b/menu.h
index 8153f5d..9232111 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.14 2000/11/12 12:33:00 kls Exp $
+ * $Id: menu.h 1.16 2000/12/25 14:25:29 kls Exp $
*/
#ifndef _MENU_H
@@ -79,11 +79,18 @@ public:
class cReplayControl : public cOsdBase {
private:
cDvbApi *dvbApi;
- bool visible, shown;
+ cMarks marks;
+ bool visible, shown, displayFrames;
void Show(void);
void Hide(void);
static char *fileName;
static char *title;
+ bool ShowProgress(bool Initial);
+ void MarkToggle(void);
+ void MarkJump(bool Forward);
+ void MarkMove(bool Forward);
+ void EditCut(void);
+ void EditTest(void);
public:
cReplayControl(void);
virtual ~cReplayControl();
diff --git a/osd.h b/osd.h
index 0d8085d..16d0ec2 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 1.17 2000/11/12 15:27:34 kls Exp $
+ * $Id: osd.h 1.18 2000/12/24 10:16:52 kls Exp $
*/
#ifndef __OSD_H
@@ -29,6 +29,7 @@ enum eOSState { osUnknown,
osReplay,
osStopRecord,
osStopReplay,
+ osCancelEdit,
osSwitchDvb,
osBack,
osEnd,
diff --git a/recording.c b/recording.c
index f45be96..064731d 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.c 1.21 2000/11/18 16:22:29 kls Exp $
+ * $Id: recording.c 1.24 2001/01/13 12:17:15 kls Exp $
*/
#define _GNU_SOURCE
@@ -26,6 +26,7 @@
#define NAMEFORMAT "%s/%s/" DATAFORMAT
#define SUMMARYFILESUFFIX "/summary.vdr"
+#define MARKSFILESUFFIX "/marks.vdr"
#define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
@@ -125,6 +126,7 @@ cRecording::cRecording(const char *FileName)
strncpy(name, FileName, p - FileName);
name[p - FileName] = 0;
strreplace(name, '_', ' ');
+ strreplace(name, '\x01', '\'');
}
// read an optional summary file:
char *SummaryFileName = NULL;
@@ -175,8 +177,10 @@ const char *cRecording::FileName(void)
if (!fileName) {
struct tm *t = localtime(&start);
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, '\'', '\x01');
+ }
}
return fileName;
}
@@ -269,3 +273,107 @@ bool cRecordings::Load(bool Deleted)
return result;
}
+// --- cMark -----------------------------------------------------------------
+
+char *cMark::buffer = NULL;
+
+cMark::cMark(int Position, const char *Comment)
+{
+ position = Position;
+ comment = Comment ? strdup(Comment) : NULL;
+}
+
+cMark::~cMark()
+{
+ delete comment;
+}
+
+const char *cMark::ToText(void)
+{
+ delete buffer;
+ asprintf(&buffer, "%s%s%s\n", IndexToHMSF(position, true), comment ? " " : "", comment ? comment : "");
+ return buffer;
+}
+
+bool cMark::Parse(const char *s)
+{
+ delete comment;
+ comment = NULL;
+ position = HMSFToIndex(s);
+ const char *p = strchr(s, ' ');
+ if (p) {
+ p = skipspace(p);
+ if (*p) {
+ comment = strdup(p);
+ comment[strlen(comment) - 1] = 0; // strips trailing newline
+ }
+ }
+ return true;
+}
+
+bool cMark::Save(FILE *f)
+{
+ return fprintf(f, ToText()) > 0;
+}
+
+// --- cMarks ----------------------------------------------------------------
+
+bool cMarks::Load(const char *RecordingFileName)
+{
+ const char *MarksFile = AddDirectory(RecordingFileName, MARKSFILESUFFIX);
+ if (cConfig<cMark>::Load(MarksFile)) {
+ Sort();
+ return true;
+ }
+ return false;
+}
+
+void cMarks::Sort(void)
+{
+ for (cMark *m1 = First(); m1; m1 = Next(m1)) {
+ for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
+ if (m2->position < m1->position) {
+ swap(m1->position, m2->position);
+ swap(m1->comment, m2->comment);
+ }
+ }
+ }
+}
+
+cMark *cMarks::Add(int Position)
+{
+ cMark *m = Get(Position);
+ if (!m) {
+ cConfig<cMark>::Add(m = new cMark(Position));
+ Sort();
+ }
+ return m;
+}
+
+cMark *cMarks::Get(int Position)
+{
+ for (cMark *mi = First(); mi; mi = Next(mi)) {
+ if (mi->position == Position)
+ return mi;
+ }
+ return NULL;
+}
+
+cMark *cMarks::GetPrev(int Position)
+{
+ for (cMark *mi = Last(); mi; mi = Prev(mi)) {
+ if (mi->position < Position)
+ return mi;
+ }
+ return NULL;
+}
+
+cMark *cMarks::GetNext(int Position)
+{
+ for (cMark *mi = First(); mi; mi = Next(mi)) {
+ if (mi->position > Position)
+ return mi;
+ }
+ return NULL;
+}
+
diff --git a/recording.h b/recording.h
index 7511c65..454c356 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.h 1.10 2000/10/03 12:27:49 kls Exp $
+ * $Id: recording.h 1.11 2000/12/16 14:25:20 kls Exp $
*/
#ifndef __RECORDING_H
@@ -47,4 +47,27 @@ public:
bool Load(bool Deleted = false);
};
+class cMark : public cListObject {
+private:
+ static char *buffer;
+public:
+ int position;
+ char *comment;
+ cMark(int Position = 0, const char *Comment = NULL);
+ ~cMark();
+ const char *ToText(void);
+ bool Parse(const char *s);
+ bool Save(FILE *f);
+ };
+
+class cMarks : public cConfig<cMark> {
+public:
+ bool Load(const char *RecordingFileName);
+ void Sort(void);
+ cMark *Add(int Position);
+ cMark *Get(int Position);
+ cMark *GetPrev(int Position);
+ cMark *GetNext(int Position);
+ };
+
#endif //__RECORDING_H
diff --git a/remote.c b/remote.c
index 349a445..691b5e9 100644
--- a/remote.c
+++ b/remote.c
@@ -6,7 +6,7 @@
*
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
- * $Id: remote.c 1.19 2000/11/11 11:22:22 kls Exp $
+ * $Id: remote.c 1.20 2000/12/03 11:55:06 kls Exp $
*/
#include "remote.h"
@@ -115,7 +115,7 @@ cRcIoRCU::cRcIoRCU(char *DeviceName)
cRcIoRCU::~cRcIoRCU()
{
- Stop();
+ Cancel();
}
void cRcIoRCU::Action(void)
@@ -420,7 +420,7 @@ cRcIoLIRC::cRcIoLIRC(char *DeviceName)
cRcIoLIRC::~cRcIoLIRC()
{
- Stop();
+ Cancel();
}
void cRcIoLIRC::Action(void)
diff --git a/svdrp.c b/svdrp.c
index 66ae8e9..0ce30db 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.12 2000/11/05 13:44:42 kls Exp $
+ * $Id: svdrp.c 1.13 2000/12/03 15:34:35 kls Exp $
*/
#define _GNU_SOURCE
@@ -18,6 +18,7 @@
#include "svdrp.h"
#include <arpa/inet.h>
#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdarg.h>
diff --git a/thread.c b/thread.c
index 67b5ab9..363190d 100644
--- a/thread.c
+++ b/thread.c
@@ -4,12 +4,15 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 1.4 2000/11/14 18:38:25 kls Exp $
+ * $Id: thread.c 1.7 2000/12/24 12:27:21 kls Exp $
*/
#include "thread.h"
+#include <errno.h>
#include <signal.h>
+#include <sys/wait.h>
#include <unistd.h>
+#include "tools.h"
// --- cThread ---------------------------------------------------------------
@@ -25,7 +28,7 @@ cThread::cThread(void)
signalHandlerInstalled = true;
}
running = false;
- parentPid = lockingPid = 0;
+ parentPid = threadPid = lockingPid = 0;
locked = 0;
}
@@ -40,6 +43,7 @@ void cThread::SignalHandler(int signum)
void *cThread::StartThread(cThread *Thread)
{
+ Thread->threadPid = getpid();
Thread->Action();
return NULL;
}
@@ -49,13 +53,37 @@ bool cThread::Start(void)
if (!running) {
running = true;
parentPid = getpid();
- pthread_create(&thread, NULL, &StartThread, (void *)this);
+ pthread_create(&thread, NULL, (void *(*) (void *))&StartThread, (void *)this);
+ usleep(10000); // otherwise calling Active() immediately after Start() causes a "pure virtual method called" error
}
return true; //XXX return value of pthread_create()???
}
-void cThread::Stop(void)
+bool cThread::Active(void)
{
+ if (threadPid) {
+ if (kill(threadPid, SIGIO) < 0) { // couldn't find another way of checking whether the thread is still running - any ideas?
+ if (errno == ESRCH)
+ threadPid = 0;
+ else
+ LOG_ERROR;
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+void cThread::Cancel(int WaitSeconds)
+{
+ if (WaitSeconds > 0) {
+ for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
+ if (!Active())
+ return;
+ usleep(10000);
+ }
+ esyslog(LOG_ERR, "ERROR: thread %d won't end (waited %d seconds) - cancelling it...", threadPid, WaitSeconds);
+ }
pthread_cancel(thread);
}
diff --git a/thread.h b/thread.h
index c85c51e..6aaee0b 100644
--- a/thread.h
+++ b/thread.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.h 1.3 2000/11/14 18:38:11 kls Exp $
+ * $Id: thread.h 1.4 2000/12/03 11:18:37 kls Exp $
*/
#ifndef __THREAD_H
@@ -28,7 +28,7 @@ class cThread {
private:
pthread_t thread;
cMutex Mutex;
- pid_t parentPid, lockingPid;
+ pid_t parentPid, threadPid, lockingPid;
int locked;
bool running;
static bool signalHandlerInstalled;
@@ -39,11 +39,12 @@ private:
protected:
void WakeUp(void);
virtual void Action(void) = 0;
- void Stop(void);
+ void Cancel(int WaitSeconds = 0);
public:
cThread(void);
virtual ~cThread();
bool Start(void);
+ bool Active(void);
};
// cThreadLock can be used to easily set a lock in a thread and make absolutely
diff --git a/timers.conf b/timers.conf
deleted file mode 100644
index 05946a5..0000000
--- a/timers.conf
+++ /dev/null
@@ -1,14 +0,0 @@
-1:15:M------:2128:2205:80:7:Neues:
-1:3:-T-----:2013:2125:99:99:SevenDays:
-1:10:-T-----:2058:2202:99:10:Quarks:
-1:25:-T-----:2305:0020:99:99:UFO:
-1:14:--W----:1920:2020:70:99:Rettungsflieger:
-0:2:--W----:2110:2325:99:99:BulleVonToelz:
-1:3:---T---:2210:2315:50:20:IngoAppelt:
-1:2:----F--:2013:2125:99:99:Farscape:
-1:1:----F--:2215:2325:50:20:7Tage7Koepfe:
-0:11:-----S-:2058:2135:99:99:Computer:
-0:2:-----S-:2220:2340:99:30:Wochenshow:
-1:11:------S:2013:2035:99:10:Centauri:
-1:15:MTWTF--:1828:1901:10:5:nano:
-1:1:MTWTF--:1553:1710:99:99:Hammerman:
diff --git a/tools.c b/tools.c
index 6d86f16..cd2c60f 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.23 2000/11/11 15:17:12 kls Exp $
+ * $Id: tools.c 1.27 2001/01/13 15:35:02 kls Exp $
*/
#define _GNU_SOURCE
@@ -15,10 +15,7 @@
#if defined(DEBUG_OSD)
#include <ncurses.h>
#endif
-#include <signal.h>
-#include <stdlib.h>
#include <sys/time.h>
-#include <sys/wait.h>
#include <unistd.h>
#define MaxBuffer 1000
@@ -30,29 +27,6 @@ void writechar(int filedes, char c)
write(filedes, &c, sizeof(c));
}
-void writeint(int filedes, int n)
-{
- write(filedes, &n, sizeof(n));
-}
-
-char readchar(int filedes)
-{
- char c;
- read(filedes, &c, 1);
- return c;
-}
-
-bool readint(int filedes, int &n)
-{
- return cFile::AnyFileReady(filedes, 0) && read(filedes, &n, sizeof(n)) == sizeof(n);
-}
-
-void purge(int filedes)
-{
- while (cFile::AnyFileReady(filedes, 0))
- readchar(filedes);
-}
-
char *readline(FILE *f)
{
static char buffer[MaxBuffer];
@@ -146,7 +120,7 @@ const char *AddDirectory(const char *DirName, const char *FileName)
return buf;
}
-#define DFCMD "df -m %s"
+#define DFCMD "df -m '%s'"
uint FreeDiskSpaceMB(const char *Directory)
{
@@ -205,7 +179,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
dsyslog(LOG_INFO, "creating directory %s", s);
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
- esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
+ LOG_ERROR_STR(s);
result = false;
break;
}
@@ -266,44 +240,32 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
if (remove(FileName) == 0)
return true;
}
- else
+ else if (errno != ENOENT) {
LOG_ERROR_STR(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)
+char *ReadLink(const char *FileName)
{
- 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));
+ char RealName[_POSIX_PATH_MAX];
+ const char *TargetName = NULL;
+ int n = readlink(FileName, RealName, sizeof(RealName) - 1);
+ if (n < 0) {
+ if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink
+ TargetName = FileName;
+ else { // some other error occurred
+ LOG_ERROR_STR(FileName);
+ }
+ }
+ else if (n < int(sizeof(RealName))) { // got it!
+ RealName[n] = 0;
+ TargetName = RealName;
}
+ else
+ esyslog(LOG_ERR, "ERROR: symlink's target name too long: %s", FileName);
+ return TargetName ? strdup(TargetName) : NULL;
}
// --- cFile -----------------------------------------------------------------
@@ -426,6 +388,45 @@ bool cFile::FileReady(int FileDes, int TimeoutMs)
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
}
+// --- cSafeFile -------------------------------------------------------------
+
+cSafeFile::cSafeFile(const char *FileName)
+{
+ f = NULL;
+ fileName = ReadLink(FileName);
+ tempName = fileName ? new char[strlen(fileName) + 5] : NULL;
+ if (tempName)
+ strcat(strcpy(tempName, fileName), ".$$$");
+}
+
+cSafeFile::~cSafeFile()
+{
+ if (f)
+ fclose(f);
+ unlink(tempName);
+ delete fileName;
+ delete tempName;
+}
+
+bool cSafeFile::Open(void)
+{
+ if (!f && fileName && tempName) {
+ f = fopen(tempName, "w");
+ if (!f)
+ LOG_ERROR_STR(tempName);
+ }
+ return f != NULL;
+}
+
+void cSafeFile::Close(void)
+{
+ if (f) {
+ fclose(f);
+ f = NULL;
+ rename(tempName, fileName);
+ }
+}
+
// --- cListObject -----------------------------------------------------------
cListObject::cListObject(void)
diff --git a/tools.h b/tools.h
index f83e7da..539dba0 100644
--- a/tools.h
+++ b/tools.h
@@ -4,13 +4,13 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.20 2000/11/12 15:27:06 kls Exp $
+ * $Id: tools.h 1.23 2001/01/13 15:36:00 kls Exp $
*/
#ifndef __TOOLS_H
#define __TOOLS_H
-#include <errno.h>
+//#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
@@ -24,19 +24,16 @@ extern int SysLogLevel;
#define isyslog if (SysLogLevel > 1) syslog
#define dsyslog if (SysLogLevel > 2) syslog
-#define LOG_ERROR esyslog(LOG_ERR, "ERROR (%s,%d): %s", __FILE__, __LINE__, strerror(errno))
-#define LOG_ERROR_STR(s) esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
+#define LOG_ERROR esyslog(LOG_ERR, "ERROR (%s,%d): %m", __FILE__, __LINE__)
+#define LOG_ERROR_STR(s) esyslog(LOG_ERR, "ERROR: %s: %m", s)
#define SECSINDAY 86400
-#define MAXPROCESSTIMEOUT 3 // seconds
#define DELETENULL(p) (delete (p), p = NULL)
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; };
+
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);
char *strn0cpy(char *dest, const char *src, size_t n);
char *strreplace(char *s, char c1, char c2);
@@ -51,8 +48,7 @@ uint FreeDiskSpaceMB(const char *Directory);
bool DirectoryOk(const char *DirName, bool LogErrors = false);
bool MakeDirs(const char *FileName, bool IsDirectory = false);
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
-bool CheckProcess(pid_t pid);
-void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
+char *ReadLink(const char *FileName);
class cFile {
private:
@@ -73,6 +69,19 @@ public:
static bool FileReady(int FileDes, int TimeoutMs = 1000);
};
+class cSafeFile {
+private:
+ FILE *f;
+ char *fileName;
+ char *tempName;
+public:
+ cSafeFile(const char *FileName);
+ ~cSafeFile();
+ operator FILE* () { return f; }
+ bool Open(void);
+ void Close(void);
+ };
+
class cListObject {
private:
cListObject *prev, *next;
@@ -105,6 +114,8 @@ template<class T> class cList : public cListBase {
public:
T *Get(int Index) const { return (T *)cListBase::Get(Index); }
T *First(void) const { return (T *)objects; }
+ T *Last(void) const { return (T *)lastObject; }
+ T *Prev(const T *object) const { return (T *)object->Prev(); }
T *Next(const T *object) const { return (T *)object->Next(); }
};
diff --git a/vdr.c b/vdr.c
index 8a84200..698cc7a 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.46 2000/11/18 13:46:56 kls Exp $
+ * $Id: vdr.c 1.49 2001/01/14 15:29:51 kls Exp $
*/
#include <getopt.h>
@@ -141,8 +141,8 @@ int main(int argc, char *argv[])
#if !defined(DEBUG_OSD) && !defined(REMOTE_KBD)
pid_t pid = fork();
if (pid < 0) {
- fprintf(stderr, "%s\n", strerror(errno));
- esyslog(LOG_ERR, "ERROR: %s", strerror(errno));
+ fprintf(stderr, "%m\n");
+ esyslog(LOG_ERR, "ERROR: %m");
abort();
}
if (pid != 0)
@@ -179,7 +179,7 @@ int main(int argc, char *argv[])
cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB);
- Channels.SwitchTo(1);
+ Channels.SwitchTo(Setup.CurrentChannel);
cEITScanner EITScanner;
@@ -306,10 +306,15 @@ int main(int argc, char *argv[])
default: break;
}
}
- if (!Menu)
+ if (!Menu) {
EITScanner.Process();
+ cVideoCutter::Active();
+ }
}
isyslog(LOG_INFO, "caught signal %d", Interrupted);
+ Setup.CurrentChannel = cDvbApi::CurrentChannel();
+ Setup.Save();
+ cVideoCutter::Stop();
delete Menu;
delete ReplayControl;
delete Interface;
diff --git a/videodir.c b/videodir.c
index 91d362d..4d5c257 100644
--- a/videodir.c
+++ b/videodir.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: videodir.c 1.2 2000/09/15 13:23:47 kls Exp $
+ * $Id: videodir.c 1.3 2000/12/24 12:51:41 kls Exp $
*/
#include "videodir.h"
@@ -180,3 +180,20 @@ bool VideoFileSpaceAvailable(unsigned int SizeMB)
}
return Dir.FreeMB() >= SizeMB;
}
+
+const char *PrefixVideoFileName(const char *FileName, char Prefix)
+{
+ static char *PrefixedName = NULL;
+
+ if (!PrefixedName || strlen(PrefixedName) <= strlen(FileName))
+ PrefixedName = (char *)realloc(PrefixedName, strlen(FileName) + 2);
+ if (PrefixedName) {
+ strcpy(PrefixedName, VideoDirectory);
+ char *p = PrefixedName + strlen(PrefixedName);
+ *p++ = '/';
+ *p++ = Prefix;
+ strcpy(p, FileName + strlen(VideoDirectory) + 1);
+ }
+ return PrefixedName;
+}
+
diff --git a/videodir.h b/videodir.h
index 7ce1531..0716a28 100644
--- a/videodir.h
+++ b/videodir.h
@@ -4,7 +4,7 @@
* 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 $
+ * $Id: videodir.h 1.2 2000/12/24 12:41:10 kls Exp $
*/
#ifndef __VIDEODIR_H
@@ -17,5 +17,6 @@ int CloseVideoFile(int FileHandle);
bool RenameVideoFile(const char *OldName, const char *NewName);
bool RemoveVideoFile(const char *FileName);
bool VideoFileSpaceAvailable(unsigned int SizeMB);
+const char *PrefixVideoFileName(const char *FileName, char Prefix);
#endif //__VIDEODIR_H