summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2001-06-02 10:47:40 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2001-06-02 10:47:40 +0200
commitc40e4eb96e43963845d1de1678a317b27e77f04e (patch)
treefc61866ba83db4bb0611cb45f1bd951eeeb56bd7
parent1ef2b1d3a149348539565902825bb168a52673a1 (diff)
downloadvdr-c40e4eb96e43963845d1de1678a317b27e77f04e.tar.gz
vdr-c40e4eb96e43963845d1de1678a317b27e77f04e.tar.bz2
Converted to the new API plus several small enhancements0.8.0
-rw-r--r--CONTRIBUTORS5
-rw-r--r--FORMATS8
-rw-r--r--HISTORY44
-rw-r--r--MANUAL11
-rw-r--r--Makefile8
-rw-r--r--channels.conf175
-rw-r--r--config.c27
-rw-r--r--config.h14
-rw-r--r--dvbapi.c1412
-rw-r--r--dvbapi.h47
-rw-r--r--dvbosd.c6
-rw-r--r--dvbosd.h6
-rw-r--r--eit.c257
-rw-r--r--eit.h10
-rw-r--r--i18n.c20
-rw-r--r--interface.c6
-rw-r--r--menu.c54
-rw-r--r--osd.h4
-rw-r--r--recording.c21
-rw-r--r--recording.h4
-rw-r--r--remux.c499
-rw-r--r--remux.h27
-rw-r--r--ringbuffer.c37
-rw-r--r--ringbuffer.h8
-rwxr-xr-xrunvdr32
-rw-r--r--thread.c39
-rw-r--r--thread.h14
-rw-r--r--tools.c18
-rw-r--r--tools.h3
-rw-r--r--vdr.c14
-rw-r--r--videodir.c4
31 files changed, 1673 insertions, 1161 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 8a67f37c..8cb99a71 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -42,6 +42,7 @@ Bastian Guse <bastian@nocopy.de>
Matthias Schniedermeyer <ms@citd.de>
for implementing the 'MarkInstantRecord' setup option
for his "schnitt" tools
+ for his "master-timer" tool
Miha Setina <mihasetina@softhome.net>
for translating the OSD texts to the Slovenian language
@@ -54,6 +55,7 @@ Deti Fliegl <deti@fliegl.de>
Dave Chapman <dave@dchapman.com>
for implementing support for the teletext PID
+ for his great support in switching to the NAPI
Hans-Peter Raschke <Hans-Peter.Raschke@Wintermann-DatenService.de>
for his support in adapting VDR to DVB-C
@@ -70,3 +72,6 @@ Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
Jürgen Sauer <jojo@automatix.de>
for implementing the -t option to set the controlling terminal
+
+Benjamin Reichardt <reichard@math.uni-goettingen.de>
+ for his help in debugging the transition to the new API
diff --git a/FORMATS b/FORMATS
index 2a357a35..9d2c8f92 100644
--- a/FORMATS
+++ b/FORMATS
@@ -13,7 +13,7 @@ Video Disk Recorder File Formats
A "channel definition" is a line with channel data, where the fields
are separated by ':' characters:
- Example: "RTL:12188:h:1:27500:163:104:0:0:12003"
+ Example: "RTL:12188:h:1:27500:163:104:105:0:12003"
The fields in a channel definition have the following meaning (from left
to right):
@@ -60,8 +60,10 @@ Video Disk Recorder File Formats
(1..31)
- Start time (first two digits for the hour, second two digits for the minutes)
- End time (first two digits for the hour, second two digits for the minutes)
- - Priority (from 00 to 99, 00 = lowest prioity, 99 = highest priority)
- - Guaranteed lifetime of recording (in days)
+ - Priority (from 0 to 99, 0 = lowest prioity, 99 = highest priority)
+ - Guaranteed lifetime of recording (in days); 0 means that this recording may
+ be automatically deleted by a new recording with higher priority, 99 means
+ that this recording will never be automatically deleted
- Name of timer (will be used to name the recording); if the name contains
any ':' characters, these have to be replaced with '|'
- Summary (any newline characters in the summary have to be replaced with '|';
diff --git a/HISTORY b/HISTORY
index 203377d3..1927a81d 100644
--- a/HISTORY
+++ b/HISTORY
@@ -290,7 +290,7 @@ Video Disk Recorder Revision History
channel, if the timer currently occupying this DVB card doesn't need the
CAM module (and thus can continue recording on a different DVB card).
- The "Yellow" button in the "What's on now/next?" menus now displays the
- schedule of the current channel from that menu.
+ schedule of the current channel from that menu.
- All DVB cards in a multi-card system now write their EIT information into the
same data structure.
- If there is more than one DVB card in the system, the non-primary cards are
@@ -332,8 +332,8 @@ Video Disk Recorder Revision History
- 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 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).
@@ -452,3 +452,41 @@ Video Disk Recorder Revision History
- Empty lines in config files no longer cause error messages.
- New SVDRP command LSTE to list the EPG data.
- The SVDRP HELP command now prints the topics in several columns.
+
+2001-06-02: Version 0.80
+
+- VDR now requires driver version 0.9.0 or higher.
+- Switched to the "new API" (thanks to Dave Chapman for his great support in
+ this task).
+- New setup parameter "LnbSLOF" that defines the switching frequency of the LNB.
+- Fixed a bug in the EPG scanner with more than one DVB card.
+- Fixed checking for free disk space, so that it works with NFS mounted drives.
+- Files are now created with mode 644.
+- Fixed checking the exit status in the 'runvdr' script.
+- Activated loading the driver in 'runvdr'. Please read the comments in 'runvdr'
+ for details.
+- The new "emergency exit" feature automatically triggers a restart of VDR (if
+ used with 'runvdr', otherwise it simply exists) if
+ * tuning the channel for a recording fails
+ * no useful data is received within the first 1MB of a recording
+ * no data is received within a recording for more than 5 seconds
+ This should make sure that a recording is successfully restarted after any
+ problems.
+- Processing the EIT data is now disabled during replay and 'Transfer Mode' in
+ order to avoid video and audio glitches (there appears to be a bandwidth
+ problem somewhere in the driver/firmware/hardware).
+- Due to the reduced amount of OSD memory provided by the driver the number of
+ lines in the OSD had to be reduced by 2. By rearranging some of the display
+ items the amount of visible information remained the same as before, though.
+ If your DVB card has even less memory (which would result in only the
+ channel switching display and the replay progress display being visible, but
+ no Main menu), try reducing the constant 'MenuLines' in dvbapi.h (currently
+ '13') even further.
+- There are two new setup parameters to define the "Default Priority" and
+ "Default Lifetime" when creating a new timer event.
+- The meaning of the "Lifetime" parameter has been modified: a value of '99'
+ now means that the recording will live "forever", and a value of '0' means
+ that the recording has no guaranteed lifetime and will be deleted whenever
+ a new recording with higher priority needs disk space.
+- Updated version of Matthias Schniedermeyer's 'schnitt' tools.
+- New 'master-timer' tool (thanks to Matthias Schniedermeyer).
diff --git a/MANUAL b/MANUAL
index f77bd276..cc9f9dae 100644
--- a/MANUAL
+++ b/MANUAL
@@ -32,11 +32,11 @@ Video Disk Recorder User's Manual
confirms any changes (or switches to a channel in the "Channels" menu).
The "Back" key goes back one level in the menu structure, discarding
any changes that might have been made in the current menu.
-
+
In the "Timers" menu, the current timer can be enabled or disabled with
the "Right" or "Left" key, respectively (enabled timers are marked with ">").
"Ok" here opens the "Edit timer" menu.
-
+
Textual options, like channel names or recording file names, can be edited
by pressing the "Right" button (which puts brackets around the current
character as in "[R]TL"), selecting the desired character position with
@@ -46,10 +46,10 @@ Video Disk Recorder User's Manual
brackets (as in abc[^]), the next press to the "Left" or "Ok" button will
actually cut off the string. Using "Up" and/or "Down" brings back the
original rest of the string (unless you have pressed "Left" or "Ok").
-
+
The "Red", "Green", "Yellow" and "Blue" buttons have special meanings
in various menus and are listed at the bottom of the on-screen-display.
-
+
At any point in the menu system, pressing the "Menu" key again will
immediately leave the menu system (discarding any pending changes).
@@ -309,7 +309,8 @@ Video Disk Recorder User's Manual
0 = instant recordings will not be marked
1 = instant recordings will be marked.
- LnbFrequLo = 9750 The low and high LNB frequencies (in MHz)
+ LnbSLOF = 11700 The switching frequency (in MHz) between low and high LOF
+ LnbFrequLo = 9750 The LNB's low and high local oscillator frequencies (in MHz)
LnbFrequHi = 10600 (these have no meaning for DVB-C receivers)
SetSystemTime = 0 Defines whether the system time will be set according to
diff --git a/Makefile b/Makefile
index e2b80bb1..507de214 100644
--- a/Makefile
+++ b/Makefile
@@ -4,11 +4,11 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.21 2001/03/18 16:47:00 kls Exp $
+# $Id: Makefile 1.22 2001/06/02 09:15:39 kls Exp $
DVBDIR = ../DVB
-INCLUDES = -I$(DVBDIR)/driver
+INCLUDES = -I$(DVBDIR)/ost/include
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
videodir.o
@@ -43,7 +43,7 @@ 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 recording.h remote.h remux.h ringbuffer.h svdrp.h thread.h tools.h videodir.h
+dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h recording.h remux.h ringbuffer.h thread.h tools.h videodir.h
dvbosd.o : dvbosd.c dvbosd.h font.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
@@ -53,7 +53,7 @@ menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h
osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
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
-remux.o : remux.c remux.h tools.h
+remux.o : remux.c remux.h thread.h tools.h
ringbuffer.o: ringbuffer.c ringbuffer.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 tools.h
diff --git a/channels.conf b/channels.conf
deleted file mode 100644
index 36b5b284..00000000
--- a/channels.conf
+++ /dev/null
@@ -1,175 +0,0 @@
-RTL:12188:h:0:27500:163:104:105:0:12003
-Sat.1:12480:v:0:27500:1791:1792:34:0:46
-Pro-7:12480:v:0:27500:255:256:32:0:898
-RTL2:12188:h:0:27500:166:128:68:0:12020
-ARD:11837:h:0:27500:101:102:0:0:28106
-BR3:11837:h:0:27500:201:202:0:0:28107
-Hessen-3:11837:h:0:27500:301:302:0:0:28108
-N3:12110:h:0:27500:2401:2402:0:0:28224
-SR3:11837:h:0:27500:501:502:0:0:28110
-WDR:11837:h:0:27500:601:602:0:0:28111
-BR-alpha:11837:h:0:27500:701:702:0:0:28112
-SWR BW:11837:h:0:27500:801:802:0:0:28113
-Phoenix:11837:h:0:27500:901:902:0:0:28114
-ZDF:11954:h:0:27500:110:120:130:0:28006
-3sat:11954:h:0:27500:210:220:230:0:28007
-KiKa:11954:h:0:27500:310:320:0:0:28008
-arte:11836:h:0:27500:401:402:0:0:28109
-ORF1:12692:h:0:22000:160:161:165:3:13001
-ORF2:12692:h:0:22000:500:501:505:3:13007
-ORF Sat:11954:h:0:27500:506:507:0:0:28010
-ZDF.info:11954:h:0:27500:610:620:0:0:28011
-CNN:12168:v:0:27500:165:100:0:0:28512
-Super RTL:12188:h:0:27500:165:120:65:0:12040
-VOX:12188:h:0:27500:167:136:0:0:12060
-DW TV:12363:v:0:27500:305:306:0:0:8905
-Kabel 1:12480:v:0:27500:511:512:33:0:899
-tm3:12480:v:0:27500:767:768:0:0:897
-DSF:12480:v:0:27500:1023:1024:0:0:900
-HOT:12480:v:0:27500:1279:1280:0:0:40
-Bloomberg TV Germany:12551:v:0:22000:162:99:0:0:12160
-BLOOMBERG TV:11817:v:0:27500:163:92:0:0:8004
-Bloomberg:12168:v:0:27500:167:112:0:0:12721
-Sky News:12552:v:0:22000:305:306:0:0:3995
-KinderNet:12574:h:0:22000:163:92:0:0:5020
-Alice:12610:v:0:22000:162:96:0:0:12200
-n-tv:12669:v:0:22000:162:96:55:0:12730
-Grand Tourisme:12670:v:0:22000:289:290:0:0:17300
-TW1:12692:h:0:22000:166:167:0:0:13013
-Eurosport:11954:h:0:27500:410:420:0:0:28009
-EinsExtra:12110:h:0:27500:101:102:0:0:28201
-EinsFestival:12110:h:0:27500:201:202:0:0:28202
-EinsMuXx:12110:h:0:27500:301:302:0:0:28203
-ZDF Theaterkanal:11954:h:0:27500:1110:1120:0:0:28016
-ZDF.doku:11954:h:0:27500:660:670:0:0:28014
-MDR:12110:h:0:27500:401:402:0:0:28204
-NICK-PARAMOUNT:12246:v:0:27500:167:108:0:0:29312
-ORB:12110:h:0:27500:501:502:0:0:28205
-B1:12110:h:0:27500:601:602:0:0:28206
-ARD Online-Kanal:12722:h:0:22000:8191:701:0:0:0
-:Premiere World
-Premiere World Promo:11798:h:0:27500:255:256:0:0:8
-Premiere:11798:h:0:27500:511:512:0:3:10
-Star Kino:11798:h:0:27500:767:768:0:3:9
-Cine Action:11798:h:0:27500:1023:1024:0:3:20
-Cine Comedy:11798:h:0:27500:1279:1280:0:3:29
-Sci Fantasy:11798:h:0:27500:1535:1536:0:3:41
-Romantic Movies:11797:h:0:27500:1791:1792:0:3:11
-Studio Universal:12090:v:0:27500:255:256:0:3:36
-13th Street:11797:h:0:27500:2303:2304:0:3:43
-Junior:12031:h:0:27500:255:256:0:3:19
-K-Toon:12032:h:0:27500:511:512:0:3:12
-Disney Channel:12090:v:0:27500:767:768:0:3:34
-Fox Kids:11797:h:0:27500:2559:2560:0:3:22
-Sunset:12031:h:0:27500:1023:1024:0:3:16
-Comedy:12031:h:0:27500:1279:1280:0:3:28
-Planet:12090:v:0:27500:1279:1280:0:3:13
-Discovery Channel:12031:h:0:27500:1791:1792:0:3:14
-Krimi&Co:12031:h:0:27500:1535:1536:0:3:23
-Filmpalast:11758:h:0:27500:2559:2560:0:3:516
-Heimatkanal:11758:h:0:27500:2815:2816:0:3:517
-Goldstar:11758:h:0:27500:3839:3840:0:3:518
-Classica:12031:h:0:27500:767:768:0:3:15
-Seasons:12090:v:0:27500:511:512:0:3:33
-Sport 1:11720:h:0:27500:255:256:0:3:17
-Sport 2:12070:h:0:27500:2047:2048:0:3:27
-Sport 3:12070:h:0:27500:2303:2304:0:3:18
-Sport 4:12070:h:0:27500:2559:2560:0:3:24
-Feed (F1 Boxengasse):11720:h:0:27500:2559:2560:0:3:242
-Feed (F1 Data):11720:h:0:27500:3071:3072:0:3:244
-Feed (F1 Multi):11720:h:0:27500:2815:2816:0:3:243
-Feed (F1 On Board):11720:h:0:27500:2303:2304:0:3:241
-Feed (F1 Verfolger):11720:h:0:27500:2047:2048:0:3:240
-Cinedom Deluxe:12070:h:0:27500:1279:1280:0:3:188
-Cinedom 1A de:11758:h:0:27500:511:512:0:3:178
-Cinedom 1A en:11758:h:0:27500:511:513:0:3:178
-Cinedom 1B:12070:h:0:27500:767:768:0:3:185
-Cinedom 1C:12070:h:0:27500:1791:1792:0:3:191
-Cinedom 1E??:11720:h:0:27500:1535:1537:0:3:176
-Cinedom 2A:12070:h:0:27500:1535:1536:0:3:189
-Cinedom 2B:11758:h:0:27500:767:768:0:3:179
-Cinedom 2C:11758:h:0:27500:1023:1024:0:3:193
-Cinedom 2D??:12070:h:0:27500:511:512:0:3:184
-Cinedom 3A:11758:h:0:27500:255:256:0:3:177
-Cinedom 3B:11758:h:0:27500:1279:1280:0:3:194
-Cinedom 3C??:12090:v:0:27500:1279:1280:17689:3:192
-Cinedom 4A:11758:h:0:27500:1535:1536:0:3:195
-Cinedom 4B:12070:h:0:27500:1023:1025:0:3:186
-Cinedom 4C??:11720:h:0:27500:767:768:0:3:181
-Cinedom 5A:12032:h:0:27500:2559:2560:0:3:187
-Beate Uhse_TV:11797:h:0:27500:2047:2048:0:3:21
-Blue Channel:11758:h:0:27500:2559:2560:0:3:516
-Blue Movie 1:11758:h:0:27500:1791:1792:0:3:513
-Blue Movie 2:11758:h:0:27500:2047:2048:0:3:514
-Blue Movie 3:11758:h:0:27500:2303:2304:0:3:515
-:
-TV Niepokalanow:11876:h:0:27500:305:321:0:0:20601
-Mosaico:11934:v:0:27500:165:100:0:0:29010
-Andalucia TV:11934:v:0:27500:166:104:0:0:29011
-TVC Internacional:11934:v:0:27500:167:108:0:0:0
-Nasza TV:11992:h:0:27500:165:98:0:0:0
-WishLine test:12012:v:0:27500:163:90:0:0:0
-Pro 7 Austria:12051:v:0:27500:161:84:0:0:0
-Kabel 1 Schweiz:12051:v:0:27500:162:163:0:0:0
-Kabel 1 Austria:12051:v:0:27500:166:167:0:0:0
-Pro 7 Schweiz:12051:v:0:27500:289:290:0:0:0
-Kiosque:12129:v:0:27500:160:80:0:0:0
-KTO:12129:v:0:27500:170:120:0:0:0
-TCM:12168:v:0:27500:160:80:0:0:0
-Cartoon Network France & Spain:12168:v:0:27500:161:84:0:0:0
-TVBS Europe:12168:v:0:27500:162:88:0:0:0
-TVBS Europe:12168:v:0:27500:162:89:0:0:0
-Travel:12168:v:0:27500:163:92:0:0:0
-TCM Espania:12168:v:0:27500:164:96:0:0:0
-MTV Spain:12168:v:0:27500:167:112:0:0:0
-TCM France:12168:v:0:27500:169:64:0:0:0
-RTL2 CH:12188:h:0:27500:164:112:0:0:0
-La Cinquieme:12207:v:0:27500:160:80:0:0:0
-ARTE:12207:v:0:27500:165:100:0:0:0
-Post Filial TV:12226:h:0:27500:255:256:0:0:0
-Canal Canaris:12246:v:0:27500:160:80:0:0:0
-Canal Canaris:12246:v:0:27500:160:81:0:0:0
-Canal Canaris:12246:v:0:27500:160:82:0:0:0
-Canal Canaris:12246:v:0:27500:160:83:0:0:0
-AB Sat Passion promo:12266:h:0:27500:160:80:0:0:0
-AB Channel 1:12266:h:0:27500:161:84:0:0:0
-Taquilla 0:12285:v:0:27500:165:100:0:0:0
-CSAT:12324:v:0:27500:160:80:0:0:0
-Mosaique:12324:v:0:27500:162:88:0:0:0
-Mosaique 2:12324:v:0:27500:163:92:0:0:0
-Mosaique 3:12324:v:0:27500:164:96:0:0:0
-Le Sesame C+:12324:v:0:27500:165:1965:0:0:0
-FEED:12344:h:0:27500:163:92:0:0:0
-RTM 1:12363:v:0:27500:162:96:0:0:0
-ESC 1:12363:v:0:27500:163:104:0:0:0
-TV5 Europe:12363:v:0:27500:164:112:0:0:0
-TV7 Tunisia:12363:v:0:27500:166:128:0:0:0
-ARTE:12363:v:0:27500:167:137:0:0:0
-RAI Uno:12363:v:0:27500:289:290:0:0:8904
-RTP International:12363:v:0:27500:300:301:0:0:0
-Fashion TV:12402:v:0:27500:163:92:0:0:0
-VideoService:12422:h:0:27500:255:256:0:0:0
-Beta Research promo:12422:h:0:27500:1023:1024:0:0:0
-Canal Canarias:12441:v:0:27500:160:80:0:0:0
-TVC International:12441:v:0:27500:512:660:0:0:0
-Fitur:12441:v:0:27500:514:662:0:0:0
-Astra Info 1:12552:v:0:22000:164:112:0:0:0
-Astra Info 2:12552:v:0:22000:165:120:0:0:0
-Astra Vision 1:12552:v:0:22000:168:144:0:0:0
-Astra Vision 1:12552:v:0:22000:168:145:0:0:0
-Astra Vision 1:12552:v:0:22000:168:146:0:0:0
-Astra Vision 1:12552:v:0:22000:168:147:0:0:0
-Astra Vision 1:12552:v:0:22000:168:148:0:0:0
-Astra Vision 1:12552:v:0:22000:168:149:0:0:0
-Astra Vision 1:12552:v:0:22000:168:150:0:0:0
-RTL Tele Letzebuerg:12552:v:0:22000:168:144:0:0:0
-Astra Mosaic:12552:v:0:22000:175:176:0:0:0
-MHP test:12604:h:0:22000:5632:8191:0:0:0
-VERONICA:12574:h:0:22000:161:84:0:0:5010
-VH1 Classic:12699:v:0:22000:3071:3072:0:0:28647
-VH-1 Germany:12699:v:0:22000:3081:3082:0:0:28648
-Via 1 - Schöner Reisen:12148:h:0:27500:511:512:0:0:44
-Video Italia:12610:v:0:22000:121:122:0:0:12220
-AC 3 promo:12670:v:0:22000:308:256:0:0:0
-ORF/ZDF:12699:h:0:22000:506:507:0:0:13012
-VIVA:12670:v:0:22000:309:310:0:0:12732
diff --git a/config.c b/config.c
index acf1996a..5c572357 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.44 2001/04/01 14:32:22 kls Exp $
+ * $Id: config.c 1.45 2001/06/02 09:42:25 kls Exp $
*/
#include "config.h"
@@ -155,7 +155,7 @@ eKeys cKeys::Get(unsigned int Code)
}
eKeys cKeys::Translate(const char *Command)
-{
+{
if (Command) {
const tKey *k = keys;
while ((k->type != kNone) && strcasecmp(k->name, Command) != 0)
@@ -166,7 +166,7 @@ eKeys cKeys::Translate(const char *Command)
}
unsigned int cKeys::Encode(const char *Command)
-{
+{
eKeys k = Translate(Command);
if (k != kNone)
return keys[k].code;
@@ -305,8 +305,8 @@ cTimer::cTimer(bool Instant)
if (stop >= 2400)
stop -= 2400;
//TODO VPS???
- priority = DEFAULTPRIORITY;
- lifetime = DEFAULTLIFETIME;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
*file = 0;
summary = NULL;
if (Instant && ch)
@@ -330,8 +330,8 @@ cTimer::cTimer(const cEventInfo *EventInfo)
stop = time->tm_hour * 100 + time->tm_min;
if (stop >= 2400)
stop -= 2400;
- priority = DEFAULTPRIORITY;
- lifetime = DEFAULTLIFETIME;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
*file = 0;
const char *Title = EventInfo->GetTitle();
if (!isempty(Title))
@@ -524,14 +524,14 @@ bool cTimer::Matches(time_t t)
}
time_t cTimer::StartTime(void)
-{
+{
if (!startTime)
Matches();
return startTime;
}
time_t cTimer::StopTime(void)
-{
+{
if (!stopTime)
Matches();
return stopTime;
@@ -734,6 +734,7 @@ cSetup::cSetup(void)
ShowInfoOnChSwitch = 1;
MenuScrollPage = 1;
MarkInstantRecord = 1;
+ LnbSLOF = 11700;
LnbFrequLo = 9750;
LnbFrequHi = 10600;
SetSystemTime = 0;
@@ -742,6 +743,8 @@ cSetup::cSetup(void)
EPGScanTimeout = 5;
SVDRPTimeout = 300;
PrimaryLimit = 0;
+ DefaultPriority = 50;
+ DefaultLifetime = 50;
CurrentChannel = -1;
}
@@ -756,6 +759,7 @@ bool cSetup::Parse(char *s)
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value);
else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value);
+ else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value);
else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value);
else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value);
else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value);
@@ -764,6 +768,8 @@ bool cSetup::Parse(char *s)
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
else if (!strcasecmp(Name, "PrimaryLimit")) PrimaryLimit = atoi(Value);
+ else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value);
+ else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value);
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
else
return false;
@@ -813,6 +819,7 @@ bool cSetup::Save(const char *FileName)
fprintf(f, "ShowInfoOnChSwitch = %d\n", ShowInfoOnChSwitch);
fprintf(f, "MenuScrollPage = %d\n", MenuScrollPage);
fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord);
+ fprintf(f, "LnbSLOF = %d\n", LnbSLOF);
fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo);
fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi);
fprintf(f, "SetSystemTime = %d\n", SetSystemTime);
@@ -821,6 +828,8 @@ bool cSetup::Save(const char *FileName)
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout);
fprintf(f, "PrimaryLimit = %d\n", PrimaryLimit);
+ fprintf(f, "DefaultPriority = %d\n", DefaultPriority);
+ fprintf(f, "DefaultLifetime = %d\n", DefaultLifetime);
fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
f.Close();
isyslog(LOG_INFO, "saved setup to %s", FileName);
diff --git a/config.h b/config.h
index 6c21fb2f..8665704f 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.44 2001/04/01 14:44:40 kls Exp $
+ * $Id: config.h 1.45 2001/06/02 09:43:04 kls Exp $
*/
#ifndef __CONFIG_H
@@ -19,10 +19,13 @@
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "0.72"
+#define VDRVERSION "0.80"
#define MaxBuffer 10000
+#define MAXPRIORITY 99
+#define MAXLIFETIME 99
+
enum eKeys { // "Up" and "Down" must be the first two keys!
kUp,
kDown,
@@ -106,9 +109,6 @@ public:
bool Switch(cDvbApi *DvbApi = NULL, bool Log = true);
};
-#define DEFAULTPRIORITY 99
-#define DEFAULTLIFETIME 99
-
class cTimer : public cListObject {
private:
time_t startTime, stopTime;
@@ -240,7 +240,7 @@ public:
bool SwitchTo(int Number, cDvbApi *DvbApi = NULL);
int MaxNumber(void) { return maxNumber; }
};
-
+
class cTimers : public cConfig<cTimer> {
public:
cTimer *GetTimer(cTimer *Timer);
@@ -266,6 +266,7 @@ public:
int ShowInfoOnChSwitch;
int MenuScrollPage;
int MarkInstantRecord;
+ int LnbSLOF;
int LnbFrequLo;
int LnbFrequHi;
int SetSystemTime;
@@ -273,6 +274,7 @@ public:
int EPGScanTimeout;
int SVDRPTimeout;
int PrimaryLimit;
+ int DefaultPriority, DefaultLifetime;
int CurrentChannel;
cSetup(void);
bool Load(const char *FileName);
diff --git a/dvbapi.c b/dvbapi.c
index a929b503..e41ef7ab 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,13 +4,15 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.66 2001/03/31 15:01:57 kls Exp $
+ * $Id: dvbapi.c 1.67 2001/06/02 09:31:03 kls Exp $
*/
#include "dvbapi.h"
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
extern "C" {
+#define HAVE_BOOLEAN
#include <jpeglib.h>
}
#include <stdlib.h>
@@ -20,20 +22,29 @@ extern "C" {
#include <sys/time.h>
#include <unistd.h>
#include "config.h"
-#include "interface.h"
#include "recording.h"
#include "remux.h"
#include "ringbuffer.h"
#include "tools.h"
#include "videodir.h"
-#define VIDEODEVICE "/dev/video"
-#define VBIDEVICE "/dev/vbi"
+#define DEV_VIDEO "/dev/video"
+#define DEV_OST_OSD "/dev/ost/osd"
+#define DEV_OST_QAMFE "/dev/ost/qamfe"
+#define DEV_OST_QPSKFE "/dev/ost/qpskfe"
+#define DEV_OST_SEC "/dev/ost/sec"
+#define DEV_OST_DVR "/dev/ost/dvr"
+#define DEV_OST_DEMUX "/dev/ost/demux"
+#define DEV_OST_VIDEO "/dev/ost/video"
+#define DEV_OST_AUDIO "/dev/ost/audio"
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE (1024*1024)
+// The maximum size of a single frame:
+#define MAXFRAMESIZE (128*1024)
+
#define FRAMESPERSEC 25
// The maximum file size is limited by the range that can be covered
@@ -55,16 +66,9 @@ extern "C" {
// The number of frames to back up when resuming an interrupted replay session:
#define RESUMEBACKUP (10 * FRAMESPERSEC)
-typedef unsigned char uchar;
+#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
-static void SetPlayMode(int VideoDev, int Mode)
-{
- if (VideoDev >= 0) {
- struct video_play_mode pmode;
- pmode.mode = Mode;
- ioctl(VideoDev, VIDIOCSPLAYMODE, &pmode);
- }
-}
+typedef unsigned char uchar;
const char *IndexToHMSF(int Index, bool WithFrame)
{
@@ -100,6 +104,7 @@ private:
public:
cIndexFile(const char *FileName, bool Record);
~cIndexFile();
+ bool Ok(void) { return index != NULL; }
void Write(uchar PictureType, uchar FileNumber, int FileOffset);
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);
@@ -158,8 +163,10 @@ cIndexFile::cIndexFile(const char *FileName, bool Record)
else
LOG_ERROR;
}
+ else if (!Record)
+ isyslog(LOG_INFO, "missing index file %s", fileName);
if (Record) {
- if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP)) >= 0) {
+ if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
if (delta) {
esyslog(LOG_ERR, "ERROR: padding index file with %d '0' bytes", delta);
while (delta--)
@@ -315,236 +322,6 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
return -1;
}
-// --- cRingBuffer_ -----------------------------------------------------------
-
-/* cRingBuffer reads data from an input file, stores it in a buffer and writes
- it to an output file upon request. The Read() and Write() functions should
- be called only when the associated file is ready to provide or receive data
- (use the 'select()' function to determine that), and the files should be
- opened in non-blocking mode.
- The '...Limit' parameters define safety limits. If they are exceeded a log entry
- will be made.
-*/
-
-class cRingBuffer_ {
-private:
- uchar *buffer;
- int size, head, tail, freeLimit, availLimit;
- int countLimit, countOverflow;
- int minFree;
- bool eof;
- int *inFile, *outFile;
-protected:
- int Free(void) { return ((tail >= head) ? size + head - tail : head - tail) - 1; }
-public:
- int Available(void) { return (tail >= head) ? tail - head : size - head + tail; }
-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_();
- virtual int Read(int Max = -1);
- virtual int Write(int Max = -1);
- bool EndOfFile(void) { return eof; }
- bool Empty(void) { return Available() == 0; }
- void Clear(void) { head = tail = 0; }
- void Skip(int n);
- };
-
-cRingBuffer_::cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
-{
- inFile = InFile;
- outFile = OutFile;
- size = Size;
- Clear();
- freeLimit = FreeLimit;
- availLimit = AvailLimit;
- eof = false;
- countLimit = countOverflow = 0;
- minFree = size - 1;
- buffer = new uchar[size];
- if (!buffer)
- esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
-}
-
-cRingBuffer_::~cRingBuffer_()
-{
- dsyslog(LOG_INFO, "buffer stats: %d free, %d overflows, limit exceeded %d times", minFree, countOverflow, countLimit);
- delete buffer;
-}
-
-int cRingBuffer_::Byte(int Offset)
-{
- if (buffer && Offset < Available()) {
- Offset += head;
- if (Offset >= size)
- Offset -= size;
- return buffer[Offset];
- }
- return -1;
-}
-
-bool cRingBuffer_::Set(int Offset, int Length, int Value)
-{
- if (buffer && Offset + Length <= Available() ) {
- Offset += head;
- while (Length--) {
- if (Offset >= size)
- Offset -= size;
- buffer[Offset] = Value;
- Offset++;
- }
- return true;
- }
- 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;
- }
- }
-}
-
-int cRingBuffer_::Read(int Max)
-{
- if (buffer) {
- eof = false;
- int free = Free();
- if (free < minFree)
- minFree = free;
- if (freeLimit) {
- if (free == 0) {
- esyslog(LOG_ERR, "ERROR: buffer overflow (size=%d)", size);
- countOverflow++;
- }
- else if (free < freeLimit) {
- dsyslog(LOG_INFO, "free buffer space dipped into limit (%d < %d)", free, freeLimit);
- countLimit++;
- }
- }
- if (free == 0)
- return 0; // the buffer is full
- int readin = 0;
- for (int i = 0; i < 2; i++) {
- // If we read in exactly as many bytes as are immediately
- // "readable" we have to do it again, because that means we
- // were at the very end of the physical buffer and possibly only
- // read in very few bytes.
- int immediate = Readable();
- int n = immediate;
- if (Max > 0 && n > Max)
- n = Max;
- if (n > 0) {
- int r = read(*inFile, buffer + tail, n);
- if (r > 0) {
- readin += r;
- tail += r;
- if (tail > size)
- esyslog(LOG_ERR, "ERROR: ooops: buffer tail (%d) exceeds size (%d)", tail, size);
- if (tail >= size)
- tail = 0;
- }
- else if (r < 0) {
- if (errno != EAGAIN) {
- LOG_ERROR;
- return -1;
- }
- }
- else
- eof = true;
- if (r == immediate && Max != immediate && tail == 0)
- Max -= immediate;
- else
- break;
- }
- }
- return readin;
- }
- return -1;
-}
-
-int cRingBuffer_::Write(int Max)
-{
- if (buffer) {
- int avail = Available();
- if (availLimit) {
- //XXX stats???
- if (avail == 0)
- //XXX esyslog(LOG_ERR, "ERROR: buffer empty!");
- {//XXX
- esyslog(LOG_ERR, "ERROR: buffer empty! %d", Max);
- return Max > 0 ? Max : 0;
- }//XXX
- else if (avail < availLimit)
-;//XXX dsyslog(LOG_INFO, "available buffer data dipped into limit (%d < %d)", avail, availLimit);
- }
- if (avail == 0)
- return 0; // the buffer is empty
- int n = Writeable();
- if (Max > 0 && n > Max)
- n = Max;
- int w = write(*outFile, buffer + head, n);
- if (w > 0) {
- head += w;
- if (head > size)
- esyslog(LOG_ERR, "ERROR: ooops: buffer head (%d) exceeds size (%d)", head, size);
- if (head >= size)
- head = 0;
- }
- else if (w < 0) {
- if (errno != EAGAIN)
- LOG_ERROR;
- else
- w = 0;
- }
- return w;
- }
- return -1;
-}
-
-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.
-
- 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 {
@@ -553,8 +330,9 @@ private:
int fileNumber;
char *fileName, *pFileNumber;
bool record;
+ bool blocking;
public:
- cFileName(const char *FileName, bool Record);
+ cFileName(const char *FileName, bool Record, bool Blocking = false);
~cFileName();
const char *Name(void) { return fileName; }
int Number(void) { return fileNumber; }
@@ -564,11 +342,12 @@ public:
int NextFile(void);
};
-cFileName::cFileName(const char *FileName, bool Record)
+cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
{
file = -1;
fileNumber = 0;
record = Record;
+ blocking = Blocking;
// Prepare the file name:
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
if (!fileName) {
@@ -589,16 +368,17 @@ cFileName::~cFileName()
int cFileName::Open(void)
{
if (file < 0) {
+ int BlockingFlag = blocking ? 0 : O_NONBLOCK;
if (record) {
dsyslog(LOG_INFO, "recording to '%s'", fileName);
- file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
+ file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
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);
+ file = open(fileName, O_RDONLY | BlockingFlag);
if (file < 0)
LOG_ERROR_STR(fileName);
}
@@ -655,6 +435,7 @@ int cFileName::NextFile(void)
class cRecordBuffer : public cRingBuffer {
private:
+ cDvbApi *dvbApi;
cFileName fileName;
cIndexFile *index;
cRemux remux;
@@ -670,18 +451,19 @@ protected:
virtual void Input(void);
virtual void Output(void);
public:
- cRecordBuffer(int *InFile, const char *FileName);
+ cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid);
virtual ~cRecordBuffer();
};
-cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
-:cRingBuffer(VIDEOBUFSIZE)
+cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid)
+:cRingBuffer(VIDEOBUFSIZE, true)
,fileName(FileName, true)
+,remux(VPid, APid, true)
{
+ dvbApi = DvbApi;
index = NULL;
pictureType = NO_PICTURE;
fileSize = 0;
- videoDev = *InFile;
recordFile = fileName.Open();
recording = false;
lastDiskSpaceCheck = time(NULL);
@@ -692,12 +474,14 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
if (!index)
esyslog(LOG_ERR, "ERROR: can't allocate index");
// let's continue without index, so we'll at least have the recording
+ videoDev = dvbApi->SetModeRecord();
Start();
}
cRecordBuffer::~cRecordBuffer()
{
Stop();
+ dvbApi->SetModeNormal(true);
delete index;
}
@@ -746,18 +530,18 @@ void cRecordBuffer::Input(void)
else if (r < 0) {
if (errno != EAGAIN) {
LOG_ERROR;
- break;
+ if (errno != EBUFFEROVERFLOW)
+ break;
}
}
else if (time(NULL) - t > 5) {
esyslog(LOG_ERR, "ERROR: video data stream broken");
- t = time(NULL);
+ cThread::EmergencyExit(true);
}
cFile::FileReady(videoDev, 100);
if (!recording)
break;
}
- SetPlayMode(videoDev, VID_PLAY_RESET);
dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
}
@@ -766,15 +550,14 @@ void cRecordBuffer::Output(void)
{
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
- uchar b[MINVIDEODATA * 2];
+ uchar b[MINVIDEODATA];
int r = 0;
for (;;) {
- usleep(1); // this keeps the CPU load low
- r += Get(b + r, sizeof(b) - r);
- if (r > 0) {
- //XXX buffer full???
+ int g = Get(b + r, sizeof(b) - r);
+ if (g > 0) {
+ r += g;
int Count = r, Result;
- const uchar *p = remux.Process(b, Count, Result, pictureType);
+ const uchar *p = remux.Process(b, Count, Result, &pictureType);
if (p) {
if (!Busy() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
break;
@@ -803,6 +586,8 @@ void cRecordBuffer::Output(void)
if (!recording)
break;
}
+ else
+ usleep(1); // this keeps the CPU load low
}
recording = false;
@@ -811,158 +596,287 @@ void cRecordBuffer::Output(void)
// --- cReplayBuffer ---------------------------------------------------------
-class cReplayBuffer : public cRingBuffer_, public cThread {
+class cReplayBuffer : public cRingBuffer {
private:
- enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
- enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
+ cDvbApi *dvbApi;
cIndexFile *index;
cFileName fileName;
int fileOffset;
- int videoDev;
+ int videoDev, audioDev;
int replayFile;
- eReplayMode mode;
+ bool eof;
+ int blockInput, blockOutput;
+ bool paused, fastForward, fastRewind;
int lastIndex, stillIndex;
- int brakeCounter;
- eReplayCmd command;
- bool active;
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
+ void Clear(bool Block = false);
void Close(void);
- void SetCmd(eReplayCmd Cmd) { LOCK_THREAD; command = Cmd; }
- void SetTemporalReference(void);
+ int ReadFrame(uchar *b, int Length, int Max);
+ void StripAudioPackets(uchar *b, int Length);
+ void DisplayFrame(uchar *b, int Length);
+ int Resume(void);
+ bool Save(void);
protected:
- virtual void Action(void);
+ virtual void Input(void);
+ virtual void Output(void);
public:
- cReplayBuffer(int *OutFile, const char *FileName);
+ cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
virtual ~cReplayBuffer();
- virtual int Read(int Max = -1);
- virtual int Write(int Max = -1);
- 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); }
+ void Pause(void);
+ void Play(void);
+ void Forward(void);
+ void Backward(void);
int SkipFrames(int Frames);
void SkipSeconds(int Seconds);
void Goto(int Position, bool Still = false);
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
};
-cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
-:cRingBuffer_(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
+:cRingBuffer(VIDEOBUFSIZE)
,fileName(FileName, false)
{
+ dvbApi = DvbApi;
index = NULL;
fileOffset = 0;
- videoDev = *OutFile;
+ videoDev = VideoDev;
+ audioDev = AudioDev;
replayFile = fileName.Open();
- mode = rmPlay;
- brakeCounter = 0;
- command = rcNone;
+ eof = false;
+ blockInput = blockOutput = false;
+ paused = fastForward = fastRewind = false;
lastIndex = stillIndex = -1;
- active = false;
if (!fileName.Name())
return;
// Create the index file:
index = new cIndexFile(FileName, false);
- if (!index)
+ if (!index) {
esyslog(LOG_ERR, "ERROR: can't allocate index");
- // let's continue without index, so we'll at least have the recording
+ }
+ else if (!index->Ok()) {
+ delete index;
+ index = NULL;
+ }
+ dvbApi->SetModeReplay();
Start();
}
cReplayBuffer::~cReplayBuffer()
{
- active = false;
- Cancel(3);
+ Stop();
+ Save();
Close();
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- SetPlayMode(videoDev, VID_PLAY_RESET);
+ dvbApi->SetModeNormal(false);
delete index;
}
-void cReplayBuffer::Action(void)
+void cReplayBuffer::Input(void)
{
- dsyslog(LOG_INFO, "replay thread started (pid=%d)", getpid());
-
- bool Paused = false;
- bool FastForward = false;
- bool FastRewind = false;
+ dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
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;
+
+ int lastIndex = -1;
+ int brakeCounter = 0;
+ uchar b[MAXFRAMESIZE];
+ while (Busy() && (blockInput || NextFile())) {
+ if (!blockInput && stillIndex < 0) {
+ int r = 0;
+ if (fastForward && !paused || fastRewind) {
+ int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
+ uchar FileNumber;
+ int FileOffset, Length;
+ if (!paused || (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, fastForward, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0) {
+ if (!NextFile(FileNumber, FileOffset))
+ break;
+ }
+ else {
+ paused = fastForward = fastRewind = false;
+ Play();
+ continue;
+ }
+ lastIndex = Index;
+ r = ReadFrame(b, Length, sizeof(b));
+ StripAudioPackets(b, Length);
+ }
+ else {
+ lastIndex = -1;
+ r = read(replayFile, b, sizeof(b));
+ }
+ if (r > 0) {
+ uchar *p = b;
+ while (r > 0 && Busy() && !blockInput) {
+ int w = Put(p, r);
+ p += w;
+ r -= w;
+ usleep(1); // this keeps the CPU load low
+ }
+ }
+ else if (r ==0)
+ eof = true;
+ else if (r < 0 && errno != EAGAIN) {
+ LOG_ERROR;
+ break;
+ }
}
- if (Read() < 0 || Write() < 0)
- break;
+ if (blockInput > 1)
+ blockInput = 1;
}
- Save();
- dsyslog(LOG_INFO, "end replaying thread");
+ dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
+}
+
+void cReplayBuffer::Output(void)
+{
+ dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
+
+ uchar b[MINVIDEODATA];
+ while (Busy()) {
+ int r = blockOutput ? 0 : Get(b, sizeof(b));
+ if (r > 0) {
+ uchar *p = b;
+ while (r > 0 && Busy() && !blockOutput) {
+ cFile::FileReadyForWriting(videoDev, 100);
+ int w = write(videoDev, p, r);
+ if (w < 0) {
+ LOG_ERROR;
+ Stop();
+ return;
+ }
+ p += w;
+ r -= w;
+ fileOffset += w;
+ }
+ }
+ if (blockOutput > 1)
+ blockOutput = 1;
+ }
+
+ dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
+}
+
+int cReplayBuffer::ReadFrame(uchar *b, int Length, int Max)
+{
+ if (Length > Max) {
+ esyslog(LOG_ERR, "ERROR: frame larger than buffer (%d > %d)", Length, Max);
+ Length = Max;
+ }
+ int r = read(replayFile, b, Length);
+ if (r >= 0) {
+ if (r != Length)
+ esyslog(LOG_ERR, "ERROR: got %d byte while reading %d", r, Length);
+ return r;
+ }
+ LOG_ERROR;
+ return -1;
+}
+
+void cReplayBuffer::StripAudioPackets(uchar *b, int Length)
+{
+ for (int i = 0; i < Length - 6; i++) {
+ if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
+ switch (b[i + 3]) {
+ case 0xC0 ... 0xDF: // audio
+ {
+ int n = b[i + 4] * 256 + b[i + 5];
+ for (int j = i; j < Length && n--; j++)
+ b[j] = 0x00;
+ }
+ break;
+ case 0xE0 ... 0xEF: // video
+ i += b[i + 4] * 256 + b[i + 5];
+ break;
+ }
+ }
+ }
+}
+
+void cReplayBuffer::DisplayFrame(uchar *b, int Length)
+{
+ StripAudioPackets(b, Length);
+ videoDisplayStillPicture sp = { (char *)b, Length };
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, true));
+ CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
+}
+
+void cReplayBuffer::Clear(bool Block)
+{
+ if (!(blockInput || blockOutput)) {
+ blockInput = blockOutput = 2;
+ time_t t0 = time(NULL);
+ while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
+ usleep(1);
+ Lock();
+ cRingBuffer::Clear();
+ CHECK(ioctl(videoDev, VIDEO_FREEZE));
+ CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
+ }
+ if (!Block) {
+ blockInput = blockOutput = 0;
+ Unlock();
+ }
+}
+
+void cReplayBuffer::Pause(void)
+{
+ paused = !paused;
+ CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
+ if (fastForward || fastRewind) {
+ if (paused)
+ Clear();
+ fastForward = fastRewind = false;
+ }
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
+ stillIndex = -1;
+}
+
+void cReplayBuffer::Play(void)
+{
+ if (fastForward || fastRewind || paused) {
+ if (!paused)
+ Clear();
+ stillIndex = -1;
+ CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
+ fastForward = fastRewind = paused = false;
+ }
+}
+
+void cReplayBuffer::Forward(void)
+{
+ if (index || paused) {
+ if (!paused)
+ Clear(true);
+ stillIndex = -1;
+ fastForward = !fastForward;
+ fastRewind = false;
+ if (paused)
+ CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
+ if (!paused)
+ Clear(false);
+ }
+}
+
+void cReplayBuffer::Backward(void)
+{
+ if (index) {
+ Clear(true);
+ stillIndex = -1;
+ fastRewind = !fastRewind;
+ fastForward = false;
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
+ Clear(false);
+ }
}
void cReplayBuffer::Close(void)
@@ -974,14 +888,6 @@ void cReplayBuffer::Close(void)
}
}
-void cReplayBuffer::SetMode(eReplayMode Mode)
-{
- mode = Mode;
- brakeCounter = 0;
- if (mode != rmPlay)
- Clear();
-}
-
int cReplayBuffer::Resume(void)
{
if (index) {
@@ -1016,9 +922,6 @@ bool cReplayBuffer::Save(void)
int cReplayBuffer::SkipFrames(int Frames)
{
if (index && Frames) {
-
- LOCK_THREAD;
-
int Current, Total;
GetIndex(Current, Total, true);
int OldCurrent = Current;
@@ -1030,16 +933,8 @@ int cReplayBuffer::SkipFrames(int Frames)
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) {
+ Clear(true);
int Index = index->Get(fileName.Number(), fileOffset);
if (Index >= 0) {
if (Seconds < 0) {
@@ -1055,31 +950,38 @@ void cReplayBuffer::SkipSeconds(int Seconds)
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
NextFile(FileNumber, FileOffset);
}
+ Clear(false);
+ Play();
}
}
void cReplayBuffer::Goto(int Index, bool Still)
{
- 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();
+ if (index) {
+ Clear(true);
+ if (++Index <= 0)
+ Index = 1; // not '0', to allow GetNextIFrame() below to work!
+ uchar FileNumber;
+ int FileOffset, Length;
+ Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
+ stillIndex = Index;
+ uchar b[MAXFRAMESIZE];
+ int r = ReadFrame(b, Length, sizeof(b));
+ if (r > 0)
+ DisplayFrame(b, r);
+ fileOffset += Length;
+ paused = true;
+ }
+ else
+ stillIndex = -1;
+ Clear(false);
+ }
}
void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
if (index) {
-
- LOCK_THREAD;
-
if (stillIndex >= 0)
Current = stillIndex;
else {
@@ -1099,178 +1001,122 @@ void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0) {
- Clear();
fileOffset = FileOffset;
replayFile = fileName.SetOffset(FileNumber, FileOffset);
}
- else if (replayFile >= 0 && EndOfFile()) {
+ else if (replayFile >= 0 && eof) {
Close();
replayFile = fileName.NextFile();
}
+ eof = 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;
- }
- }
- }
-}
-
-int cReplayBuffer::Read(int Max = -1)
-{
- if (mode != rmPlay) {
- if (index) {
- if (Available())
- return 0; // write out the entire block
- if (mode == rmStill) {
- uchar FileNumber;
- int FileOffset, Length;
- if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- return -1;
- Max = Length;
- }
- command = rcPause;
- }
- 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;
- }
- }
- }
- }
- else
- lastIndex = -1;
- //XXX timeout as in recording???
- if (NextFile()) {
- int readin = 0;
- do {
- // If Max is > 0 here we need to make sure we read in the entire block!
- 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)
- return 0;
- return -1;
-}
-
-int cReplayBuffer::Write(int Max)
-{
- int Written = 0;
- if (Max) {
- int w;
- do {
- w = cRingBuffer_::Write(Max);
- if (w >= 0) {
- fileOffset += w;
- Written += w;
- if (Max < 0)
- break;
- Max -= w;
- }
- else
- return w;
- } while (Max > 0); // we MUST write this entire frame block
- }
- return Written;
-}
-
// --- cTransferBuffer -------------------------------------------------------
-class cTransferBuffer : public cThread {
+class cTransferBuffer : public cRingBuffer {
private:
- bool active;
+ cDvbApi *dvbApi;
int fromDevice, toDevice;
+ cRemux remux;
protected:
- virtual void Action(void);
+ virtual void Input(void);
+ virtual void Output(void);
public:
- cTransferBuffer(int FromDevice, int ToDevice);
+ cTransferBuffer(cDvbApi *DvbApi, int ToDevice, dvb_pid_t VPid, dvb_pid_t APid);
virtual ~cTransferBuffer();
};
-cTransferBuffer::cTransferBuffer(int FromDevice, int ToDevice)
+cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, dvb_pid_t VPid, dvb_pid_t APid)
+:cRingBuffer(VIDEOBUFSIZE, true)
+,remux(VPid, APid)
{
- fromDevice = FromDevice;
+ dvbApi = DvbApi;
+ fromDevice = dvbApi->SetModeRecord();
toDevice = ToDevice;
- active = false;
Start();
}
cTransferBuffer::~cTransferBuffer()
{
- active = false;
- Cancel(3);
- SetPlayMode(fromDevice, VID_PLAY_RESET);
- SetPlayMode(toDevice, VID_PLAY_RESET);
+ Stop();
+ dvbApi->SetModeNormal(true);
}
-void cTransferBuffer::Action(void)
+void cTransferBuffer::Input(void)
{
- dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
+ dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
- 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
+ uchar b[MINVIDEODATA];
+ int n = 0;
+ while (Busy()) {
+ cFile::FileReady(fromDevice, 100);
+ int r = read(fromDevice, b + n, sizeof(b) - n);
+ if (r > 0) {
+ n += r;
+ int Count = n, Result;
+ const uchar *p = remux.Process(b, Count, Result);
+ if (p) {
+ while (Result > 0 && Busy()) {
+ int w = Put(p, Result);
+ p += w;
+ Result -= w;
+ }
+ }
+ if (Count > 0) {
+ n -= Count;
+ memmove(b, b + Count, n);
+ }
+ }
+ else if (r < 0) {
+ if (errno != EAGAIN) {
+ LOG_ERROR;
+ if (errno != EBUFFEROVERFLOW)
+ break;
+ }
+ }
}
- while (active) {
- if (Buffer.Read() < 0 || Buffer.Write() < 0)
- break;
- usleep(1); // this keeps the CPU load low
+
+ dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
+}
+
+void cTransferBuffer::Output(void)
+{
+ dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
+
+ bool GotBufferReserve = false;
+ uchar b[MINVIDEODATA];
+ while (Busy()) {
+ if (!GotBufferReserve) {
+ if (Available() < MAXFRAMESIZE)
+ usleep(100000); // allow the buffer to collect some reserve
+ else
+ GotBufferReserve = true;
+ }
+ int r = Get(b, sizeof(b));
+ if (r > 0) {
+ uchar *p = b;
+ while (r > 0 && Busy()) {
+ int w = write(toDevice, p, r);
+ if (w < 0) {
+ LOG_ERROR;
+ Stop();
+ return;
+ }
+ p += w;
+ r -= w;
+ }
+ }
+ else
+ usleep(1); // this keeps the CPU load low
}
- dsyslog(LOG_INFO, "data transfer thread stopped (pid=%d)", getpid());
+
+ dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
}
// --- cCuttingBuffer --------------------------------------------------------
-class cCuttingBuffer : public cRingBuffer_, public cThread {
+class cCuttingBuffer : public cThread {
private:
bool active;
int fromFile, toFile;
@@ -1285,15 +1131,14 @@ public:
};
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);
+ fromFileName = new cFileName(FromFileName, false, true);
+ toFileName = new cFileName(ToFileName, true, true);
fromIndex = new cIndexFile(FromFileName, false);
toIndex = new cIndexFile(ToFileName, true);
toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
@@ -1329,22 +1174,31 @@ void cCuttingBuffer::Action(void)
int LastIFrame = 0;
toMarks.Add(0);
toMarks.Save();
+ uchar buffer[MAXFRAMESIZE];
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);
+ if (fromFile >= 0) {
+ if (Length <= (int)sizeof(buffer)) {
+ if (read(fromFile, buffer, Length) < 0) {
+ LOG_ERROR;
+ break;
+ }
+ }
+ else {
+ esyslog(LOG_ERR, "ERROR: frame larger than buffer (%d > %d)", Length, sizeof(buffer));
+ break;
+ }
+ }
else
break;
}
@@ -1362,7 +1216,7 @@ void cCuttingBuffer::Action(void)
}
LastIFrame = 0;
}
- cRingBuffer_::Write(Length);
+ write(toFile, buffer, Length);
toIndex->Write(PictureType, toFileName->Number(), FileSize);
FileSize += Length;
if (!LastIFrame)
@@ -1424,13 +1278,31 @@ bool cVideoCutter::Active(void)
// --- cDvbApi ---------------------------------------------------------------
+
+static const char *OstName(const char *Name, int n)
+{
+ static char buffer[_POSIX_PATH_MAX];
+ snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
+ return buffer;
+}
+
+static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false)
+{
+ const char *FileName = OstName(Name, n);
+ int fd = open(FileName, Mode);
+ if (fd < 0 && ReportError)
+ LOG_ERROR_STR(FileName);
+ return fd;
+}
+
int cDvbApi::NumDvbApis = 0;
int cDvbApi::useDvbApi = 0;
cDvbApi *cDvbApi::dvbApi[MAXDVBAPI] = { NULL };
cDvbApi *cDvbApi::PrimaryDvbApi = NULL;
-cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
+cDvbApi::cDvbApi(int n)
{
+ vPid = aPid = 0;
siProcessor = NULL;
recordBuffer = NULL;
replayBuffer = NULL;
@@ -1438,21 +1310,42 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
transferringFromDvbApi = NULL;
ca = 0;
priority = -1;
- videoDev = open(VideoFileName, O_RDWR | O_NONBLOCK);
- if (videoDev >= 0) {
- siProcessor = new cSIProcessor(VbiFileName);
+
+ // Devices that are only present on DVB-C or DVB-S cards:
+
+ fd_qamfe = OstOpen(DEV_OST_QAMFE, n, O_RDWR);
+ fd_qpskfe = OstOpen(DEV_OST_QPSKFE, n, O_RDWR);
+ fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
+
+ // Devices that all DVB cards must have:
+
+ fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxa = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+
+ // Devices not present on "budget" cards:
+
+ fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
+ fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
+ fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
+
+ // Devices that may not be available, and are not necessary for normal operation:
+
+ videoDev = OstOpen(DEV_VIDEO, n, O_RDWR);
+
+ // Devices that will be dynamically opened and closed when necessary:
+
+ fd_dvr = -1;
+
+ // We only check the devices that must be present - the others will be checked before accessing them:
+
+ if (((fd_qpskfe >= 0 && fd_sec >= 0) || fd_qamfe >= 0) && fd_demuxv >= 0 && fd_demuxa >= 0 && fd_demuxt >= 0) {
+ siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
if (!dvbApi[0]) // only the first one shall set the system time
siProcessor->SetUseTSTime(Setup.SetSystemTime);
- siProcessor->AddFilter(0x14, 0x70); // TDT
- siProcessor->AddFilter(0x14, 0x73); // TOT
- siProcessor->AddFilter(0x12, 0x4e); // event info, actual TS, present/following
- siProcessor->AddFilter(0x12, 0x4f); // event info, other TS, present/following
- siProcessor->AddFilter(0x12, 0x50); // event info, actual TS, schedule
- siProcessor->AddFilter(0x12, 0x60); // event info, other TS, schedule
- siProcessor->Start();
}
else
- LOG_ERROR_STR(VideoFileName);
+ esyslog(LOG_ERR, "ERROR: can't open video device %d", n);
cols = rows = 0;
ovlGeoSet = ovlStat = ovlFbSet = false;
@@ -1480,16 +1373,14 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
cDvbApi::~cDvbApi()
{
- if (videoDev >= 0) {
- delete siProcessor;
- Close();
- StopReplay();
- StopRecord();
- StopTransfer();
- OvlO(false); //Overlay off!
- //XXX the following call sometimes causes a segfault - driver problem?
- //XXX close(videoDev);
- }
+ delete siProcessor;
+ Close();
+ StopReplay();
+ StopRecord();
+ StopTransfer();
+ OvlO(false); //Overlay off!
+ // We're not explicitly closing any device files here, since this sometimes
+ // caused segfaults. Besides, the program is about to terminate anyway...
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
endwin();
#endif
@@ -1555,37 +1446,32 @@ int cDvbApi::Index(void)
return -1;
}
+bool cDvbApi::Probe(const char *FileName)
+{
+ if (access(FileName, F_OK) == 0) {
+ dsyslog(LOG_INFO, "probing %s", FileName);
+ int f = open(FileName, O_RDONLY);
+ if (f >= 0) {
+ close(f);
+ return true;
+ }
+ else if (errno != ENODEV && errno != EINVAL)
+ LOG_ERROR_STR(FileName);
+ }
+ else if (errno != ENOENT)
+ LOG_ERROR_STR(FileName);
+ return false;
+}
+
bool cDvbApi::Init(void)
{
NumDvbApis = 0;
for (int i = 0; i < MAXDVBAPI; i++) {
if (useDvbApi == 0 || (useDvbApi & (1 << i)) != 0) {
- char fileName[strlen(VIDEODEVICE) + 10];
- sprintf(fileName, "%s%d", VIDEODEVICE, i);
- if (access(fileName, F_OK | R_OK | W_OK) == 0) {
- dsyslog(LOG_INFO, "probing %s", fileName);
- int f = open(fileName, O_RDWR);
- if (f >= 0) {
- struct video_capability cap;
- int r = ioctl(f, VIDIOCGCAP, &cap);
- close(f);
- if (r == 0 && (cap.type & VID_TYPE_DVB)) {
- char vbiFileName[strlen(VBIDEVICE) + 10];
- sprintf(vbiFileName, "%s%d", VBIDEVICE, i);
- dvbApi[NumDvbApis++] = new cDvbApi(fileName, vbiFileName);
- }
- }
- else {
- if (errno != ENODEV)
- LOG_ERROR_STR(fileName);
- break;
- }
- }
- else {
- if (errno != ENOENT)
- LOG_ERROR_STR(fileName);
+ if (Probe(OstName(DEV_OST_QPSKFE, i)) || Probe(OstName(DEV_OST_QAMFE, i)))
+ dvbApi[NumDvbApis++] = new cDvbApi(i);
+ else
break;
- }
}
}
PrimaryDvbApi = dvbApi[0];
@@ -1616,6 +1502,8 @@ const cSchedules *cDvbApi::Schedules(cThreadLock *ThreadLock) const
bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// just do this once?
struct video_mbuf mbuf;
@@ -1711,6 +1599,8 @@ bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX,
bool cDvbApi::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// get the actual X-Server settings???
// plausibility-check problem: can't be verified w/o X-server!!!
@@ -1752,6 +1642,8 @@ bool cDvbApi::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette)
bool cDvbApi::OvlG(int SizeX, int SizeY, int PosX, int PosY)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// get the actual X-Server settings???
struct video_capability vc;
@@ -1798,6 +1690,8 @@ bool cDvbApi::OvlG(int SizeX, int SizeY, int PosX, int PosY)
bool cDvbApi::OvlC(int ClipCount, CRect *cr)
{
+ if (videoDev < 0)
+ return false;
if (ovlGeoSet && ovlFbSet) {
for (int i = 0; i < ClipCount; i++) {
ovlClipRects[i].x = cr[i].x;
@@ -1815,6 +1709,8 @@ bool cDvbApi::OvlC(int ClipCount, CRect *cr)
bool cDvbApi::OvlP(__u16 Brightness, __u16 Colour, __u16 Hue, __u16 Contrast)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
ovlBrightness = Brightness;
ovlColour = Colour;
@@ -1836,6 +1732,8 @@ bool cDvbApi::OvlP(__u16 Brightness, __u16 Colour, __u16 Hue, __u16 Contrast)
bool cDvbApi::OvlO(bool Value)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
if (!ovlGeoSet && Value)
return false;
@@ -1870,8 +1768,8 @@ void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg)
#else
void cDvbApi::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
{
- if (videoDev >= 0) {
- struct drawcmd dc;
+ if (fd_osd >= 0) {
+ osd_cmd_t dc;
dc.cmd = cmd;
dc.color = color;
dc.x0 = x0;
@@ -1879,7 +1777,7 @@ void cDvbApi::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
dc.x1 = x1;
dc.y1 = y1;
dc.data = (void *)data;
- ioctl(videoDev, VIDIOCSOSDCOMMAND, &dc);
+ CHECK(ioctl(fd_osd, OSD_SEND_CMD, &dc));
usleep(10); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
// XXX and sometimes the OSD was no longer displayed).
// XXX Increase the value if the problem still persists on your particular system.
@@ -1905,7 +1803,7 @@ void cDvbApi::Open(int w, int h)
d *= lineHeight;
int x = (720 - MenuColumns * charWidth) / 2; //TODO PAL vs. NTSC???
int y = (576 - MenuLines * lineHeight) / 2 + d;
- osd = new cDvbOsd(videoDev, x, y, x + w - 1, y + h - 1, 4);
+ osd = new cDvbOsd(fd_osd, x, y, x + w - 1, y + h - 1, 4);
#define SETCOLOR(n, r, g, b, o) Cmd(OSD_SetColor, n, r, g, b, o)
SETCOLOR(clrTransparent, 0x00, 0x00, 0x00, 0);
#endif
@@ -2037,60 +1935,230 @@ void cDvbApi::Flush(void)
#endif
}
+int cDvbApi::SetModeRecord(void)
+{
+ // Sets up the DVB device for recording
+
+ SetPids(true);
+ if (fd_dvr >= 0)
+ close(fd_dvr);
+ fd_dvr = OstOpen(DEV_OST_DVR, Index(), O_RDONLY | O_NONBLOCK);
+ if (fd_dvr < 0)
+ LOG_ERROR;
+ return fd_dvr;
+}
+
+void cDvbApi::SetModeReplay(void)
+{
+ // Sets up the DVB device for replay
+
+ if (fd_video >= 0 && fd_audio >= 0) {
+ if (siProcessor)
+ siProcessor->SetStatus(false);
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_PLAY));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_video, VIDEO_PLAY));
+ }
+}
+
+void cDvbApi::SetModeNormal(bool FromRecording)
+{
+ // Puts the DVB device back into "normal" viewing mode (after replay or recording)
+
+ if (FromRecording) {
+ close(fd_dvr);
+ fd_dvr = -1;
+ SetPids(false);
+ }
+ else {
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_video, VIDEO_STOP, true));
+ CHECK(ioctl(fd_audio, AUDIO_STOP, true));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ if (siProcessor)
+ siProcessor->SetStatus(true);
+ }
+ }
+}
+
+bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, dvb_pid_t Pid, dmxOutput_t Output)
+{
+ dmxPesFilterParams pesFilterParams;
+ if (Pid == 0 || Pid == 0xFFFF) {
+ CHECK(ioctl(fd, DMX_STOP, Pid));
+ return true;
+ }
+ pesFilterParams.pid = Pid;
+ pesFilterParams.input = DMX_IN_FRONTEND;
+ pesFilterParams.output = Output;
+ pesFilterParams.pesType = PesType;
+ pesFilterParams.flags = DMX_IMMEDIATE_START;
+ if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
+ LOG_ERROR;
+ return false;
+ }
+ return true;
+}
+
+bool cDvbApi::SetPids(bool ForRecording)
+{
+ return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
+ SetApid(aPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER);
+}
+
bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr)
{
- if (videoDev >= 0) {
- cThreadLock ThreadLock(siProcessor); // makes sure the siProcessor won't access the vbi-device while switching
- StopTransfer();
- StopReplay();
- SetPlayMode(videoDev, VID_PLAY_RESET);
- struct frontend front;
- ioctl(videoDev, VIDIOCGFRONTEND, &front);
+ // Make sure the siProcessor won't access the device while switching
+ cThreadLock ThreadLock(siProcessor);
+
+ StopTransfer();
+ StopReplay();
+
+ // Avoid noise while switching:
+
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ }
+
+ bool ChannelSynced = false;
+
+ if (fd_qpskfe >= 0 && fd_sec >= 0) { // DVB-S
+
+ // Frequency offsets:
+
unsigned int freq = FrequencyMHz;
- if (front.type == FRONT_DVBS) {
- front.ttk = (freq < 11700UL) ? 0 : 1;
- if (freq < 11700UL) {
- freq -= Setup.LnbFrequLo;
- front.ttk = 0;
- }
- else {
- freq -= Setup.LnbFrequHi;
- front.ttk = 1;
- }
+ int tone = SEC_TONE_OFF;
+
+ if (freq < (unsigned int)Setup.LnbSLOF) {
+ freq -= Setup.LnbFrequLo;
+ tone = SEC_TONE_OFF;
}
- front.channel_flags = Ca ? DVB_CHANNEL_CA : DVB_CHANNEL_FTA;
- front.pnr = Pnr;
- front.freq = freq * 1000000UL;
- front.diseqc = Diseqc;
- front.srate = Srate * 1000;
- front.volt = (Polarization == 'v' || Polarization == 'V') ? 0 : 1;
- front.video_pid = Vpid;
- front.audio_pid = Apid;
- front.tt_pid = Tpid;
- front.fec = 8;
- front.AFC = 1;
- front.qam = 2;
- ioctl(videoDev, VIDIOCSFRONTEND, &front);
- if (front.sync & 0x1F == 0x1F) {
- if (this == PrimaryDvbApi && siProcessor)
- siProcessor->SetCurrentServiceID(Pnr);
- currentChannel = ChannelNumber;
- // If this DVB card can't receive this channel, let's see if we can
- // use the card that actually can receive it and transfer data from there:
- if (this == PrimaryDvbApi && Ca && Ca != Index() + 1) {
- cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
- if (CaDvbApi) {
- if (!CaDvbApi->Recording()) {
- if (CaDvbApi->SetChannel(ChannelNumber, FrequencyMHz, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr))
- transferringFromDvbApi = CaDvbApi->StartTransfer(videoDev);
- }
+ else {
+ freq -= Setup.LnbFrequHi;
+ tone = SEC_TONE_ON;
+ }
+
+ qpskParameters qpsk;
+ qpsk.iFrequency = freq * 1000UL;
+ qpsk.SymbolRate = Srate * 1000UL;
+ qpsk.FEC_inner = FEC_AUTO;
+
+ int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
+
+ // DiseqC:
+
+ secCommand scmd;
+ scmd.type = 0;
+ scmd.u.diseqc.addr = 0x10;
+ scmd.u.diseqc.cmd = 0x38;
+ scmd.u.diseqc.numParams = 1;
+ scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
+
+ secCmdSequence scmds;
+ scmds.voltage = volt;
+ scmds.miniCommand = SEC_MINI_NONE;
+ scmds.continuousTone = tone;
+ scmds.numCommands = 1;
+ scmds.commands = &scmd;
+
+ CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
+
+ // Tuning:
+
+ CHECK(ioctl(fd_qpskfe, QPSK_TUNE, &qpsk));
+
+ // Wait for channel sync:
+
+ qpskEvent event;
+ int res = ioctl(fd_qpskfe, QPSK_GET_EVENT, &event);
+ if (res == -EBUFFEROVERFLOW)
+ res = ioctl(fd_qpskfe, QPSK_GET_EVENT, &event);
+ if (res >= 0)
+ ChannelSynced = event.type == FE_COMPLETION_EV;
+ else
+ esyslog(LOG_ERR, "ERROR in qpsk get event");
+ }
+ else if (fd_qamfe >= 0) { // DVB-C
+
+ // Frequency and symbol rate:
+
+ qamParameters qam;
+ qam.Frequency = FrequencyMHz * 1000000UL;
+ qam.SymbolRate = Srate * 1000UL;
+ qam.FEC_inner = FEC_AUTO;
+ qam.QAM = QAM_64;
+
+ // Tuning:
+
+ CHECK(ioctl(fd_qamfe, QAM_TUNE, &qam));
+
+ // Wait for channel sync:
+
+ qamEvent event;
+ int res = ioctl(fd_qamfe, QAM_GET_EVENT, &event);
+ if (res == -EBUFFEROVERFLOW)
+ res = ioctl(fd_qamfe, QAM_GET_EVENT, &event);
+ if (res >= 0)
+ ChannelSynced = event.type == FE_COMPLETION_EV;
+ else
+ esyslog(LOG_ERR, "ERROR in qam get event");
+ }
+ else {
+ esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device");
+ return false;
+ }
+
+ if (!ChannelSynced) {
+ esyslog(LOG_ERR, "ERROR: channel %d not sync'ed!", ChannelNumber);
+ return false;
+ }
+
+ // PID settings:
+
+ vPid = Vpid;
+ aPid = Apid;
+ if (!SetPids(false)) {
+ esyslog(LOG_ERR, "ERROR: failed to set PIDs for channel %d", ChannelNumber);
+ return false;
+ }
+ SetTpid(Tpid, DMX_OUT_DECODER);
+ if (fd_audio >= 0)
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ if (this == PrimaryDvbApi && siProcessor)
+ siProcessor->SetCurrentServiceID(Pnr);
+ currentChannel = ChannelNumber;
+
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+ if (this == PrimaryDvbApi && Ca && Ca != Index() + 1) {
+ cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
+ if (CaDvbApi) {
+ if (!CaDvbApi->Recording()) {
+ if (CaDvbApi->SetChannel(ChannelNumber, FrequencyMHz, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr)) {
+ SetModeReplay();
+ transferringFromDvbApi = CaDvbApi->StartTransfer(fd_video);
}
}
- return true;
}
- esyslog(LOG_ERR, "ERROR: channel %d not sync'ed (front.sync=%X)!", ChannelNumber, front.sync);
}
- return false;
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
+ }
+
+ return true;
}
bool cDvbApi::Transferring(void)
@@ -2101,7 +2169,7 @@ bool cDvbApi::Transferring(void)
cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev)
{
StopTransfer();
- transferBuffer = new cTransferBuffer(videoDev, TransferToVideoDev);
+ transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid);
return this;
}
@@ -2110,7 +2178,6 @@ void cDvbApi::StopTransfer(void)
if (transferBuffer) {
delete transferBuffer;
transferBuffer = NULL;
- SetPlayMode(videoDev, VID_PLAY_RESET);
}
if (transferringFromDvbApi) {
transferringFromDvbApi->StopTransfer();
@@ -2143,37 +2210,36 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
esyslog(LOG_ERR, "ERROR: StartRecord() called while recording - ignored!");
return false;
}
- if (videoDev >= 0) {
- StopTransfer();
+ StopTransfer();
- StopReplay(); // 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:
+ // Check FileName:
- if (!FileName) {
- esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)");
- return false;
- }
- isyslog(LOG_INFO, "record %s", FileName);
+ if (!FileName) {
+ esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)");
+ return false;
+ }
+ isyslog(LOG_INFO, "record %s", FileName);
- // Create directories if necessary:
+ // Create directories if necessary:
- if (!MakeDirs(FileName, true))
- return false;
+ if (!MakeDirs(FileName, true))
+ return false;
- // Create recording buffer:
+ // Create recording buffer:
- recordBuffer = new cRecordBuffer(&videoDev, FileName);
+ recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid);
- if (recordBuffer) {
- ca = Ca;
- priority = Priority;
- return true;
- }
- else
- esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
+ if (recordBuffer) {
+ ca = Ca;
+ priority = Priority;
+ return true;
}
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
+
return false;
}
@@ -2195,7 +2261,7 @@ bool cDvbApi::StartReplay(const char *FileName)
}
StopTransfer();
StopReplay();
- if (videoDev >= 0) {
+ if (fd_video >= 0 && fd_audio >= 0) {
// Check FileName:
@@ -2207,7 +2273,7 @@ bool cDvbApi::StartReplay(const char *FileName)
// Create replay buffer:
- replayBuffer = new cReplayBuffer(&videoDev, FileName);
+ replayBuffer = new cReplayBuffer(this, fd_video, fd_audio, FileName);
if (replayBuffer)
return true;
else
@@ -2318,7 +2384,7 @@ void cEITScanner::Process(void)
time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
for (int i = 0; i < cDvbApi::NumDvbApis; i++) {
- cDvbApi *DvbApi = cDvbApi::GetDvbApi(i, 0);
+ cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, 0);
if (DvbApi) {
if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) {
diff --git a/dvbapi.h b/dvbapi.h
index de646121..f9808299 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -4,22 +4,28 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.h 1.35 2001/02/11 10:41:10 kls Exp $
+ * $Id: dvbapi.h 1.36 2001/06/02 09:44:00 kls Exp $
*/
#ifndef __DVBAPI_H
#define __DVBAPI_H
-// FIXME: these should be defined in ../DVB/driver/dvb.h!!!
-typedef unsigned int __u32;
-typedef unsigned short __u16;
-typedef unsigned char __u8;
-
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
#include <ncurses.h>
#endif
+#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files
+ // FIXME: shouldn't every header file include ALL the other header
+ // FIXME: files it depends on? The sequence in which header files
+ // FIXME: are included here should not matter - and it should NOT
+ // FIXME: be necessary to include <stdlib.h> here!
+#include <linux/videodev.h>
+#include <ost/dmx.h>
+#include <ost/sec.h>
+#include <ost/frontend.h>
+#include <ost/video.h>
+#include <ost/audio.h>
+#include <ost/osd.h>
#include <stdio.h>
-#include <dvb.h>
#include "dvbosd.h"
#include "eit.h"
#include "thread.h"
@@ -30,7 +36,7 @@ typedef struct CRect {
signed short x, y, width, height;
};
-#define MenuLines 15
+#define MenuLines 13 // XXX originally 15, but since driver version 2001-05-25 there is less OSD memory :-(
#define MenuColumns 40
const char *IndexToHMSF(int Index, bool WithFrame = false);
@@ -55,9 +61,19 @@ public:
};
class cDvbApi {
+ friend class cRecordBuffer;
+ friend class cReplayBuffer;
+ friend class cTransferBuffer;
private:
int videoDev;
- cDvbApi(const char *VideoFileName, const char *VbiFileName);
+ int fd_osd, fd_qpskfe, fd_qamfe, fd_sec, fd_dvr, fd_audio, fd_video, fd_demuxa, fd_demuxv, fd_demuxt;
+ int vPid, aPid;
+ bool SetPid(int fd, dmxPesType_t PesType, dvb_pid_t Pid, dmxOutput_t Output);
+ bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); }
+ bool SetApid(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa, DMX_PES_AUDIO, Apid, Output); }
+ bool SetTpid(int Tpid, dmxOutput_t Output) { return SetPid(fd_demuxt, DMX_PES_TELETEXT, Tpid, Output); }
+ bool SetPids(bool ForRecording);
+ cDvbApi(int n);
public:
~cDvbApi();
@@ -86,8 +102,10 @@ public:
// recording and stop recording if necessary.
int Index(void);
// Returns the index of this DvbApi.
+ static bool Probe(const char *FileName);
+ // Probes for existing DVB devices.
static bool Init(void);
- // Initializes the DVB API and probes for existing DVB devices.
+ // Initializes the DVB API.
// Must be called before accessing any DVB functions.
static void Cleanup(void);
// Closes down all DVB devices.
@@ -184,12 +202,15 @@ private:
cReplayBuffer *replayBuffer;
int ca;
int priority;
-protected:
int Ca(void) { return ca; }
// Returns the ca of the current recording session (0..MAXDVBAPI).
int Priority(void) { return priority; }
- // Returns the priority of the current recording session (0..99),
+ // Returns the priority of the current recording session (0..MAXPRIORITY),
// or -1 if no recording is currently active.
+ int SetModeRecord(void);
+ // Initiates recording mode and returns the file handle to read from.
+ void SetModeReplay(void);
+ void SetModeNormal(bool FromRecording);
public:
int SecondsToFrames(int Seconds);
// Returns the number of frames corresponding to the given number of seconds.
@@ -238,7 +259,7 @@ public:
// 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.
+ // if Still is true.
};
class cEITScanner {
diff --git a/dvbosd.c b/dvbosd.c
index 395ad32e..0ea90e7e 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.7 2000/12/09 11:13:00 kls Exp $
+ * $Id: dvbosd.c 1.8 2001/05/26 11:49:35 kls Exp $
*/
#include "dvbosd.h"
@@ -157,7 +157,7 @@ cDvbOsd::~cDvbOsd()
void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
{
if (videoDev >= 0) {
- struct drawcmd dc;
+ osd_cmd_t dc;
dc.cmd = cmd;
dc.color = color;
dc.x0 = x0;
@@ -169,7 +169,7 @@ void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
sigset_t set, oldset;
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
- ioctl(videoDev, VIDIOCSOSDCOMMAND, &dc);
+ ioctl(videoDev, OSD_SEND_CMD, &dc);
usleep(10); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
// XXX and sometimes the OSD was no longer displayed).
// XXX Increase the value if the problem still persists on your particular system.
diff --git a/dvbosd.h b/dvbosd.h
index eefdb621..88320fa4 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.5 2000/12/09 10:32:47 kls Exp $
+ * $Id: dvbosd.h 1.6 2001/05/01 14:41:42 kls Exp $
*/
#ifndef __DVBOSD_H
@@ -18,11 +18,11 @@ typedef unsigned char __u8;
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
#include <ncurses.h>
#endif
+#include <ost/osd.h>
#include <stdio.h>
-#include <dvb.h>
#include "font.h"
-enum eDvbColor {
+enum eDvbColor {
#ifndef DEBUG_OSD
clrTransparent,
#endif
diff --git a/eit.c b/eit.c
index 6cbe504d..3de61020 100644
--- a/eit.c
+++ b/eit.c
@@ -13,17 +13,16 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: eit.c 1.15 2001/04/01 15:36:09 kls Exp $
+ * $Id: eit.c 1.16 2001/05/26 10:58:01 kls Exp $
***************************************************************************/
#include "eit.h"
#include <ctype.h>
-#include <dvb_comcode.h>
-#include <dvb_v4l.h>
#include <fcntl.h>
#include <fstream.h>
#include <iomanip.h>
#include <iostream.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -1108,31 +1107,43 @@ cMutex cSIProcessor::schedulesMutex;
/** */
cSIProcessor::cSIProcessor(const char *FileName)
{
+ fileName = strdup(FileName);
masterSIProcessor = numSIProcessors == 0; // the first one becomes the 'master'
useTStime = false;
filters = NULL;
- if ((fsvbi = open(FileName, O_RDONLY)) >= 0)
- {
- if (!numSIProcessors++) // the first one creates it
- schedules = new cSchedules;
- filters = (SIP_FILTER *)calloc(MAX_FILTERS, sizeof(SIP_FILTER));
- }
- else
- LOG_ERROR_STR(FileName);
+ if (!numSIProcessors++) // the first one creates it
+ schedules = new cSchedules;
+ filters = (SIP_FILTER *)calloc(MAX_FILTERS, sizeof(SIP_FILTER));
+ SetStatus(true);
+ Start();
}
cSIProcessor::~cSIProcessor()
{
- if (fsvbi >= 0)
+ active = false;
+ Cancel(3);
+ ShutDownFilters();
+ delete filters;
+ if (!--numSIProcessors) // the last one deletes it
+ delete schedules;
+ delete fileName;
+}
+
+void cSIProcessor::SetStatus(bool On)
+{
+ LOCK_THREAD;
+ schedulesMutex.Lock();
+ ShutDownFilters();
+ if (On)
{
- active = false;
- Cancel(3);
- ShutDownFilters();
- delete filters;
- if (!--numSIProcessors) // the last one deletes it
- delete schedules;
- close(fsvbi);
+ AddFilter(0x14, 0x70); // TDT
+ AddFilter(0x14, 0x73); // TOT
+ AddFilter(0x12, 0x4e); // event info, actual TS, present/following
+ AddFilter(0x12, 0x4f); // event info, other TS, present/following
+ AddFilter(0x12, 0x50); // event info, actual TS, schedule
+ AddFilter(0x12, 0x60); // event info, other TS, schedule
}
+ schedulesMutex.Unlock();
}
/** use the vbi device to parse all relevant SI
@@ -1140,19 +1151,10 @@ information and let the classes corresponding
to the tables write their information to the disk */
void cSIProcessor::Action()
{
- if (fsvbi < 0) {
- esyslog(LOG_ERR, "cSIProcessor::Action() called without open file - returning");
- return;
- }
-
dsyslog(LOG_INFO, "EIT processing thread started (pid=%d)%s", getpid(), masterSIProcessor ? " - master" : "");
- unsigned char buf[4096+1]; // max. allowed size for any EIT section (+1 for safety ;-)
- unsigned int seclen;
- unsigned int pid;
time_t lastCleanup = time(NULL);
time_t lastDump = time(NULL);
- struct pollfd pfd;
active = true;
@@ -1187,100 +1189,123 @@ void cSIProcessor::Action()
}
}
- /* wait data become ready from the bitfilter */
- pfd.fd = fsvbi;
- pfd.events = POLLIN;
- if(poll(&pfd, 1, 1000) != 0) /* timeout is 5 secs */
+ // set up pfd structures for all active filter
+ pollfd pfd[MAX_FILTERS];
+ int NumUsedFilters = 0;
+ for (int a = 0; a < MAX_FILTERS ; a++)
{
- // fprintf(stderr, "<data>\n");
- /* read section */
- read(fsvbi, buf, 8);
- seclen = (buf[6] << 8) | buf[7];
- pid = (buf[4] << 8) | buf[5];
- read(fsvbi, buf, seclen);
-
- //dsyslog(LOG_INFO, "Received pid 0x%02x with table ID 0x%02x and length of %04d\n", pid, buf[0], seclen);
+ if (filters[a].inuse)
+ {
+ pfd[NumUsedFilters].fd = filters[a].handle;
+ pfd[NumUsedFilters].events = POLLIN;
+ NumUsedFilters++;
+ }
+ }
- switch (pid)
+ // wait until data becomes ready from the bitfilter
+ if (poll(pfd, NumUsedFilters, 1000) != 0)
+ {
+ for (int a = 0; a < NumUsedFilters ; a++)
{
- case 0x14:
- if (buf[0] == 0x70)
+ if (pfd[a].revents & POLLIN)
+ {
+ /* read section */
+ unsigned char buf[4096+1]; // max. allowed size for any EIT section (+1 for safety ;-)
+ if (read(filters[a].handle, buf, 3) == 3)
{
- if (useTStime)
+ int seclen = ((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF);
+ int pid = filters[a].pid;
+ int n = read(filters[a].handle, buf + 3, seclen);
+ if (n == seclen)
{
- cTDT ctdt((tdt_t *)buf);
- ctdt.SetSystemTime();
+ seclen += 3;
+ //dsyslog(LOG_INFO, "Received pid 0x%02x with table ID 0x%02x and length of %04d\n", pid, buf[0], seclen);
+ switch (pid)
+ {
+ case 0x14:
+ if (buf[0] == 0x70)
+ {
+ if (useTStime)
+ {
+ cTDT ctdt((tdt_t *)buf);
+ ctdt.SetSystemTime();
+ }
+ }
+ /*XXX this comes pretty often:
+ else
+ dsyslog(LOG_INFO, "Time packet was not 0x70 but 0x%02x\n", (int)buf[0]);
+ XXX*/
+ break;
+
+ case 0x12:
+ if (buf[0] != 0x72)
+ {
+ LOCK_THREAD;
+
+ schedulesMutex.Lock();
+ cEIT ceit(buf, seclen, schedules);
+ ceit.ProcessEIT();
+ schedulesMutex.Unlock();
+ }
+ else
+ dsyslog(LOG_INFO, "Received stuffing section in EIT\n");
+ break;
+
+ default:
+ break;
+ }
}
+ else
+ dsyslog(LOG_INFO, "read incomplete section - seclen = %d, n = %d", seclen, n);
}
- /*XXX this comes pretty often:
- else
- dsyslog(LOG_INFO, "Time packet was not 0x70 but 0x%02x\n", (int)buf[0]);
- XXX*/
- break;
-
- case 0x12:
- if (buf[0] != 0x72)
- {
- LOCK_THREAD;
-
- schedulesMutex.Lock();
- cEIT ceit(buf, seclen, schedules);
- ceit.ProcessEIT();
- schedulesMutex.Unlock();
- }
- else
- dsyslog(LOG_INFO, "Received stuffing section in EIT\n");
- break;
-
- default:
- break;
+ }
}
}
- else
- {
- LOCK_THREAD;
-
- //XXX this comes pretty often
- //isyslog(LOG_INFO, "Received timeout from poll, refreshing filters\n");
- RefreshFilters();
- }
-// WakeUp();
}
+
+ dsyslog(LOG_INFO, "EIT processing thread ended (pid=%d)%s", getpid(), masterSIProcessor ? " - master" : "");
}
/** Add a filter with packet identifier pid and
table identifer tid */
bool cSIProcessor::AddFilter(u_char pid, u_char tid)
{
- if (fsvbi < 0)
- return false;
-
- int section = ((int)tid << 8) | 0x00ff;
-
- struct bitfilter filt = {
- pid,
- { section, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
- SECTION_CONTINUOS, 0,
- FILTER_MEM,
- {},
- };
-
- if (ioctl(fsvbi, VIDIOCSBITFILTER, &filt) < 0)
- return false;
+ dmxSctFilterParams sctFilterParams;
+ sctFilterParams.pid = pid;
+ memset(&sctFilterParams.filter.filter, 0, DMX_FILTER_SIZE);
+ memset(&sctFilterParams.filter.mask, 0, DMX_FILTER_SIZE);
+ sctFilterParams.timeout = 0;
+ sctFilterParams.flags = DMX_IMMEDIATE_START;
+ sctFilterParams.filter.filter[0] = tid;
+ sctFilterParams.filter.mask[0] = 0xFF;
for (int a = 0; a < MAX_FILTERS; a++)
{
- if (filters[a].inuse == false)
+ if (!filters[a].inuse)
{
filters[a].pid = pid;
filters[a].tid = tid;
- filters[a].handle = filt.handle;
- filters[a].inuse = true;
- // dsyslog(LOG_INFO, " Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
+ if ((filters[a].handle = open(fileName, O_RDWR | O_NONBLOCK)) >= 0)
+ {
+ if (ioctl(filters[a].handle, DMX_SET_FILTER, &sctFilterParams) >= 0)
+ filters[a].inuse = true;
+ else
+ {
+ esyslog(LOG_ERR, "ERROR: can't set filter");
+ close(filters[a].handle);
+ return false;
+ }
+ // dsyslog(LOG_INFO, " Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
+ }
+ else
+ {
+ esyslog(LOG_ERR, "ERROR: can't open filter handle");
+ return false;
+ }
return true;
}
}
+ esyslog(LOG_ERR, "ERROR: too many filters");
return false;
}
@@ -1294,27 +1319,19 @@ bool cSIProcessor::SetUseTSTime(bool use)
}
/** */
-bool cSIProcessor::ShutDownFilters()
+bool cSIProcessor::ShutDownFilters(void)
{
- if (fsvbi < 0)
- return false;
-
- bool ret = true;
-
for (int a = 0; a < MAX_FILTERS; a++)
{
- if (filters[a].inuse == true)
+ if (filters[a].inuse)
{
- if (ioctl(fsvbi, VIDIOCSSHUTDOWNFILTER, &filters[a].handle) < 0)
- ret = false;
-
+ close(filters[a].handle);
// dsyslog(LOG_INFO, "Deregistered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
-
filters[a].inuse = false;
}
}
- return ret;
+ return true; // there's no real 'boolean' to return here...
}
/** */
@@ -1323,25 +1340,3 @@ bool cSIProcessor::SetCurrentServiceID(unsigned short servid)
LOCK_THREAD;
return schedules ? schedules->SetCurrentServiceID(servid) : false;
}
-
-/** */
-bool cSIProcessor::RefreshFilters()
-{
- if (fsvbi < 0)
- return false;
-
- bool ret = true;
-
- ret = ShutDownFilters();
-
- for (int a = 0; a < MAX_FILTERS; a++)
- {
- if (filters[a].inuse == false && filters[a].pid != 0 && filters[a].tid != 0)
- {
- if (!AddFilter(filters[a].pid, filters[a].tid))
- ret = false;
- }
- }
-
- return ret;
-}
diff --git a/eit.h b/eit.h
index 3d491935..e6bbd13e 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.6 2001/04/01 15:14:12 kls Exp $
+ * $Id: eit.h 1.7 2001/05/25 12:56:53 kls Exp $
***************************************************************************/
#ifndef __EIT_H
@@ -129,16 +129,16 @@ private:
bool masterSIProcessor;
bool useTStime;
SIP_FILTER *filters;
- int fsvbi;
+ char *fileName;
bool active;
- bool RefreshFilters(void);
void Action(void);
+ bool AddFilter(u_char pid, u_char tid);
+ bool ShutDownFilters(void);
public:
cSIProcessor(const char *FileName);
~cSIProcessor();
+ void SetStatus(bool On);
bool SetUseTSTime(bool use);
- bool AddFilter(u_char pid, u_char tid);
- bool ShutDownFilters(void);
bool SetCurrentServiceID(unsigned short servid);
const cSchedules *Schedules(void) { return schedules; }
};
diff --git a/i18n.c b/i18n.c
index b2c321aa..87da256d 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.16 2001/03/31 09:58:14 kls Exp $
+ * $Id: i18n.c 1.17 2001/06/02 09:39:36 kls Exp $
*
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
@@ -452,6 +452,12 @@ const tPhrase Phrases[] = {
"Marca la registrazione",
"Direkte opnamen markeren",
},
+ { "LnbSLOF",
+ "LnbSLOF",
+ "LnbSLOF",
+ "LnbSLOF",
+ "LnbSLOF",
+ },
{ "LnbFrequLo",
"Untere LNB-Frequenz",
"Spodnja LNB-frek.",
@@ -500,6 +506,18 @@ const tPhrase Phrases[] = {
"", // TODO
"", // TODO
},
+ { "DefaultPriority",
+ "Default Priorität",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "DefaultLifetime",
+ "Default Lebensdauer",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
// The days of the week:
{ "MTWTFSS",
"MDMDFSS",
diff --git a/interface.c b/interface.c
index 55cdb0b9..c8d1fb13 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.35 2001/02/18 10:46:13 kls Exp $
+ * $Id: interface.c 1.36 2001/06/02 09:05:54 kls Exp $
*/
#include "interface.h"
@@ -281,9 +281,9 @@ void cInterface::Title(const char *s)
void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
- ClearEol(0, -3, s ? BgColor : clrBackground);
+ ClearEol(0, -2, s ? BgColor : clrBackground);
if (s)
- Write(0, -3, s, FgColor, BgColor);
+ Write(0, -2, s, FgColor, BgColor);
}
void cInterface::Info(const char *s)
diff --git a/menu.c b/menu.c
index 09c180c4..456ac137 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.70 2001/03/18 10:16:56 kls Exp $
+ * $Id: menu.c 1.71 2001/06/02 09:59:54 kls Exp $
*/
#include "menu.h"
@@ -539,9 +539,9 @@ cMenuEditChannel::cMenuEditChannel(int Index)
Add(new cMenuEditChrItem( tr("Polarization"), &data.polarization, "hv"));
Add(new cMenuEditIntItem( tr("Diseqc"), &data.diseqc, 0, 10)); //TODO exact limits???
Add(new cMenuEditIntItem( tr("Srate"), &data.srate, 22000, 27500)); //TODO exact limits - toggle???
- Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 10000)); //TODO exact limits???
- Add(new cMenuEditIntItem( tr("Apid"), &data.apid, 0, 10000)); //TODO exact limits???
- Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 10000)); //TODO exact limits???
+ Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 0xFFFE));
+ Add(new cMenuEditIntItem( tr("Apid"), &data.apid, 0, 0xFFFE));
+ Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0xFFFE));
Add(new cMenuEditIntItem( tr("CA"), &data.ca, 0, cDvbApi::NumDvbApis));
Add(new cMenuEditIntItem( tr("Pnr"), &data.pnr, 0));
}
@@ -589,7 +589,7 @@ void cMenuChannelItem::Set(void)
if (!channel->groupSep)
asprintf(&buffer, "%d\t%s", channel->number, channel->name );
else
- asprintf(&buffer, "\t%s", channel->name);
+ asprintf(&buffer, "\t%s", channel->name);
SetText(buffer, false);
}
@@ -904,13 +904,13 @@ cMenuEditTimer::cMenuEditTimer(int Index, bool New)
if (New)
data.active = 1;
Add(new cMenuEditBoolItem(tr("Active"), &data.active));
- Add(new cMenuEditChanItem(tr("Channel"), &data.channel));
- Add(new cMenuEditDayItem( tr("Day"), &data.day));
- Add(new cMenuEditTimeItem(tr("Start"), &data.start));
- Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
+ Add(new cMenuEditChanItem(tr("Channel"), &data.channel));
+ Add(new cMenuEditDayItem( tr("Day"), &data.day));
+ Add(new cMenuEditTimeItem(tr("Start"), &data.start));
+ Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
//TODO VPS???
- Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, 99));
- Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, 99));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), FileNameChars));
}
}
@@ -963,9 +963,9 @@ void cMenuTimerItem::Set(void)
{
char *buffer = NULL;
asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
- timer->active ? '>' : ' ',
- timer->channel,
- timer->PrintDay(timer->day),
+ timer->active ? '>' : ' ',
+ timer->channel,
+ timer->PrintDay(timer->day),
timer->start / 100,
timer->start % 100,
timer->stop / 100,
@@ -1265,7 +1265,7 @@ eOSState cMenuWhatsOn::Switch(void)
eOSState cMenuWhatsOn::Record(void)
{
cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
- if (item) {
+ if (item) {
cTimer *timer = new cTimer(item->eventInfo);
cTimer *t = Timers.GetTimer(timer);
if (!t) {
@@ -1391,7 +1391,7 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel)
eOSState cMenuSchedule::Record(void)
{
cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
- if (item) {
+ if (item) {
cTimer *timer = new cTimer(item->eventInfo);
cTimer *t = Timers.GetTimer(timer);
if (!t) {
@@ -1608,6 +1608,7 @@ void cMenuSetup::Set(void)
Add(new cMenuEditBoolItem(tr("ShowInfoOnChSwitch"), &data.ShowInfoOnChSwitch));
Add(new cMenuEditBoolItem(tr("MenuScrollPage"), &data.MenuScrollPage));
Add(new cMenuEditBoolItem(tr("MarkInstantRecord"), &data.MarkInstantRecord));
+ Add(new cMenuEditIntItem( tr("LnbSLOF"), &data.LnbSLOF));
Add(new cMenuEditIntItem( tr("LnbFrequLo"), &data.LnbFrequLo));
Add(new cMenuEditIntItem( tr("LnbFrequHi"), &data.LnbFrequHi));
Add(new cMenuEditBoolItem(tr("SetSystemTime"), &data.SetSystemTime));
@@ -1615,7 +1616,9 @@ void cMenuSetup::Set(void)
Add(new cMenuEditIntItem( tr("MarginStop"), &data.MarginStop));
Add(new cMenuEditIntItem( tr("EPGScanTimeout"), &data.EPGScanTimeout));
Add(new cMenuEditIntItem( tr("SVDRPTimeout"), &data.SVDRPTimeout));
- Add(new cMenuEditIntItem( tr("PrimaryLimit"), &data.PrimaryLimit));
+ Add(new cMenuEditIntItem( tr("PrimaryLimit"), &data.PrimaryLimit, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("DefaultPriority"), &data.DefaultPriority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("DefaultLifetime"), &data.DefaultLifetime, 0, MAXLIFETIME));
}
eOSState cMenuSetup::ProcessKey(eKeys Key)
@@ -1945,11 +1948,14 @@ cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), dvbApi->Index() + 1);
}
timer->SetRecording(true);
- Channels.SwitchTo(timer->channel, dvbApi);
- cRecording Recording(timer);
- if (dvbApi->StartRecord(Recording.FileName(), Channels.GetByNumber(timer->channel)->ca, timer->priority))
- Recording.WriteSummary();
- Interface->DisplayRecording(dvbApi->Index(), true);
+ if (Channels.SwitchTo(timer->channel, dvbApi)) {
+ cRecording Recording(timer);
+ if (dvbApi->StartRecord(Recording.FileName(), Channels.GetByNumber(timer->channel)->ca, timer->priority))
+ Recording.WriteSummary();
+ Interface->DisplayRecording(dvbApi->Index(), true);
+ }
+ else
+ cThread::EmergencyExit(true);
}
cRecordControl::~cRecordControl()
@@ -1979,7 +1985,7 @@ bool cRecordControl::Process(void)
{
if (!timer || !timer->Matches())
return false;
- AssertFreeDiskSpace();
+ AssertFreeDiskSpace(timer->priority);
return true;
}
@@ -1993,7 +1999,7 @@ bool cRecordControls::Start(cTimer *Timer)
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
- cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca, Timer ? Timer->priority : DEFAULTPRIORITY);
+ cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority);
if (dvbApi) {
Stop(dvbApi);
for (int i = 0; i < MAXDVBAPI; i++) {
diff --git a/osd.h b/osd.h
index c0fb1a1e..8c86f2b7 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.20 2001/02/03 15:13:59 kls Exp $
+ * $Id: osd.h 1.21 2001/06/02 09:04:19 kls Exp $
*/
#ifndef __OSD_H
@@ -14,7 +14,7 @@
#include "interface.h"
#include "tools.h"
-#define MAXOSDITEMS 9
+#define MAXOSDITEMS (MenuLines - 4)
enum eOSState { osUnknown,
osMenu,
diff --git a/recording.c b/recording.c
index fc0daa57..2802c5b0 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.29 2001/03/31 09:38:30 kls Exp $
+ * $Id: recording.c 1.30 2001/06/02 10:07:01 kls Exp $
*/
#define _GNU_SOURCE
@@ -39,7 +39,7 @@
#define DELETEDLIFETIME 1 // hours after which a deleted recording will be actually removed
#define REMOVECHECKDELTA 3600 // seconds between checks for removing deleted files
#define DISKCHECKDELTA 300 // seconds between checks for free disk space
-#define REMOVELATENCY 10 // seconds to wait until next check after removing a file
+#define REMOVELATENCY 10 // seconds to wait until next check after removing a file
void RemoveDeletedRecordings(void)
{
@@ -66,7 +66,7 @@ void RemoveDeletedRecordings(void)
}
}
-void AssertFreeDiskSpace(void)
+void AssertFreeDiskSpace(int Priority)
{
// With every call to this function we try to actually remove
// a file, or mark a file for removal ("delete" it), so that
@@ -94,10 +94,13 @@ void AssertFreeDiskSpace(void)
cRecording *r = Recordings.First();
cRecording *r0 = NULL;
while (r) {
- if ((time(NULL) - r->start) / SECSINDAY > r->lifetime) {
+ if (r->lifetime == MAXLIFETIME)
+ continue; // recordings with MAXLIFETIME live forever
+ if ((r->lifetime == 0 && Priority > r->priority) || // the recording has guaranteed lifetime and the new recording has higher priority
+ (time(NULL) - r->start) / SECSINDAY > r->lifetime) { // the recording's guaranteed lifetime has expired
if (r0) {
- if (r->priority < r0->priority)
- r0 = r;
+ if (r->priority < r0->priority || (r->priority == r0->priority && r->start < r0->start))
+ r0 = r; // in any case we delete the one with the lowest priority (or the older one in case of equal priorities)
}
else
r0 = r;
@@ -153,7 +156,7 @@ int cResumeFile::Read(void)
bool cResumeFile::Save(int Index)
{
if (fileName) {
- int f = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP);
+ int f = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (f >= 0) {
if (write(f, &Index, sizeof(Index)) != sizeof(Index))
LOG_ERROR_STR(fileName);
@@ -183,7 +186,7 @@ cRecording::cRecording(cTimer *Timer)
for (char *p = name; *p; p++) {
switch (*p) {
case '\n': *p = ' '; break;
- case '/': *p = '-'; break;
+ case '/': *p = '-'; break;
}
}
summary = Timer->summary ? strdup(Timer->summary) : NULL;
@@ -239,7 +242,7 @@ cRecording::cRecording(const char *FileName)
delete summary;
summary = NULL;
}
-
+
}
else
esyslog(LOG_ERR, "can't allocate %d byte of memory for summary file '%s'", size + 1, SummaryFileName);
diff --git a/recording.h b/recording.h
index b561fa2f..059133e7 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.13 2001/02/11 10:45:52 kls Exp $
+ * $Id: recording.h 1.14 2001/06/02 10:00:25 kls Exp $
*/
#ifndef __RECORDING_H
@@ -15,7 +15,7 @@
#include "tools.h"
void RemoveDeletedRecordings(void);
-void AssertFreeDiskSpace(void);
+void AssertFreeDiskSpace(int Priority);
class cResumeFile {
private:
diff --git a/remux.c b/remux.c
index e48296d8..6212ff07 100644
--- a/remux.c
+++ b/remux.c
@@ -4,7 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $
+ * The parts of this code that implement cTS2PES have been taken from
+ * the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
+ * VDR's needs.
+ *
+ * $Id: remux.c 1.2 2001/05/27 10:27:15 kls Exp $
*/
/* The calling interface of the 'cRemux::Process()' function is defined
@@ -62,17 +66,364 @@
*/
#include "remux.h"
+#include "thread.h"
#include "tools.h"
-#if defined(REMUX_NONE)
+// --- cTS2PES ---------------------------------------------------------------
+
+#include <netinet/in.h>
+
+//XXX TODO: these should really be available in some driver header file!
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+
+#define TS_SIZE 188
+#define PAY_START 0x40
+#define PID_MASK_HI 0x1F
+//flags
+#define ADAPT_FIELD 0x20
+//XXX TODO
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (4*MAX_PLENGTH)
+
+#define IPACKS 2048
+
+class cTS2PES {
+private:
+ int size;
+ int found;
+ int count;
+ uint8_t *buf;
+ uint8_t cid;
+ int plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ int mpeg;
+ uint8_t check;
+ int which;
+ bool done;
+ uint8_t *resultBuffer;
+ int *resultCount;
+ static uint8_t headr[];
+ void store(uint8_t *Data, int Count);
+ void reset_ipack(void);
+ void send_ipack(void);
+ void write_ipack(const uint8_t *Data, int Count);
+ void instant_repack(const uint8_t *Buf, int Count);
+public:
+ cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size);
+ ~cTS2PES();
+ void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
+ };
+
+uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
+
+cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size)
+{
+ resultBuffer = ResultBuffer;
+ resultCount = ResultCount;
+ size = Size;
+
+ if (!(buf = new uint8_t[size]))
+ esyslog(LOG_ERR, "Not enough memory for ts_transform");
+
+ reset_ipack();
+}
+
+cTS2PES::~cTS2PES()
+{
+ delete buf;
+}
+
+void cTS2PES::store(uint8_t *Data, int Count)
+{
+ //XXX overflow check???
+ memcpy(resultBuffer + *resultCount, Data, Count);
+ *resultCount += Count;
+}
+
+void cTS2PES::reset_ipack(void)
+{
+ found = 0;
+ cid = 0;
+ plength = 0;
+ flag1 = 0;
+ flag2 = 0;
+ hlength = 0;
+ mpeg = 0;
+ check = 0;
+ which = 0;
+ done = false;
+ count = 0;
+}
+
+void cTS2PES::send_ipack(void)
+{
+ if (count < 10)
+ return;
+ buf[3] = cid;
+ buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
+ buf[5] = (uint8_t)((count - 6) & 0x00FF);
+ store(buf, count);
+
+ switch (mpeg) {
+ case 2:
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ count = 9;
+ break;
+ case 1:
+ buf[6] = 0x0F;
+ count = 7;
+ break;
+ }
+}
+
+void cTS2PES::write_ipack(const uint8_t *Data, int Count)
+{
+ if (count < 6) {
+ memcpy(buf, headr, 3);
+ count = 6;
+ }
+
+ if (count + Count < size) {
+ memcpy(buf + count, Data, Count);
+ count += Count;
+ }
+ else {
+ int rest = size - count;
+ memcpy(buf + count, Data, rest);
+ count += rest;
+ send_ipack();
+ if (Count - rest > 0)
+ write_ipack(Data + rest, Count - rest);
+ }
+}
+
+void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
+{
+ int c = 0;
+
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
+ switch (found ) {
+ case 0:
+ case 1:
+ if (Buf[c] == 0x00)
+ found++;
+ else
+ found = 0;
+ c++;
+ break;
+ case 2:
+ if (Buf[c] == 0x01)
+ found++;
+ else if (Buf[c] != 0)
+ found = 0;
+ c++;
+ break;
+ case 3:
+ cid = 0;
+ switch (Buf[c]) {
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ done = true;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ found++;
+ cid = Buf[c++];
+ break;
+ default:
+ found = 0;
+ break;
+ }
+ break;
+ case 4:
+ if (Count - c > 1) {
+ unsigned short *pl = (unsigned short *)(Buf + c);
+ plength = ntohs(*pl);
+ c += 2;
+ found += 2;
+ }
+ else {
+ plen[0] = Buf[c];
+ found++;
+ return;
+ }
+ break;
+ case 5: {
+ plen[1] = Buf[c++];
+ unsigned short *pl = (unsigned short *)plen;
+ plength = ntohs(*pl);
+ found++;
+ }
+ break;
+ case 6:
+ if (!done) {
+ flag1 = Buf[c++];
+ found++;
+ if ((flag1 & 0xC0) == 0x80 )
+ mpeg = 2;
+ else {
+ esyslog(LOG_INFO, "ERROR: can't record MPEG1!");
+ hlength = 0;
+ which = 0;
+ mpeg = 1;
+ flag2 = 0;
+ }
+ }
+ break;
+ case 7:
+ if (!done && mpeg == 2) {
+ flag2 = Buf[c++];
+ found++;
+ }
+ break;
+ case 8:
+ if (!done && mpeg == 2) {
+ hlength = Buf[c++];
+ found++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!plength)
+ plength = MMAX_PLENGTH - 6;
+
+ if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
+ switch (cid) {
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (mpeg == 2 && found == 9) {
+ write_ipack(&flag1, 1);
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+
+ if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
+ while (c < Count && found < 14) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (c == Count)
+ return;
+ }
-cRemux::cRemux(void)
+ while (c < Count && found < plength + 6) {
+ int l = Count - c;
+ if (l + found > plength + 6)
+ l = plength + 6 - found;
+ write_ipack(Buf + c, l);
+ found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+ if (done) {
+ if (found + Count - c < plength + 6) {
+ found += Count - c;
+ c = Count;
+ }
+ else {
+ c += plength + 6 - found;
+ found = plength + 6;
+ }
+ }
+
+ if (plength && found == plength + 6) {
+ send_ipack();
+ reset_ipack();
+ if (c < Count)
+ instant_repack(Buf + c, Count - c);
+ }
+ }
+ return;
+}
+
+void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
+{
+ if (!Buf)
+ return;
+
+ if (Buf[1] & PAY_START) {
+ if (plength == MMAX_PLENGTH - 6 && found > 6) {
+ plength = found - 6;
+ found = 0;
+ send_ipack();
+ reset_ipack();
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = Buf[4] + 1;
+ if (off + 4 > 187)
+ return;
+ }
+
+ instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
+}
+
+// --- cRemux ----------------------------------------------------------------
+
+cRemux::cRemux(dvb_pid_t VPid, dvb_pid_t APid, bool ExitOnFailure)
{
+ vPid = VPid;
+ aPid = APid;
+ exitOnFailure = ExitOnFailure;
synced = false;
+ skipped = 0;
+ resultCount = resultDelivered = 0;
+ vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
+ aTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
}
cRemux::~cRemux()
{
+ delete vTS2PES;
+ delete aTS2PES;
+}
+
+int cRemux::GetPid(const uchar *Data)
+{
+ return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
}
int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
@@ -104,70 +455,130 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
return -1;
}
-const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType)
+const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType)
{
- int Skip = 0;
+ uchar dummyPictureType;
+ if (!PictureType)
+ PictureType = &dummyPictureType;
- PictureType = NO_PICTURE;
+/*XXX
+ // test recording the raw TS:
+ Result = Count;
+ *PictureType = I_FRAME;
+ return Data;
+XXX*/
- if (Count >= MINVIDEODATA) {
- for (int i = 0; i < Count; i++) {
- if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
- switch (Data[i + 3]) {
+ // Remove any previously delivered data from the result buffer:
+
+ if (resultDelivered) {
+ if (resultDelivered < resultCount)
+ memmove(resultBuffer, resultBuffer + resultDelivered, resultCount - resultDelivered);
+ resultCount -= resultDelivered;
+ resultDelivered = 0;
+ }
+
+ // Convert incoming TS data into multiplexed PES:
+
+ int used = 0;
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ dvb_pid_t pid = GetPid(Data + i + 1);
+ if (Data[i + 3] & 0x10) { // got payload
+ if (pid == vPid)
+ vTS2PES->ts_to_pes(Data + i);
+ else if (pid == aPid)
+ aTS2PES->ts_to_pes(Data + i);
+ }
+ used += TS_SIZE;
+ if (resultCount > (int)sizeof(resultBuffer) / 2)
+ break;
+ }
+ Count = used;
+
+/*XXX
+ // test recording without determining the real frame borders:
+ *PictureType = I_FRAME;
+ Result = resultDelivered = resultCount;
+ return Result ? resultBuffer : NULL;
+XXX*/
+
+ // Check if we're getting anywhere here:
+
+ if (!synced && skipped >= 0) {
+ if (skipped > 1024*1024) {
+ esyslog(LOG_ERR, "ERROR: no useful data seen within %d byte of video stream", skipped);
+ skipped = -1;
+ if (exitOnFailure)
+ cThread::EmergencyExit(true);
+ }
+ else
+ skipped += Count;
+ }
+
+ // Check for frame borders:
+
+ *PictureType = NO_PICTURE;
+
+ if (resultCount >= MINVIDEODATA) {
+ for (int i = 0; i < resultCount; i++) {
+ if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
+ switch (resultBuffer[i + 3]) {
case SC_VIDEO:
{
uchar pt = NO_PICTURE;
- int l = ScanVideoPacket(Data, Count, i, pt);
- if (l < 0) {
- if (Skip < Count)
- Count = Skip;
+ int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
+ if (l < 0)
return NULL; // 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 (pt == I_FRAME) {
- Skip = i;
- synced = true;
- }
- else {
- i += l;
- Skip = i;
- break;
- }
+ else if (!synced) {
+ if (pt == I_FRAME) {
+ resultDelivered = i; // will drop everything before this position
+ synced = true;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
}
- if (synced)
- PictureType = pt;
- }
- else {
- Count = i;
- Result = i - Skip;
- return Data + Skip;
}
}
- else if (!synced) {
- i += l;
- Skip = i;
- break;
+ if (synced) {
+ *PictureType = pt;
+ Result = l;
+ const uchar *p = resultBuffer + resultDelivered;
+ resultDelivered += l;
+ return p;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
}
- i += l - 1; // -1 to compensate for i++ in the loop!
}
break;
case SC_AUDIO:
- i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop!
+ {
+ int l = GetPacketLength(resultBuffer, resultCount, i);
+ if (l < 0)
+ return NULL; // no useful data found, wait for more
+ if (synced) {
+ Result = l;
+ const uchar *p = resultBuffer + resultDelivered;
+ resultDelivered += l;
+ return p;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
+ }
+ }
break;
}
}
}
}
- if (Skip < Count)
- Count = Skip;
return NULL; // no useful data found, wait for more
}
-#elif defined(REMUX_TEST)
-#endif
-
diff --git a/remux.h b/remux.h
index bceb676b..6705e341 100644
--- a/remux.h
+++ b/remux.h
@@ -4,16 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.h 1.1 2001/03/31 08:42:27 kls Exp $
+ * $Id: remux.h 1.2 2001/05/26 11:50:52 kls Exp $
*/
#ifndef __REMUX_H
#define __REMUX_H
-// There are various experiments with different types of remultiplexers
-// going on at the moment. Select the remultiplexer here:
-#define REMUX_NONE 1
-//#define REMUX_TEST 1
+#include <time.h> //XXX FIXME: DVB/ost/include/ost/dmx.h should include <time.h> itself!!!
+#include <ost/dmx.h>
// Picture types:
#define NO_PICTURE 0
@@ -21,6 +19,7 @@
#define P_FRAME 2
#define B_FRAME 3
+//XXX -> remux.c???
// Start codes:
#define SC_PICTURE 0x00 // "picture header"
#define SC_SEQU 0xB3 // "sequence header"
@@ -30,22 +29,28 @@
#define SC_VIDEO 0xE0
// The minimum amount of video data necessary to identify frames:
-#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
+#define MINVIDEODATA (16*1024) // just a safe guess (max. size of any frame block, plus some safety)
typedef unsigned char uchar;
+class cTS2PES;
class cRemux {
private:
-#if defined(REMUX_NONE)
+ bool exitOnFailure;
bool synced;
+ int skipped;
+ dvb_pid_t vPid, aPid;
+ cTS2PES *vTS2PES, *aTS2PES;
+ uchar resultBuffer[MINVIDEODATA * 4];//XXX
+ int resultCount;
+ int resultDelivered;
+ int GetPid(const uchar *Data);
int GetPacketLength(const uchar *Data, int Count, int Offset);
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
-#elif defined(REMUX_TEST)
-#endif
public:
- cRemux(void);
+ cRemux(dvb_pid_t VPid, dvb_pid_t APid, bool ExitOnFailure = false);
~cRemux();
- const uchar *Process(const uchar *Data, int &Count, int &Result, uchar &PictureType);
+ const uchar *Process(const uchar *Data, int &Count, int &Result, uchar *PictureType = NULL);
};
#endif // __REMUX_H
diff --git a/ringbuffer.c b/ringbuffer.c
index 3b2f5cac..8511a1c2 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -7,7 +7,7 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
- * $Id: ringbuffer.c 1.1 2001/03/18 16:47:00 kls Exp $
+ * $Id: ringbuffer.c 1.2 2001/05/20 11:58:08 kls Exp $
*/
#include "ringbuffer.h"
@@ -37,9 +37,10 @@ public:
// --- cRingBuffer ------------------------------------------------------------
-cRingBuffer::cRingBuffer(int Size)
+cRingBuffer::cRingBuffer(int Size, bool Statistics)
{
size = Size;
+ statistics = Statistics;
buffer = NULL;
inputThread = NULL;
outputThread = NULL;
@@ -60,7 +61,17 @@ cRingBuffer::~cRingBuffer()
delete inputThread;
delete outputThread;
delete buffer;
- dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
+ if (statistics)
+ dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
+}
+
+int cRingBuffer::Available(void)
+{
+ mutex.Lock();
+ int diff = head - tail;
+ int cont = (diff >= 0) ? diff : size + diff;
+ mutex.Unlock();
+ return cont;
}
void cRingBuffer::Clear(void)
@@ -78,17 +89,17 @@ int cRingBuffer::Put(const uchar *Data, int Count)
int diff = tail - head;
mutex.Unlock();
int free = (diff > 0) ? diff - 1 : size + diff - 1;
- // Statistics:
- int fill = size - free - 1 + Count;
- if (fill >= size)
- fill = size - 1;
- if (fill > maxFill) {
- maxFill = fill;
- int percent = maxFill * 100 / (size - 1);
- if (percent > 75)
- dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
+ if (statistics) {
+ int fill = size - free - 1 + Count;
+ if (fill >= size)
+ fill = size - 1;
+ if (fill > maxFill) {
+ maxFill = fill;
+ int percent = maxFill * 100 / (size - 1);
+ if (percent > 75)
+ dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
+ }
}
- //
if (free <= 0)
return 0;
if (free < Count)
diff --git a/ringbuffer.h b/ringbuffer.h
index 605f553f..49be769c 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ringbuffer.h 1.1 2001/03/18 16:47:00 kls Exp $
+ * $Id: ringbuffer.h 1.2 2001/05/20 11:56:44 kls Exp $
*/
#ifndef __RINGBUFFER_H
@@ -28,7 +28,11 @@ private:
uchar *buffer;
int maxFill;
bool busy;
+ bool statistics;
protected:
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+ int Available(void);
bool Busy(void) { return busy; }
void Clear(void);
// Immediately clears the ring buffer.
@@ -45,7 +49,7 @@ protected:
// Runs as a separate thread and shall continuously call Get() to
// retrieve data from the ring buffer and write it to a destination.
public:
- cRingBuffer(int Size);
+ cRingBuffer(int Size, bool Statistics = false);
virtual ~cRingBuffer();
bool Start(void);
bool Active(void);
diff --git a/runvdr b/runvdr
index 5ec34585..158cba81 100755
--- a/runvdr
+++ b/runvdr
@@ -1,18 +1,40 @@
#!/bin/sh
+# runvdr: Loads the DVB driver and runs VDR
+#
+# If VDR exits abnormally, the driver will be reloaded
+# and VDR restarted.
+#
+# Set the environment variable VDRUSR to the user id you
+# want VDR to run with. If VDRUSR is not set, VDR will run
+# as 'root', which is not necessarily advisable.
+#
+# Since this script loads the DVB driver, it must be started
+# as user 'root'.
+#
+# Any command line parameters will be passed on to the
+# actual 'vdr' program.
+#
+# See the main source file 'vdr.c' for copyright information and
+# how to reach the author.
+#
+# $Id: runvdr 1.5 2001/06/01 16:23:29 kls Exp $
+
DVBDIR="../DVB/driver"
VDRPRG="./vdr"
-VDRCMD="$VDRPRG -w 60"
+VDRCMD="$VDRPRG -w 60 $*"
KILLPROC="/sbin/killproc -TERM"
+(cd $DVBDIR; make insmod)
+
while (true) do
-# (cd $DVBDIR; make reload)
-# sleep 3
- $VDRCMD
- if test $? -ne 1; then exit; fi
+ su -c "$VDRCMD" $VDRUSR
+ if test $? -eq 0; then exit; fi
date
echo "restarting VDR"
$KILLPROC $VDRPRG
sleep 10
+ (cd $DVBDIR; make reload)
+ date
done
diff --git a/thread.c b/thread.c
index 363190d8..aa6ed64c 100644
--- a/thread.c
+++ b/thread.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 1.7 2000/12/24 12:27:21 kls Exp $
+ * $Id: thread.c 1.8 2001/05/25 09:37:00 kls Exp $
*/
#include "thread.h"
@@ -14,12 +14,41 @@
#include <unistd.h>
#include "tools.h"
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+ lockingPid = 0;
+ locked = 0;
+ pthread_mutex_init(&mutex, NULL);
+}
+
+cMutex::~cMutex()
+{
+ pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+ if (getpid() != lockingPid || !locked)
+ pthread_mutex_lock(&mutex);
+ lockingPid = getpid();
+ locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+ pthread_mutex_unlock(&mutex);
+}
+
// --- cThread ---------------------------------------------------------------
// The signal handler is necessary to be able to use SIGIO to wake up any
// pending 'select()' call.
bool cThread::signalHandlerInstalled = false;
+bool cThread::emergencyExitRequested = false;
cThread::cThread(void)
{
@@ -110,6 +139,14 @@ void cThread::WakeUp(void)
kill(parentPid, SIGIO); // makes any waiting 'select()' call return immediately
}
+bool cThread::EmergencyExit(bool Request)
+{
+ if (!Request)
+ return emergencyExitRequested;
+ esyslog(LOG_ERR, "initiating emergency exit");
+ return emergencyExitRequested = true; // yes, it's an assignment, not a comparison!
+}
+
// --- cThreadLock -----------------------------------------------------------
cThreadLock::cThreadLock(cThread *Thread)
diff --git a/thread.h b/thread.h
index 6aaee0b9..8885fd46 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.4 2000/12/03 11:18:37 kls Exp $
+ * $Id: thread.h 1.5 2001/05/25 09:36:27 kls Exp $
*/
#ifndef __THREAD_H
@@ -16,11 +16,13 @@
class cMutex {
private:
pthread_mutex_t mutex;
+ pid_t lockingPid;
+ int locked;
public:
- cMutex(void) { pthread_mutex_init(&mutex, NULL); }
- ~cMutex() { pthread_mutex_destroy(&mutex); }
- void Lock(void) { pthread_mutex_lock(&mutex); }
- void Unlock(void) { pthread_mutex_unlock(&mutex); }
+ cMutex(void);
+ ~cMutex();
+ void Lock(void);
+ void Unlock(void);
};
class cThread {
@@ -31,6 +33,7 @@ private:
pid_t parentPid, threadPid, lockingPid;
int locked;
bool running;
+ static bool emergencyExitRequested;
static bool signalHandlerInstalled;
static void SignalHandler(int signum);
static void *StartThread(cThread *Thread);
@@ -45,6 +48,7 @@ public:
virtual ~cThread();
bool Start(void);
bool Active(void);
+ static bool EmergencyExit(bool Request = false);
};
// cThreadLock can be used to easily set a lock in a thread and make absolutely
diff --git a/tools.c b/tools.c
index 5a3730d3..de1a52c3 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.33 2001/04/22 10:31:29 kls Exp $
+ * $Id: tools.c 1.34 2001/05/20 08:30:54 kls Exp $
*/
#define _GNU_SOURCE
@@ -411,6 +411,22 @@ bool cFile::FileReady(int FileDes, int TimeoutMs)
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
}
+bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
+{
+#ifdef DEBUG_OSD
+ refresh();
+#endif
+ fd_set set;
+ struct timeval timeout;
+ FD_ZERO(&set);
+ FD_SET(FileDes, &set);
+ if (TimeoutMs < 100)
+ TimeoutMs = 100;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = TimeoutMs * 1000;
+ return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
+}
+
// --- cSafeFile -------------------------------------------------------------
cSafeFile::cSafeFile(const char *FileName)
diff --git a/tools.h b/tools.h
index a9d61218..b4a0cb67 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.25 2001/04/01 14:13:42 kls Exp $
+ * $Id: tools.h 1.26 2001/05/20 08:29:45 kls Exp $
*/
#ifndef __TOOLS_H
@@ -67,6 +67,7 @@ public:
bool Ready(bool Wait = true);
static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
static bool FileReady(int FileDes, int TimeoutMs = 1000);
+ static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
};
class cSafeFile {
diff --git a/vdr.c b/vdr.c
index 8ed209a6..88dc2a1d 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.56 2001/04/01 11:16:54 kls Exp $
+ * $Id: vdr.c 1.57 2001/05/25 09:31:09 kls Exp $
*/
#include <getopt.h>
@@ -267,6 +267,11 @@ int main(int argc, char *argv[])
}
while (!Interrupted) {
+ // Handle emergency exits:
+ if (cThread::EmergencyExit()) {
+ esyslog(LOG_ERR, "emergency exit requested - shutting down");
+ break;
+ }
// Restart the Watchdog timer:
if (WatchdogTimeout > 0) {
int LatencyTime = WatchdogTimeout - alarm(WatchdogTimeout);
@@ -388,7 +393,8 @@ int main(int argc, char *argv[])
else
LastActivity = time(NULL);
}
- isyslog(LOG_INFO, "caught signal %d", Interrupted);
+ if (Interrupted)
+ isyslog(LOG_INFO, "caught signal %d", Interrupted);
Setup.CurrentChannel = cDvbApi::CurrentChannel();
Setup.Save();
cVideoCutter::Stop();
@@ -401,5 +407,9 @@ int main(int argc, char *argv[])
isyslog(LOG_INFO, "exiting");
if (SysLogLevel > 0)
closelog();
+ if (cThread::EmergencyExit()) {
+ esyslog(LOG_ERR, "emergency exit!");
+ return 1;
+ }
return 0;
}
diff --git a/videodir.c b/videodir.c
index 2824d04e..6f35a3dc 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.4 2001/02/11 13:48:30 kls Exp $
+ * $Id: videodir.c 1.5 2001/05/01 09:48:57 kls Exp $
*/
#include "videodir.h"
@@ -137,7 +137,7 @@ int OpenVideoFile(const char *FileName, int Flags)
}
}
}
- int Result = open(ActualFileName, Flags, S_IRUSR | S_IWUSR | S_IRGRP);
+ int Result = open(ActualFileName, Flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (ActualFileName != FileName)
delete ActualFileName;
return Result;