diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 8a67f37c..8cb99a71 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -42,6 +42,7 @@ Bastian Guse Matthias Schniedermeyer for implementing the 'MarkInstantRecord' setup option for his "schnitt" tools + for his "master-timer" tool Miha Setina for translating the OSD texts to the Slovenian language @@ -54,6 +55,7 @@ Deti Fliegl Dave Chapman for implementing support for the teletext PID + for his great support in switching to the NAPI Hans-Peter Raschke for his support in adapting VDR to DVB-C @@ -70,3 +72,6 @@ Arnold Niessen Jürgen Sauer for implementing the -t option to set the controlling terminal + +Benjamin Reichardt + 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 { 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 #include #include extern "C" { +#define HAVE_BOOLEAN #include } #include @@ -20,20 +22,29 @@ extern "C" { #include #include #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); -protected: - virtual void Action(void); -public: - cReplayBuffer(int *OutFile, const char *FileName); - virtual ~cReplayBuffer(); - virtual int Read(int Max = -1); - virtual int Write(int Max = -1); - void SetMode(eReplayMode Mode); + 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); - void Pause(void) { SetCmd(rcPause); } - void Play(void) { SetCmd(rcPlay); } - void Forward(void) { SetCmd(rcForward); } - void Backward(void) { SetCmd(rcBackward); } +protected: + virtual void Input(void); + virtual void Output(void); +public: + cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName); + virtual ~cReplayBuffer(); + 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); - - // Create directories if necessary: - - if (!MakeDirs(FileName, true)) - return false; - - // Create recording buffer: - - recordBuffer = new cRecordBuffer(&videoDev, FileName); - - if (recordBuffer) { - ca = Ca; - priority = Priority; - return true; - } - else - esyslog(LOG_ERR, "ERROR: can't allocate recording buffer"); + if (!FileName) { + esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)"); + return false; } + isyslog(LOG_INFO, "record %s", FileName); + + // Create directories if necessary: + + if (!MakeDirs(FileName, true)) + return false; + + // Create recording buffer: + + 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"); + 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 #endif +#include // 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 here! +#include +#include +#include +#include +#include +#include +#include #include -#include #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 #endif +#include #include -#include #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 -#include -#include #include #include #include #include +#include #include #include #include @@ -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, "\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); - - switch (pid) + if (filters[a].inuse) { - 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; + pfd[NumUsedFilters].fd = filters[a].handle; + pfd[NumUsedFilters].events = POLLIN; + NumUsedFilters++; } } - else - { - LOCK_THREAD; - //XXX this comes pretty often - //isyslog(LOG_INFO, "Received timeout from poll, refreshing filters\n"); - RefreshFilters(); + // wait until data becomes ready from the bitfilter + if (poll(pfd, NumUsedFilters, 1000) != 0) + { + for (int a = 0; a < NumUsedFilters ; a++) + { + 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) + { + 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) + { + 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); + } + } + } } -// 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 * Italian translations provided by Alberto Carraro @@ -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 --------------------------------------------------------------- -cRemux::cRemux(void) +#include + +//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; + } + + 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 //XXX FIXME: DVB/ost/include/ost/dmx.h should include itself!!! +#include // 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 #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 @@ -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;